3. Android输入子系统基础

在车载 Android 系统中,触摸屏、物理按键、旋钮、方向盘按键、红外遥控等输入设备最终都要经过 Android 原生输入子系统,才能变成应用可消费的 MotionEvent 或 KeyEvent。理解 InputManagerService、EventHub、InputReader、InputDispatcher 四个核心组件及其数据流,是后续做 CarInputService 定制、按键映射、焦点策略、输入注入与排障的基础。

一、核心组件与职责边界

Android 输入子系统运行在 system_server 进程中,由 Native 层与 Java 层共同组成。Java 层的入口是 InputManagerService(IMS),它负责生命周期管理、与 WindowManagerService 协作、向应用分发策略配置;Native 层则由 InputManager、InputReader、InputDispatcher、EventHub 构成实际的数据处理管线。

  • EventHub:直接通过 epoll 监听 /dev/input/ 下的设备节点,读取原始 input_event,并负责设备热插拔的发现与移除。
  • InputReader:从 EventHub 取出原始事件,结合设备能力(InputDevice、InputMapper)做协议解析、坐标变换、去抖、多点触控合成,输出标准化的 NotifyMotionArgs / NotifyKeyArgs。
  • InputDispatcher:接收 InputReader 的标准化事件,结合窗口焦点、触摸目标、ANR 超时、输入通道(InputChannel)状态,决定把事件投递给哪个窗口。
  • InputManagerService:Java 层门面,负责启动 Native 输入线程、注册输入设备监听、处理 monitorInput、注入事件、与 WMS 同步焦点窗口。
关键理解:EventHub 是“设备接入层”,InputReader 是“协议解析层”,InputDispatcher 是“策略分发层”,IMS 是“系统服务门面层”。四者职责清晰,排障时必须先定位事件卡在哪一层。

二、输入事件完整数据流

下面这张图展示了从硬件中断到应用回调的完整链路,也是本节最需要建立的整体心智模型。

Android 输入子系统数据流(车载场景) 硬件输入设备 触摸屏 / 按键 / 旋钮 /dev/input/eventX EventHub epoll 监听设备节点 热插拔发现 / 原始事件 InputReader InputMapper 解析 坐标变换 / 多点合成 InputDispatcher 焦点窗口 / 触摸目标 ANR 超时 / 通道投递 InputManagerService(Java 层门面) 启动 Native 线程 · 注入事件 · 与 WMS 同步焦点 · 设备监听 应用窗口 ViewRootImpl dispatchTouchEvent InputChannel socketpair 双向通道 连接 Dispatcher 与窗口 CarInputService 按键映射 / 旋钮策略 输入拦截 / 场景定制 ① 硬件中断 → EventHub 读取原始 input_event ② InputReader 解析为 NotifyMotionArgs / NotifyKeyArgs ③ InputDispatcher 按焦点与通道投递 → 应用或 CarInputService 消费

三、关键参数与线程模型

InputReader 与 InputDispatcher 各自运行在独立线程中,通过 InputListener 接口解耦。InputReader 线程负责阻塞读取 EventHub,InputDispatcher 线程负责分发与超时监控。两者通过 mInboundQueue 传递事件,避免读设备被分发阻塞拖慢。

组件 所在线程 核心输入 核心输出 典型排障点
EventHub InputReader 线程 /dev/input/eventX RawEvent 设备节点权限、热插拔丢失
InputReader InputReader 线程 RawEvent + 设备能力 NotifyArgs 坐标错乱、按键码不识别
InputDispatcher InputDispatcher 线程 NotifyArgs + 焦点窗口 InputChannel 投递 ANR、焦点窗口为空、通道阻塞
InputManagerService system_server 主线程 系统调用 / WMS 回调 服务接口 注入失败、监听未注册

四、车载场景案例:旋钮事件如何到达应用

假设车机有一个旋钮,硬件上报 EV_KEY / KEY_VOLUMEUP。EventHub 读取后交给 InputReader,InputReader 通过 KeyboardInputMapper 生成 NotifyKeyArgs。InputDispatcher 根据当前焦点窗口决定投递目标。如果应用未处理,事件会继续冒泡到 PhoneWindowManager 或 CarInputService 的拦截逻辑。

// 查看输入设备与事件节点
adb shell getevent -lp /dev/input/event2

// 实时观察原始事件
adb shell getevent -lt /dev/input/event2

// 查看输入设备列表
adb shell dumpsys input | grep -A 5 "Input Devices"

// 查看分发器状态与焦点窗口
adb shell dumpsys input | grep -A 20 "Input Dispatcher State"
在车载项目中,旋钮、方向盘按键常被映射为 KEYCODE_DPAD_* 或自定义按键码。若应用收不到事件,先用 getevent 确认内核层有上报,再用 dumpsys input 确认 InputReader 是否识别,最后检查焦点窗口与 InputChannel 是否正常。

五、常见误区与复盘

  • 误区一:把 IMS 当成事件处理核心。IMS 只是门面,真正解析与分发在 Native 层,排障要看 InputReader / InputDispatcher 日志。
  • 误区二:忽略设备能力配置。如果 .idc 或 kl 文件缺失,InputReader 无法正确映射按键码,表现为“设备存在但无事件”。
  • 误区三:焦点窗口为空仍期待应用收到事件。InputDispatcher 必须有有效焦点窗口或触摸目标,否则事件会被丢弃或触发 ANR。
  • 误区四:在应用层做全局按键拦截。车载全局按键应优先在 CarInputService 或 InputDispatcher 策略层处理,避免多应用竞争。
调试输入问题时,务必区分“内核有事件”和“框架有事件”。getevent 有输出只代表 EventHub 能读到;dumpsys input 中 RecentQueue 有记录才代表 InputReader 已解析;应用收到回调才代表 InputDispatcher 投递成功。三层逐一确认,才能快速定位问题层级。