Android hid 数据传输(device 端 )
最近一直在处理hid 数据需求,简而言之就是两台设备直接可以通过usb 线互相传递数据。
项目架构
为什么Device 端要采用HID(人机接口设备)的方式发送和接收数据呢?
主要是速度快,举个例子,就是鼠标移动,屏幕可以及时响应,用的也是这种协议。
因为Host端底层我们控制不了,不能保证都支持Hid 协议,所以Host 端采用跨平台方案,libusb 协议。
Liusb 网上介绍的很多啦,可以运行在各个平台,windows ,android.linux,是一种理想中的跨平台数据传输方案。
项目主要功能
1,sensor数据传输
2,TP 数据传输(按键传输同理)
项目主要技术点
1,TP数据监听
2,sensor数据监听
3,hid 数据传输丢失问题
4,HID 节点生成监听
5,开机启动native 服务处理数据
6,selinux 权限问题
技术实现
代码结构
TP数据监听
驱动所有的滑动 和 按键 上报都是通过节点的方式,不同平台节点有所差异,需要和驱动沟通。我试验的平台节点是:
#define INPUT_KEY_NODE "/dev/input/event1"
#define INPUT_TP_NODE "/dev/input/event3"
所以监听这两个就行了,我们这里采用的是poll 的方式,有数据的时候会回调,没有的话会阻塞
主要代码
void HidReceiver::listenThread()
{struct pollfd fds[IN_FILES];fds[0].events = POLLIN;fds[1].events = POLLIN;fds[2].events = POLLIN;int result;char buff[512];
// sleep(1);LOGD("hid open");hid_fd = open(DEVICE_NODE, O_RDWR | O_NONBLOCK);int key_fd = open(INPUT_KEY_NODE, O_RDWR | O_NONBLOCK);int tp_fd = open(INPUT_TP_NODE, O_RDWR | O_NONBLOCK);LOGD("nod %d,%d,%d",hid_fd,key_fd,tp_fd);fds[0].fd = hid_fd;fds[1].fd = key_fd;fds[2].fd = tp_fd;unsigned char data[sizeof(input_event)];input_event dev_data;while(1){result = poll(fds, IN_FILES, -1);if (result == 0) {LOGD("Poll timeout");} else if(result > 0){if ((fds[0].revents & POLLIN)){ int size = read(fds[0].fd, buff, sizeof(buff));if(size > 0){process_event(buff);}}if ((fds[1].revents & POLLIN)){ int size = read (fds[1].fd, (unsigned char*)data, sizeof(input_event));LOGD("size:%d", size);memcpy(&dev_data, data, sizeof(input_event));LOGD("Keyevent size:%d", dev_data.type);// sensordata.sensorType = 0x104;// sensordata.ievent = dev_data;// process_event(sensordata);}if ((fds[2].revents & POLLIN)){ int size = read (fds[2].fd, (unsigned char*)data, sizeof(input_event));memcpy(&dev_data, data, sizeof(input_event));#if 0LOGD("abs size:%d", dev_data.type);if (dev_data.type == EV_ABS){if (dev_data.code == ABS_MT_POSITION_X){x = dev_data.value;if (x < 0) x = 0;LOGD("rel X:%d", dev_data.value);}else if(dev_data.code == ABS_MT_POSITION_Y){LOGD("\nx=%d,y=%d,dev_data.code=%d\n", x, y,dev_data.code);y = dev_data.value;if (y < 0) y = 0;sensordata.sensorType = 0x104;sensordata.abs_x = x;sensordata.abs_y = y;process_event(sensordata);x = 0;y = 0;}}#endifif (dev_data.type == EV_KEY){LOGD("EV_KEY %d",dev_data.code);switch(dev_data.code){case KEY_KP5://双击case KEY_DASHBOARD://单机case KEY_F17://左滑case KEY_ISO://右滑case KEY_F16://上滑case KEY_CONFIG://下滑sensordata.sensorType = 0x104;sensordata.type = dev_data.type;sensordata.code = dev_data.code;sensordata.value = dev_data.value;sensordata.priority = 3;process_event(sensordata);break; }}}} }
}
代码中DEVICE_NODE 用于监听hid 数据的,这个后面说。
sensor数据监听
void SensorTransfer::listenThread()
{int64_t stamp;LOGD("listenThread");while (m_bListening){ASensorEvent event;while (ASensorEventQueue_getEvents(m_pEvtQue, &event, 1) > 0){stamp = event.timestamp;switch (event.type){// case ASENSOR_TYPE_GYROSCOPE:// printf("GYROSCOPE:(%llu, %f,%f,%f)\n", (unsigned long long)stamp, event.data[0], event.data[1], event.data[2]);// break;case ASENSOR_TYPE_ACCELEROMETER:// printf("ACCELEROMETER: (%llu, %f,%f,%f)\n", (unsigned long long)stamp, event.data[0], event.data[1], event.data[2]);sensordata.stamp = stamp;sensordata.sensorType = 0x100;sensordata.xvalue = event.data[0];sensordata.yvalue = event.data[1];sensordata.zvalue = event.data[2];saveSensorData(sensordata);sensordata.priority = 1;break;case ASENSOR_TYPE_GRAVITY:// printf("GRAVITY: (%llu, %f,%f,%f)\n", (unsigned long long)stamp, event.data[0], event.data[1], event.data[2]);sensordata.stamp = stamp;sensordata.sensorType = 0x101;sensordata.xvalue = event.data[0];sensordata.yvalue = event.data[1];sensordata.zvalue = event.data[2];sensordata.priority = 1;saveSensorData(sensordata);break;case ASENSOR_TYPE_PROXIMITY:sensordata.stamp = stamp;sensordata.sensorType = 0x102;sensordata.lightvalue = event.data[0];sensordata.priority = 1;saveSensorData(sensordata);break;default:break;}}usleep(1000);}
}
这个参考的一个博主的方案,主要是通过循环读取native sensor 数据。
监听HID节点删除添加
void HidReceiver::nodWatch(){int length, i = 0;int fd;int wd;char buffer[BUF_LEN];fd = inotify_init();if (fd < 0) {LOGD("inotify_init");}wd = inotify_add_watch(fd, DEV_NODE, IN_CREATE );if (wd < 0) {LOGD("inotify_add_watch");}LOGD("Monitoring directory: %s", DEV_NODE);bool monitor = true;while (monitor) {LOGD("start monitor");length = read(fd, buffer, BUF_LEN); if (length < 0) {LOGD("read");} i = 0;LOGD("read %d",length);while (i < length) {struct inotify_event *event = (struct inotify_event *) &buffer[i];LOGD("inotify_event %d",event->len);if (event->len) {LOGD("ievent->mask %d",event->mask);if (event->mask & IN_CREATE) {LOGD("Created: %s", event->name);if(strcmp(event->name,"hidg0") == 0){LOGD("Created: hidg0");monitor = false;startListen();inotify_rm_watch(fd, wd);return;}} else if (event->mask & IN_DELETE) {LOGD("Deleted: %s", event->name);} else if (event->mask & IN_MODIFY) {LOGD("Modified: %s", event->name);} else if (event->mask & IN_MOVED_FROM) {LOGD("Moved from: %s", event->name);} else if (event->mask & IN_MOVED_TO) {LOGD("Moved to: %s", event->name);}}i += EVENT_SIZE + event->len;}}
}
Hid 数据传输和数据丢失问题
hid 数据怎么传,其实很简单,写节点就可以了,但是数据量太大的时候,会出现写节点失败,同时,按键或者TP 等数据,也会丢失,sensor 数据丢失感知倒不是很大,但是按键和触摸这些传输失败,Host端就无法响应,体验会很差。
目前采用的方案是
Bufferqueue + 延时 解决HID 数据丢失的问题(生产者消费者模式)
priority_queue 解决用户主动操作的数据优先级问题,主要是TP 和 按键,保证优先响应
主要代码:
消费者
// 消费者线程,读取队列中的数据并发送
void SensorTransfer::consumer() {while (true) {std::unique_lock<std::mutex> lock(queueMutex);dataCondition.wait(lock, [this] { return !bufferQueue.empty(); });// if (!bufferQueue.empty())// break; // 程序结束if(!bufferQueue.empty()){sensor_data data = bufferQueue.top();// 发送数据到 HID 设备int written = write(hid_fd,&data,sizeof(struct sensor_data));if(written >=0){bufferQueue.pop();}else{//LOGD("rewrite data result fail");std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 控制发送速率}if(data.sensorType == 259){LOGD("consumer sn");std::this_thread::sleep_for(std::chrono::milliseconds(5)); }if(data.sensorType == 260){LOGD("consumer KEY");std::this_thread::sleep_for(std::chrono::milliseconds(5)); }}else{LOGD("consumer 等待");}}
}
生产者
int SensorTransfer::saveSensorData(sensor_data data) {//std::lock_guard<std::mutex> lock(queueMutex);if (bufferQueue.size() < MAX_QUEUE) { // 限制队列最大长度bufferQueue.push(data);sensor_data topdata = bufferQueue.top();if(topdata.sensorType != 259&&topdata.sensorType != 260){dataCondition.notify_one(); // 通知消费者线程}}else{LOGD("buffer is full");}return 0;
}
selinux 添加
这个是老一套了,之前也写过文章,可以参考这里直接贴上主要权限
新增hidtransfer.te
type hidtransfer, domain,mlstrustedsubject;
typeattribute hidtransfer coredomain;
type hidtransfer_exec, system_file_type, exec_type, file_type;
binder_use(hidtransfer)
init_daemon_domain(hidtransfer)allow hidtransfer system_server:unix_stream_socket {read write};
allow hidtransfer tty_device:chr_file {write read getattr};
allow hidtransfer hid_device:chr_file { read getattr open ioctl write};
allow hidtransfer device:dir read;
allow hidtransfer system_server:binder call;
allow hidtransfer tty_device:chr_file ioctl;
allow hidtransfer serialno_prop:file { map getattr open read};
allow hidtransfer permission_service:service_manager find;
allow hidtransfer sensorservice_service:service_manager find;
allow hidtransfer input_device:chr_file { read write open };
allow hidtransfer input_device:dir { search };
allow hidtransfer device:dir watch;
allow hidtransfer system_server:fd use;
file_contexts
/system/bin/hidtransfer u:object_r:hidtransfer_exec:s0
/dev/hidg0 u:object_r:hid_device:s0
device.te
type hid_device,dev_type;
参考:
1.Android Native Sensor(C++)实例_sensor hal 陀螺仪读取数据实现代码-CSDN博客
相关文章:

Android hid 数据传输(device 端 )
最近一直在处理hid 数据需求,简而言之就是两台设备直接可以通过usb 线互相传递数据。 项目架构 为什么Device 端要采用HID(人机接口设备)的方式发送和接收数据呢? 主要是速度快,举个例子,就是鼠标移动&am…...

MaxEnt模型在物种分布模拟中如何应用?R语言+MaxEnt模型融合物种分布模拟、参数优化方法、结果分析制图与论文写作
目录 第一章 以问题导入的方式,深入掌握原理基础 第二章 常用数据检索与R语言自动化下载及可视化方法 第三章 R语言数据清洗与特征变量筛选 第四章 基于ArcGIS、R数据处理与进阶 第五章 基于Maxent的物种分布建模与预测 第六章 基于R语言的模型参数优化 第七…...
第3章:文本样式 --[CSS零基础入门]
CSS(层叠样式表)允许你以多种方式定制文本的外观。以下是一些常用的文本和字体相关的CSS属性: 1.字体 字体系列 当然,下面是两个使用不同字体系列的CSS示例。每个示例都展示了如何指定一个字体系列,并提供备用字体以确保在用户的系统中找不到首选字体时仍有合适的字体可…...
mysql 架构详解
MySQL的架构可以自顶向下分为多个层次,每个层次都有其特定的功能和组件。以下是对MySQL架构的详细解析: 一、整体架构概述 MySQL的整体架构包括MySQL Connectors(连接器)、MySQL Shell、连接层、服务层、存储引擎层和文件系统层…...
无代码探索AI大模型:腾讯云函数计算的卓越实践
在数字化转型的浪潮中,人工智能(AI)技术已经成为企业提升竞争力的关键。然而,对于许多业务人员来说,技术门槛高、开发周期长等问题限制了他们快速探索和应用AI大模型的能力。同时,对于缺乏GPU资源的开发者来…...
解决Ubuntu在VMware关机时,老是一个光标在那里闪动几分钟,才能关机的问题
把强制关机的等待时间缩短即可。 编辑 /etc/systemd/system.conf 文件 sudo gedit /etc/systemd/system.conf把 #DefaultTimeoutStartSec90s修改为 #DefaultTimeoutStartSec10s改完之后重载一下: sudo systemctl daemon-reload然后关机再试一下,这样…...

word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图
目录 问题解决问题poi-tl介绍 功能实现引入依赖功能介绍 功能实例饼图模版代码效果图 雷达图(模版同饼图)代码效果图 柱状图(模版同饼图)代码效果图 附加CustomCharts 工具类CustomChartSingleSeriesRenderData 数据对象CustomCha…...
常见网络钓鱼类型
网络钓鱼是一种网络攻击,是指具有恶意动机的攻击者伪装欺骗人们并收集用户名或密码等敏感信息的一系列行为。由于网络钓鱼涉及心理操纵并依赖于人为失误(而不是硬件或软件漏洞),因此被认定为是一种社会工程攻击。 1. 普通网络钓鱼(群攻&…...

数字图像处理考研考点(持续更新)
一、数字图像基本概念 1、人眼视觉特性 (1)眼睛上有两类光感受器:锥状体和杆状体 锥状体(锥细胞):约 700 万个,对颜色高度敏感,每个锥状体都连接到神经末梢,人可以充分地分辨图像细节。锥细胞…...

Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba介绍 在当今的软件开发领域,微服务架构因其灵活性、可扩展性和独立性等优势而备受青睐。Spring Cloud Alibaba 作为一款强大的一站式微服务解决方案,为开发者提供了丰富的工具和组件,帮助他们轻松构建和管理复杂的微服务…...
ubuntu16.04部署dify教程
文章目录 1、克隆 Dify 源代码至本地环境2、加速Dify镜像文件下载3、启动 Dify4、访问 Dify5、更新 Dify6、常见问题及解决方案(1)容器restarting(2)日志文件上限(3)重置管理员密码(4࿰…...

JavaWeb文件上传
文件上传总览 文件上传主要是指将本地文件(包括但不限于图片、视频、音频等)上传到服务器,提供其他用户浏览或下载的过程。在日常生活中,我们在很多情况下都需要使用文件上传功能,比如:发微博、发朋友圈等…...

软件工程——期末复习(3)
一、题目类(老师重点提到过的题目) 1、高可靠性是否意味着高可用性?试举例证明自己的观点? 答:高可靠性不意味着高可用性 可靠性说明系统已经准备好,马上可以使用;可用性是系统可以无故障的持续运行,是一…...
apache的BeanUtils的Converter被相互污染覆盖问题
问题描述 apache的BeanUtils工具集中用来把map对象转换为java对象的BeanUtils#populate方法会因为单例的原因其转换器Converter被相互污染覆盖问题 maven依赖 <dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</…...

TCP的“可靠性”(上)
目录 TCP的“可靠性”(上)确认应答(可靠性传输的基础)超时重传连接管理(三次握手,四次挥手) TCP的“可靠性”(上) 想必大家都或多或少的听说过TCP的特性:有连…...

超标量处理器设计笔记(5)虚拟存储器、地址转换、page fault
虚拟存储器 概述地址转换单级页表多级页表案例最好情况:虚拟地址是连续的最差情况:每个第二级 PT 都装有一项 增加级数 Page Fault 程序保护 概述 当程序比物理内存空间更大时,无法全部装在物理内存中,需要对程序进行切片 虚拟…...

SparkSQL 读写数据攻略:从基础到实战
目录 一、输入Source 1)代码演示最普通的文件读取方式: 2) 通过jdbc读取数据库数据 3) 读取table中的数据【hive】 二、输出Sink 实战一:保存普通格式 实战二:保存到数据库中 实战三:将结果保存在h…...
react 使用状态管理调用列表接口渲染列表(包含条件查询,统一使用查询按钮,重置功能),避免重复多次调用接口的方法
react开发调用api接口一般使用useEffect来监听值的变化,通过值的变化与否来进行接口调用。 比如我们要进行一个查询接口 const [pageParams, setPage] useState({name: ,id: ,});const [dataList, setDataList] useState([]);const getList async () > {const…...

Stable Audio Open模型部署教程:用AI打造独家节拍,让声音焕发新活力!
Stable Audio Open 是一个开源的文本到音频模型,允许用户从简单的文本提示中生成长达 47 秒的高质量音频数据。该模型非常适合创建鼓点、乐器即兴演奏、环境声音、拟音录音和其他用于音乐制作和声音设计的音频样本。用户还可以根据他们的自定义音频数据微调模型&…...
加油站-(贪心算法)
题目描述 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...