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

【干货】Android系统定制基础篇:第十五部分(Android支持鼠标右键返回、GPIO 控制方案、属性标识USB摄像头的VID与PID)

1、修改 frameworks/native/services/inputflinger/InputReader.cpp 如下:

diff --git a/frameworks/native/services/inputflinger/InputReader.cpp b/frameworks/native/services/inputflinger/Inp
index 7207a83..2721800 100755
--- a/frameworks/native/services/inputflinger/InputReader.cpp
+++ b/frameworks/native/services/inputflinger/InputReader.cpp
@@ -1422,6 +1422,7 @@ uint32_t CursorButtonAccumulator::getButtonState() const {result |= AMOTION_EVENT_BUTTON_PRIMARY;}if (mBtnRight) {
+        /*char targetProduct[PROPERTY_VALUE_MAX] = {0};property_get("ro.target.product", targetProduct, "");if (strcmp(targetProduct, "box") == 0) {
@@ -1429,6 +1430,9 @@ uint32_t CursorButtonAccumulator::getButtonState() const {} else {result |= AMOTION_EVENT_BUTTON_SECONDARY;}
+        */
+        
+        result |= AMOTION_EVENT_BUTTON_BACK;}if (mBtnMiddle) {result |= AMOTION_EVENT_BUTTON_TERTIARY;

二、Android GPIO 控制方案
GPIO 功能在 Android Framework 中增加 GPIO 相关 API,让 APP 可以直接通过 JAVA API 操控 GPIO。支持 输入、输出、模拟按键 三种模式。做为输入时可以用于app获取外部设备的电平状态。做为输出时可以输出高低电平,用于控制外设。当做为模拟按键时,此 GPIO 低电平时 APP 会收到对应的键值。

移植
参考项目 Android-GPIOControlDriver,移植驱动和 Framework 代码。
使用
1.在 APP 源码 aidl/android/os/ 目录下新建 IGpioService.aidl,如下:

package android.os;/** {@hide} */
interface IGpioService
{int gpioWrite(int gpio, int value);int gpioRead(int gpio);int gpioDirection(int gpio, int direction, int value);int gpioRegKeyEvent(int gpio);int gpioUnregKeyEvent(int gpio);int gpioGetNumber();
}

2.参考下面源码调用 GPIO 相关 API:

package com.ayst.sample.items.gpio;import android.annotation.SuppressLint;
import android.content.Context;
import android.os.IBinder;
import android.os.IGpioService;
import android.os.RemoteException;import java.lang.reflect.Method;/*** Created by Administrator on 2018/11/6.*/
public class Gpio {private IGpioService mGpioService;@SuppressLint("WrongConstant")public Gpio(Context context) {Method method = null;try {method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);IBinder binder = (IBinder) method.invoke(null, new Object[]{"gpio"});mGpioService = IGpioService.Stub.asInterface(binder);} catch (Exception e) {e.printStackTrace();}}/*** GPIO write** @param gpio  0~Number* @param value 0: Low 1: High*/public void gpioWrite(int gpio, int value) {if (null != mGpioService) {try {mGpioService.gpioWrite(gpio, value);} catch (RemoteException e) {e.printStackTrace();}}}/*** GPIO read** @param gpio 0~Number* @return 0: Low 1: High other:error*/public int gpioRead(int gpio) {if (null != mGpioService) {try {return mGpioService.gpioRead(gpio);} catch (RemoteException e) {e.printStackTrace();}}return -1;}/*** GPIO direction** @param gpio      0~Number* @param direction 0: input 1: output* @param value     0: Low 1: High*/public void gpioDirection(int gpio, int direction, int value) {if (null != mGpioService) {try {mGpioService.gpioDirection(gpio, direction, value);} catch (RemoteException e) {e.printStackTrace();}}}/*** GPIO register key event** @param gpio 0~Number*/public void gpioRegKeyEvent(int gpio) {if (null != mGpioService) {try {mGpioService.gpioRegKeyEvent(gpio);} catch (RemoteException e) {e.printStackTrace();}}}/*** GPIO unregister key event** @param gpio 0~Number*/public void gpioUnregKeyEvent(int gpio) {if (null != mGpioService) {try {mGpioService.gpioUnregKeyEvent(gpio);} catch (RemoteException e) {e.printStackTrace();}}}/*** Get GPIO number** @return <0: error other: GPIO number*/public int gpioGetNumber() {if (null != mGpioService) {try {return mGpioService.gpioGetNumber();} catch (RemoteException e) {e.printStackTrace();}}return -1;}
}

