当前位置: 代码迷 >> 综合 >> input子系统 Android6.0输入系统之EventHub源码分析
  详细解决方案

input子系统 Android6.0输入系统之EventHub源码分析

热度:42   发布时间:2024-01-13 21:59:07.0

上篇文章《Android6.0输入系统之InputManagerService构成分析》 完成了IMS的创建,接着就沿着输入系统这条路继续往下走。 
在EventHub的构造函数中,它通过INotify与Epoll机制建立起对设备点增删事件及可读状态的监听。INotify是Linux内核所提供的一种文件系统变化通知机制。它可以为应用程序监控文件系统的变化,如文件的新建、删除、读写等等。它有两个基本对象,inotify对象对应一个队列,应用程序可以向inotify对象添加多个监听,当被监听的事件发生时,可以通过read()函数从inotify对象中将事件信息读取出来;而watch对象则用来描述文件系统的变化事件的监听,它是一个二元组,包括监听目标和事件掩码两个元素,监听目标是文件系统的一个路径,可以是文件也可以是文件夹。Epoll可以使用一次等待监听多个描述符的可读/可写状态。 
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);

    //1.首先使用epoll_create()函数创建一个epoll对象
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    ...
    //2.创建一个inotify对象,用来监听设备节点的增删事件
    mINotifyFd = inotify_init();
    //将存储设备点的路径dev/input作为监听对象添加到inotify对象中
    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);

    //3.将mINotifyFd作为epoll的一个监控对象
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;  //监听mINotifyFd可读
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    //将对mINotifyFd的监听注册到epoll对象中
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    ...
}

getEvents()函数的工作方式、输入设备管理、原始输入事件的监听与读取
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    /* event指针指向在buffer中下一个可用于存储事件的RawEvent结构体。每存储一个事件,event指针都会向后偏移一个元素 */
    RawEvent* event = buffer;
    //capacity记录了buffer中的剩余的元素数量
    size_t capacity = bufferSize;
    bool awoken = false;
    //循环getEvent()函数的主体
    for (;;) {
        ...
        //遍历mClosingDevices链表,为每一个已卸载的设备生成DEVICE_REMOVED事件
        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;
            //设置事件的类型为DEVICE_REMOVED
            event->type = DEVICE_REMOVED;
            //将event指针移动到下一个可用于填充事件的RawEvent
            event += 1;
            //生成DEVICE_REMOVED事件之后,被卸载的Device对象就不再需要了
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;

            //scanDevicesLocked()将会把dev/input下所有可用的输入设备打开并储存到Device结构体中
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

        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;
            }
        }

        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }

        // Grab the next input event.
        bool deviceChanged = false;

        //处理未被inputReader取走的输入事件与设备事件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            /* 通过Epoll事件的data字段确定此事件表示mINotifyFd可读注意EPOLL_ID_INOTIFY在EventHub的构造函数中作为data字段向Epoll注册mINotifyFd的可读事件*/
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;  //标记INotify事件待处理
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;   //继续处理下一条Epoll事件
            }
            //其他Epoll事件的处理
            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;
            }
            //通过Epoll的data.u32字段获取设备Id,进而获取对应的Device对象
            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }

            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                //如果Epoll事件为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);
                    //将读取到的每一个input_event结构体中的数据转换为RawEvent对象,并存储在buffer参数中以返回给调用者
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        ...
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
          ...
        //若mINotifyFd有数据可读,说明该设备节点发生了增删操作
        //若INotify事件待处理
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            /* 调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件这个函数将完成设备的加载与卸载 */
            readNotifyLocked();
            deviceChanged = true;
        }

        //设备节点增删操作发生时,则重新执行循环体,以便将设备变化的事件放入buffer中
        //若处理了INotify事件,则返回到循环开始处,生成设备增删事件
        if (deviceChanged) {
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }

        //若此次getEvents()调用没能获取事件,说明mPendingEventIndex中没有事件可用。
        mPendingEventIndex = 0;

       ...

    //返回本次getEvents()调用所读的事件数据
    return event - buffer;
}

加载所有输入设备由scanDevicesLocked()函数完成,代码如下:

void EventHub::scanDevicesLocked() {
    //调用scanDirLocked()函数遍历dev/input文件夹下的所有设备节点并打开
    status_t res = scanDirLocked(DEVICE_PATH);
    if(res < 0) {
        ALOGE("scan dir failed for %s\n", DEVICE_PATH);
    }
    //时刻打开这一个名为VIRTUAL_KEYBOARD虚拟的输入设备
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}

scanDevicesLocked()遍历指定文件夹下的所有设备节点,分别对其执行openDeviceLocked()完成设备的打开操作,为设备节点创建并加载Device结构体。代码如下:

status_t EventHub::openDeviceLocked(const char *devicePath) {
    ...
    //打开设备节点的文件描述符,用于获取设备信息以及读取原始输入事件
    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
    }
    //通过ioctl()函数从设备节点中获取输入设备的厂商信息
    InputDeviceIdentifier identifier;
    ...

    //分配一个设备id并创建Device结构体
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    ...
    //为设备加载配置信息
    loadConfigurationLocked(device);

    // 通过ioctl函数获取设备的事件位掩码
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

    ...
    //将设备节点的描述符的可读事件注册到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;
    }

    ...
    //调用addDeviceLocked()将Device添加到mDevices字典中
    addDeviceLocked(device);
    return 0;
}

调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件这个函数将完成设备的加载与卸载。代码如下:

status_t EventHub::readNotifyLocked() {
    ...
    //从mINotifyFd中读取INotify事件列表
    res = read(mINotifyFd, event_buf, sizeof(event_buf));
    ...
    //逐个处理列表中的事件
    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); //若事件类型为IN_CREATE,则加载对应的设备
            } 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;
}

EventHub作为直接操作设备节点的输入系统组件,通过接口getEvent()向使用者提供抽取设备事件与原始输入事件的功能,其核心都是在getEvent()函数中完成的。下面接着讨论EventHub所提供的原始输入事件如何被加工为Android输入事件——InputReader。
 

  相关解决方案