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。。。(这里忽略了一些个别情况)。

原创粉丝点击