loop线程已经运行起来了,如果不出意外,它是不会终止的;不妨以此为起点,再开始一段新的旅程,我要去探索input事件的获取。
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); mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); 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; 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); }前面一堆类似mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD)初始化成员变量的就比较简单了,向下看需要补充点知识,epoll机制和inotify机制。
class InputReader : public InputReaderInterface { ...... static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; } void InputReader::loopOnce() { ...... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); ...... }EventHub::getEvents()要做的事情太多了,一点一点分析吧。
1 RawEvent
从EventHub中取出的原始事件。struct RawEvent { nsecs_t when; //时间 int32_t deviceId; //device ID,如果是内嵌键盘mBuiltInKeyboardId为0 int32_t type; //device操作,添加,移除或者事件类型 int32_t code; //事件编码 int32_t value; //值 };
2 input_event
这是kernel里完全对应的一个事件结构struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };3 mNeedToReopenDevices是说需要重复打开,构造EventHub的时候,它肯定是false的;还不知道什么时候需要这个东东,先放一放。
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); //打开目录"/dev/input" if(dir == NULL) return -1; strcpy(devname, dirname); //devname = "/dev/input" filename = devname + strlen(devname);//filename就是devname上的一个游标,此时游到了strlen(devname)处 *filename++ = '/';//devname = "/dev/input/",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);//假设找到为event0,则devname = "/dev/input/event0" openDeviceLocked(devname); /*openDeviceLocked创建device,并初始化device->configuration(IDC配置文件),device->KeyMap->keyLayoutMap(*kl按键布局文件)、device->KeyMap->keyCharacterMap(按键字符映射文件)。还初始化了device->classes输入设备类别,比如device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT。创建device->id和device的映射。 */ }//通过while循环创建/dev/input目录文件对应的所以device closedir(dir); return 0; }status_t EventHub::openDeviceLocked(const char *devicePath)
if (count) { processEventsLocked(mEventBuffer, count); }InputReader::processEventsLocked()中根据rawEvent->type进行事件处理。到下一次进入getEvents()时,event != buffer就不会成立了,就可以epoll_wait()来查询前面设置的几个事件是否发生,有几个?一个是mINotifyFd,一个是mWakeReadPipeFd,一个是我们open的input device。
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; }
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; ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); res = read(mINotifyFd, event_buf, sizeof(event_buf));//读取notify的事件,就是dev/input有没有增加或者删除 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++ = '/';//dev/input/ 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); /* mOpeningDevices标记的刚刚open的第一个device,当所有RawEvent的DEVICE_ADDED事件都处理完后,mOpeningDevices为NULL。所以close的时候,先看一下通过mOpeningDevices能不能找到要close的device,如果能,分情况: 要删除的device是mOpeningDevices链中的一个,那么找到要删除的前一个pred,pred->next = device->next;然后delete device。 要删除的device是mOpeningDevices,那就没有前一个了mOpeningDevices = device->next;然后delete device。 如果不能,现在就不能删除了,万一还有事件没有处理完,它的client还在呢,得通知它。现在只做标记: device->next = mClosingDevices; mClosingDevices = device; 显然在下一次getEvents()中会处理。 */ } } event_size = sizeof(*event) + event->len; res -= event_size; event_pos += event_size; } return 0; }(2) eventItem.data.u32 == EPOLL_ID_WAKE,awoken = true。还要读mWakeReadPipeFd,一直读到没有东西可读为止。为什么能读到,说明有写mWakeWritePipeFd阿。
void EventHub::wake() { ALOGV("wake() called"); ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); } while (nWrite == -1 && errno == EINTR); if (nWrite != 1 && errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } }那什么时候需要wake()呢?比如requestRefreshConfiguration,需要重新load配置文件的时候,我们就不能继续处理epoll_wait()查询到的事件了,要break出for循环,更新了配置文件后再来处理epoll_wait()查询到的事件。
#else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; event->value = iev.value;每copy一个事件event += 1;事件buffer加1,capacity -= 1;buffer长度减一。capacity == 0表示buffer已经满了,只能下一次循环再把事件读到buffer里了,先break出处理epoll事件的while,去loopOnce()里处理下满的buffer;别忘了事件指针mPendingEventIndex -= 1,不然下次不读了。
// 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 }这很简单了,先关闭所有device,设置重新扫描标志,break出while,就进入loopOnce()处理了;再回来的时候就重新扫描了。