当前位置: 首页 > 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]
http://www.lryc.cn/news/93770.html

相关文章:

  • ubuntu18 修改dns服务器地址为google
  • RHCE shell 作业一
  • Qqis中采用栅格工具生成XYZ瓦片(目录)简介
  • 【Axure教程】根据标签数自动调整尺寸的多选下拉列表
  • 【python】js逆向基础案例——有道翻译
  • 面经系列.飞猪 Java开发工程师.杭州.2023.6.14一面面经
  • 基于物联网及云平台的光伏运维系统
  • Android kotlin 实现仿京东多个item向左自动排队(横向、动手滑动、没有首尾滑动)功能
  • 美团买菜基于 Flink 的实时数仓建设
  • 前端vue入门(纯代码)08
  • Xubuntu22.04之便签工具(一百八十)
  • Unity入门4——重要组件与API
  • NFS服务器安装及NFS制备程序安装
  • matlab+yalmip+cplex求解车辆路径优化问题(VRP)--matlab中yalmip函数介绍
  • 实战:用dockerfile创建镜像实现springboot项目容器化
  • 【Flask】配置项解析与加载
  • Dinky: 实时即未来,让 Flink SQL 纵享丝滑--如何本地编译、运行
  • 2023 Nature 健康系统规模的语言模型是通用预测引擎
  • 《2023 年 React 生态》
  • 武职302303笔记-day01
  • Python面试高频100题【11~15题】
  • Linux下Redis 存储
  • QML 快速上手3 - QuickControl2
  • QT Creator写一个简单的电压电流显示器
  • 前端需要的技能
  • Qt——Qt控件之基于模型的项目视图组-QTreeView树形视图控件的使用总结(Qt仿word标题列表的实现)
  • spring boot框架步骤
  • 动态创建select
  • linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘
  • STM32软件定时器