当前位置: 首页 > 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…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...