输入

// 将GPIO_0设置为输入
gpio.gpioDirection(0, 0, 0);
// 读GPIO_0电平
int level = gpio.gpioRead(0);

输出

// 将GPIO_0设置为输出,并默认输出低电平
gpio.gpioDirection(0, 1, 0);
// GPIO_0输出高电平
gpio.gpioWrite(0, 1);

按键模式

/* 将GPIO_0设置按键模式
当GPIO_0为低电平时将收到KeyEvent.KEYCODE_GPIO_0 KeyEvent.ACTION_DOWN,
当GPIO_0为高电平时收到KeyEvent.KEYCODE_GPIO_0 KeyEvent.ACTION_UP
*/
gpio.gpioRegKeyEvent(0);

在这里插入图片描述完整 DEMO 源码请参考:https://github.com/aystshen/topband_sample

三、属性标识USB摄像头的VID与PID

Android 在使用多个 USB 摄像头时,根据加载顺序不同他们的设备文件顺序不同,比如:“video0, video1, video2”,每次启动它们的顺序都可能不同,这样 APP 就无法知道哪个设备文件对应的是哪个摄像头,因此下面方案增加属性来标识设备文件与摄像头 VID、PID 的关系,这样就解决了上面的问题。
实现

diff --git a/frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 26d5ac9..cfd8479 100755
--- a/frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -51,6 +51,7 @@ import android.os.UserManager;import android.os.storage.StorageManager;import android.os.storage.StorageVolume;import android.provider.Settings;
+import android.text.TextUtils;import android.util.Pair;import android.util.Slog;@@ -61,8 +62,10 @@ import com.android.internal.os.SomeArgs;import com.android.internal.util.IndentingPrintWriter;import com.android.server.FgThread;+import java.io.BufferedReader;import java.io.File;import java.io.FileNotFoundException;
+import java.io.FileReader;import java.io.IOException;import java.util.HashMap;import java.util.HashSet;
@@ -95,6 +98,10 @@ public class UsbDeviceManager {* The non-persistent property which stores the current USB actual state.*/private static final String USB_STATE_PROPERTY = "sys.usb.state";
+    
+    private static final String USB_CAMERA_PROPERTY_PRE = "topband.dev.";
+
+    private static final String USB_CAMERA_CLASS_PATH = "/sys/class/video4linux";/*** ro.bootmode value when phone boots into usual Android.
@@ -215,15 +222,29 @@ public class UsbDeviceManager {if (devPath != null && devPath.contains("/devices/platform")) {if ("video4linux".equals(subSystem)) {Intent intent = new Intent(Intent.ACTION_USB_CAMERA);
+String action = event.get("ACTION");
+                    String name = event.get("DEVNAME");
+                    String idProduct = searchAndReadFileBackward("/sys" + devPath, "idProduct");
+                    String idVendor = searchAndReadFileBackward("/sys" + devPath, "idVendor");
+
+                    if (DEBUG) Slog.d(TAG, action + " usb camera: " + name + " [" + idVendor + ":" + idProduct + "]");if ("remove".equals(action)){Slog.d(TAG,"usb camera removed");intent.setFlags(Intent.FLAG_USB_CAMERA_REMOVE);SystemProperties.set("persist.sys.usbcamera.status","remove");
+
+                        if (!name.isEmpty()) {
+                            SystemProperties.set(USB_CAMERA_PROPERTY_PRE + name, "");
+                        }} else if ("add".equals(action)) {Slog.d(TAG,"usb camera added");intent.setFlags(Intent.FLAG_USB_CAMERA_ADD);SystemProperties.set("persist.sys.usbcamera.status","add");
+
+                        if (!name.isEmpty() && !idProduct.isEmpty() && !idVendor.isEmpty()) {
+                            SystemProperties.set(USB_CAMERA_PROPERTY_PRE + name, idVendor + ":" + idProduct);
+                        }}int num = android.hardware.Camera.getNumberOfCameras();
@@ -243,6 +264,99 @@ public class UsbDeviceManager {}}};
+    
+    private void initUsbCameraProperty() {
+        File filePath = new File(USB_CAMERA_CLASS_PATH);
+        File[] files = filePath.listFiles();
+        if (null != files) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    String idProduct = searchAndReadFileForward(file.getPath() + "/device/input", "product");
+                    String idVendor = searchAndReadFileForward(file.getPath() + "/device/input", "vendor");
+                    if (!TextUtils.isEmpty(idProduct) || !idVendor.isEmpty()) {
+                        if (DEBUG) Slog.v(TAG, "initUsbCameraProperty, add camera property: " + idVendor + ":" + idProduct);
+                        SystemProperties.set(USB_CAMERA_PROPERTY_PRE + file.getName(), idVendor + ":" + idProduct);
+                    }
+                }
+            }
+        }
+    }
+
+    private String searchAndReadFileForward(String filePath, String fileName) {
+        if (TextUtils.isEmpty(filePath) || TextUtils.isEmpty(fileName)) {
+            return "";
+        }
+
+        return searchAndReadFileForward(new File(filePath), fileName);
+    }
+
+    private String searchAndReadFileForward(File filePath, String fileName) {
+        if (null != filePath && !TextUtils.isEmpty(fileName)) {
+            Slog.v(TAG, "searchAndReadFileForward, path: " + filePath.getPath());
+
+            File file = new File(filePath.getPath() + "/" + fileName);
+            if (file.exists()) {
+                return readFileByLines(file.getPath());
+            }
+
+            File[] files = filePath.listFiles();
+            if (null != files) {
+                for (File subfile : files) {
+                    if (subfile.isDirectory()) {
+                        return searchAndReadFileForward(subfile, fileName);
+                    }
+                }
+            }
+        }
+
+        return "";
+    }
+
+    private String searchAndReadFileBackward(String filePath, String fileName) {
+        if (TextUtils.isEmpty(filePath) || TextUtils.isEmpty(fileName)) {
+            return "";
+        }
+
+        return searchAndReadFileBackward(new File(filePath), fileName);
+    }
+
+    private String searchAndReadFileBackward(File filePath, String fileName) {
+        if (null != filePath && !TextUtils.isEmpty(fileName)) {
+            File file = new File(filePath.getPath() + "/" + fileName);
+            if (file.exists()) {
+                return readFileByLines(file.getPath());
+            }
+
+            searchAndReadFileBackward(filePath.getParentFile(), fileName);
+        }
+
+        return "";
+    }
+
+    private static String readFileByLines(String fileName) {
+        File file = new File(fileName);
+        BufferedReader reader = null;
+        StringBuilder builder = new StringBuilder();
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            String tempString;
+            while ((tempString = reader.readLine()) != null) {
+                builder.append(tempString);
+            }
+            reader.close();
+            return builder.toString();
+        } catch (IOException e) {
+            Slog.e(TAG, "readFileByLines, " + e.getMessage());
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+        return "";
+    }public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,UsbSettingsManager settingsManager) {
@@ -494,7 +608,7 @@ public class UsbDeviceManager {UsbManager.removeFunction(UsbManager.removeFunction(persisted,UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));}}	String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();updateState(state);
@@ -548,7 +662,7 @@ public class UsbDeviceManager {} else if ("CONFIGURED".equals(state)) {connected = 1;configured = 1;if ("true".equals(SystemProperties.get("ro.usb.default_mtp")) &&UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP))mUsbDataUnlocked = true;} else {
@@ -1030,6 +1144,7 @@ public class UsbDeviceManager {updateUsbNotification(false);updateAdbNotification(false);updateUsbFunctions();
+                    initUsbCameraProperty();break;case MSG_LOCALE_CHANGED:updateAdbNotification(true);

验证
编译运行后,读取属性如下:

[topband.dev.video0]: [0bda:2714]
[topband.dev.video1]: [0bda:b321]

相关文章:

【干货】Android系统定制基础篇:第十五部分(Android支持鼠标右键返回、GPIO 控制方案、属性标识USB摄像头的VID与PID)

1、修改 frameworks/native/services/inputflinger/InputReader.cpp 如下&#xff1a; diff --git a/frameworks/native/services/inputflinger/InputReader.cpp b/frameworks/native/services/inputflinger/Inp index 7207a83..2721800 100755 --- a/frameworks/native/servi…...

ubuntu18 修改dns服务器地址为google

域名解析被干扰的有点严重&#xff0c;直接使用谷歌dns服务器来解析ip 第一、永久修改DNS方法 1、修改 /etc/systemd/resolved.conf 文件 vi /etc/systemd/resolved.conf这里我们可以看到这些参数&#xff1a; # 指定 DNS 服务器&#xff0c;以空白分隔&#xff0c;支持 IP…...

RHCE shell 作业一

1. 设置邮箱 [rootserver ~]# yum install s-nail -y [rootserver ~]# vim /etc/s-nail.rc 编写脚本 [rootserver ~]# vim homework1.sh 设置定时任务 [rootserver ~]# vim /etc/crontab 2. [rootserver ~]# vim homework2.sh 测试&#xff1a; 3. [rootserve…...

Qqis中采用栅格工具生成XYZ瓦片(目录)简介

目录 前言 一、Qgis的相关功能 1、数据准备 2、将两个xyz图源添加到图层 二、Qgis栅格工具生成 1、生成xyz图块工具在哪里 2、生成xyz图块怎么用 3、下载结果 4、Leaflet加载离线瓦块 总结 前言 在上一篇博客中&#xff0c;介绍了一种在Qgis中基于QMetaTiles插件进行xyz瓦…...

【Axure教程】根据标签数自动调整尺寸的多选下拉列表

​多选下拉列表常用于需要用户从一组选项中选择多个选项的情况。它提供了一个下拉菜单&#xff0c;用户可以点击展开并选择他们感兴趣的多个选项。多选下拉列表可以用于展示可选标签&#xff0c;并允许用户选择多个标签。例如&#xff0c;在一个博客发布界面上&#xff0c;可以…...

【python】js逆向基础案例——有道翻译

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 课程亮点: 1、爬虫的基本流程 2、反爬的基本原理 3、nodejs的使用 4、抠代码基本思路 环境介绍: python 3.8 pycharm 2022专业版 >>> 免费使用教程文末名片获取 requests >>> pip install req…...

面经系列.飞猪 Java开发工程师.杭州.2023.6.14一面面经

本人是2023年6月14日面试的,面试的岗位是飞猪的Java研发工程师,地点是杭州,面试时长37分钟,没有问八股文。全都围绕项目在问,在提问的过程中会涉及到原理以及具体操作。整体面试感觉下来,面试官很随和,通过面试也能和面试官学到很多,交流了对未来行业的看法,也对某些技…...

基于物联网及云平台的光伏运维系统

系统结构 在光伏变电站安装逆变器、以及多功能电力计量仪表&#xff0c;通过网关将采集的数据上传至服务器&#xff0c;并将数据进行集中存储管理。用户可以通过PC访问平台&#xff0c;及时获取分布式光伏电站的运行情况以及各逆变器运行状况。平台整体结构如图所示。 光伏背景…...

Android kotlin 实现仿京东多个item向左自动排队(横向、动手滑动、没有首尾滑动)功能

文章目录 一、实现效果二、引入依赖三、源码实现1、适配器2、视图实现一、实现效果 二、引入依赖 在app的build.gradle在添加以下代码 1、implementation com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6,这个里面带的适配器,直接调用就即可 BaseRecyclerViewAdapt…...

美团买菜基于 Flink 的实时数仓建设

美团买菜基于 Flink 的实时数仓建设 1. 背景介绍2. 技术愿景和架构设计3. 典型场景、挑战与应对3.1 动态 ETA 实时特征3.2 实时数据经营分析 4. 未来规划 1. 背景介绍 美团买菜是美团自营生鲜零售平台&#xff0c;上面所有的商品都由美团亲自采购&#xff0c;并通过供应链物流体…...

前端vue入门(纯代码)08

【08.webStorage--本地存储】 (1). 什么是 localStorage 和 sessionStorage &#xff1f; 它们都是浏览器的一种本地存储数据的方式它们只是存在本地&#xff0c;即浏览器中&#xff0c;不会发送到服务器 它们的区别&#xff1a; localStorage&#xff1a;永久存在浏览器中…...

Xubuntu22.04之便签工具(一百八十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

Unity入门4——重要组件与API

一、GameObject &#xff08;一&#xff09;成员变量 // 名字 print(this.gameObject.name); this.gameObject.name "Lesson4唐老狮改名"; print(this.gameOb…...

NFS服务器安装及NFS制备程序安装

NFS服务器安装及NFS制备程序安装 NFS服务器安装 NFS是一种分布式文件系统协议&#xff0c;由sun公司开发&#xff0c;旨在允许客户端主机可以像访问本地存储一样通过网络访问服务端文件 安装NFS服务器 登录需要安装NFS服务器的主机执行以下命令完成NFS安装 yum -y install…...

matlab+yalmip+cplex求解车辆路径优化问题(VRP)--matlab中yalmip函数介绍

YALMIP是一个MATLAB工具箱&#xff0c;用于建模和求解优化问题。它支持线性规划、二次规划、整数规划、鲁棒优化、半定规划等优化问题的建模和求解。下面是一些常用的函数的详细介绍&#xff1a; 1. sdpvar&#xff1a;用于定义优化问题中的变量。可以定义实数、向量和矩阵型变…...

实战:用dockerfile创建镜像实现springboot项目容器化

文章目录 前言技术积累docker基本操作命令dockerfile简介dockerfile指令说明 实战演示创建dockerfile创建挂载目录构建dockerfile启动容器完成验证 写在最后 前言 docker容器化方案是当下流行的服务部署方式&#xff0c;在软件领域举足轻重。我公司的测试、线上环境都采用dock…...

【Flask】配置项解析与加载

文章目录 默认配置表配置项加载方式直接配置存放在app.config字典通过.py配置文件通过class类加载对象通过.json配置文件通过环境变量配置内置命令行 默认配置表 配置项默认值备注DEBUGFalse是否开启 Debug 模式。如果为True&#xff0c;修改了代码&#xff0c;只需要保存&…...

Dinky: 实时即未来,让 Flink SQL 纵享丝滑--如何本地编译、运行

什么是Dinky 实时即未来,Dinky 为 Apache Flink 而生,让 Flink SQL 纵享丝滑。 Dinky 是一个开箱即用、易扩展,以 Apache Flink 为基础,连接 OLAP 和数据湖等众多框架的一站式实时计算平台,致力于流批一体和湖仓一体的探索与实践。 最后,Dinky 的发展皆归功于 Apache …...

2023 Nature 健康系统规模的语言模型是通用预测引擎

文章目录 一、论文关键信息二、论文主要内容三、总结与讨论🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、论文关键信息 论文标题:Health system-scale language models are all-purpose prediction engines 期刊信息:2023 Nature 论文地址:h...

《2023 年 React 生态》

大家好&#xff0c;我是 Chocolate。 前不久看到一篇不错的内容&#xff0c;来自于 The React Ecosystem in 2023&#xff0c;也结合自己今年使用的 React 生态总结一下。 本文并非视频演讲稿&#xff0c;和视频内容还是有一点点区别&#xff0c;视频内容相对来说会更加详细一…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...