在Input相关服务的创建和启动中,我们知道了InputManager在start函数中会创建一个InputReader,其内部有一个线程会循环调用InputReader.loopOnce,实现InputReader对输入事件的持续读取,那么我们以此为起点,分析一下InputReader读取事件流程。
void InputReader::loopOnce() {
......
// 1. 从EventHub读取原始事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
std::scoped_lock _l(mLock);
mReaderIsAliveCondition.notify_all();
// 2. InputReader.processEventsLocked处理事件。
if (count) {
processEventsLocked(mEventBuffer, count);
}
......
// 3. QueuedInputListener分发事件
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
可以分为三部分:
1)、EventHub.getEvent获取输入事件。
2)、InputReader.processEventsLocked处理事件。
3)、QueuedInputListener分发事件。
一、从EventHub读取原始事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
EventHub.getEvents函数内容很庞大,这里先挖个坑暂不分析,总结就是如果此时有输入事件,那么通过EventHub.getEvent来读取事件,将输入事件封装为RawEvent对象。否则,InputReader读取线程就会睡眠在EventHub.getEvent函数上:
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
epoll_wait,等待epoll文件描述符上的I / O事件。
最终将从EventHub中读取的原始事件保存到mEventBuffer中,count代表了原始事件的个数。
二、加工原始事件
Key和Motion分发流程类似,因此在这里以Key事件为例看一下整个流程时序图:
上一步中将从EventHub中读取到的原始信息保存在了RawEvent类型的mEventBuffer中,接着使用InputReader.processEventsLocked方法进一步处理事件。
1 InputReader.processEventsLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
......
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
这里将普通的事件输入事件和设备的输入事件分开处理:
1)、输入设备事件的定义在EventHub.h:
// Synthetic raw event type codes produced when devices are added or removed.
enum {
// Sent when a device is added.
DEVICE_ADDED = 0x10000000,
// Sent when a device is removed.
DEVICE_REMOVED = 0x20000000,
// Sent when all added/removed devices from the most recent scan have been reported.
// This event is always sent at least once.
FINISHED_DEVICE_SCAN = 0x30000000,
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
DEVICE_ADDED对应一次设备添加事件,DEVICE_REMOVED对应一次设备删除事件,FINISHED_DEVICE_SCAN对应一次所有设备的扫描事件。
2)、如果事件type小于FIRST_SYNTHETIC_EVENT,那么此次不是一次增加/删除/扫描设备事件,调用InputReader.processEventsForDeviceLocked,让对应设备去处理本次输入事件。
2 InputReader.processEventsForDeviceLocked
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
size_t count) {
auto deviceIt = mDevices.find(eventHubId);
......
std::shared_ptr<InputDevice>& device = deviceIt->second;
......
device->process(rawEvents, count);
}
从mDevices中获取当前事件对应的设备对象InputDevice,然后调用InputDevice.process处理RawEvent。
3 InputDevice.process
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
......
if (mDropUntilNextSync) {
......
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
......
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
mapper.process(rawEvent);
});
}
--count;
}
}
这里出现了一个新概念,InputMapper:
/* An input mapper transforms raw input events into cooked event data.
* A single input device can have multiple associated input mappers in order to interpret
* different classes of events.
InputMapper用来将原始的输入事件转换为处理过的输入数据,一个输入设备可能对应多个InputMapper。
InputMapper有多个子类,TouchInputMapper、KeyboardInputMapper、SensorInputMapper等等,用来处理不同类型的输入事件。
上一步中,我们找到了一个输入设备可以处理当前输入事件,那么这一步就是遍历调用该InputDevice对应的所有InputMapper,调用InputMapper的proces函数,去处理输入事件。
4 InputMapper处理原始输入事件
我们重点分析键盘事件和触摸屏事件。
4.1 键盘事件
键盘设备对应的InputMapper是KeyboardInputMapper。
4.1.1 KeyboardInputMapper.process
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
usageCode);
}
break;
}
case EV_MSC: {
......
}
case EV_SYN: {
......
}
}
}
查阅资料,Linux系统中总共有这几种输入事件:
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件,如KEY_VOLUMEDOWN
EV_REL 0x02 相对坐标, 如shubiao上报的坐标
EV_ABS 0x03 绝对坐标,如触摸屏上报的坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈
EV_PWR 电源
EV_FF_STATUS 状态
暂不分析其他事件,那么对于键盘事件中的按键事件,调用KeyboardInputMapper.processKey函数继续进行处理。
4.1.2. KeyboardInputMapper.processKey
void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
......
NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
getDisplayId(), policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
将原始的输入数据封装为NotifyKeyArgs类型,然后通知listener。
4.2 触摸屏事件
触摸屏设备对应的InputMapper是TouchInputMapper。
4.2.1 TouchInputMapper.process
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when, rawEvent->readTime);
}
}
1)、mCursorButtonAccumulator用来处理鼠标和触摸盘按键事件,mCursorScrollAccumulator用来处理鼠标滑动事件,mTouchButtonAccumulator用来处理手写笔和之类的事件。
2)、调用TouchInputMapper.sync函数进行事件的同步。
4.2.2 TouchInputMapper.sync
4.2.3 TouchInputMapper.processRawTouches
4.2.4 TouchInputMapper.cookAndDispatch
4.2.5 TouchInputMapper.dispatchTouches
4.2.6 TouchInputMapper.dispatchMotion
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
uint32_t source, int32_t action, int32_t actionButton,
int32_t flags, int32_t metaState, int32_t buttonState,
int32_t edgeFlags, const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex,
BitSet32 idBits, int32_t changedId, float xPrecision,
float yPrecision, nsecs_t downTime) {
......
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
policyFlags, action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move(frames));
getListener()->notifyMotion(&args);
}
将原始的输入数据封装为NotifyMotionArgs类型,然后通知listener。
5 QueuedInputListener.notifyKey & QueuedInputListener.notifyMotion
从上面的分析可以看到:
1)、对于键盘事件,通过KeyboardInputMapper的处理,最终将原始输入事件封装为NotifyKeyArgs对象,然后通过getListener获取到传递对象,调用notifyKey将封装好的事件传过去。
2)、对于触摸屏事件,通过TouchInputMapper的处理,最终将原始输入事件封装为NotifyMotionArgs对象,然后通过getListener获取到传递对象,调用notifyMotion将封装好的事件传过去。
这里直接放结论,InputMapper.getListener函数返回了InputReader.mQueuedListener。
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
那么上面无论是键盘事件:
getListener()->notifyKey(&args);
或者触摸事件:
getListener()->notifyMotion(&args);
调用的都是QueuedInputListener的相关函数:
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
traceEvent(__func__, args->id);
mArgsQueue.push_back(new NotifyKeyArgs(*args));
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
traceEvent(__func__, args->id);
mArgsQueue.push_back(new NotifyMotionArgs(*args));
}
最终的结果是,将封装的NotifyKeyArgs和NotifyMotionArgs加入到了QueuedInputListener的成员变量mArgsQueue中。
三、通知InputDispatcher有新事件发生
Key和Motion分发流程类似,因此在这里以Key事件为例看一下整个流程时序图:
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
上一步中,调用了InputReader.processEventsLocked函数,将所有原始事件加工为了NotifyArgs类型,并且加入到了mQueuedListener.mArgsQueue,那么这一步就是调用QueuedInputListener.flush将加工好的事件分发给InputReader相应的listener。
1 QueuedInputListener.flush
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
这里的mArgsQueue队列中已经有了从EventHub中读取到的并且已经被加工为NotifyArgs类型的事件了,接着调用NotifyArgs.notify函数。
2 NotifyKeyArgs.notify & NotifyMotionArgs.notify
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
......
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
NotifyArgs是NotifyKeyArgs和NotifyMotionArgs的基类,根据事件类型分别调用不同子类的notify函数。
这里传入的listener是QueueInputListener.mInnerListener,在之前的分析中我们知道它对应的是一个InputClassifier对象。
3 InputClassifier.notifyKey & InputClassifier.notifyMotion
void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
// pass through
mListener->notifyKey(args);
}
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
std::scoped_lock lock(mLock);
// MotionClassifier is only used for touch events, for now
const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
if (!sendToMotionClassifier) {
mListener->notifyMotion(args);
return;
}
NotifyMotionArgs newArgs(*args);
newArgs.classification = mMotionClassifier->classify(newArgs);
mListener->notifyMotion(&newArgs);
}
在之前的分析我们知道了,InputClassifier.mListener指向了一个InputDispatcher,那么这里调用的自然也是InputDispatcher的相关函数。
3.1 InputDispatcher.notifyKey
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
......
KeyEvent event;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
args->downTime, args->eventTime);
android::base::Timer t;
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
}
bool needWake;
{ // acquire lock
......
std::unique_ptr<KeyEntry> newEntry =
std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
args->displayId, policyFlags, args->action, flags,
keyCode, args->scanCode, metaState, repeatCount,
args->downTime);
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
1)、根据传入的NotifyKeyArgs的信息封装一个KeyEvent对象,在将输入事件传给Activity之前,调用NativeInputManager.interceptKeyBeforeQueueing对KeyEvent进行预处理,最终会调用到PhoneWindowManager#interceptKeyBeforeQueueing中,决定是否要把输入事件发送给用户。
2)、根据传入的NotifyKeyArgs的信息封装一个KeyEntry对象,然后调用InputDispatcher.InputDispatcher.enqueueInboundEventLocked。
3)、如果InputDispatcher.InputDispatcher.enqueueInboundEventLocked返回true,那么说明需要唤醒InputDispatcher的分发线程。
3.2 InputDispatcher.notifyMotion
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
......
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
}
bool needWake;
{ // acquire lock
mLock.lock();
......
// Just enqueue a new motion event.
std::unique_ptr<MotionEntry> newEntry =
std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
args->source, args->displayId, policyFlags,
args->action, args->actionButton, args->flags,
args->metaState, args->buttonState,
args->classification, args->edgeFlags,
args->xPrecision, args->yPrecision,
args->xCursorPosition, args->yCursorPosition,
args->downTime, args->pointerCount,
args->pointerProperties, args->pointerCoords, 0, 0);
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
内容和InputDispatcher.notifyKey相似:
1)、在将输入事件传给Activity之前,将传入的NotifyMotionArgs的信息发送给NativeInputManager.interceptMotionBeforeQueueing进行预处理,最终会调用PhoneWindowManager#interceptMotionBeforeQueueing,由PhoneWindowManager决定是否要拦截此次事件。
2)、根据传入的NotifyMotionArgs的信息封装一个MotionEntry对象,然后调用InputDispatcher.InputDispatcher.enqueueInboundEventLocked。
3)、如果InputDispatcher.InputDispatcher.enqueueInboundEventLocked返回true,那么说明需要唤醒InputDispatcher的分发线程。
3.3 InputDispatcher.enqueueInboundEventLocked
不管是Key事件还是Motion事件,在经过PhoneWindowManager预处理后,如果PhoneWindowManager不拦截此次事件,那么接下来就是调用InputDispatcher.enqueueInboundEventLocked函数继续分发事件给相关窗口。
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(std::move(newEntry));
EventEntry& entry = *(mInboundQueue.back());
traceInboundQueueLengthLocked();
switch (entry.type) {
case EventEntry::Type::KEY: {
......
}
case EventEntry::Type::MOTION: {
......
}
case EventEntry::Type::FOCUS: {
LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
// nothing to do
break;
}
}
return needWake;
}
首先判断mInboundQueue是否为空,将本次处理的EventEntry加入到mInboundQueue中。接着判断是否要唤醒InputDispatcher的分发线程,有三种唤醒情况:
1)、mInboundQueue为空。
2)、mInboundQueue不为空,此时为按键事件。如果当前按键事件将触发App切换,并且如果此时该按键抬起,需要丢弃之前所有的事件,并且唤醒InputDispatcher分发线程。
3)、mInboundQueue不为空,此时为触摸事件。如果当前触摸事件之前的事件需要被删除,那么唤醒InputDispatcher分发线程。
4 InputDispatcher分发线程的唤醒
将输入事件加入InputDispatcher.mInboundQueue中,下一步就是唤醒InputDispatcher分发线程。
先来补充一下Looper的睡眠和唤醒机制:
C++类Looper中的睡眠和唤醒机制是通过pollOnce和wake函数提供的,它们又是利用操作系统(Linux内核)的epoll机制来完成的。
在Looper的构造函数中,会创建一个管道,然后调用epoll_create获取一个epoll的实例的描述符,最后将管道读端描述符作为一个事件报告项添加给epoll。
Looper的pollOnce函数将最终调用到其pollInner函数。在后者里面,将调用epoll_wait睡眠等待其监控的文件描述符是否有可I/O事件的到来,若有(哪怕只有一个),epoll_wait将会醒来。
Looper的wake函数用于向管道中写入字符,pollOnce将会从epoll_wait中唤醒。
我们知道InputDispatcher内部有一个的分发线程,在循环调用dispatchOnce来进行事件的分发,但即使是循环调用,这个线程也不可能真的无限制在一直分发,而是当有事件可读时,才进行分发,其他时间分发线程进入休眠状态。
InputDispatcher.dispatchOnce函数的最后,在事件分发完成后会调用Looper.pollOnce将当前线程睡眠:
void InputDispatcher::dispatchOnce() {
......
// 上面是分发事件的具体内容
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
那么上面在InputDispatcher.notifyKey或者是InputDispatcher.notifyMotion中调用Looper.wake的时候:
if (needWake) {
mLooper->wake();
}
InputDispatcher.dispatchOnce中的Looper.pollOnce函数就可以返回,上一次的InputDispatcher.dispatchOnce函数才会全部执行完成并且返回,进而可以循环进行下一次的InputDispatcher.dispatchOnce,开启新一轮事件分发。
四、总结
1)、InputReader通过EventHub.getEvents读取原始事件RawEvent。
2)、InputReader调用InputReader.processEventsLocked函数将原始事件加工为NotifyArgs类型,然后存储到InputReader的QueuedInputListener类型的成员变量mQueuedListener内部的mArgsQueue中进行排队等待分发。
3)、InputReader调用QueuedInputListener.flush函数,将QueuedInputListener.mArgsQueue存储的事件一次性发送到InputDispatcher,最终事件是被封装成了EventEntry类型,添加到了InputDispatcher.mInboundQueue中,InputReader并且唤醒了InputDispatcher的分发线程,后续InputDispatcher分发线程会重新调用InputDispatcher.dispatchOnce函数来进行事件的分发。