Android Input流程分析(二):EventHub
来源:互联网 发布:互联网大数据挖掘 编辑:程序博客网 时间:2024/05/17 01:54
现在,InputReader线程已经开始运行,在InputReaderThread::threadLoop开始读取EventHub送上来的事件。mReader指向一个InputReader对象,调用InputReader::loopOnce函数。
/frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true;}
先忽略其他细节,InputReader是在getEvents中捞取事件的。mEventBuffer是一个RawEvent结构体数组,mEventHub指向一个EventHub对象。
/frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; bool inputDevicesChanged = false; Vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex _l(mLock); oldGeneration = mGeneration; timeoutMillis = -1; uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; timeoutMillis = 0; refreshConfigurationLocked(changes); } else if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) {#if DEBUG_RAW_EVENTS ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);#endif mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } if (oldGeneration != mGeneration) { inputDevicesChanged = true; getInputDevicesLocked(inputDevices); } } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked // on another thread similarly waiting to acquire the InputReader lock thereby // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. mQueuedListener->flush();}
EventHub在什么时候创建的呢?在NativeInputManager的构造函数中。看看EventHub的构造函数。首先创建了epoll文件描述符mEpollFd。 inotify是一个内核用于通知用户空间程序文件系统变化的机制。inotify_init用于创建一个inotify的fd,inotify_add_watch中DEVICE_PATH为“/dev/input”,inotify_add_watch作用是监控/dev/input目录下文件的变化。之后,将mEpollFd加入到epoll监控队列中,并将EPOLL_ID_INOTIFY保存到eventItem的union成员epoll_data中。然后,创建一个管道,将读端加入到epoll监控队列中,并将EPOLL_ID_WAKE保存到eventItem的union成员epoll_data中。
/frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), 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); int major, minor; getLinuxRelease(&major, &minor); // EPOLLWAKEUP was introduced in kernel 3.5 mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);}
回到EventHub::getEvents函数。readBuffer是内核input_event数组,event指向RawEvent数组下个可用的地址,capacity表示input_event数组的剩余容量,awoken表示是否通过写入管道唤醒阻塞的epoll_wait。随后进入一个死循环。
/frameworks/native/services/inputflinger/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ALOG_ASSERT(bufferSize >= 1); AutoMutex _l(mLock); struct input_event readBuffer[bufferSize]; RawEvent* event = buffer; size_t capacity = bufferSize; bool awoken = false; ...
这部分是重启设备处理流程。当Input的一些设置被改变时,mNeedToReopenDevices会变true,表示要重启设备。调用closeAllDevicesLocked关闭所有打开的设备,将mNeedToScanDevices设为true,表示下次进入getEvents函数时要扫描打开设备。之后,使用break退出主循环,等待loopOnce再一次调用getEvents函数进行扫描设备的操作。
/frameworks/native/services/inputflinger/EventHub.cpp
for (;;) { 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 } ...
mDevices类型为KeyedVector< int32_t, Device*>,第一个模板参数为Device的id。第二个模板参数是对应的Device结构体指针,KeyedVector内部使用SortedVector实现,里面元素按key值的大小排列。closeAllDevicesLocked就是逐一对mDevices里的Device执行closeDeviceLocked函数进行关闭。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::closeAllDevicesLocked() { while (mDevices.size() > 0) { closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); }}
mBuiltInKeyboardId在EventHub构造函数中被初始化为NO_BUILT_IN_KEYBOARD(-2)。在打开设备时,若设备的类别为键盘,游戏手柄及虚拟键盘时,mBuiltInKeyboardId会被设为该设备的id。当要关闭的设备类型是上述类型时,将 mBuiltInKeyboardId重新置成NO_BUILT_IN_KEYBOARD。isVirtual函数判断Device的fd是否小于0,若大于或等于0,将这个fd从mEpollFd监控队列中移除。releaseControllerNumberLocked将Device的controllerNumber置0,并清除掉mControllerNumbers相应的标志位。然后将参数对应的Device从mDevices中移除,并将对应的fd关闭。
mOpeningDevices对应一个打开的Device组成的链表的头节点。从这个头节点开始遍历,找到要关闭的Device,将其从链表中删除。若找不到,将这个要关闭的Device插入到到mClosingDevices链表的头部,mClosingDevices仍指向这个链表的头部。mClosingDevices对应一个关闭的Device组成的链表的头节点。
/frameworks/native/services/inputflinger/EventHub.cpp
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()) { if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); } } releaseControllerNumberLocked(device); 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. device->next = mClosingDevices; mClosingDevices = device; }}
这里要提一下Device结构体。
/frameworks/native/services/inputflinger/EventHub.h
struct Device { Device* next;//Device是以链表的形式组织的 int fd; // 文件描述符 const int32_t id;//唯一id const String8 path;//设备路径 const InputDeviceIdentifier identifier;//厂商信息 uint32_t classes;//类别 //掩码数组 uint8_t keyBitmask[(KEY_MAX + 1) / 8]; uint8_t absBitmask[(ABS_MAX + 1) / 8]; uint8_t relBitmask[(REL_MAX + 1) / 8]; uint8_t swBitmask[(SW_MAX + 1) / 8]; uint8_t ledBitmask[(LED_MAX + 1) / 8]; uint8_t ffBitmask[(FF_MAX + 1) / 8]; uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; //配置信息 String8 configurationFile; PropertyMap* configuration; //键盘映射表 VirtualKeyMap* virtualKeyMap; KeyMap keyMap; sp<KeyCharacterMap> overlayKeyMap; sp<KeyCharacterMap> combinedKeyMap; //力反馈相关 bool ffEffectPlaying; int16_t ffEffectId; // initially -1 int32_t controllerNumber; int32_t timestampOverrideSec; int32_t timestampOverrideUsec; ...
遍历mClosingDevices链表,每次得到一个Device,填充好一个RawEvent,标记为DEVICE_REMOVED类型,event指针加1表示使用掉了一个RawEvent,然后delete掉这个Device,将mNeedToSendFinishedDeviceScan标记为true,表示待会要发送扫描完成的事件。capacity减1,若等于0表示nput_event数组已无剩余空间,退出遍历。
/frameworks/native/services/inputflinger/EventHub.cpp
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; event->type = DEVICE_REMOVED; event += 1; delete device; mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; } }
InputReader第一次调用getEvents时,上面步骤都是不执行的。mNeedToScanDevices在构造函数中被初始化为true,所以会从这里开始。
关键代码是调用scanDevicesLocked。最后也将mNeedToSendFinishedDeviceScan 设为true,待会会发送设备扫描完成事件。
/frameworks/native/services/inputflinger/EventHub.cpp
if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; }
scanDirLocked扫描/dev/input下的文件,并以这些文件的完整路径名为入参调用openDeviceLocked函数。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::scanDevicesLocked() { status_t res = scanDirLocked(DEVICE_PATH);//DEVICE_PATH为"/dev/input" if(res < 0) { ALOGE("scan dir failed for %s\n", DEVICE_PATH); } if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { createVirtualKeyboardLocked(); }}
openDeviceLocked函数实现比较繁琐。选择重点的来讲。第一部分,使用从fd获得的信息填充identifier的成员,包括name,bus,product,vendor,version,location和uniqueId。assignDescriptorLocked用来设置identifier的descriptor和nonce成员。
/frameworks/native/services/inputflinger/EventHub.cpp
status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; ALOGV("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } InputDeviceIdentifier identifier; // Get device name. if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name.setTo(buffer); } // Check to see if the device is on our excluded list for (size_t i = 0; i < mExcludedDevices.size(); i++) { const String8& item = mExcludedDevices.itemAt(i); if (identifier.name == item) { ALOGI("ignoring event id %s driver %s\n", devicePath, item.string()); close(fd); return -1; } } // Get device driver version. int driverVersion; if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; } // Get device identifier. struct input_id inputId; if(ioctl(fd, EVIOCGID, &inputId)) { ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; } identifier.bus = inputId.bustype; identifier.product = inputId.product; identifier.vendor = inputId.vendor; identifier.version = inputId.version; // Get device physical location. if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.location.setTo(buffer); } // Get device unique id. if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.uniqueId.setTo(buffer); } // Fill in the descriptor. assignDescriptorLocked(identifier); // Make file descriptor non-blocking for use with poll(). if (fcntl(fd, F_SETFL, O_NONBLOCK)) { ALOGE("Error %d making device file descriptor non-blocking.", errno); close(fd); return -1; } ...}
看看assignDescriptorLocked函数。uniqueId用来描述identifier的唯一性,当uniqueId为空时,就使用nonce来确保唯一性。descriptor是用identifier各个成员的值按一定格式组合起来再使用SHA1加密得到的值,generateDescriptor函数的作用就是产生这个SHA1值赋给descriptor。如果uniqueId为空,检查mDevices中各个Device对应的descriptor是否等于当前得到的descriptor,若相等,将nonce加1,重新使用generateDescriptor生成新的descriptor,直到这个新的descriptor是唯一的。
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { // Compute a device descriptor that uniquely identifies the device. // The descriptor is assumed to be a stable identifier. Its value should not // change between reboots, reconnections, firmware updates or new releases // of Android. In practice we sometimes get devices that cannot be uniquely // identified. In this case we enforce uniqueness between connected devices. // Ideally, we also want the descriptor to be short and relatively opaque. identifier.nonce = 0; String8 rawDescriptor = generateDescriptor(identifier); if (identifier.uniqueId.isEmpty()) { // If it didn't have a unique id check for conflicts and enforce // uniqueness if necessary. while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) { identifier.nonce++; rawDescriptor = generateDescriptor(identifier); } } ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(), identifier.descriptor.string());}
/frameworks/native/services/inputflinger/EventHub.cpp
static String8 generateDescriptor(InputDeviceIdentifier& identifier) { String8 rawDescriptor; rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product); // TODO add handling for USB devices to not uniqueify kbs that show up twice if (!identifier.uniqueId.isEmpty()) { rawDescriptor.append("uniqueId:"); rawDescriptor.append(identifier.uniqueId); } else if (identifier.nonce != 0) { rawDescriptor.appendFormat("nonce:%04x", identifier.nonce); } if (identifier.vendor == 0 && identifier.product == 0) { // If we don't know the vendor and product id, then the device is probably // built-in so we need to rely on other information to uniquely identify // the input device. Usually we try to avoid relying on the device name or // location but for built-in input device, they are unlikely to ever change. if (!identifier.name.isEmpty()) { rawDescriptor.append("name:"); rawDescriptor.append(identifier.name); } else if (!identifier.location.isEmpty()) { rawDescriptor.append("location:"); rawDescriptor.append(identifier.location); } } identifier.descriptor = sha1(rawDescriptor); return rawDescriptor;}
回到openDeviceLocked函数中。在EventHub构造函数中,mNextDeviceId初始化为1。这样,新创建的Device的id会从1起逐渐递增。之后是一大串的Log。
/frameworks/native/services/inputflinger/EventHub.cpp
int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath), identifier); ALOGV("add device %d: %s\n", deviceId, devicePath); ALOGV(" bus: %04x\n" " vendor %04x\n" " product %04x\n" " version %04x\n", identifier.bus, identifier.vendor, identifier.product, identifier.version); ALOGV(" name: \"%s\"\n", identifier.name.string()); ALOGV(" location: \"%s\"\n", identifier.location.string()); ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string()); ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string()); ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
loadConfigurationLocked用于加载按键文件,详见我另一篇博客:《Android加载按键文件流程》。之后的流程统一按处理按键事件展开。
/frameworks/native/services/inputflinger/EventHub.cpp
...// Load the configuration file for the device. loadConfigurationLocked(device);...
EV_KEY表示按键类型的事件。能够上报这类事件的设备有键盘,鼠标,手柄,手写板等一切拥有按钮的设备(包括手机上的实体按键)。在Device结构体中,对应的事件位掩码keyBitmask描述了可以产生的事件的集合。按键事件的全集包括字符按键,方向键,控制键,鼠标键,游戏按键等。
/frameworks/native/services/inputflinger/EventHub.cpp
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); ... // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1)); bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; }
如果Device类型为keyboard或joystick,加载解析.kl和.kcm文件。类型为keyboard的情况还会把mBuiltInKeyboardId设置为该Device的id。根据传上来的按键集合,可以将keyboard类型分为几类:
INPUT_DEVICE_CLASS_ALPHAKEY表示字符按键设备,INPUT_DEVICE_CLASS_DPAD表示拥有方向键的设备,INPUT_DEVICE_CLASS_GAMEPAD表示游戏手柄设备。
/frameworks/native/services/inputflinger/EventHub.cpp
// Load the key map. // We need to do this for joysticks too because the key layout may specify axes. status_t keyMapStatus = NAME_NOT_FOUND; if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { // Load the keymap for the device. keyMapStatus = loadKeyMapLocked(device); } // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; } // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycodeLocked(device, AKEYCODE_Q)) { device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } // See if this device has a DPAD. if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { device->classes |= INPUT_DEVICE_CLASS_DPAD; } // See if this device has a gamepad. for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) { if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; break; }
将Device对应的fd添加到epoll监控队列中,监听事件为EPOLLIN。在3.5版本以上的Linux中,增设监听事件EPOLLWAKEUP。
/frameworks/native/services/inputflinger/EventHub.cpp
// Register with epoll. struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; if (mUsingEpollWakeup) { eventItem.events |= EPOLLWAKEUP; } eventItem.data.u32 = deviceId; 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; }
最后,往mDevices添加id-Device*键值对,往mOpeningDevices表示的链表头部插入该Device,mOpeningDevices继续指向该链表的头部。
/frameworks/native/services/inputflinger/EventHub.cpp
...addDeviceLocked(device); return 0;}
/frameworks/native/services/inputflinger/EventHub.cpp
void EventHub::addDeviceLocked(Device* device) { mDevices.add(device->id, device); device->next = mOpeningDevices; mOpeningDevices = device;}
值得注意的是,在scanDevicesLocked函数中,还将创建VirtualKeyboard,此处不再详述。至此,/dev/input/下的所有Device信息都已经被初始化,mOpeningDevices链表已经就绪。现在返回到getEvents函数中。
遍历mOpeningDevices链表,每个Device对应生成一个RawEvent。RawEvent的时间戳设为当前时间,deviceId设为mBuiltInKeyboardId或Device的id,事件类型记为DEVICE_ADDED。mNeedToSendFinishedDeviceScan设为true,表示要发送设备扫描完成事件,最后将capacity减1,因为产生了一个input_event事件,input_event数组剩余容量减1。
/frameworks/native/services/inputflinger/EventHub.cpp
...while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; ALOGV("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; event += 1; mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; } }
每次重启,卸载,开启设备时,都要将mNeedToSendFinishedDeviceScan设为true,这会对应一个RawEvent和input_event.
/frameworks/native/services/inputflinger/EventHub.cpp
if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; event->when = now; event->type = FINISHED_DEVICE_SCAN; event += 1; if (--capacity == 0) { break; } }
InputReader在线程循环中不断调用loopOnce,也就意味着getEvents函数会被循环调用。先看看第一次调用的情况。前面提到,第一次进入getEvents函数,入口点在scanDevicesLocked函数以用来扫描打开的设备。在EventsHub的构造函数中,
mPendingEventIndex和mPendingEventCount都被初始化为0,所以第一次进入getEvents函数时并不会进入以下循环:
/frameworks/native/services/inputflinger/EventHub.cpp
...while (mPendingEventIndex < mPendingEventCount) {...
设备的添加导致产生RawEvent事件,使得event指针不等于RawEvents数组首地址。所以接下来会退出getEvents函数的主循环,直接返回产生的RawEvent数量。
/frameworks/native/services/inputflinger/EventHub.cpp
if (event != buffer || awoken) { break; } ... return event - buffer;
第二次进入getEvents时,由于mNeedToScanDevices已被置为false,所以不会再去扫描设备。此时event的值与buffer相同,这次会进入到epoll_wait中。epoll监听了以下fd:Inotify的fd,第一次扫描的设备fd,唤醒管道读端的fd。epoll_wait在等待timeoutMillis的时间内,返回的pollResult为发生的目标事件数量,同时赋给mPendingEventCount,发生的事件保存在mPendingEventItems中。下一步,重新进入getEvent函数主循环。
/frameworks/native/services/inputflinger/EventHub.cpp
...int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);...else { // Some events occurred. mPendingEventCount = size_t(pollResult); }...
mPendingEventCount已经为正,终于可以进入while (mPendingEventIndex < mPendingEventCount)循环了。
遍历mPendingEventItems中的epoll_event,如果是Inotify的EPOLLIN事件,将mPendingINotify设为true。之后contine这个循环。
/frameworks/native/services/inputflinger/EventHub.cpp
while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { if (eventItem.events & EPOLLIN) { mPendingINotify = true; } else { ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; }
如果是管道读端的EPOLLIN事件,把awoken设置成true,读走管道读端的数据后,continue这个循环。
/frameworks/native/services/inputflinger/EventHub.cpp
if (eventItem.data.u32 == EPOLL_ID_WAKE) { if (eventItem.events & EPOLLIN) { ALOGV("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 { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; }
根据eventItem找到发生EPOLLIN事件的Device后,从该Device对应的fd读取数据到readBuffer中,readBuffer是一个input_event结构体数组。如果数取的数据为空或者出错返回ENODEV,说明对应的Device已经被移除,deviceChanged设为true,调用closeDeviceLocked处理该设备移除事件。如果正常返回,count得到在Device上发生的input_event数量。遍历readBuffer上的每个input_event,接下来用input_event的成员去初始化RawEvent,readBuffer剩余容量减1。若剩余容量为0,则退出当前循环。
/frameworks/native/services/inputflinger/EventHub.cpp
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); ... 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. ALOGW("could not get event, removed? (fd: %d size: %" PRId32 " bufferSize: %zu capacity: %zu 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]; ALOGV("%s got: time=%d.%06d, 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); // Some input devices may have a better concept of the time // when an input event was actually generated than the kernel // which simply timestamps all events on entry to evdev. // This is a custom Android extension of the input protocol // mainly intended for use with uinput based device drivers. 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; } } ... event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; event->value = iev.value; event += 1; capacity -= 1; } 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 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()); } }
处理完mPendingEventItems的事件后,mPendingEventIndex的值与mPendingEventCount的值相等。如果之前处理的mPendingEventItem中的事件有发生在Inotify的(mPendingINotify为true),说明发生了设备增删事件,此时会调用readNotifyLocked处理设备增删事件,将deviceChanged设为true,mPendingINotify 设为false。
/frameworks/native/services/inputflinger/EventHub.cpp
// 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. if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; }
EventHub::readNotifyLocked函数就是遍历所有的Inotify事件,根据inotify_event的mask值判断设备是新增还是卸载进行处理。如果是设备新增事件,调用openDeviceLocked进行处理。如果是设备卸载事件,调用closeDeviceByPathLocked进行处理,closeDeviceByPathLocked内部调用了closeDeviceLocked。
/frameworks/native/services/inputflinger/EventHub.cpp
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)); 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) { 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; } return 0;}
deviceChanged被设置为true的地方有三处:1.读取Device到readBuffer中的内容为空或者产生ENODEV的错误时;2.Device事件为EPOLLHUP(被挂起)时;3.发生Inotify事件,处理完mClosingDevices,mOpeningDevices,mDevices等数据结构的变化及Device的信息初始化操作后。
continue表示重新进入getEvents函数主循环,根据设备变化事件生成相应的RawEvent。
/frameworks/native/services/inputflinger/EventHub.cpp
... if (deviceChanged) { continue; }
第二次进入getEvents函数,可能由于没有生成RawEvent,导致event的值和buffer的值相等,所以不会进入break阶段。生成RawEvent的地方有:1.进入getEvents函数时mNeedToReopenDevices为true,表示需要重启设备;2.第一次进入getEvents函数,需要扫描/dev/input/下面的设备;3.mNeedToSendFinishedDeviceScan为true,表示生成扫描完成事件,发生在重启设备,首次扫描设备,设备新增和卸载阶段。也就是说,第二次进入getEvents函数时,只要不发生上述事件,event的值和buffer的值就会相等。将mPendingEventIndex置0,是为了能使下次进入getEvents函数能进入while (mPendingEventIndex < mPendingEventCount)循环,之后流程会走到epoll_wait阶段。
epoll_wait监控了Inotify的fd,管道读端的fd和设备的fd。epoll_wait可能会发生阻塞,因为第四个参数值可能为-1,而设备那边真的风平浪静没有任何动作发生。这样,epoll_wait函数便不能返回。Android设置了EventHub::wake往管道写端写入一个‘w’以唤醒epoll_wait,使epoll_wait函数得以返回,以重新进入getEvents函数主循环,重新进入主循环后会在”if (event != buffer || awoken)”处返回。
发生break退出getEvents函数主循环的地方有以下几处:1.input_event结构体数组readBuffer被塞满;2.重启设备;3.生成了RawEvent事件;4.管道唤醒操作;5.epoll_wait返回值为0。
epoll_wait之所以设置在”if (event != buffer || awoken)”之后是因为生成了RawEvent就不用再监听了,直接返回。没有生成就继续监听,以期待进入主循环生成RawEvent。
getEvents函数最终返回生成的RawEvent数量。
/frameworks/native/services/inputflinger/EventHub.cpp
if (event != buffer || awoken) { break; } ... 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. mPendingEventCount = size_t(pollResult); } } // All done, return the number of events we read. return event - buffer;}
/frameworks/native/services/inputflinger/EventHub.cpp
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); }}
总结
首次进入getEvents函数的流程是扫描/dev/input下的设备,并添加到epoll监控队列中。第二次进入getEvents函数时,启用epoll_wait进行监听,之后进入主循环。首次循环中,根据epoll_wait返回的结果,在循环中生成对应的RawEvent,若有RawEvent,getEvents函数会返回等待第三次进入。若没有RawEvent,会进入二次循环,此时epoll_wait可能又带来了新的事件让程序去生成RawEvent。。。(这里忽略了一些个别情况)。
- Android Input流程分析(二):EventHub
- Android Input Framework(二)---EventHub
- Android Input Framework(二)---EventHub
- Android Input Framework(二)---EventHub
- EventHub分析(二)
- Android 输入系统(二)EventHub
- Android输入事件流程中的EventHub分析及源码演示
- Android输入事件流程中的EventHub分析及源码演示
- Android输入事件流程中的EventHub分析及源码演示
- Android输入事件流程中的EventHub分析及源码演示
- Android Input流程分析(一):启动
- Android Input流程分析(三):InputReader
- Android Input流程分析(四):InputDispatcher
- 对“Android输入事件流程中的EventHub分析及源码演示”的补充
- Android 4.2 Input 流程分析
- Android 4.2 Input 流程分析
- Android 4.2 Input 流程分析
- Android: Bluetooth Input 连接流程分析
- PAT_A 1049. Counting Ones (30)
- 分时操作系统与实时操作系统
- jQuery的引入与应用
- jdbc获取数据库元数据的四个接口
- caffe训练
- Android Input流程分析(二):EventHub
- HDU -- 5601 N*M bulbs 【思维 + 规律】
- Redis与memcached
- oracle查看当前用户信息
- 高效学习-编程
- ibatis与mybatis区别?Hibernate、MyBatis、JDBC区别?
- 不用框架,python实现卷积神经网络
- 用数字电路和模拟电路搭建出循迹小车(二)
- 指针的引用与运算