接着上一篇的InputManagerService,这里主要介绍一下EventHub。EventHub主要是访问/dev/input下的所有设备节点,并将输入事件、设备节点的增删返给InputReader。
由上一篇可知,EventHub对象是在NativeInputManager构造函数中创建的。先看一下EventHub构造函数中都做了些什么
EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); //创建epoll对象,mEpollFd为epoll对象的描述符 mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); //创建inotify对象,mINotifyFd为inotify对象的描述符 mINotifyFd = inotify_init(); //DEVICE_PATH值为"/dev/input",监听该目录下的设备节点创建与删除操作。通过read函数读取事件。 int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d", DEVICE_PATH, errno); struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; //监听可读事件 eventItem.data.u32 = EPOLL_ID_INOTIFY; //EPOLL_CTL_ADD表示增加事件 //epoll_ctl将事件监听添加到epoll对象中去。 result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2]; result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", errno); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); eventItem.data.u32 = EPOLL_ID_WAKE; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); }这段代码主要工作:
1.初始化一些成员变量
2.创建epoll对象,EPOLL_SIZE_HINT = 8代表最大监听数为8.
3.创建inotify对象,监听/dev/input下设备节点的增删。
4.将mINotifyFd添加到epoll中,作为一个监控对象。
5.创建管道,将管道读取端的可读事件添加到epoll中。使epoll_wait()返回,唤醒InputReader线程。
EventHub的主要工作都是在getEvents函数中,InputReaderThread通过循环调用EventHub的getEvents()函数获取输入事件。getEvents中做了些什么,现在看一看。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ALOG_ASSERT(bufferSize >= 1); AutoMutex _l(mLock); struct input_event readBuffer[bufferSize]; //每存一个事件,event指针向后移动一个元素。 RawEvent* event = buffer; //capacity存buffer中剩余端元素数量,capacity为0,表示buffer已满。 size_t capacity = bufferSize; bool awoken = false; for (;;) { ............... //循环体。 } // All done, return the number of events we read. return event - buffer; }
以上是getEvents()的整体模型,接着看循环体中的方法。
//获取系统当前时间(native层的方法)。 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); // Reopen input devices if needed. if (mNeedToReopenDevices) {//判断是否需要重新打开设备 mNeedToReopenDevices = false; ALOGI("Reopening all input devices due to a configuration change."); closeAllDevicesLocked();//关闭、卸载所有设备 mNeedToScanDevices = true; //下次扫描设备 break; // return to the caller before we actually rescan }由EventHub构造函数可知mNeedToReopenDevices为初始值false,第一次调用getEvents()时不会运行上面的代码块。其中调用了closeAllDevicesLocked()函数
void EventHub::closeAllDevicesLocked() {
while (mDevices.size() > 0) {
closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
}
}
closeAllDevicesLocked()函数中遍历mDevices,通过closeDeviceLocked()函数卸载所有这些设备。closeDeviceLocked()函数如下
void EventHub::closeDeviceLocked(Device* device) { ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", device->path.string(), device->identifier.name.string(), device->id, device->fd, device->classes); if (device->id == mBuiltInKeyboardId) { ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", device->path.string(), mBuiltInKeyboardId); mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; } //判断是否是虚拟设备 if (!device->isVirtual()) { //从epoll中删除对这些设备的监听 if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); } } //移除设备 mDevices.removeItem(device->id); device->close(); // Unlink for opening devices list if it is present. Device* pred = NULL; bool found = false; for (Device* entry = mOpeningDevices; entry != NULL; ) { if (entry == device) { found = true; break; } pred = entry; entry = entry->next; } if (found) { // Unlink the device from the opening devices list then delete it. // We don't need to tell the client that the device was closed because // it does not even know it was opened in the first place. ALOGI("Device %s was immediately closed after opening.", device->path.string()); if (pred) { pred->next = device->next; } else { mOpeningDevices = device->next; } delete device; } else { // Link into closing devices list. // The device will be deleted later after we have informed the client. //把这些设备添加到mClosingDevices,用来生成DEVICE_REMOVED事件 device->next = mClosingDevices; mClosingDevices = device; } }closeDeviceLocked()函数主要工作:
3.将删除端Device对象添加到mClosingDevices中。用于之后向InputReader发送DEVICE_REMOVED事件
// Report any devices that had last been added/removed. //遍历mClosingDevices,生成DEVICE_REMOVED事件 while (mClosingDevices) { Device* device = mClosingDevices; ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; event->when = now;//设置事件的时间戳 event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; //设置事件对应的设备id event->type = DEVICE_REMOVED; //设在事件类型DEVICE_REMOVED event += 1; //event指向下一个RawEvent对象 delete device; //释放不需要的device mNeedToSendFinishedDeviceScan = true; //capacity为0时,表示buffer已满,则停止循环将事件返回给调用者(也就是InputReader),剩余的事件等下次getEvents调用 if (--capacity == 0) { break; } }mClosingDevices初始值为0,所以刚开始调用getEvents()函数不会运行上述代码块。该块中主要是遍历mClosingDevices,生成DEVICE_REMOVED事件。
if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); //打开/dev/input下所有输入设备 mNeedToSendFinishedDeviceScan = true; }mNeedToScanDevices初始值为true,所以第一次getEvents会运行该代码块。该代码块主要工作:
1 mNeedToScanDevices赋值为false,避免重复扫描打开设备。
2 调用scanDevicesLocked(),//打开/dev/input下所有输入设备。
3 mNeedToSendFinishedDeviceScan赋值为true,用于生成FINISHED_DEVICE_SCAN事件。
接着看一下scanDevicesLocked()函数
void EventHub::scanDevicesLocked() { status_t res = scanDirLocked(DEVICE_PATH); if(res < 0) { ALOGE("scan dir failed for %s\n", DEVICE_PATH); } //创建一个虚拟的输入设备(这个不太清楚)。 if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { createVirtualKeyboardLocked(); } }
status_t EventHub::scanDirLocked(const char *dirname) { char devname[PATH_MAX]; char *filename; DIR *dir; struct dirent *de; dir = opendir(dirname); if(dir == NULL) return -1; strcpy(devname, dirname); filename = devname + strlen(devname); *filename++ = '/'; while((de = readdir(dir))) { if(de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; strcpy(filename, de->d_name); openDeviceLocked(devname); // } closedir(dir); return 0; }scanDirLocked()函数遍历/dev/input文件夹下的所有设备节点,并分别执行openDeviceLocked(devname),加载设备。openDeviceLocked()函数比较长,就不全部贴出来了,只将一些重要的部分弄出来。
status_t EventHub::openDeviceLocked(const char *devicePath) { //打开设备节点 int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } //获取device的name、driver version、id等。 。。。。。。 //创建Device int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath), identifier); 。。。。。。 // Load the configuration file for the device. 加载配置信息 loadConfigurationLocked(device); // Figure out the kinds of events the device reports. //设置device->classes,为设备分配类别(鼠标、键盘、触摸板等) // Register with epoll.将设备节点描述符的可读事件添加到Epoll中。 struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = deviceId; //设备id if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { ALOGE("Could not add device fd to epoll instance. errno=%d", errno); delete device; return -1; } 。。。。。 //将device添加到mDevice中。 addDeviceLocked(device); return 0; }a ddDeviceLocked将Device添加到mDevice中,同时也会添加到mOpeningDevices中,用来生成DEVICE_ADDED事件,发送给InputReader。这之后就可以通过getEvents读取到设备产生的输入事件了。
while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; ALOGD("Reporting device opened: id=%d, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; event->when = now; //设置事件端时间戳 event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; event->type = DEVICE_ADDED; //设置事件类型DEVICE_ADDED event += 1; //event指向下一个RawEvent对象,用于填写下一次事件 mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { //查看buffer是否已满 break; } }由于上面scanDevicesLocked时将/dev/input下的设备节点打开,并添加到mOpeningDevices中,所以会运行此代码块。这里主要是遍历mOpeningDevices,设置DEVICE_ADDED事件。
if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; event->when = now; //设置事件端时间戳 event->type = FINISHED_DEVICE_SCAN; //设置事件类型FINISHED_DEVICE_SCAN event += 1; if (--capacity == 0) { break; } }上述三个代码块都会将mNeedToSendFinishedDeviceScan设为true,所以接着生成FINISHED_DEVICE_SCAN类型的事件。也就是当设备增删事件后,需要向getEvents()函数调用者发送FINISHED_DEVICE_SCAN事件。
接着是while循环,检查是否有未处理的设备事件。
while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; //EPOLL_ID_INOTIFY是EventHub初始化时,用来检测/dev/input下添加、删除设备事件。 if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { if (eventItem.events & EPOLLIN) { mPendingINotify = true; //符合条件标记inotify事件待处理。 } else { ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue;//继续处理mPendingEventItems中的事件 } 。。。。。。 } // readNotify() will modify the list of devices so this must be done after // processing all other events to ensure that we read all remaining events // before closing the devices. //判断是否有待处理的inotify事件。 if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; } ALOGD("getEvents -----9 deviceChanged:%d",deviceChanged); // Report added or removed devices immediately. if (deviceChanged) { continue;//如果处理有inotify事件,处理后,进行下一次循环,生成设备增删事件。 } 。。。。。。mPendingINotify赋值为false,表示之后没有待处理的inotify事件。
readNotifyLocked() 处理inotify事件。
deviceChanged设置为true,进行下一次循环,生成设备增删事件。
接着看一下readNotifyLocked()函数中做了些什么。status_t EventHub::readNotifyLocked() { int res; char devname[PATH_MAX]; char *filename; char event_buf[512]; int event_size; int event_pos = 0; struct inotify_event *event; ALOGD("EventHub::readNotify nfd: %d\n", mINotifyFd); //从mINotifyFd读取inotify事件,存到event_buf中, res = read(mINotifyFd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; ALOGW("could not get event, %s\n", strerror(errno)); return -1; } //printf("got %d bytes of event information\n", res); 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) {//判断事件类型是否是IN_CREATE openDeviceLocked(devname); //加载对应设备 } else { //事件类型不是IN_CREATE,则是IN_DELETE 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; } return 0; }
主要工作
1 通过read()函数读取iNotify事件
2 遍历所有的iNotify事件
3 判断iNotify事件类型,IN_CREATE类型则加载设备,IN_DELETE类型则卸载设备。
接着是判断是否是管道事件
while (mPendingEventIndex < mPendingEventCount) { 。。。。。 if (eventItem.data.u32 == EPOLL_ID_WAKE) { if (eventItem.events & EPOLLIN) { ALOGD("awoken after wake()"); awoken = true; //用来唤醒InputRead线程 char buffer[16]; ssize_t nRead; do {//从mWakeReadPipeFd读取管道事件 nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } 。。。。 } 。。。。 // Return now if we have collected any events or if we were explicitly awoken. if (event != buffer || awoken) { break; //退出循环返回到InputReader。 }
接着到输入事件了。根据输入事件,进行设置时间戳、id、类型、值等。拥有返回给调用者InputReader。
while (mPendingEventIndex < mPendingEventCount) { 。。。。。 //通过eventItem.data.u32 获取设备id ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); if (deviceIndex < 0) { ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.", eventItem.events, eventItem.data.u32); continue; } //从mDevices获取设备 Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) {//epoll事件是EPOLLIN,可读,读取结果保存在readBuffer中,capacity是限制一次读取事件的个数 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. ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d " "capacity: %d errno: %d)\n", device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device);//关闭该设备 } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { ALOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { ALOGE("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++) { struct input_event& iev = readBuffer[i]; if (iev.type == EV_MSC) { if (iev.code == MSC_ANDROID_TIME_SEC) { device->timestampOverrideSec = iev.value; continue; } else if (iev.code == MSC_ANDROID_TIME_USEC) { device->timestampOverrideUsec = iev.value; continue; } } if (device->timestampOverrideSec || device->timestampOverrideUsec) { iev.time.tv_sec = device->timestampOverrideSec; iev.time.tv_usec = device->timestampOverrideUsec; if (iev.type == EV_SYN && iev.code == SYN_REPORT) { device->timestampOverrideSec = 0; device->timestampOverrideUsec = 0; } ALOGV("applied override time %d.%06d", int(iev.time.tv_sec), int(iev.time.tv_usec)); } #ifdef HAVE_POSIX_CLOCKS //设置更准确的时间 event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; ALOGV("event time %lld, now %lld", event->when, now); if (event->when >= now + 10 * 1000000000LL) { // Double-check. Time may have moved on. nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC); if (event->when > time) { ALOGW("An input event from %s has a timestamp that appears to " "have been generated using the wrong clock source " "(expected CLOCK_MONOTONIC): " "event time %lld, current time %lld, call time %lld. " "Using current time instead.", device->path.string(), event->when, time, now); event->when = time; } else { ALOGV("Event time is ok but failed the fast path and required " "an extra call to systemTime: " "event time %lld, current time %lld, call time %lld.", event->when, time, now); } } #else event->when = now; #endif //设置一些信息 event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; event->value = iev.value; event += 1; //移动到下一该可用元素 capacity -= 1; //可用数量减少1 } if (capacity == 0) {//buffer存满了,mPendingEventIndex至为-1,下一次循环处理未完的事件。 // 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 if (eventItem.events & EPOLLHUP) {//事件类型为挂起 ALOGI("Removing device %s due to epoll hang-up event.", device->identifier.name.string()); deviceChanged = true; closeDeviceLocked(device); //卸载设备 } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); } } 。。。。。。 rerutn event-buffer;//返回事件数量 }
while循环后面是判断是否有inotify事件,wake事件。接着是获取epoll事件 。先获取到epoll事件,下次循环才会处理while循环。
mPendingEventIndex = 0; mLock.unlock(); // release lock before poll, must be before release_wake_lock release_wake_lock(WAKE_LOCK_ID); //等待事件 int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock if (pollResult == 0) { // Timed out. mPendingEventCount = 0; break; } if (pollResult < 0) { // An error occurred. mPendingEventCount = 0; // Sleep after errors to avoid locking up the system. // Hopefully the error is transient. if (errno != EINTR) { ALOGW("poll failed (errno=%d)\n", errno); usleep(100000); } } else { // Some events occurred. //保存epoll事件数量,用于下次for循环处理 mPendingEventCount = size_t(pollResult); } }
到现在getEvents函数基本上就完了。还有许多内容待揣摩。
3 总结
EventHub主要工作:设备管理(加载、卸载)、输入事件读取。核心代码就是getEvent(),该代码中完成了这些事情。
其中使用到的epoll、inotify、管道这些技术可以好好研究下。。。。
。。。。。。