Android 7系统输入(三):InputReader — 原始事件到Android事件的转换引擎

系列目录 :第一篇:从硬件到应用的事件旅程 | 第二篇:EventHub --- 原始事件的采集者 | 第三篇:InputReader --- 原始事件到Android事件的转换引擎 | 第四篇:InputDispatcher --- 事件分发与ANR超时机制 | 第五篇:应用侧 --- InputChannel、ViewRootImpl与事件消费


一、InputReader 的位置与职责

复制代码
EventHub  →  InputReader  →  InputDispatcher  →  APP
RawEvent       ▲              NotifyArgs
           本篇聚焦

InputReader 的职责是把 EventHub 采集到的 RawEvent 加工成 Android 框架层可理解的 KeyEvent 和 MotionEvent,然后通过 InputListener 接口传递给 InputDispatcher

这涉及:

  • 不同设备类型需要不同的处理策略(键盘 vs 触摸 vs 鼠标)
  • 坐标系统需要转换(物理像素 → 逻辑坐标 → 显示坐标)
  • 多点触控需要跟踪多个手指的状态
  • 按键需要做 Linux 键码 → Android 键码的映射
  • 需要考虑屏幕旋转等设备状态

源码位置:

复制代码
frameworks/native/services/inputflinger/InputReader.cpp
frameworks/native/services/inputflinger/InputReader.h
frameworks/native/services/inputflinger/InputListener.h

本文中所有代码块如未特别标注,均来自 InputReader.cpp。


二、InputReader 的线程模型与主循环

2.1 线程启动

源码路径frameworks/native/services/inputflinger/InputReader.cpp

cpp 复制代码
void InputReader::start() {
    mThread = new InputReaderThread(*this);
    mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
}

InputReaderThread 不断调用 InputReader::loopOnce()

cpp 复制代码
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

2.2 主循环 loopOnce()

cpp 复制代码
void InputReader::loopOnce() {
    // 1. 从 EventHub 获取事件(阻塞等待)
    size_t count = mEventHub->getEvents(timeout, mEventBuffer, EVENT_BUFFER_SIZE);

    {
        AutoMutex _l(mLock);

        if (count) {
            // 2. 逐个处理 RawEvent
            processEventsLocked(mEventBuffer, count);
        }

        // 3. 设备超时检测(如触摸抬起后生成完成信号)
    }

    // 4. 批量通知 InputDispatcher
    mQueuedListener->flush();
}

三、InputReader 的核心数据流

3.1 总体架构

复制代码
EventHub::getEvents()
    │  RawEvent[]
    ▼
InputReader::processEventsLocked()
    │
    ├── DEVICE_ADDED    → addDeviceLocked() → 创建 InputDevice + Mapper
    ├── DEVICE_REMOVED  → removeDeviceLocked()
    ├── FINISHED_DEVICE_SCAN → 通知配置变更
    └── 普通输入事件 → processEventsForDeviceLocked()
                          │
                          ▼ InputDevice::process()
                          │
                          ▼ InputMapper::process(rawEvent)
                          │
                          ▼  NotifyArgs
                          InputListener::notify*(args)

3.2 关键类关系

复制代码
InputReader
    ├── mEventHub (EventHub*)
    ├── mDevices (KeyedVector<int32_t, InputDevice*>)
    │       └── InputDevice
    │             ├── mMappers (Vector<InputMapper*>)
    │             │     ├── SwitchInputMapper
    │             │     ├── KeyboardInputMapper
    │             │     ├── CursorInputMapper
    │             │     ├── TouchInputMapper
    │             │     │     ├── SingleTouchInputMapper
    │             │     │     └── MultiTouchInputMapper
    │             │     ├── JoystickInputMapper
    │             │     └── ExternalStylusInputMapper
    │             └── device info (classes, identifier, keyMap, ...)
    └── mQueuedListener (QueuedInputListener*)
              └── 暂存 NotifyArgs,在 loopOnce() 末尾批量 flush

四、InputDevice:设备的内部表示

4.1 InputDevice 的创建

当 EventHub 上报 DEVICE_ADDED 时,InputReader 调用 addDeviceLocked()

cpp 复制代码
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);

    InputDevice* device = new InputDevice(&mContext, deviceId, ...);

    // 根据设备类别添加对应的 InputMapper
    // 一个设备可能有多个 Mapper(如键盘+轨迹球的复合设备)
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        device->addMapper(new KeyboardInputMapper());
    }
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper());
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper());
    }
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new CursorInputMapper());
    }

    device->configure(...);
    device->reset(...);
    mDevices.add(deviceId, device);
}

4.2 InputDevice::process()

