【转】Android-Input input&按键布局文件
https://source.android.com/devices/input
输入
![Android 输入 HAL 图标](https://source.android.com/devices/input/images/ape_fwk_hal_input.png)
Android 输入子系统名义上是由遍历系统多个层的事件管道组成。
输入管道
在最低层,物理输入设备会生成描述状态更改(例如按键按压和触摸接触点)的信号。设备固件以某种方式编码和传输这些信号,例如向系统发送 USB HID 报告或在 I2C 总线上产生中断。
然后,信号由 Linux 内核中的设备驱动程序解码。Linux 内核为许多标准的外围设备提供驱动程序,特别是那些符合 HID 协议的外围设备。然而,原始设备制造商 (OEM) 通常必须为在低级别紧密集成到系统的嵌入式设备(如触摸屏)提供自定义驱动程序。
输入设备驱动程序负责通过 Linux 输入协议将设备特定信号转换为标准输入事件格式。Linux 输入协议在 linux/input.h
内核头文件中定义了一组标准事件类型和代码。这样一来,内核之外的组件就不需要关注物理扫描代码、HID 用途、I2C 消息、GPIO 引脚等方面的详细信息。
接下来,Android EventHub
组件通过打开与每个输入设备关联的 evdev
驱动程序从内核读取输入事件。然后,Android InputReader 组件根据设备类别解码输入事件,并生成 Android 输入事件流。在此过程中,Linux 输入协议事件代码将根据输入设备配置、键盘布局文件和各种映射表,转化为 Android 事件代码。
最后,InputReader
将输入事件发送到 InputDispatcher,后者将这些事件转发到相应的窗口。
控制点
在输入管道中有几个阶段会影响对输入设备行为的控制。
驱动程序和固件配置
输入设备驱动程序经常通过在寄存器中设置参数甚或上传固件本身来配置输入设备的行为。对于诸如触摸屏等嵌入式设备尤其如此,其中一大部分校准过程涉及到调整这些参数或修复固件,以提供所需的精度和响应能力并抑制噪声。
驱动程序配置选项通常在内核板级支持包 (BSP) 中指定为模块参数,以便同一驱动程序可以支持多种不同的硬件实现。
本文档旨在介绍驱动程序或固件配置,但在总体上提供了有关设备校准的指导。
板配置属性
内核板级支持包 (BSP) 可以通过由 Android InputReader 组件使用的 SysFS 导出板配置属性,如虚拟键在触摸屏上的位置。
有关不同设备如何使用板配置属性的详细信息,请参阅设备类别部分。
资源叠加层
有几个输入行为是通过 config.xml
中的资源叠加层配置的,例如机盖开关的操作。
以下是几个例子:
-
config_lidKeyboardAccessibility
:指定机盖开关对硬件键盘可访问还是隐藏的影响。 -
config_lidNavigationAccessibility
:指定机盖开关对触控板可访问还是隐藏的影响。 -
config_longPressOnPowerBehavior
:指定当用户按住电源按钮时应该发生的情况。 -
config_lidOpenRotation
:指定机盖开关对屏幕方向的影响。
要详细了解每个配置选项,请参阅 frameworks/base/core/res/res/values/config.xml
中的文档。
按键映射
Android EventHub
和 InputReader
组件使用按键映射来配置按键、操纵杆按钮和操纵杆轴从 Linux 事件代码到 Android 事件代码的映射。映射可能会因设备或语言的不同而不同。
要详细了解不同设备如何使用按键映射,请参阅设备类别部分。
输入设备配置文件
Android EventHub
和 InputReader
组件使用输入设备配置文件来配置特殊设备特征,例如,如何报告触摸尺寸信息。
要详细了解不同设备如何使用输入设备配置映射,请参阅设备类别部分。
了解 HID 用途和事件代码
通常使用几个不同的标识符来指代键盘上的任何指定按键、游戏控制器上的按钮、操纵杆轴或其他控件。这些标识符之间的关系并非总是一样的:它们取决于一组映射表,其中一些映射是固定的,还有一些映射随设备特征、设备驱动程序、当前语言区域、系统配置、用户偏好设置和其他因素而变化。
- 物理扫描代码
-
物理扫描代码是与每个按键、按钮或其他控件相关联的设备特定标识符。由于物理扫描代码通常因设备而异,因此固件或设备驱动程序负责将其映射到标准标识符,例如 HID 用途或 Linux 按键代码。
扫描代码主要用于键盘。其他设备通常使用 GPIO 引脚、I2C 消息或其他方式在低级别进行通信。因此,软件堆栈的上层依赖于设备驱动程序来确定发生的操作。
- HID 用途
-
HID 用途是一种标准标识符,用于报告控件(如键盘按键、操纵杆轴、鼠标按钮或触摸接触点)的状态。大多数 USB 和蓝牙输入设备都符合 HID 规范,使得系统能够以统一的方式与它们进行连接。
Android 框架依赖于 Linux 内核 HID 驱动程序来将 HID 用途代码转换为 Linux 按键代码和其他标识符。因此,主要是外设制造商关注 HID 用途。
- Linux 按键代码
-
Linux 按键代码是用于按键或按钮的标准标识符。Linux 按键代码在
linux/input.h
头文件中使用以前缀KEY_
或BTN_
开头的常量进行定义。Linux 内核输入驱动程序负责将物理扫描代码、HID 用途和其他设备特定信号转换为 Linux 按键代码,并作为EV_KEY
事件的一部分提供有关它们的信息。Android API 有时将与按键相关联的 Linux 按键代码称为其“扫描代码”。这在技术上是错误的,但是有助于在 API 中区分 Linux 按键代码和 Android 按键代码。
- Linux 相对或绝对轴代码
-
Linux 相对或绝对轴代码是一种标准标识符,用于报告沿着某个轴的相对位移或绝对位置,例如鼠标沿其 X 轴的相对位移或者操纵杆沿其 X 轴的绝对位置。Linux 轴代码在
linux/input.h
头文件中使用以前缀REL_
或ABS_
开头的常量进行定义。Linux 内核输入驱动程序负责将 HID 用途和其他设备特定信号转换为 Linux 轴代码,并作为EV_REL
和EV_ABS
事件的一部分提供有关它们的信息。 - Linux 开关代码
-
Linux 开关代码是用于报告设备上的开关(例如机盖开关)状态的标准标识符。Linux 开关代码在
linux/input.h
头文件中使用以前缀SW_
开头的常量进行定义。Linux 内核输入驱动程序以EV_SW
事件形式报告开关状态更改。Android 应用通常不会接收来自开关的事件,但系统可能会在内部使用它们来控制各种特定于设备的功能。
- Android 按键代码
-
Android 按键代码是在 Android API 中定义的标准标识符,用于指示特定按键(如“主屏幕”)。Android 按键代码由
android.view.KeyEvent
类定义为以前缀KEYCODE_
开头的常量。按键布局指定了 Linux 按键代码如何映射到 Android 按键代码。可以使用不同的按键布局,具体取决于键盘型号、语言、国家/地区、布局或特殊功能。
使用特定于设备和语言区域的按键字符映射将 Android 按键代码组合转换为字符代码。例如,当同时按下标识为
KEYCODE_SHIFT
和KEYCODE_A
的按键时,系统将在按键字符映射中查找该组合并找到大写字母“A”,然后将其插入当前聚焦的文本微件中。 - Android 轴代码
-
Android 轴代码是在 Android API 中定义的标准标识符,用于指示特定设备轴。Android 轴代码由
android.view.MotionEvent
类定义为以前缀AXIS_
开头的常量。按键布局指定了 Linux 轴代码如何映射到 Android 轴代码。可能会使用不同的按键布局,具体取决于设备型号、语言、国家/地区、布局或特殊功能。
- Android 元状态
-
Android 元状态是在 Android API 中定义的标准标识符,用于指示按哪些辅助键。Android 元状态由
android.view.KeyEvent
类定义为以前缀META_
开头的常量。当前元状态由 Android InputReader 组件确定,该组件用于监控何时按下/释放辅助键(如
KEYCODE_SHIFT_LEFT
)并设置/重置相应的元状态标记。辅助键和元状态之间的关系采用硬编码,但是按键布局可以改变辅助键本身的映射方式,这反过来又会影响元状态。
- Android 按钮状态
-
Android 按钮状态是在 Android API 中定义的标准标识符,用于指示按下(鼠标或触控笔上的)哪些按钮。Android 按钮状态由
android.view.MotionEvent
类定义为以前缀BUTTON_
开头的常量。当前的按钮状态由 Android InputReader 组件确定,该组件用于监控何时按下/释放(鼠标或触控笔上的)按钮并设置/重置相应的按钮状态标记。
按钮和按钮状态之间的关系采用硬编码。
延伸阅读
按键布局文件
按键布局文件(.kl
文件)将 Linux 按键代码和坐标轴代码映射到 Android 按键代码和坐标轴代码,并指定相关的策略标记。设备专属按键布局文件:
- 对具有按键(包括音量、电源和耳机媒体按键等特殊按键)的内部(内置)输入设备而言是必要文件。
- 对其他输入设备而言是可选文件,而对特殊用途的键盘和操纵杆而言则是推荐文件。
如果没有可用的设备专属按键布局文件,则系统将改选默认文件。
位置
按键布局文件由 USB 供应商、产品(可能还包括版本)ID 或输入设备名称来确定位置。系统会按顺序查阅以下路径:
/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
当构建包含设备名称的文件路径时,设备名称中除“0-9”、“a-z”、“A-Z”、“-”或“_”之外的所有字符将替换为“_”。
常规按键布局文件
系统提供了一个特殊的内置常规按键布局文件,名为 Generic.kl
。此按键布局旨在支持各种标准外部键盘和操纵杆。请勿修改常规按键布局!
语法
按键布局文件是由按键或坐标轴声明和标记组成的纯文本文件。
按键声明
按键声明包含关键字 key
(后跟一个 Linux 按键代码编号和 Android 按键代码名称),或该关键字的用途(后跟 HID 用途和 Android 按键代码名称)。HID 用途由 32 位整数表示,其中高 16 位表示 HID 用途页面,低 16 位表示 HID 用途 ID。任何一项声明都可以后跟一组由空格分隔的可选策略标记。
key 1 ESCAPE key 114 VOLUME_DOWN key 16 Q VIRTUAL key usage 0x0c006F BRIGHTNESS_UP
可识别以下策略标记:
FUNCTION
:按键应解读为如同也按下了 FUNCTION 键。GESTURE
:按键由用户手势(例如手掌摸触摸屏)生成。VIRTUAL
:按键是与主触摸屏相邻的虚拟软键(电容式按钮)。这会导致启用特殊的去抖动逻辑(请参阅下文)。
坐标轴声明
每个坐标轴声明都包含关键字 axis
,后跟一个 Linux 坐标轴代码编号和限定符,用于控制坐标轴(包括至少一个 Android 坐标轴代码名称)的行为。
基本坐标轴
基本坐标轴仅会将 Linux 坐标轴代码映射到 Android 坐标轴代码名称。以下声明将 ABS_X
(由 0x00
表示)映射到 AXIS_X
(由 X
表示)。
axis 0x00 X
在上述示例中,如果 ABS_X
值为 5
,则 AXIS_X
设置为 5
。
分轴
分轴将一个 Linux 坐标轴代码映射到两个 Android 坐标轴代码名称,以便小于或大于阈值的值在映射时可以分割到两个不同的坐标轴。当设备报告的单个物理轴对两个不同的互斥逻辑轴进行编码时,此映射非常有用。
当 ABS_Y
轴的值(由 0x01
表示)小于 0x7f
时,以下声明会将其映射到 AXIS_GAS
;当该值大于 0x7f
时,则会将其映射到 AXIS_BRAKE
。
axis 0x01 split 0x7f GAS BRAKE
在上述示例中,如果 ABS_Y
的值为 0x7d
,则 AXIS_GAS
设为 2
(0x7f - 0x7d
),并且 AXIS_BRAKE
设为 0
。相反,如果 ABS_Y
的值为 0x83
,则 AXIS_GAS
设为 0
,并且 AXIS_BRAKE
设为 4
(0x83 - 0x7f
)。最后,如果 ABS_Y
的值等于 0x7f
的分值,则 AXIS_GAS
和 AXIS_BRAKE
均设为 0
。
反转坐标轴
反转坐标轴会反转坐标轴值的符号。以下声明将 ABS_RZ
(由 0x05
表示)映射到 AXIS_BRAKE
(由 BRAKE
表示),并通过求补来反转输出。
axis 0x05 invert BRAKE
在上述示例中,如果 ABS_RZ
的值为 2
,则 AXIS_BRAKE
设置为 -2
。
中心平面选项
由于存在噪声,即使在操纵杆未被使用时,操纵杆设备也可能会报告输入事件。这种噪声通常来自左侧杆和/或右侧杆,会导致驱动程序报告接近 0 的位置值。“中心平面”值用于指定预期会从处于静止状态的控制杆获得的噪声量。
Linux 输入协议为输入设备驱动程序提供了一种用于指定操纵杆轴中心平面值的方式,但并非所有驱动程序都会报告该值,并且有些驱动程序会提供不正确的值。为了解决这个问题,可以在轴声明后面加一个 flat
选项,以用于指定轴的中心位置周围多宽的区域应被视为居中的区域。
例如,如果设备驱动程序报告的 AXIS_X
值介于 0 到 100 之间,则 Android 输入系统会将 0 映射到 -1,并将 100 映射到 1。该范围的中心在未缩放的坐标中为 50,在经过缩放的坐标中为 0。如果平面值等于 10,那么开发者应假定报告的任何介于 -0.1 到 0.1 之间的 AXIS_X
值(在未缩放的坐标中,则是介于 40 到 60 之间)均为噪声,并将来自操纵杆的这些值视为零。
注意:尽管按键布局文件会指定驱动程序坐标空间的值,但由 android.view.InputDevice.MotionRange#getFlat() 报告的值是在 Android 坐标空间内的值。
axis 0x03 Z flat 4096
在以上示例中,中心平面值设为了 4096
。
注释
注释行以“#”开头,并持续到这一行的结束位置:
# A comment!
空白行会被忽略。
示例
键盘
# This is an example of a key layout file for a keyboard. 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 key 11 0 key 12 MINUS key 13 EQUALS key 14 DEL # etc...
系统控件
# This is an example of a key layout file for basic system controls, # such as volume and power keys which are typically implemented as GPIO pins # the device decodes into key presses. key 114 VOLUME_DOWN key 115 VOLUME_UP key 116 POWER
电容式按钮
# This is an example of a key layout file for a touch device with capacitive buttons. key 139 MENU VIRTUAL key 172 HOME VIRTUAL key 158 BACK VIRTUAL key 217 SEARCH VIRTUAL
耳机插孔媒体控件
# This is an example of a key layout file for headset mounted media controls. # A typical headset jack interface might have special control wires or detect known # resistive loads as corresponding to media functions or volume controls. # This file assumes that the driver decodes these signals and reports media # controls as key presses. key 163 MEDIA_NEXT key 165 MEDIA_PREVIOUS key 226 HEADSETHOOK
操纵杆
# This is an example of a key layout file for a joystick. # These are the buttons that the joystick supports, represented as keys. key 304 BUTTON_A key 305 BUTTON_B key 307 BUTTON_X key 308 BUTTON_Y key 310 BUTTON_L1 key 311 BUTTON_R1 key 314 BUTTON_SELECT key 315 BUTTON_START key 316 BUTTON_MODE key 317 BUTTON_THUMBL key 318 BUTTON_THUMBR # Left and right stick. # The reported value for flat is 128 in a range of -32767 to 32768, which is absurd. # This confuses applications that rely on the flat value because the joystick # actually settles in a flat range of +/- 4096 or so. We override it here. axis 0x00 X flat 4096 axis 0x01 Y flat 4096 axis 0x03 Z flat 4096 axis 0x04 RZ flat 4096 # Triggers. axis 0x02 LTRIGGER axis 0x05 RTRIGGER # Hat. axis 0x10 HAT_X axis 0x11 HAT_Y
虚拟软键
在下列使用情形中,输入系统提供了特殊的功能来实现虚拟软键:
- 如果虚拟软键以图形方式显示在屏幕上(例如在 Galaxy Nexus 上),则它们将由系统界面包中的导航栏组件实现。由于要在系统的高层中实现图形虚拟软键,因此不涉及按键布局文件,以下信息将不适用。
- 如果虚拟软键作为属于主触摸屏一部分的扩展可触摸区域进行实现(例如在 Nexus One 上),则输入系统会使用虚拟按键映射文件将 X/Y 触摸坐标转换为 Linux 按键代码,然后使用按键布局文件将 Linux 按键代码转换为 Android 按键代码(有关虚拟按键映射文件的详细信息,请参阅触摸设备)。触摸屏输入设备的按键布局文件必须指定相应的按键映射并使每个按键都包含
VIRTUAL
标记。 - 如果虚拟软键作为独立于主触摸屏的电容式按钮进行实现(例如在 Nexus S 上),则内核设备驱动程序或固件负责将触摸转换为 Linux 按键代码,然后输入系统使用按键布局文件将其转换为 Android 按键代码。电容式按钮输入设备的按键布局文件必须指定相应的按键映射并使每个按键都包含
VIRTUAL
标记。
如果虚拟软键位于触摸屏内或离触摸屏很近,则当用户在屏幕底部附近触摸或在屏幕上从上往下或从下往上滑动手指时,容易不小心按到按钮。为了避免出现这种情况,输入系统会应用一个短暂的去抖动,以便在最近触摸触摸屏之后的短时间内忽略虚拟软键按压(该延迟称为“虚拟键安静时间”)。
要启用虚拟软键去抖动,请执行以下操作:
- 为触摸屏或电容式按钮输入设备提供按键布局文件,并为每个按键设置
VIRTUAL
标记。key 139 MENU VIRTUAL key 172 HOME VIRTUAL key 158 BACK VIRTUAL key 217 SEARCH VIRTUAL
- 在框架
config.xml
资源的资源叠加层中设置虚拟按键安静时间的值。<!-- Specifies the amount of time to disable virtual keys after the screen is touched to filter out accidental virtual key presses due to swiping gestures or taps near the edge of the display. May be 0 to disable the feature. It is recommended that this value be no more than 250 ms. This feature should be disabled for most devices. --> <integer name="config_virtualKeyQuietTimeMillis">250</integer>
验证
您应使用验证按键映射工具验证您的按键布局文件。