对input系统在framework层的分析从一次触摸屏丢失上报事件开始:由于设备节点/dev/input/input0存在,而且getevent能响应点触摸屏的动作,所以把问题定位到了EventHub和InputManager这一层。
InputManager的结构很简单,对外开放
virtual status_t start(); virtual status_t stop(); virtual sp<InputReaderInterface> getReader(); virtual sp<InputDispatcherInterface> getDispatcher();
四个成员函数,内部带有私有成员
sp<InputReaderInterface> mReader; sp<InputReaderThread> mReaderThread; sp<InputDispatcherInterface> mDispatcher; sp<InputDispatcherThread> mDispatcherThread;其构造过程也很简单:
mDispatcher = new InputDispatcher(dispatcherPolicy); -- 根据输入的参数构造一个dispatcher mReader = new InputReader(eventHub, readerPolicy, mDispatcher); -- 根据参数构造一个inputreader initialize();
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); -- 产生reader线程用于从设备节点读取input event mDispatcherThread = new InputDispatcherThread(mDispatcher); -- 产生dispachter线程分发event }InputManager的start主要就是让这两个线程跑起来:
status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); ... result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); ... return OK; }
接着先来分析InputReader:
InputReaderThread run之后会重复的调用
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }
亦即mReader->loopOnce();
void InputReader::loopOnce() { RawEvent rawEvent; mEventHub->getEvent(& rawEvent); process(& rawEvent); }
InputReader上来就找EventHub要数据(getEvent()),所以我们要先看看EventHub的getEvent都做了些什么工作:
bool EventHub::getEvent(RawEvent* outEvent) { outEvent->deviceId = 0; ... // 第一次调用getEvent的时候,需要调用openPlatformInput来搜索输入系统来查找可用的设备 if (!mOpened) { mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; mOpened = true; mNeedToSendFinishedDeviceScan = true; } }
bool EventHub::openPlatformInput(void) { // 为poll()调用作准备 mFDCount = 1; mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); mFDs[0].events = POLLIN; mFDs[0].revents = 0; mDevices[0] = NULL; ... // 上面有赋值device_path = "/dev/input" res = scanDir(device_path); ... return true; }
int EventHub::openDevice(const char *deviceName) { int version; int fd; struct pollfd *new_mFDs; device_t **new_devices; char **new_device_names; char name[80]; char location[80]; char idstr[80]; struct input_id id; LOGV("Opening device: %s", deviceName); AutoMutex _l(mLock); // 根据路径打开文件获取version, id, name等信息 fd = open(deviceName, O_RDWR); if(fd < 0) ... if(ioctl(fd, EVIOCGVERSION, &version)) ... if(ioctl(fd, EVIOCGID, &id)) ... if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) ... // check to see if the device is on our excluded list - 如果此设备在排除名单内,忽略它 List<String8>::iterator iter = mExcludedDevices.begin(); List<String8>::iterator end = mExcludedDevices.end(); for ( ; iter != end; iter++) { ... } // 读取location, idstr等信息 if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) ... if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) ... // 将设备文件设置为非阻塞 if (fcntl(fd, F_SETFL, O_NONBLOCK)) ... // 在mDeviceById数组中寻找“空位”,扩充mDeviceById的空间并把新device加到尾部 int devid = 0; while (devid < mNumDevicesById) { if (mDevicesById[devid].device == NULL) { break; } devid++; } if (devid >= mNumDevicesById) { device_ent* new_devids = (device_ent*)realloc(mDevicesById, sizeof(mDevicesById[0]) * (devid + 1)); if (new_devids == NULL) { LOGE("out of memory"); return -1; } mDevicesById = new_devids; mNumDevicesById = devid+1; mDevicesById[devid].device = NULL; mDevicesById[devid].seq = 0; } mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; if (mDevicesById[devid].seq == 0) { mDevicesById[devid].seq = 1<<SEQ_SHIFT; } // 为后面用poll()来获取kernel层上报的input event作准备 new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); if (new_mFDs == NULL || new_devices == NULL) { LOGE("out of memory"); return -1; } mFDs = new_mFDs; mDevices = new_devices; device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); if (device == NULL) { LOGE("out of memory"); return -1; } device->fd = fd; mFDs[mFDCount].fd = fd; mFDs[mFDCount].events = POLLIN; mFDs[mFDCount].revents = 0; // Figure out the kinds of events the device reports. // 分辨本device的输入类型: keypad? touchscreen? gamepad? uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; memset(key_bitmask, 0, sizeof(key_bitmask)); // 测试本device是否为键盘,是的话把key_bitmask保存下来 LOGV("Getting keys..."); if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { ... } // 是否为轨迹球或鼠标 // See if this is a trackball (or mouse). if (test_bit(BTN_MOUSE, key_bitmask)) { ... } // 是否为touchscreen,是否支持多点触摸 // See if this is a touch pad. uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; memset(abs_bitmask, 0, sizeof(abs_bitmask)); LOGV("Getting absolute controllers..."); if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) { ... } #ifdef EV_SW // 是否为switch设备,这个宏没有打开 #endif // 如果是键盘的话,加载keypad-layout,即/usr/keylayout/中的xxx.kl文件 if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { ... status_t status = device->layoutMap->load(keylayoutFilename); if (status) { LOGE("Error %d loading key layout.", status); } ... // tell the world about the devname (the descriptive name) - 找一个首选keyboard出来 if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) { // the built-in keyboard has a well-known device ID of 0, // this device better not go away. mHaveFirstKeyboard = true; mFirstKeyboardId = device->id; property_set("hw.keyboards.0.devname", name); } else { // ensure mFirstKeyboardId is set to -something-. if (mFirstKeyboardId == 0) { mFirstKeyboardId = device->id; } } // 如果键盘有数字键,方向键,游戏控制键等特殊按钮,都一一标记到classes中 if (hasKeycodeLocked(device, AKEYCODE_Q)) ... 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; } } LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n", device->id, name, propName, keylayoutFilename); } // 如果不知道这个device是啥类型,忽略之 if (device->classes == 0) { ... } // 加到mDevicesById和mOpeningDevices中,回头getEvent()会处理mOpenningDevices链表中的新增device mDevicesById[devid].device = device; device->next = mOpeningDevices; mOpeningDevices = device; mDevices[mFDCount] = device; mFDCount++; return 0; }
bool EventHub::getEvent(RawEvent* outEvent) { .... // main loop here - 主循环体 for (;;) { // Report any devices that had last been removed. - 当有设备从系统中移除时 if (mClosingDevices != NULL) { ... } // Report any devices that had last been added - 当有设备加入到系统中时 if (mOpeningDevices != NULL) { device_t* device = mOpeningDevices; LOGV("Reporting device opened: id=0x%x, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; if (device->id == mFirstKeyboardId) { outEvent->deviceId = 0; } else { outEvent->deviceId = device->id; } outEvent->type = DEVICE_ADDED; outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); mNeedToSendFinishedDeviceScan = true; return true; // 一个设备上报一个DEVICE_ADDED事件 } // After finish scanning all input devices in system, send finished siganal at boot time if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; outEvent->type = FINISHED_DEVICE_SCAN; outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); return true; } // 下面为普通event获取和上报过程 ... ... } }
从上面的代码段可知,对于mOpeningDevices中的device,getEvent()调用都是每读取一个马上上报DEVICE_ADDED事件并返回。所以如果我们的系统中有一个keypad和一个touchscreen,则在扫描阶段就会有3次getEvent()返回:键盘的DEVICE_ADDED,触摸屏的DEVICE_ADDED和表示扫描完成的FINISHED_DEVICE_SCAN。