cpp 复制代码
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (size_t i = 0; i < count; i++) {
        const RawEvent* rawEvent = &rawEvents[i];
        // 逐个 Mapper 处理,不相关的会被忽略
        for (size_t j = 0; j < mMappers.size(); j++) {
            mMappers[j]->process(rawEvent);
        }
    }
}

五、InputMapper 体系详解

5.1 KeyboardInputMapper:键盘事件处理

将按键原始事件转换为 Android KeyEvent:

cpp 复制代码
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;

        // 1. 键码映射:Linux scanCode → Android keyCode
        int32_t keyCode;
        if (getEventHub()->mapKey(getDeviceId(), scanCode,
                                  usageCode, &keyCode, &policyFlags)) {

            // 2. 确定动作
            bool keyDown = (rawEvent->value != 0);
            int32_t action = keyDown ? AKEY_EVENT_ACTION_DOWN
                                     : AKEY_EVENT_ACTION_UP;

            // 3. 更新 Meta 状态(Shift、Ctrl、Alt 等修饰键)
            if (keyDown) {
                newMetaState = addMetaState(oldMetaState, keyCode);
            } else {
                newMetaState = clearMetaState(oldMetaState, keyCode);
            }

            // 4. 生成 NotifyKeyArgs → 通知 Dispatcher
            NotifyKeyArgs args(..., action, keyCode, scanCode, metaState, ...);
            getListener()->notifyKey(&args);
        }
        break;
    }
    case EV_SYN:
        // 同步事件,刷新状态
        break;
    case EV_LED:
        // 更新 LED 状态(如 Caps Lock 指示灯)
        break;
    }
}

键码映射 是 KeyboardInputMapper 的核心。EventHub 加载了 .kl 文件,mapKey() 通过 KeyMap 完成查找:

复制代码
Linux scanCode  →  KeyLayoutMap  →  Android keyCode
     30         →  Generic.kl    →  AKEYCODE_A (29)
    114         →  Generic.kl    →  AKEYCODE_VOLUME_DOWN (25)

5.2 MultiTouchInputMapper:多点触控处理

最复杂的 InputMapper,需要跟踪多个手指、识别多种动作、完成坐标变换。

协议 A vs 协议 B

Linux 多点触控有两种协议:

协议 A(老协议)------简单的多点坐标上报,不跟踪手指 ID:

复制代码
ABS_MT_POSITION_X 100
ABS_MT_POSITION_Y 200
SYN_MT_REPORT
ABS_MT_POSITION_X 300
ABS_MT_POSITION_Y 400
SYN_MT_REPORT
SYN_REPORT

协议 B(现代协议) ------通过 ABS_MT_TRACKING_ID 跟踪每个触点:

复制代码
ABS_MT_SLOT          0
ABS_MT_TRACKING_ID   45      ← 手指 #45 按下
ABS_MT_POSITION_X    100
ABS_MT_POSITION_Y    200
ABS_MT_PRESSURE      80
SYN_REPORT

Android 7 主要使用协议 B。

核心流程
cpp 复制代码
void MultiTouchInputMapper::sync(nsecs_t when) {
    // 1. 获取所有触点的快照
    for (size_t i = 0; i < mMultiTouchMotionAccumulator.getSlotCount(); i++) {
        const Slot* slot = mMultiTouchMotionAccumulator.getSlot(i);
        if (slot->isInUse()) {
            // trackingId >= 0 → 按下的手指
            // trackingId == -1 → 抬起的槽位
            rawX = slot->x;
            rawY = slot->y;
            // ... 记录到 pointerCoords
        }
    }

    // 2. 坐标变换
    calculateTransformedXY(rawX, rawY, &transformedX, &transformedY);

    // 3. 确定动作类型
    int32_t action;
    if (pointerCount == 0 && oldPointerCount > 0)
        action = AMOTION_EVENT_ACTION_UP;        // 全部抬起
    else if (pointerCount > 0 && oldPointerCount == 0)
        action = AMOTION_EVENT_ACTION_DOWN;      // 首次按下
    else if (pointerCount > oldPointerCount)
        action = AMOTION_EVENT_ACTION_POINTER_DOWN(...); // 新手指按下
    else if (pointerCount < oldPointerCount)
        action = AMOTION_EVENT_ACTION_POINTER_UP(...);   // 某手指抬起
    else
        action = AMOTION_EVENT_ACTION_MOVE;      // 移动

    // 4. 生成 NotifyMotionArgs → 通知 Dispatcher
    NotifyMotionArgs args(..., action, pointerCount, pointerCoords, ...);
    getListener()->notifyMotion(&args);
}
坐标变换
cpp 复制代码
void TouchInputMapper::calculateTransformedXY(int32_t rawX, int32_t rawY,
                                               float* outX, float* outY) {
    float x = rawX, y = rawY;

    // 1. 归一化到 [0, 1] 范围
    x = (x - rawMinX) / (rawMaxX - rawMinX);
    y = (y - rawMinY) / (rawMaxY - rawMinY);

    // 2. 应用屏幕旋转(0°/90°/180°/270°)
    rotate(x, y, surfaceOrientation);

    // 3. 缩放到显示视口
    if (viewport) {
        *outX = viewportX + x * viewportWidth;
        *outY = viewportY + y * viewportHeight;
    }
}

