上一篇博文介绍了利用uinput创建和使用虚拟设备,设备产生的事件序列会被存储在/dev/input目录下,同一设备产生的事件单独存储在同一文件中。那么存入/dev/input目录下的事件序列是怎样被读取、处理、传递到应用层的呢,这篇文章就以鼠标的一个移动事件为例,讲解事件在framework层的传递流程。
写入到/dev/input目录下的设备事件,将由Android framework目录中EventHub.cpp文件进行读取。但事件的产生并非周期性的,并且需要主动获取/dev/input的变动事件。Android系统并没有采取轮询的方式进行处理,而是借由linux系统中的INotify与Epoll机制。
在EventHub.cpp的构造函数中,有以下一段代码
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
...
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
...
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
程序中,INotify用于检查文件系统中/dev/input目录的发生的变化,当目录中有添加、删除文件,或是在文件中写入设备事件,都会产生一个inotify事件。而Epoll的作用就是监听inotify事件的发生。这样通过epoll_wait(),就能够实时检测到设备事件的发生。
在EventHub.cpp中,确定设备与事件类型的入口是在getEvent(),该函数的调用位于InputReader.cpp的loopOnce函数中。在EventHub.cpp中,首先通过如下方式确定读取事件的路径
static const char *DEVICE_PATH = "/dev/input";
于是在getEvent函数中,通过调用scanDevicesLocked()函数获取事件,
获取事件类型的关键代码如下,可见这段代码之上的部分有对事件的收集处理,之后将一个事件的数据统一放入结构体RawEvent中,方便后续传递,其中event为RawEvent结构体的对象
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
另外又是如何知道发出事件的设备类型呢?也是在getEvent()中,该函数通过readNotifyLocked()间接调用openDeviceLocked()
首先看readNotifyLocked()调用openDeviceLocked()函数的代码
strcpy(devname, DEVICE_PATH);
filename = devname + strlen(devname);
*filename++ = '/';
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
strcpy(filename, event->name);
if(event->mask & IN_CREATE) {
openDeviceLocked(devname);
} else {
ALOGI("Removing device '%s' due to inotify event\n", devname);
closeDeviceByPathLocked(devname);
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
接下来,在函数openDeviceLocked()中分辨出设备的类型,如鼠标
// See if this is a cursor device such as a trackball or mouse.
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
通过前面的流程可获取一个设备事件并进行分类,正如前面所说,getEvent()的调用位于InputReader.cpp的loopOnce函数中
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
该过程返回mEventBuffer,即第2部分讲述的使用getEvent函数获得的RawEvent结构体。这样InputReader.cpp便获取到设备事件,接下来就是对事件的处理过程。从该句代码继续往下看,有这样一句
processEventsLocked(mEventBuffer, count);
对事件的处理过程便从这里开始,processEventsLocked()遍历获取到的event数组
for (const RawEvent* rawEvent = rawEvents; count;)
在for 循环中的主要处理代码如下
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >=
EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
}
以上的语句中,使用if判断是否为常规的event,随后通过deviceId判断是否为同一设备,这样便对每一个事件进行了分组。之后调用processEventsForDeviceLocked()做进一步的处理,processEventsForDeviceLocked()内容定义为
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
可见,通过以上代码,去除了属性为unknown deviceId以及ignored deviceId的event事件。如为正常事件,则调用InputDevice类中process()函数进行处理。函数的原型为
void InputDevice::process(const RawEvent* rawEvents, size_t count)
函数中第一句为注释,可见是为每一个mapper依次进行事件处理
// Process all of the events in order for each mapper.
通过判断rawEvent->type及rawEvent->code,决定是否执行mapper的proces函数,如果调用,那么接下来是如何处理的呢?这里以一个鼠标的移动事件为例。对于鼠标的移动,执行一下语句后
mapper->process(rawEvent);
会调用CursorInputMapper的process函数,定义为
void CursorInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorMotionAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT){
sync(rawEvent->when);
}
}
鼠标移动是调用的mCursorMotionAccumulator.process(rawEvent)这一句,用以上报鼠标移动的Rel_X,Rel_Y。经过3次process的处理后,便完成了一个事件的处理。
依然是在CursorInputMapper的process()函数中,如果为一个同步sync的事件,那么就执行sync()进行同步,函数原型为
void CursorInputMapper::sync(nsecs_t when)
经过一系列同步过程后,随后执行如下语句
getListener()->notifyMotion(&args);
上面一句的作用是调用InputListener.cpp中QueuedInputListener类的notifyMotion函数,如下
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
函数的功能便是将事件push进mArgsQueue链表队列,此即为InputDispatcher的mInboundQueue队列。
在loopOnce函数中,做完一次事件的处理与分发后,将会执行如下语句
mQueuedListener->flush();
函数flush()中,会执行
args->notify(mInnerListener);
该语句又将调用InputListener.cpp中NotifyMotionArgs类的notify函数,如下
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
可见这里又调用了一次notifyMotion(),而这次调用的即为InputDispatcher中的notifyMotion(),至此InputDispatcher接受到通知,进行后续处理。
通过上述可知,InputReader处理及存储完一个事件后,通过使用InputListener中NotifyMotionArgs类来调用 InputDispatcher的notify函数,其原型为
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args)
该函数通过调用InputManager.cpp的initialize函数唤醒InputDispatcherThread,然后通过调用dispatchOnce()开始事件的分发
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
事件的分发从dispatchOnce()开始。首先看函数最后一行
mLooper->pollOnce(timeoutMillis);
作用是通过执行pollOnce(),进入epoll_wait等待状态,等待回调、超时或被唤醒。当有事件时,判断是否有挂起命令,如果没有则执行以下语句
dispatchOnceInnerLocked(&nextWakeupTime);
该函数的作用是调用dropInboundEventLocked()从InboundQueue中读取事件以及调用dispatchMotionLocked()进行事件分发
首先看取出InboundQueue队列中事件的代码,先判断了dropReason
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN;
}
接下来看看进行事件分发的前提条件,即判断后获取dropReason的值
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
现在进入dispatchMotionLocked()函数,首先执行以下代码找到目标窗口,可以看到内容中出现了两个重要的寻找窗口函数
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
if (isPointerEvent) {
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
在找到目标窗口后,通过调用dispatchEventLocked()执行事件分发,见以下语句,通过getConnectionIndexLocked()从mConnectionByFd队列获得目标connection,再执行prepareDispatchCycleLocked()做后续处理
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
之后,在prepareDispatchCycleLocked函数中过滤掉不正常状态的connection后,调用enqueueDispatchEntriesLocked()函数。
在enqueueDispatchEntriesLocked()函数中,通过eventEntry、inputTarget的差异调用不同的enqueueDispatchEntryLocked()函数。被调用的enqueueDispatchEntryLocked()函数末尾有如下语句,作用就是将事件加入到OutboundQueue队列中。
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
traceOutboundQueueLengthLocked(connection);
之后,enqueueDispatchEntriesLocked()函数执行的语句如下
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
现在进入到startDispatchCycleLocked()函数,函数执行以下语句将事件从OutboundQueue取出事件,重新放入connection的wait queue中等待被发送至应用层
// Re-enqueue the event on the wait queue.
connection->outboundQueue.dequeue(dispatchEntry);
traceOutboundQueueLengthLocked(connection);
connection->waitQueue.enqueueAtTail(dispatchEntry);
traceWaitQueueLengthLocked(connection);
同样是在startDispatchCycleLocked()函数中,当事件分发完毕后,便通知应用层接收事件,处理的方法是执行如下语句
status = connection->inputPublisher.publishMotionEvent();
该语句实际上是执行InputTransport.cpp中的publishMotionEvent函数。该函数创建InputMessage对象msg后,定义消息msg的属性,通过sendMessage()函数以socket方式发送出去,通知应用层接收事件。
至此,事件进入framework层直至被发送到应用层的流程已分析完毕。
framework层传递事件的整个程序执行顺序如下
① InputManager.cpp中创建EventHub对象,调用EventHub构造函数。随EventHub对象创建的还有InputDispatcher及InputReader对象。
② 在EventHub.cpp中,EventHub构造函数建立Epoll机制监听设备事件发生,当有事件发生时,从/dev/input目录读取事件,并辨别事件类型。
③ InputReader获取EventHub收集到的事件,经过处理后存储到InboundQueue队列中,通过notify唤醒InputDispatcherThread。
④ InputDispatcher从InboundQueue取出事件,寻找到事件传递的窗口,然后将事件临时放入OutboundQueue,之后取出利用socket发送给目标窗口。
本文主要介绍鼠标的移动事件在framework层的传递流程,若是其他设备事件,参考本文的分析方法,也能梳理出其进入framework层后是怎样被传递到应用层的。
https://blog.csdn.net/cheris_cheris/article/details/53306833
https://blog.csdn.net/jscese/article/details/42739197
https://www.jianshu.com/p/d22d86cb04c7