1、概述
根据Input子系统框架之深入理解EventHub分析。输入设备扫描完成,并加入epoll中,监听事件。从前面的getEvents函数分析得知,当按键事件发生后,getEvents函数返回。 这里再贴一下Input 处理时间流程图,然后按步骤详细分析。
上面的type是linux的输入系统里的事件,具体的值可以查看 查看input.h
kernel/include/uapi/linux/input.h
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_SYN 0x00 同步事件
#define EV_KEY 0x01 按键事件
#define EV_REL 0x02 相对坐标
#define EV_ABS 0x03 绝对坐标
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_MSC 0x04 其它
#define EV_SW 0x05
#define EV_LED 0x11 LED
#define EV_SND 0x12 声音
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_REP 0x14 Repeat
#define EV_FF 0x15 力反馈
#define EV_PWR 0x16 电源
#define EV_FF_STATUS 0x17 状态
2、 InputReader::loopOnce()
返回到InputReader的loopOnce函数
[->frameworks/native/services/inputflinger/InputReader.cpp]
void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
//当有按键事件发生时,count将不为0
if (count) {
processEventsLocked(mEventBuffer, count);
}
} // release lock
}
当有按键事件发生时,count将不为0,之后会调用processEventsLocked来处理RawEvent。
3、InputReader.processEventsLocked()
[->frameworks/native/services/inputflinger/InputReader.cpp]
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) {
int32_t deviceId = rawEvent->deviceId;
//依次处理rawEvent
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
}
count -= batchSize;
rawEvent += batchSize;
}
}
该函数调用processEventsForDeviceLocked依次处理rawEvent
4、InputReader.processEventsForDeviceLocked()
[->frameworks/native/services/inputflinger/InputReader.cpp]
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
InputDevice* device = mDevices.valueAt(deviceIndex);
//调用InputDevice的process函数
device->process(rawEvents, count);
}
这里根据deviceId获取到InputDevice,然后调用InputDevice的process函数
5、InputDevice.process()
[->frameworks/native/services/inputflinger/InputReader.cpp]
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
// InputMapper是做什么的呢,它是用于解析原始输入事件的。比如back, home等VirtualKey,
// 传上来时是个Touch事件,这里要根据坐标转化为相应的按键事件。再比如多点触摸时,需要计算
// 每个触摸点分别属于哪条轨迹,安卓系统中每种输入设备都对应了一种Mapper,比如
// SwitchInputMapper, VibratorInputMapper,KeyBoardInputMapper
mapper->process(rawEvent);
}
}
}
}
这里的mMappers成员变量保存了一系列输入设备事件处理对象,例如负责处理键盘事件的KeyboardKeyMapper对象以及负责处理触摸屏事件的TouchInputMapper对象, 它们是在InputReader类的成员函数createDeviceLocked中创建的。这里查询每一个InputMapper对象是否要对当前发生的事件进行处理。由于发生的是键盘事件,真正会对该事件进行处理的只有KeyboardKeyMapper对象。
6、KeyboardInputMapper.process()
[->frameworks/native/services/inputflinger/InputReader.cpp]
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)) {
int32_t keyCode;
uint32_t flags;
// 调用EventHub中的mapKey函数进行转化
// 传入参数
// scanCode:驱动程序上报的扫描码;keyCode:转化之后的Android使用的按键值
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
keyCode = AKEYCODE_UNKNOWN;
flags = 0;
}
//映射成功之后,处理该按键
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
函数首先调用isKeyboardOrGamepadKey来判断键盘扫描码是否正确,如果正确则调用processKey来进一步处理
7、KeyboardInputMapper.processKey()
[->frameworks/native/services/inputflinger/InputReader.cpp]
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
int32_t scanCode, uint32_t policyFlags) {
// 根据扫描码scanCode、按键码keyCode、newMetaState、downTime按下的时间进行处理
// NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
......
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
// 通知Listener处理,Dispatch线程会监听该事件,并处理,下次博文会具体分析
getListener()->notifyKey(&args);
}
这个函数首先对按键作一些处理,根据扫描码scanCode、按键码keyCode、newMetaState、downTime按下的时间进行处理,最后函数会调用:
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
getListener()->notifyKey(&args);
这里getListener是InputReader初始化时传入的对象,即QueuedInputListener,则会调用QueuedInputListener的notifyKey函数
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args));
}
InputReader的loopOnce()的结尾会调用QueuedInputListener::flush()统一回调缓冲队列中各元素的notify()接口:
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();
}
进一步调用:
void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyConfigurationChanged(this);
}
以按键事件为例,由于InputDispatcher 实现了InputListenerInterface接口的notifyConfigurationChanged()函数,所以最后会调用到InputDispatcher的notifyKey()函数中。
8、 InputDispatcher.notifyKey()
[->frameworks/native/services/inputflinger/InputDispatcher.cpp]
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
......
// 构造一个KeyEvent对象
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
// 调用mPolicy的interceptKeyBeforeQueueing函数,该函数最后会调用到java层的PhoneWindowManagerService函数
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
bool needWake;
{ // acquire lock
mLock.lock();
......
//构造一个KeyEntry对象,调用enqueueInboundEventLocked函数将按键事件加入队列
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
该函数首先调用validateKeyEvent来判断是否是有效按键事件,实际判断是否是UP/DOWN事件
然后构造一个KeyEvent对象,调用mPolicy的interceptKeyBeforeQueueing函数,该函数最后会调用到java层的PhoneWindowManagerService函数
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
之后会调用构造一个KeyEntry对象,调用enqueueInboundEventLocked函数将按键事件加入队列,如果返回true,则调用mLooper.wake函数唤醒等待的InputDispatcher,进行按键分发。
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
9、InputDispatcher.enqueueInboundEventLocked()
[->frameworks/native/services/inputflinger/InputDispatcher.cpp]
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
case EventEntry::TYPE_KEY: {
// ......
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
}
return needWake;
}
将EventEntry加入到mInboundQueue中,该函数两种情况下会返回true,一是当加入该键盘事件到mInboundQueue之前,mInboundQueue为空,这表示InputDispatc herThread线程正在睡眠等待InputReaderThread线程的唤醒,因此,它返回true表示要唤醒InputDispatccherThread线程;二是加入该键盘事件到mInboundQueue之前,mInboundQueue不为空,但是此时用户按下的是Home键等需要切换APP的按键,我们知道,在切换App时,新的App会把它的键盘消息接收通道注册到InputDispatcher中去,并且会等待InputReader的唤醒,因此,在这种情况下,也需要返回true,表示要唤醒InputDispatccherThread线程。如果不是这两种情况,那么就说明InputDispatccherThread线程现在正在处理前面的键盘事件,不需要唤醒它。
至此,InputDispatcherThread被唤醒,开始进行按键分发。
10、总结:
InputReaderThread不断调用InputReader的pollOnce()->getEvents()函数来得到事件,这些事件可以是输入事件,也可以是由inotify监测到设备增减变更所触发的事件。第一次进入时会扫描/dev/input目录建立设备列表,存在mDevice成员变量中(EventHub中有设备列表KeyedVector mDevices;对应的,InputReader中也有设备列表KeyedVector mDevices。这里先添加到前者,然后会在InputReader::addDeviceLocked()中添加到后者。),同时将增加的fd加到epoll的等待集合中。在接下来的epoll_wait()等待时,如果有事件就会返回,同时返回可读事件数量。在这里,从Input driver读出的事件从原始的input_event结构转为RawEvent结构,放到getEvents()的输出参数buffer中。getEvents()返回后,InputReader调用processEventsLocked()处理事件,对于设备改变,会根据实际情况调用addDeviceLocked(), removeDeviceLocked()和handleConfigurationChangedLocked()。对于其它设备中来的输入事件,会调用processEventsForDeviceLocked()进一步处理。其中会根据当时注册的InputMapper对事件进行处理,然后将事件处理请求放入缓冲队列(QueuedInputListener中的mArgsQueue)。
回到流程主线上,在InputReader的loopOnce()的结尾会调用QueuedInputListener::flush()统一回调缓冲队列中各元素的notify()接口。
以按键事件为例,最后会调用到InputDispatcher的notifyKey()函数中。这里先将参数封装成KeyEvent: 然后把它作为参数调用NativeInputManager的interceptKeyBeforeQueueing()函数。顾名思义,就是在放到待处理队列前看看是不是需要系统处理的系统按键,它会通过JNI调回Java世界,最终调到PhoneWindowManager的interceptKeyBeforeQueueing()。然后,基于输入事件信息创建KeyEntry对象,调用enqueueInboundEventLocked()将之放入队列等待InputDiaptcherThread线程拿出处理。