由浅入深学习android input系统 - input事件采集(InputReader)

Posted 许佳佳233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由浅入深学习android input系统 - input事件采集(InputReader)相关的知识,希望对你有一定的参考价值。

android input系列文章
由浅入深学习android input系统(一) - input事件如何传递到View
由浅入深学习android input系统(二) - input事件如何传递到app进程( InputDispatcher )
由浅入深学习android input系统(三) - InputChannel解析
由浅入深学习android input系统(四) - input事件采集(InputReader)
由浅入深学习android input系统(五) - input系统的启动

概述

前面讲到,系统进程是通过InputDispatcher将事件分发给app进程的。

前文地址:由浅入深学习android input系统(三) - InputChannel解析

那么InputDispatcher的事件又是从哪里来的呢?
android是怎么收到硬件层的事件的呢?
本文将对此进行探索。

InputReader

本身的答案就是在InputReader这个类中,InputReader会启动一个线程一直循环处理逻辑,主要做两件事:

  1. 获取硬件设备的事件
  2. 将获取到的原始数据进行处理后传递给InputDispatcher
void InputReader::loopOnce() 
————————————————省略
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
————————————————省略
        if (count) 
            processEventsLocked(mEventBuffer, count);
        
————————————————省略

InputReader获取硬件事件

InputReader获取硬件事件是通过mEventHub的getEvents()方法。

mEventHub监听的是/dev/input目录下的设备节点,对这块内容由兴趣的读者可以自行看下mEventHub源码。

代码逻辑如下:

  1. 启动一个for循环去获取事件,如果没有事件就会上锁,一直到有事件进来才会继续执行逻辑。
  2. 如果有设备已经关闭,需要从监听的设备中删除,并且将”设备关闭“作为一个事件记录到event中。
  3. 如果有新的设备添加进来,需要加入监听设备的链表,并且将”设备添加“作为事件记录到event中。
  4. 遍历发生的硬件事件,首先找到事件对应的硬件,然后从该硬件读取事件相关的数据存储到event中。
  5. 最终返回发生事件的个数。(方法外本身就有对event对象的引用,因此不需要将event传出去。)
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;
    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
        

        // Report any devices that had last been added/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;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) 
                break;
            
        

        if (mNeedToScanDevices) 
            mNeedToScanDevices = false;
            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;
        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;
            

            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;
            

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


                        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;
                            
                        
                        if (device->timestampOverrideSec || device->timestampOverrideUsec) 
                            iev.time.tv_sec = device->timestampOverrideSec;
                            iev.time.tv_usec = device->timestampOverrideUsec;
                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) 
                                device->timestampOverrideSec = 0;
                                device->timestampOverrideUsec = 0;
                            
                            ALOGV("applied override time %d.%06d",
                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
                        


                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);

                        if (event->when >= now + 10 * 1000000000LL) 
                            // Double-check.  Time may have moved on.
                            nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
                            if (event->when > time) 
                                ALOGW("An input event from %s has a timestamp that appears to "
                                        "have been generated using the wrong clock source "
                                        "(expected CLOCK_MONOTONIC): "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".  "
                                        "Using current time instead.",
                                        device->path.string(), event->when, time, now);
                                event->when = time;
                             else 
                                ALOGV("Event time is ok but failed the fast path and required "
                                        "an extra call to systemTime: "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".",
                                        event->when, time, now);
                            
                        
                        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());
            
        
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) 
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        

        // Report added or removed devices immediately.
        if (deviceChanged) 
            continue;
        

        // Return now if we have collected any events or if we were explicitly awoken.
        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) 
            mPendingEventCount = 0;
            if (errno != EINTR) 
                ALOGW("poll failed (errno=%d)\\n", errno);
                usleep(100000);
            
         else 
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        
    

    return event - buffer;

InputReader处理硬件事件

硬件的事件有很多种,包括”设备输入“、”设备添加“、”设备删除“等。
”设备输入“中也包括”键盘事件“和”触摸事件“。
我们本文主要探索”设备输入“中的”触摸事件“。

调用的任务栈如下:

  • InputReader.loopOnce()
  • InputReader.processEventsLocked()
  • InputReader.processEventsForDeviceLocked()
  • InputDevice.process()
  • InputMapper.process()
    等同于TouchInputMapper.process()
  • TouchInputMapper.sync()
  • TouchInputMapper.processRawTouches()
  • TouchInputMapper.cookAndDispatch()
  • TouchInputMapper.dispatchTouches()
  • TouchInputMapper.dispatchMotion()
  • getListener().notifyMotion()
    等同于InputDispatcher.notifyMotion()
  • InputDispatcher.dispatchOnce()

逻辑大致如下:

  1. InputReader在接收到事件之后将原始数据交给InputDevice处理。
  2. 每个InputDevice会有自己对应的处理事件的Mapper,InputDevice会把事件交给InputDevice继续处理,此处探索的是触摸事件,因此是TouchInputMapper。
    (InputDevice还有SwitchInputMapper,KeyBoardInputMapper等,分别对应不同的输入方式)
  3. TouchInputMapper中会将原始数据转化成InputDispatcher可以接收的数据后,将数据传给InputDispatcher,然后唤醒InputDispatcher线程。最终会在InputDispatcher的dispatchOnce()中处理。

对InputDispatcher还不了解的读者可以看下笔者前几篇文章:

继续探索

问题:

  1. InputReader什么时候初始化?
  2. InputDispatcher什么时候初始化?

有兴趣的读者可以关注下笔者后续的文章,或者自行阅读下相关源码。

以上是关于由浅入深学习android input系统 - input事件采集(InputReader)的主要内容,如果未能解决你的问题,请参考以下文章

由浅入深学习android input系统 - input系统的启动

由浅入深学习android input系统 - input事件采集(InputReader)

由浅入深学习android input系统 - input事件采集(InputReader)

由浅入深学习android input系统 - input事件如何传递到View

由浅入深学习android input系统 - input事件如何传递到View

由浅入深学习android input系统 - InputChannel解析