【Android】事件输入系统-代码层次解读
本文基于Android-4.0
理论层次解读
请看:www.cnblogs.com/lcw/p/3373214.html
如何管理各种驱动设备
在理论中谈到EventHub,这个一看就是一个做实事的,肯定不是领导,哪它的领导是谁呢?
从以下几方面来分析此问题:
- 每个功能模块是怎么产生的?
- 读取设备输入流程?
- 事件分发流程?
各个功能模块是怎么产生的?
先看一下每个模块的工作职责:EventHub, InputReader, InputManager...
模块功能
EventHub
它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。
EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。
InputReader
InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.
InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。
InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。
InputDispatcher
InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。
InputManager
InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出所筒。
InputManager使用两个线程:
- InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。
- InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。
InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。
InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。
创建流程
- 在android_server_InputManager_nativeInit中创建NativeInputManager对象,并保存到gNativeInputManager中;
- 在创建NativeInputManager对象时,它会创建EventHub对象<且创建是其成员mNeedToScanDevices的值为true>,然后把刚创建的EventHub对象作为参数创建InputManager对象;
- 在创建InputManager对象时,创建InputReader对象,然后把它作为参数创建InputReaderThread;创建InputDispatcher对象,然后把它作为参数创建InputDispatcherThread对象;(注意:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建)
创建InputReader对象
- 把EventHub、readerPolicy<实质为NativeInputManager对象>和创建的InputDispatcher对象作为参数创建InputReader对象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- 在创建InputReader时, 保存EventHub对象到mEventHub中,并创建QueuedInputListener对象并保存在mQueuedListener中
创建InputDispatcher对象
- 把传入的参数dispatcherPolicy<实质为NativeInputManager对象>作为参数创建InputDispatcher对象:mDispatcher = new InputDispatcher(dispatcherPolicy);
- 在创建InputDispatcher时,创建了一个looper对象:mLooper = new Looper(false);
启动流程
-
在android_server_InputManager_nativeStart中调用InputManager::start,代码如下:
result = gNativeInputManager->getInputManager()->start();
-
在InputManager::start中,调用mDispatcherThread->run和mReaderThread->run,代码如下:
result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
在上面的Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程。
至此InputReader线程和InputDispatcher线程都已经工作,详细信息见Thread::_threadLoop,在此函数中它将调用mDispatcherThread或mReaderThread的threadLoop函数来做真正的事。
mReaderThread->threadLoop
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }
mDispatcherThread->threadLoop
bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; }
EventInput对象关系图
设备操作流程
从EventHub::getEvents读取的事件数据结构如下:
struct RawEvent { nsecs_t when; //事件发生的时间 int32_t deviceId; //产生此事件的设备,比如发送FINISHED_DEVICE_SCAN,不需要填此项 int32_t type; //事件类型(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN) int32_t scanCode; int32_t keyCode; int32_t value; uint32_t flags; };
读取事件时的调用流程为:
Thread::_threadLoop-> InputReaderThread::threadLoop-> InputReader::loopOnce-> EventHub::getEvents->
打开设备
在EventHub::getEvents中,当mNeedToScanDevices为true时<当创建EventHub对象时,它就为true>,它将从/dev/input目录下查找所有设备,并进行打开,获取其相关属性,最后加入mDevices列表中。
EventHub::scanDevicesLocked->
EventHub::scanDirLocked("/dev/input")->
EventHub::openDeviceLocked
打开事件输入设备
打开事件输入设备,在用户态调用open,则在kernel态中调用evdev_open函数,evdev_open处理流程如下:
- 首先从参数inode中获取在evdev_table中的索引,从而获取对应的evdev对象
- 创建evdev_client对象,创建此对象时同时为其buffer成员分配对应的内存
- 把新创建evdev_client对象添加到client_list链表中
- 把client保存在file的private_data中
- 调用evdev_open_device->input_open_device->input_dev.open函数打开设备。
读取输入事件
要说EventHub::getEvents如何获取输入事件,不得不先说说它的几个相关的成员变量:
- mPendingEventCount:调用epoll_wait时的返回值,如果没有事件,则其值为0;
- mPendingEventIndex:当前需要处理的事件索引
- mEpollFd:epoll实例,在EventHub::EventHub中初始化此例,所有输入事件通过epoll_wait来获取,每一个事件的数据结构为:struct epoll_event
注:epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。
查看设备上是否有事件
在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,当然,在调用epoll_event之前,mPendingEventIndex被清0,直正的事件处理在下面的代码中。
// Grab the next input event. bool deviceChanged = false; while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { if (eventItem.events & EPOLLIN) { mPendingINotify = true; } else { LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; } if (eventItem.data.u32 == EPOLL_ID_WAKE) { if (eventItem.events & EPOLLIN) { LOGV("awoken after wake()"); awoken = true; char buffer[16]; ssize_t nRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { LOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); if (deviceIndex < 0) { LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.", eventItem.events, eventItem.data.u32); continue; } Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) { int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d) ", device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { LOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { LOGE("could not get event (wrong size: %d)", readSize); } else { int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { const struct input_event& iev = readBuffer[i]; LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of // event dispatch latency from the time the event is enqueued onto // the evdev client buffer. // // The event's timestamp fortuitously uses the same monotonic clock // time base as the rest of Android. The kernel event device driver // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; LOGV("event time %lld, now %lld", event->when, now); #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->scanCode = iev.code; event->value = iev.value; event->keyCode = AKEYCODE_UNKNOWN; event->flags = 0; if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) { status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, &event->keyCode, &event->flags); LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d ", iev.code, event->keyCode, event->flags, err); } event += 1; } capacity -= count; if (capacity == 0) { // The result buffer is full. Reset the pending event index // so we will try to read the device again on the next iteration. mPendingEventIndex -= 1; break; } } } else { LOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); } }