映射表基本概念
由于Android调用getEvents得到的key是linux发送过来的scan code,而Android处理的是类似于KEY_UP这种统一类型的key code,因此需要有映射表把scan code转换成key code。映射表在板子上的位置是/system/usr/keylayout/xxx.kl,先看一下映射表是什么样子的,下面截选了一段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
可以看到每行都是一个映射项,映射项格式如下:
key [scan code] [key label] [flag label] [flag label] ...
- key是关键字,表明这个映射项是作为键值映射
- scan code是从linux device取得的键值
- key label是把scan code映射到key code中间的关键字,通过该关键字可以得到key code。
- flag label即按键的标记的关键字,通过flag label可以得到flag,一行映射项后面可以有多个flag label
从3和4可以知道,还有一个key label到key code的过程,以及flag label到flag的过程
另外,映射表是设备相关的。由于不同设备发送到Android的scan code可能会不同,因此每个设备需要用自身对应的映射表才能正确解析出key code。
映射表加载过程
1. 获取设备相关信息
在构造EventHub的时候,就决定了需要扫描输入设备。然后会在第一次getEvents进行一次扫描。
扫描输入设备主要有两个目的:
- 得到该设备的各种信息,如:设备名称,设备版本,设备产品码等,这些信息都可以作为该设备的标识。
- 知道该设备所发送事件的类型,如:按键事件,触控事件,滑动事件,开关事件,xy坐标等;通过所发送事件的类型,就能定位出设备的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
扫描的目录是/dev/input,linux中每加入一个输入设备,都会在该目录下创建设备文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
在openDeviceLocked中就能清晰分析出扫描设备的两个目的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
|
2. 加载映射表
通过设备信息与设备类型,我们就能去加载正确的映射表了
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 | |
加载配置文件分为下面几个步骤
1. 通过设备的配置文件去加载配置文件内制定好的映射表
2. 如果1不成功则通过设备信息加载对应的映射表
3. 如果2不成功则加载通用映射表
4. 如果3不成功则加载虚拟映射表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
一般的情况我们会走第2步,因此从probeKeyMap往下分析
1 2 3 4 5 6 7 8 9 10 | |
对于按键,有键盘按键与自定义按键两种,两者加载的文件后缀不同。键盘按键的映射表后缀是.kcm,而自定义按键映射表后缀是.kl。另外两者映射表的格式也不同,我们这里以自定义按键映射表为例,其中有三个步骤:
- 获取映射表文件路径
- 加载映射表文件
- 如果加载映射表文件成功的话,设置该路径为当前设备的自定义映射文件路径。(否则会去解析Generic.kl或者virtual.kl)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1. 获取映射表文件路径
我们从加载映射表文件的步骤2进来,那传入的name为空,则调用到getInputDeviceConfigurationFilePathByDeviceIdentifier,即通过设备标识来产生路径
1 2 3 4 5 6 | |
如果设备标识中的vendor,product,version都不为0的话,表明可以通过这些信息来组合成一个字符串,这个字符串就是映射表文件的前缀,否则,会设备名称deviceIdentifier.name就是映射表文件的前缀。后缀通过type指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
假设当前设备的设备名称是input_ir,传入的type是INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT,则设备的文件名为input_ir.kl
2.加载映射表文件
加载映射表文件最终目的是解析该文件得到映射表,其中也分为三个步骤:
- 打开映射表文件
- 创建映射表
- 解析映射表文件并把映射项加入映射表
1 2 3 4 5 6 7 8 9 10 |
|
我们直接看最重要的解析部分
parse函数是一个while循环,一行一行地解析映射表项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
每一行的解析步骤如下:
- 跳过行首的空格符
- 如果开头第一个字符是”#”,跳过当前行
- 如果开头的关键词是key,跳过空白分割符,调用parseKey解析,如果解析出错则返回错误
- 如果开头的关键词是axis,跳过空白分隔符,调用parseAxis解析,如果解析出错则返回错误
- 如果开头的关键词是其他的词,说明这个映射表文件有误,返回错误
- 跳过行末的空格符
- 如果行末还有”#”以外的字符,说明这个映射表文件有误,返回错误
下面以parseKey为例,分析它是怎么解析出scan code与key code的(由于我们没用到usage code,所以忽略usage,直接分析scan code流程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
我们在前面说过,还有个从key label到key code的流程,该流程就是在getKeyCodeByLabel中实现的
1 2 3 | |
最终从KEYCODES这个列表内,根据label查找key code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
同理,在解析flag的时候也是从FLAGS这个列表内查找flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|