Android Input事件

EventHub

// frameworks/native/services/inputflinger/EventHub.cpp
status_t EventHub::openDeviceLocked(const char* devicePath) {
    ...

    // Get device name.
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name = buffer;
    }


    // product vendor 用来获取kl映射文件
    // Get device identifier.
    struct input_id inputId;
    if(ioctl(fd, EVIOCGID, &inputId)) {
        ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
        close(fd);
        return -1;
    }
    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;
    identifier.vendor = inputId.vendor;
    identifier.version = inputId.version;

    ...

    // 推断设备类型,设置class
    // See if this is a touch pad.
    // Is this a new modern multi-touch driver?
    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
        // Some joysticks such as the PS3 controller report axes that conflict
        // with the ABS_MT range.  Try to confirm that the device really is
        // a touch screen.
        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
        }
    // Is this an old style single-touch driver?
    } else if (test_bit(BTN_TOUCH, device->keyBitmask)
            && test_bit(ABS_X, device->absBitmask)
            && test_bit(ABS_Y, device->absBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_TOUCH;
    // Is this a BT stylus?
    } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
                test_bit(BTN_TOUCH, device->keyBitmask))
            && !test_bit(ABS_X, device->absBitmask)
            && !test_bit(ABS_Y, device->absBitmask)) {
        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
        // Keyboard will try to claim some of the buttons but we really want to reserve those so we
        // can fuse it with the touch screen data, so just take them back. Note this means an
        // external stylus cannot also be a keyboard device.
        device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
        ALOGI("has INPUT_DEVICE_CLASS_EXTERNAL_STYLUS");
    }

    // 根据kl映射文件, 设置class
    // 'Q' key support = cheap test of whether this is an alpha-capable kbd
    if (hasKeycodeLocked(device, AKEYCODE_Q)) {
        device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
        ALOGI("has INPUT_DEVICE_CLASS_ALPHAKEY");
    }

    // See if this device has a DPAD.
    if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
            hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
            hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
            hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
            hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
        device->classes |= INPUT_DEVICE_CLASS_DPAD;
        ALOGI("has INPUT_DEVICE_CLASS_DPAD");
    }


    // See if this device has a gamepad. 游戏手柄
    for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
        if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
            device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
            ALOGI("has INPUT_DEVICE_CLASS_GAMEPAD");
            break;
        }
    }

    ...
}

通过ioctl获取设备信息,比如设备名,product vendor id等信息, 在根据id信息拼接出映射文件,得到keyLayout

根据这些信息设置输入设备的class类型

# dumpsys input
    5: fts
      Classes: 0x00000015
      Path: /dev/input/event3
      Enabled: true
      Descriptor: ddfb717d907e8defcc82f81ba273111db984c56a
      Location: fts/input0
      ControllerNumber: 0
      UniqueId:
      Identifier: bus=0x0018, vendor=0x0001, product=0x0002, version=0x0100
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false

image

游戏手柄

static const int32_t GAMEPAD_KEYCODES[] = {
        AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
        AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
        AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
        AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
        AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
        AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
};


// See if this device has a gamepad. 游戏手柄
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
    if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
        device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
        break;
    }
}

Classes确认后,会通知InputReader::createDeviceLocked创建设备

InputReader


InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);


    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }

    // Devices with mics.
    if (classes & INPUT_DEVICE_CLASS_MIC) {
        device->setMic(true);
    }

    // 根据class 创建对应Mapper
    // Switch-like devices.
    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
        device->addMapper(new SwitchInputMapper(device));
    }

    // Scroll wheel-like devices.
    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
        device->addMapper(new RotaryEncoderInputMapper(device));
    }

    // Vibrator-like devices.
    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
        device->addMapper(new VibratorInputMapper(device));
    }

    // 根据class确认source keyboardType
    // Keyboard-like devices.
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

    //单点或者多点触控
    // Touchscreens and touchpad devices.
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        ALOGI("add mapper MultiTouchInputMapper");
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        ALOGI("add mapper SingleTouchInputMapper");
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ...
}

这里每个Mapper都是InputMapper的扩展,InputMapper是基类,比如说

  1. configure // 配置
  2. sync // 同步
  3. process // 处理
  4. syncTouch // 同步点击
  5. reset // 移除设备时候触发

当收到对应的Event事件,下面是sync的调用

dipper:/ # getevent -l
add device 1: /dev/input/event5
  name:     "sdm845-tavil-snd-card Button Jack"
add device 2: /dev/input/event4
  name:     "sdm845-tavil-snd-card Headset Jack"
could not get driver version for /dev/input/mice, Not a typewriter
add device 3: /dev/input/event0
  name:     "qpnp_pon"
add device 4: /dev/input/event1
  name:     "uinput-goodix"
add device 5: /dev/input/event3
  name:     "fts"
add device 6: /dev/input/event2
  name:     "gpio-keys"

/dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000080
/dev/input/event3: EV_KEY       BTN_TOUCH            DOWN
/dev/input/event3: EV_KEY       BTN_TOOL_FINGER      DOWN
/dev/input/event3: EV_ABS       ABS_MT_POSITION_X    00000199
/dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    0000066d
/dev/input/event3: EV_SYN       SYN_REPORT           00000000   //触发对应mapper的sync
/dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   ffffffff
/dev/input/event3: EV_KEY       BTN_TOUCH            UP
/dev/input/event3: EV_KEY       BTN_TOOL_FINGER      UP
/dev/input/event3: EV_SYN       SYN_REPORT           00000000
void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);
    // 是同步事件,调用sync
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        reportEventForStatistics(rawEvent->when);
        sync(rawEvent->when);
    }
}

