Android Input流程分析(二):EventHub

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Invoker123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值