安卓遥控最常用的是红外遥控和2.4G无线飞鼠,原理都是差不多的。
空中飞鼠属于USB协议里的HID设备,驱动目录: kernel\drivers\hid\,键值和kernel\drivers\hid\hid-input.c里有个数组有关。
红外遥控发射红外码值比如:0x32,通过kernel里面配置的dtsi文件映射到一个linux code
然后通过device目录下面的某个kl文件映射到一个具体的android keycode
这样就可以在应用层通过onKeyDown()监听到这个按键:
红外码值 --> linux层 --> android层--> onKeyDown()监听按键
红外码值和linux的映射:<0xbf KEY_BACK>在kernel的dts文件中某一组 ir_key{}里面
0xbf是红外码值
KEY_BACK是linux的keycode,
在文件kernel/include/uapi/linux/input.h 中定义
有的平台可能是别的文件名,大概都是在这个位置。
linux和安卓层的映射文件在device的配置文件,后缀为kl
key 89 RO
key 87 F11
F11是安卓keycode,在下列这些文件中定义:
frameworks/base/core/java/android/view/KeyEvent.java
frameworks/native/include/android/keycodes.h
frameworks/native/include/input/InputEventLabels.h
frameworks/base/core/res/res/values/attrs.xml
假如我们现在接到一个任务需要适配一个新的遥控器,首先我们测试一下遥控的客户码和所有的按键红外码值,具体的测试方法各个方案平台都有对应,通过串口打印信息可以看到。
RK打印红外码值
adb shell环境下输入
echo 1 > /sys/module/rockchip_pwm_remotectl/parameters/code_print
cat /proc/kmsg
按遥控按键就会有码值打印
全志打印红外码值
echo 0xff > /sys/module/sunxi_ir_rx/parameters/debug_mask
将红外码配置到对应的dtsi文件里面:
ir_key3 {
usercode = <0x1dcc>;
key_table =
<0xee KEY_REPLY>,
<0xf0 KEY_BACK>,
<0xf8 KEY_UP>,
<0xbb KEY_DOWN>,
<0xef KEY_LEFT>,
<0xed KEY_RIGHT>,
<0xfc KEY_HOME>,
<0xf1 KEY_VOLUMEUP>,
<0xfd KEY_VOLUMEDOWN>,
<0xb7 KEY_SEARCH>,
<0xff KEY_POWER>,
<0xf3 KEY_MUTE>,
<0xbf KEY_MENU>,
<0xf9 KEY_ENTER>,
<0xb3 388>,
<0xbe KEY_1>,
<0xba KEY_2>,
<0xb2 KEY_3>,
<0xbd KEY_4>,
<0xf9 KEY_5>,
<0xb1 KEY_6>,
<0xfc KEY_7>,
<0xf8 KEY_8>,
<0xb0 KEY_9>,
<0xb6 KEY_0>,
<0xb5 KEY_BACKSPACE>;
};
编译kernel烧录之后,adb shell 输入 getevent 可以看到类似如下打印:
U5X:/ # getevent
add device 1: /dev/input/event2
name: "cec_input"
could not get driver version for /dev/input/mice, Not a typewriter
add device 2: /dev/input/event1
name: "gpio_keypad"
could not get driver version for /dev/input/mouse0, Not a typewriter
add device 3: /dev/input/event0
name: "aml_keypad"
随便按一个按键可以看到:
/dev/input/event0: 0001 0069 00000001
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0001 0069 00000000
/dev/input/event0: 0000 0000 00000000
这串打印表示了一个完整的按键按下和抬起,码值是0x69转换为十进制为105 这个105就是dtsi文件里映射的linux码值。
可以看到遥控的按键事件是/dev/input/event0 对应的输入设备就是
add device 3: /dev/input/event0
name: "aml_keypad"
adb shell 下 输入 dumpsys input 可以看到这样的一段打印
3: aml_keypad
Classes: 0x0000082b
Path: /dev/input/event0
Descriptor: d2c52ff0f656fac4cd7b7a118d575e0109a9fe1c
Location: keypad/input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0010, vendor=0x0001, product=0x0001, version=0x0100
KeyLayoutFile: /system/usr/keylayout/Vendor_0001_Product_0001.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
说明遥控器对应的kl文件是KeyLayoutFile: /system/usr/keylayout/Vendor_0001_Product_0001.kl
我们这个是已经匹配好了kl文件,关于系统匹配kl文件有个优先级的规则:
/system/usr/key1ayout/Vendor_xxx_Product_xxx_Version_xxx.kl
/system/usr/keylayout/Vendor_xxx_Product_xxx.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/Vendor_xxx_Product_xxx_Version_xxx.kl
/data/system/devices/keylayout/Vendor_xxx_Product_xxx.kl
/data/system/devices/key1ayout/DEVICE_NAME.kl
/system/usr/key1ayout/Generic.kl
/data/system/devices/keylayout/Generic.kl
具体的代码流程有时间可以去研究一下,系统如果没有匹配到任何kl文件或者匹配你的kl文件出错,就会调用默认的Generic.kl 如果你还没有配置自己的kl文件,dumpsys input 打印可以看这一段:
Identifier: bus=0x0010, vendor=0x0001, product=0x0001,version=0x0100
你可以通过vendor 和 product的值来创建kl文件。
你也可以通过这个设备名aml_keypad来创建kl文件,优先级比VID,PID命名的kl文件低一点。
设备名kl的命名规则是:设备名称中除"0-9"、“a-z”、“A-Z”、"-“或”_“之外的所有字符将替换为下划线,所以这里可以创建一个/system/usr/keylayout/aml_keypad.kl文件
有时候在添加的时候会发现,明明已经创建好了kl文件,但是发现遥控按键还是有问题,看打印信息dumpsys input,匹配的kl文件竟然是Generic.kl这个系统的默认文件,这时候看打印通常会提示kl文件里面某个keycode没有定义。
所以kl文件里面配置的安卓Keycode一定要在这几个文件里有定义好,否则kl匹配会报错就会自动匹配到默认的Generic.kl文件了
frameworks/base/core/java/android/view/KeyEvent.java
frameworks/native/include/android/keycodes.h
frameworks/native/include/input/InputEventLabels.h
frameworks/base/core/res/res/values/attrs.xml
kl文件Vendor_0001_Product_0001.kl创建好后,可以参照系统自带的kl文件来配置linux code和 android keycode的映射。
key 28 ENTER
比如这个key 28 里面的 28就是linux code 对应dtsi文件里面的 KEY_ENTER,为啥我知道KEY_ENTER=28呢?这个当然不是瞎蒙的,是看这个文件 kernel/include/uapi/linux/input.h 知道的:
#define KEY_ENTER 28
当然你可能说我这个文件里面根本没有,那有可能你的是在别的文件里面,大概都是在这个目录下,去找一下,很容易找到的。
那后面的ENTER是怎么回事呢,其实就是frameworks/base/core/java/android/view/KeyEvent.java这个文件里面的,可以打开看一下,很容易就找到了这个定义:
public static final int KEYCODE_ENTER = 66;
明明是KEYCODE_ENTER 为什么kl里面写成ENTER呢,其实这个我也不知道,我就知道kl里面都是这样配置的,把KEYCODE_ 这一段去掉,保留下划线后面这一段。依葫芦画瓢这样配置好之后就完成了 ‘红外码-->linux code--->android keycode’完整的映射。
这个时候你可以看一下这个frameworks\base\services\core\java\com\android\server\policyPhoneWindowManager.java文件,把里面这个static final boolean DEBUG_INPUT = false;改成true之后,按键按下的时候,就有keycode打印出来,这个就是android keycode了。
可以看到KeyEvent.java里面定义了很多个按键,有几百个,你在键盘上看到的所有按键,基本上都有定义,但是你说我还嫌不够,我想定义几个自己的按键玩玩也是可以的。接下来是时候展示真正的技术了
1.在你的kl文件里面添加
key 252 HEADSET_PTT
2.input.h(kernel/include/uapi/linux/input.h)
#define KEY_HEADSET_PTT 252
3./frameworks/native/include/android/KeyCodes.h
enum {
AKEYCODE_HEADSET_PTT = 276, //276跟其他不能重复 };
4./frameworks/native/include/input/InputEventLabels.h
DEFINE_KEYCODE(HEADSET_PTT),
5.frameworks/base/core/res/res/values/attrs.xml
<attr name="keycode"> ... <enum name="KEYCODE_HEADSET_PTT" value="276" /> ... </attr>
6.frameworks/base/core/java/android/view/KeyEvent.java
/**
* @hide
*/
public static final int KEYCODE_HEADSET_PTT = 276; //这个值和attrs.xml InputEventLabels.h值相同
private static final int LAST_KEYCODE = KEYCODE_HEADSET_PTT;
public static final boolean isSystemKey(int keyCode) {
... switch (keyCode) {
case KeyEvent.KEYCODE_HEADSET_PTT: //
return true;
}
...
}
... public static final boolean isMediaKey(int keyCode) { //这个也可以加 也可以不加
switch (keyCode) {
case KeyEvent.KEYCODE_HEADSET_PTT:
return true;
}
}
7.接下来就可以在这个文件里监听你的按键了
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
... @Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
... case KeyEvent.KEYCODE_HEADSET_PTT:{
if(down){
...
}else{
...
} break;
}
...
}
工作时间匆忙之中整理而成,难免有些词不达意和错漏之处,欢迎批评指正。