这里不列举全部过程了,不同mapper有不同的实现。 另外一个比较重要的函数 处理RawEvent数据


void TouchInputMapper::sync(nsecs_t when) {
    const RawState* last = mRawStatesPending.empty() ?
            &mCurrentRawState : &mRawStatesPending.back();
    // Push a new state.
    mRawStatesPending.emplace_back();

    RawState* next = &mRawStatesPending.back();
    next->clear();
    next->when = when;
    // 各种同步
    // Sync button state.
    next->buttonState = mTouchButtonAccumulator.getButtonState()
            | mCursorButtonAccumulator.getButtonState();

    // Sync scroll
    next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
    next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
    mCursorScrollAccumulator.finishSync();

    // Sync touch
    // 这个只有 SingleTouchInputMapper 和 MultiTouchInputMapper 有实现
    syncTouch(when, next);

    processRawTouches(false /*timeout*/);
}


void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    mCurrentCookedState.clear(); //清理现在的状态

    // 主要是处理数据,比如display方向 重新计算x y坐标等等
    cookPointerData();          //mCurrentCookedState 被重新赋值

    if (mDeviceMode == DEVICE_MODE_POINTER) {
        // 省略 我设备是DEVICE_MODE_DIRECT 模式
    } else{
        if (mDeviceMode == DEVICE_MODE_DIRECT
                && mConfig.showTouches && mPointerController != nullptr) {
            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);

            mPointerController->setButtonState(mCurrentRawState.buttonState);
            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    mCurrentCookedState.cookedPointerData.touchingIdBits,
                    mViewport.displayId);
        }

        if (!mCurrentMotionAborted) {
            // 各种Dispatch只是参数不同,最终都会到InputDispatch::notifyMotion
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }

        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
            mCurrentMotionAborted = false;
        }

    }
}

各种Dispatch只是参数不同,最终都会到InputDispatch::notifyMotion

InputDispatch

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {

        // Just enqueue a new motion event.
        MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, 0x6b000000,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState, args->classification,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);

        needWake = enqueueInboundEventLocked(newEntry);
}

重新构建MotionEntry进入队列并唤醒

入列

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty(); //空就不需要唤醒
    mInboundQueue.enqueueAtTail(entry); //添加到队列尾部

    case EventEntry::TYPE_MOTION: {
        // 一定是Down才处理
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                && mInputTargetWaitApplicationToken != nullptr) {
            int32_t displayId = motionEntry->displayId;
            int32_t x = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_X));
            int32_t y = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_Y));

            // 找到对应的窗口
            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
            if (touchedWindowHandle != nullptr
                    && touchedWindowHandle->getApplicationToken()
                            != mInputTargetWaitApplicationToken) {
                // User touched a different application than the one we are waiting on.
                // Flag the event, and start pruning the input queue.
                mNextUnblockedEvent = motionEntry;
                needWake = true; //设为唤醒
            }
        }
        break;
    }
}

出列


bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {

    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;

    // Identify targets.
    std::vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    int32_t injectionResult; // 结果
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }

}

input命令注入

以input tap为例

public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER;
public static final int SOURCE_CLASS_POINTER = 0x00000002;

class InputTap implements InputCmd {
    @Override
    public void run(int inputSource, int displayId) {
        inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
        sendTap(inputSource, Float.parseFloat(nextArgRequired()),
                Float.parseFloat(nextArgRequired()), displayId);
    }

    void sendTap(int inputSource, float x, float y, int displayId) {
        final long now = SystemClock.uptimeMillis();
        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
                displayId);
        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
    }
}

其实就是发送MotionEvent.ACTION_DOWNMotionEvent.ACTION_DOWN 两个事件,进入到InputDispatcher::injectInputEvent, 是不经过InputReader的

int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
        uint32_t policyFlags) {

    firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
            motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(),
            policyFlags,
            action, actionButton, motionEvent->getFlags(),
            motionEvent->getMetaState(), motionEvent->getButtonState(),
            motionEvent->getClassification(), motionEvent->getEdgeFlags(),
            motionEvent->getXPrecision(), motionEvent->getYPrecision(),
            motionEvent->getDownTime(),
            uint32_t(pointerCount), pointerProperties, samplePointerCoords,
            motionEvent->getXOffset(), motionEvent->getYOffset());

    bool needWake = false;
    for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) {
        EventEntry* nextEntry = entry->next;
        needWake |= enqueueInboundEventLocked(entry);
        entry = nextEntry;
    }

}

殊途同归,也是通过enqueueInboundEventLocked函数进入队列

Android应用层分发

未完待续

总结

  1. 设备IOCTL 和 keylayout决定设备classes
  2. 设备的classes决定了不同类型 Mapper, 每个mapper都有其对应的input event输入事件处理实现
  3. InputDispatch分发殊途同归,都是通过enqueueInboundEventLocked进入队列,通过
posted @ 2025-09-03 19:00  梦过无声  阅读(36)  评论(0)    收藏  举报