当前位置: 首页 > news >正文

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 数据需求&#xff0c;简而言之就是两台设备直接可以通过usb 线互相传递数据。 项目架构 为什么Device 端要采用HID&#xff08;人机接口设备&#xff09;的方式发送和接收数据呢&#xff1f; 主要是速度快&#xff0c;举个例子&#xff0c;就是鼠标移动&am…...

MaxEnt模型在物种分布模拟中如何应用?R语言+MaxEnt模型融合物种分布模拟、参数优化方法、结果分析制图与论文写作

目录 第一章 以问题导入的方式&#xff0c;深入掌握原理基础 第二章 常用数据检索与R语言自动化下载及可视化方法 第三章 R语言数据清洗与特征变量筛选 第四章 基于ArcGIS、R数据处理与进阶 第五章 基于Maxent的物种分布建模与预测 第六章 基于R语言的模型参数优化 第七…...

第3章:文本样式 --[CSS零基础入门]

CSS(层叠样式表)允许你以多种方式定制文本的外观。以下是一些常用的文本和字体相关的CSS属性: 1.字体 字体系列 当然,下面是两个使用不同字体系列的CSS示例。每个示例都展示了如何指定一个字体系列,并提供备用字体以确保在用户的系统中找不到首选字体时仍有合适的字体可…...

mysql 架构详解

MySQL的架构可以自顶向下分为多个层次&#xff0c;每个层次都有其特定的功能和组件。以下是对MySQL架构的详细解析&#xff1a; 一、整体架构概述 MySQL的整体架构包括MySQL Connectors&#xff08;连接器&#xff09;、MySQL Shell、连接层、服务层、存储引擎层和文件系统层…...

无代码探索AI大模型:腾讯云函数计算的卓越实践

在数字化转型的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术已经成为企业提升竞争力的关键。然而&#xff0c;对于许多业务人员来说&#xff0c;技术门槛高、开发周期长等问题限制了他们快速探索和应用AI大模型的能力。同时&#xff0c;对于缺乏GPU资源的开发者来…...

解决Ubuntu在VMware关机时,老是一个光标在那里闪动几分钟,才能关机的问题

把强制关机的等待时间缩短即可。 编辑 /etc/systemd/system.conf 文件 sudo gedit /etc/systemd/system.conf把 #DefaultTimeoutStartSec90s修改为 #DefaultTimeoutStartSec10s改完之后重载一下&#xff1a; sudo systemctl daemon-reload然后关机再试一下&#xff0c;这样…...

word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图

目录 问题解决问题poi-tl介绍 功能实现引入依赖功能介绍 功能实例饼图模版代码效果图 雷达图&#xff08;模版同饼图&#xff09;代码效果图 柱状图&#xff08;模版同饼图&#xff09;代码效果图 附加CustomCharts 工具类CustomChartSingleSeriesRenderData 数据对象CustomCha…...

常见网络钓鱼类型

网络钓鱼是一种网络攻击&#xff0c;是指具有恶意动机的攻击者伪装欺骗人们并收集用户名或密码等敏感信息的一系列行为。由于网络钓鱼涉及心理操纵并依赖于人为失误(而不是硬件或软件漏洞)&#xff0c;因此被认定为是一种社会工程攻击。 1. 普通网络钓鱼&#xff08;群攻&…...

数字图像处理考研考点(持续更新)

一、数字图像基本概念 1、人眼视觉特性 &#xff08;1&#xff09;眼睛上有两类光感受器&#xff1a;锥状体和杆状体 锥状体(锥细胞)&#xff1a;约 700 万个&#xff0c;对颜色高度敏感&#xff0c;每个锥状体都连接到神经末梢&#xff0c;人可以充分地分辨图像细节。锥细胞…...

Spring Cloud Alibaba:一站式微服务解决方案

Spring Cloud Alibaba介绍 在当今的软件开发领域&#xff0c;微服务架构因其灵活性、可扩展性和独立性等优势而备受青睐。Spring Cloud Alibaba 作为一款强大的一站式微服务解决方案&#xff0c;为开发者提供了丰富的工具和组件&#xff0c;帮助他们轻松构建和管理复杂的微服务…...

ubuntu16.04部署dify教程

文章目录 1、克隆 Dify 源代码至本地环境2、加速Dify镜像文件下载3、启动 Dify4、访问 Dify5、更新 Dify6、常见问题及解决方案&#xff08;1&#xff09;容器restarting&#xff08;2&#xff09;日志文件上限&#xff08;3&#xff09;重置管理员密码&#xff08;4&#xff0…...

JavaWeb文件上传

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

软件工程——期末复习(3)

一、题目类(老师重点提到过的题目) 1、高可靠性是否意味着高可用性&#xff1f;试举例证明自己的观点&#xff1f; 答&#xff1a;高可靠性不意味着高可用性 可靠性说明系统已经准备好&#xff0c;马上可以使用&#xff1b;可用性是系统可以无故障的持续运行&#xff0c;是一…...

apache的BeanUtils的Converter被相互污染覆盖问题

问题描述 apache的BeanUtils工具集中用来把map对象转换为java对象的BeanUtils#populate方法会因为单例的原因其转换器Converter被相互污染覆盖问题 maven依赖 <dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</…...

TCP的“可靠性”(上)

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

超标量处理器设计笔记(5)虚拟存储器、地址转换、page fault

虚拟存储器 概述地址转换单级页表多级页表案例最好情况&#xff1a;虚拟地址是连续的最差情况&#xff1a;每个第二级 PT 都装有一项 增加级数 Page Fault 程序保护 概述 当程序比物理内存空间更大时&#xff0c;无法全部装在物理内存中&#xff0c;需要对程序进行切片 虚拟…...

SparkSQL 读写数据攻略:从基础到实战

目录 一、输入Source 1&#xff09;代码演示最普通的文件读取方式&#xff1a; 2&#xff09; 通过jdbc读取数据库数据 3) 读取table中的数据【hive】 二、输出Sink 实战一&#xff1a;保存普通格式 实战二&#xff1a;保存到数据库中 实战三&#xff1a;将结果保存在h…...

react 使用状态管理调用列表接口渲染列表(包含条件查询,统一使用查询按钮,重置功能),避免重复多次调用接口的方法

react开发调用api接口一般使用useEffect来监听值的变化&#xff0c;通过值的变化与否来进行接口调用。 比如我们要进行一个查询接口 const [pageParams, setPage] useState({name: ,id: ,});const [dataList, setDataList] useState([]);const getList async () > {const…...

Stable Audio Open模型部署教程:用AI打造独家节拍,让声音焕发新活力!

Stable Audio Open 是一个开源的文本到音频模型&#xff0c;允许用户从简单的文本提示中生成长达 47 秒的高质量音频数据。该模型非常适合创建鼓点、乐器即兴演奏、环境声音、拟音录音和其他用于音乐制作和声音设计的音频样本。用户还可以根据他们的自定义音频数据微调模型&…...

加油站-(贪心算法)

题目描述 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 gas…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema&#xff0c;不需要复杂的查询&#xff0c;只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 &#xff1a;在几秒钟…...

k8s从入门到放弃之Pod的容器探针检测

k8s从入门到放弃之Pod的容器探针检测 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;容器探测是指kubelet对容器执行定期诊断的过程&#xff0c;以确保容器中的应用程序处于预期的状态。这些探测是保障应用健康和高可用性的重要机制。Kubernetes提供了两种种类型…...