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

游戏手柄
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是基类,比如说
- configure // 配置
- sync // 同步
- process // 处理
- syncTouch // 同步点击
- 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_DOWN 和 MotionEvent.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应用层分发
未完待续
总结
- 设备IOCTL 和 keylayout决定设备classes
- 设备的classes决定了不同类型 Mapper, 每个mapper都有其对应的input event输入事件处理实现
- InputDispatch分发殊途同归,都是通过enqueueInboundEventLocked进入队列,通过

浙公网安备 33010602011771号