5.3 CursorInputMapper:鼠标/轨迹球

处理 EV_REL 类型的相对位移:

cpp 复制代码
void CursorInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_REL:
        if (rawEvent->code == REL_X) mRelX = rawEvent->value;
        if (rawEvent->code == REL_Y) mRelY = rawEvent->value;
        if (rawEvent->code == REL_WHEEL) mRelWheel = rawEvent->value;
        break;
    case EV_KEY:
        // 鼠标按键(左键 BTN_LEFT、右键 BTN_RIGHT、中键 BTN_MIDDLE)
        if (rawEvent->code == BTN_LEFT) ...;
        break;
    case EV_SYN:
        sync(rawEvent->when); // 累积的相对位移一次生成 MotionEvent
        break;
    }
}

鼠标光标坐标由 InputDispatcher 维护,CursorInputMapper 只提供相对位移量。


六、QueuedInputListener:批量通知机制

InputReader 不会每加工一个事件就通知 InputDispatcher,而是批量暂存,最后一起通知

cpp 复制代码
class QueuedInputListener : public InputListenerInterface {
    Vector<NotifyArgs> mArgsQueue;

    virtual void notifyKey(const NotifyKeyArgs* args) {
        mArgsQueue.push(new NotifyKeyArgs(*args));  // 暂存
    }

    virtual void notifyMotion(const NotifyMotionArgs* args) {
        mArgsQueue.push(new NotifyMotionArgs(*args)); // 暂存
    }

    void flush() {
        for (size_t i = 0; i < mArgsQueue.size(); i++) {
            mInnerListener->notifyXxx(mArgsQueue[i]); // 批量转发
        }
        mArgsQueue.clear();
    }
};

loopOnce() 末尾调用 mQueuedListener->flush() 一次性把本轮所有事件传递给 InputDispatcher。减少跨线程通信开销。


七、InputListener 接口

cpp 复制代码
class InputListenerInterface {
public:
    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs*) = 0;
    virtual void notifyKey(const NotifyKeyArgs*) = 0;
    virtual void notifyMotion(const NotifyMotionArgs*) = 0;
    virtual void notifySwitch(const NotifySwitchArgs*) = 0;
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs*) = 0;
};

InputDispatcher 实现了这个接口,notifyKey()notifyMotion() 即分发入口。


八、一个触摸事件的完整加工过程

以用户手指触摸屏幕为例:

复制代码
RawEvent 序列(从 EventHub 读取):
  EV_ABS  ABS_MT_TRACKING_ID  45       ← 新手指
  EV_ABS  ABS_MT_POSITION_X   320
  EV_ABS  ABS_MT_POSITION_Y   580
  EV_ABS  ABS_MT_PRESSURE     128
  EV_SYN  SYN_REPORT                  ← 一帧数据完毕

InputReader 处理:
  1. processEventsLocked() 
     → processEventsForDeviceLocked()
     → InputDevice::process()

  2. MultiTouchInputMapper 处理每个 RawEvent
     slot 0: trackingId=45, x=320, y=580, pressure=128
     
  3. EV_SYN 到达 → sync(when):
     a. 检查 trackingId 45 是否是新的 → 是 → 判定为 ACTION_DOWN
     b. calculateTransformedXY(320, 580):
        - 归一化: x=320/1080=0.296, y=580/1920=0.302
        - 旋转: 无旋转
        - 映射视口: x=0.296*1080=320, y=0.302*1920=580
     c. 生成 NotifyMotionArgs(ACTION_DOWN, pointerCount=1, ...)

  4. notifyMotion(&args) → 加入 mArgsQueue

  5. loopOnce() 末尾 → flush() → InputDispatcher::notifyMotion()

九、总结

组件 职责
InputReaderThread 驱动 loopOnce() 主循环
InputDevice 代表一个物理设备,管理一组 Mapper
KeyboardInputMapper Linux 键码 → Android 键码映射,Meta 状态管理
MultiTouchInputMapper 多点触控跟踪,协议 B 解析,动作类型判定
SingleTouchInputMapper 单点触控,协议 A 兼容
CursorInputMapper 相对位移累积,鼠标按键处理
QueuedInputListener 批量通知,减少跨线程通信
坐标变换 raw → 归一化 → 旋转 → 视口映射

下一篇将聚焦 InputDispatcher,揭开事件分发与 ANR 超时机制的面纱。