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

diff --git a/frameworks/native/services/inputflinger/InputReader.cpp b/frameworks/native/services/inputflinger/Inpindex 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 gpio0~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 gpio0~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.javaindex 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]