android系统的输入事件来源在linux内核提供的/dev/input
的设备节点下, 当该设备下及诶点有数据刻度时,将数据独处并进行一系列的翻译和加工,而后在全部的窗口中寻找合适的接受者,并派发给它;linux
输入系统整体流程以下(引之深刻理解android卷3 ): android
为了接下来说解原理方便, 在这里模拟一个输入设备, 方法-写一个驱动;ubuntu
/* 参考drivers\input\keyboard\gpio_keys.c */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/input.h> static struct input_dev *input_emulator_dev; static int input_emulator_init(void) { int i; int ret; /* 1. 分配一个input_dev结构体 */ input_emulator_dev = input_allocate_device(); /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, input_emulator_dev->evbit); set_bit(EV_REP, input_emulator_dev->evbit); /* 2.2 能产生全部的按键 */ for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) input_emulator_dev->keybit[i] = ~0UL; /* 2.3 为android构造一些设备信息 */ input_emulator_dev->name = "smart remote"; input_emulator_dev->id.bustype = 1; input_emulator_dev->id.vendor = 0x0001; input_emulator_dev->id.product = 0x0010; input_emulator_dev->id.version = 1; /* 3. 注册 */ ret = input_register_device(input_emulator_dev); if (ret < 0) { return -1; } return 0; } static void input_emulator_exit(void) { input_unregister_device(input_emulator_dev); input_free_device(input_emulator_dev); } module_init(input_emulator_init); module_exit(input_emulator_exit); MODULE_LICENSE("GPL");
KERN_DIR = /media/sourcelink/Backups/5.CompileCode/firefly3288/kernel all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += smart_remote.o
编译完后,放到板子上加载,使用命令cat /proc/bus/input/devices
查看设备加载状况以下:浏览器
root@firefly:/data/nfs # ins insmod installd root@firefly:/data/nfs # insmod smart_remote.ko root@firefly:/data/nfs # cat /proc/bus/input/devices .... I: Bus=0001 Vendor=0001 Product=0010 Version=0001 N: Name="smart remote" P: Phys= S: Sysfs=/devices/virtual/input/input3 U: Uniq= H: Handlers=sysrq event3 ddr_freq keychord B: PROP=0 B: EV=100003 B: KEY=ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
如上是我板子加载完驱动的状况,原默认的信息我已经删除, 也能够看下在<font color=red>/dev/input</font>, 节点下多了个<font color=red>event3</font>;工具
root@firefly:/data/nfs # ls /dev/input/ event0 event1 event2 event3
这样就完成一个输入设备的模拟,接下来介绍两个工具模拟事件的产生;布局
android系统提供了<font color=red>getevent</font> 与<font color=red>sendevent</font>两个工具供开发从设备节点中读取和写入事件;测试
语法:spa
getevent [-opera] [节点路径]
可使用getevent -help
查看一些具体的operation; 翻译
若是不带指定设备节点, 这样会监控全部的设备节点:3d
root@firefly:/data/nfs # getevent add device 1: /dev/input/event3 name: "smart remote" add device 2: /dev/input/event2 name: "RK_ES8323 Headphone Jack" add device 3: /dev/input/event1 name: "rk29-keypad" add device 4: /dev/input/event0 name: "ff680000.pwm"
如今我将本身的键盘插到开发板上了,使用getevent
命令来监控事件;
当我按下并松开键盘上的数字1
键获取到的数据以下:
root@firefly:/data/nfs # getevent /dev/input/event4 0004 0004 0007001e 0001 0002 00000001 0000 0000 00000000 0004 0004 0007001e 0001 0002 00000000 0000 0000 00000000
数据意义依次是: 事件类型, 事件代码, 事件值
事件值的1表示按下, 0表示抬起, 观察数据能够发现每次按下或抬起都会获取到0000 0000 00000000
的数据, 表示同步事件,通知这次事件已经结束能够进行处理了;
这里的事件代码是linux端发来的原始数据, 在android端还会进行一次键值布局,下面笔者会讲解这个映射关系;
语法:
setevent [节点路径] [事件类型] [事件代码] [事件值]
如今操做下往设备几点写入一个事件, 打开我开发板的浏览器:
依次输入以下指令:
sendevent /dev/input/event3 1 2 1 sendevent /dev/input/event3 1 2 0 sendevent /dev/input/event3 0 0 0
效果以下:
屏幕上出现了一个数字1, 最后发送的0 0 0
表示同步事件,通知这次事件已经结束能够进行处理了.
按键事件来源于linux内核, 可是在android端对按键的值有从新作一个布局,即将linux key code转换为android key code, 这个布局依赖于个.kl
文件, 全称: Key Layout Files;
该文件搜索路径以下:
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/odm/usr/keylayout/DEVICE_NAME.kl
/vendor/usr/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/odm/usr/keylayout/Generic.kl
/vendor/usr/keylayout/Generic.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl
从上面路径信息能够看出Key Layout Files文件的命名和厂家信息有关, 具体为供应商id, 产品id和设备名有关;
好比咱们的模拟输入设备的须要的.kl
文件能够命名为Vendor_0001_Product_0010.kl
或smart remote.kl
;
输入系统在检测到有新设备接入时, 在上述路径查找对应符合规则的.kl
文件并加载它,若是没有找到则加载Generic.kl
文件;
拷贝一个Generic.kl
和咱们模拟按键设备名字同样, 并加上权限,若是你的板子上没有该目录的话则建立它;
cp /system/usr/keylayout/Generic.kl /data/system/devices/keylayout/smart_remote.kl chmod 777 /data/system/devices/keylayout/smart_remote.kl
咱们打开这个文件看下里面的内容:
key 1 ESCAPE key 2 1 key 3 2 key 4 3 key 5 4 key 6 5 key 7 6 key 8 7 key 9 8 key 10 9 ....
看到这就明白了为何咱们前面输入的键值2,最后在浏览器上看到了1; 咱们修改下这个文件:
key 1 ESCAPE key 2 3 ...
卸载驱动, 再从新加载驱动后,再依次输入以下指令:
sendevent /dev/input/event3 1 2 1 sendevent /dev/input/event3 1 2 0 sendevent /dev/input/event3 0 0 0
效果以下:
这样就达到了输入一样按键却获得不一样之的效果了;
负责将android key code与修饰符的组合映射到Unicode字符。
/odm/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/odm/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX.kcm
/odm/usr/keychars/DEVICE_NAME.kcm
/vendor/usr/keychars/DEVICE_NAME.kcm
/system/usr/keychars/DEVICE_NAME.kcm
/data/system/devices/keychars/DEVICE_NAME.kcm
/odm/usr/keychars/Generic.kcm
/vendor/usr/keychars/Generic.kcm
/system/usr/keychars/Generic.kcm
/data/system/devices/keychars/Generic.kcm
/odm/usr/keychars/Virtual.kcm
/vendor/usr/keychars/Virtual.kcm
/system/usr/keychars/Virtual.kcm
/data/system/devices/keychars/Virtual.kcm
输入系统在建立设备时会在这些路径下查找对应的.kl
和.kcm
文件进行加载, 若是没有找到对应的文件将会加载Generic.kl
和Generic.kcm
文件
咱们如今根据咱们的模拟设备来更改下kcm文件, 操做以下:
mkdir /data/system/devices/keychars cp /system/usr/keychars/Generic.kcm /data/system/devices/keychars/smart_remote.kcm
打开该文件查看下里面内容:
### Basic QWERTY keys ### key A { label: 'A' base: 'a' shift, capslock: 'A' } key B { label: 'B' base: 'b' shift, capslock: 'B' } key C { label: 'C' base: 'c' shift, capslock: 'C' alt: '\u00e7' shift+alt: '\u00c7' } ....
以按键<font color=red>A</font>为例, 查看.kl
文件当输入事件代码为30
时, 对应到android的key A
,
根据.kcm
文件知道此时会映射成字符a
, 当按下shift
键时再按下A
会显示字符A
;
输入以下指令看下效果:
sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
为了方便你们查看都使用了文件前面的内容进行修改并演示;
若是想当按下a键时显示字符b, 按下shift+a时显示字符2修改下kcm文件,以下:
key A { label: 'A' base: 'b' shift, capslock: '2' } ...
从新卸载驱动并加载驱动, 再次执行:
sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
如今试下同时按下shift键的效果, 依次输入以下:
sendevent /dev/input/event3 1 42 1 sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
果真和咱们修改的kcm文件映射的字符保持了一致;
按键事件的转化流程大体以下图:
若是想个性化定制输入的按键的键值和字符显示只须要修改kl和kcm文件;