输入

Android Input HAL icon

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) 可能会通过 SysFS 导出板级配置属性,供 Android InputReader 组件使用,例如触摸屏上虚拟按键的位置。

有关不同设备如何使用板级配置属性的详细信息,请参阅设备类别部分。

资源叠加

一些输入行为通过 config.xml 中的资源叠加进行配置,例如盖子开关的操作。

以下是一些示例

  • config_lidKeyboardAccessibility:指定盖子开关对硬件键盘是可访问还是隐藏的影响。

  • config_lidNavigationAccessibility:指定盖子开关对触控板是可访问还是隐藏的影响。

  • config_longPressOnPowerBehavior:指定用户按住电源按钮时应发生的情况。

  • config_lidOpenRotation:指定盖子开关对屏幕方向的影响。

有关每个配置选项的详细信息,请参阅 frameworks/base/core/res/res/values/config.xml 中的文档。

按键映射

Android EventHubInputReader 组件使用按键映射来配置从 Linux 事件代码到按键、操纵杆按钮和操纵杆轴的 Android 事件代码的映射。映射可能取决于设备或语言。

有关不同设备如何使用按键映射的详细信息,请参阅设备类别部分。

输入设备配置文件

Android EventHubInputReader 组件使用输入设备配置文件来配置特殊的设备特性,例如如何报告触摸尺寸信息。

有关不同设备如何使用输入设备配置映射的详细信息,请参阅设备类别部分。

了解 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_RELEV_ABS 事件的一部分传递。

Linux 开关代码

Linux 开关代码是用于报告设备上开关状态的标准标识符,例如盖子开关。Linux 开关代码在 linux/input.h 头文件中定义,使用以 SW_ 前缀开头的常量。Linux 内核输入驱动程序将开关状态更改报告为 EV_SW 事件。

Android 应用程序通常不接收来自开关的事件,但系统可能会在内部使用它们来控制各种特定于设备的功能。

Android 按键代码

Android 按键代码是在 Android API 中定义的标准标识符,用于指示特定按键,例如“HOME”。Android 按键代码由 android.view.KeyEvent 类定义为以 KEYCODE_ 前缀开头的常量。

按键布局指定 Linux 按键代码如何映射到 Android 按键代码。根据键盘型号、语言、国家/地区、布局或特殊功能,可以使用不同的按键布局。

Android 按键代码的组合使用设备和特定于语言环境的按键字符映射转换为字符代码。例如,当同时按下标识为 KEYCODE_SHIFTKEYCODE_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 组件确定,该组件监视何时按下/释放按钮(在鼠标或触控笔上)并设置/重置相应的按钮状态标志。

按钮和按钮状态之间的关系是硬编码的。

延伸阅读

  1. Linux 输入事件代码
  2. Linux 多点触控协议
  3. Linux 输入驱动程序
  4. Linux 力反馈
  5. HID 信息,包括 HID 用法表