美文网首页4星
Android IMS原理解析之InputReader

Android IMS原理解析之InputReader

作者: 雷涛赛文 | 来源:发表于2021-08-26 17:31 被阅读0次

           接着上篇文章Android IMS原理解析的分析,本文主要分析Input事件获取:

    Input事件获取

           前面讲到,在start()后会启动InputReaderThread线程不断的从EventHub中抽取原始输入事件并进行加工处理,InputReaderThread继承自C的Thread类,Thread类封装了pthread线程工具,提供了与java层Thread类相似的API。
           C的Thread类提供了一个名为threadLoop()的纯虚函数,当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程,看一下InputReaderThread的逻辑实现:

    bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
    }
    

           threadLoop()内部执行了mReader->loopOnce(),然后返回true,即:InputReaderThread启动后,其线程循环不断地执行InputReader.loopOnce()方法。这个loopOnce()函数作为线程循环的循环体包含了InputReader的所有工作。
           注意:C层的Thread类与java层的Thread类有一个显著的区别:
           C层Thread类内建了线程循环,threadLoop()就是一次循环而已,只要返回值为true,threadLoop()将会不断地被内建的循环调用。
           Java层Thread类的run()函数则是整个线程的全部,一旦执行完退出,线程便结束了。
           接下来看一下loopOnce()具体做了什么工作:

    void InputReader::loopOnce() {
        .....
        .....
        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
        .....
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
        ......
        mQueuedListener->flush();
    }
    

           InputReader一次线程循环,即执行一次loopOnce(),主要执行了三项工作:
           1.从EventHub中获取未处理的事件列表,读取的结果存储在参数mEventBuffer中,返回值表示事件的个数;当EventHub中无事件可抽取时,此函数的调用将会阻塞直到事件到来或者超时;
           2.通过processEventsLocked()对事件进行处理,对于原始输入事件,进行转译、封装与加工后将结果暂存到mQueuedListener中;
           3.发布事件,所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher;
           接下来对三项工作进行逐一分析:

    1.EventHub->getEvents()

           调用EventHub的getEvents()方法来获取事件列表,EventHub是如何工作的呢?
           EventHub的直译是事件集线器,它将所有的输入事件通过一个接口getEvents()把从多个输入设备节点中读取的事件交给InputReader,它是输入系统最底层的一个组件,使用了INotify与Epoll两套机制,通过构造方法就可以看到:

    static const char *DEVICE_PATH = "/dev/input"
    EventHub::EventHub(void) : xxxx {
        .......
        //1.使用epoll_create()函数创建一个epoll对象。EPOLL_SIZE_HINT指定最大监听个数为8,
        //这个epoll对象将用来监听设备节点是否有数据可读(有无事件)
        mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    
        //2.创建一个inotify对象。这个inotify对象将被用来监听设备节点的增删事件
        mINotifyFd = inotify_init();
    
        //将存储设备节点的路径/dev/input作为监听对象添加到inotify对象中。当此文件夹下的设备节点发生创建与删除事件时,
        //都可以通过mINotifyFd读取事件的详细信息
        int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    
        //3.将mINotifyFd作为epoll的一个监控对象。当inotify事件到来时,epoll_wait()将立即返回,
        //EventHub便可以从mINotifyFd中读取设备节点的增删信息,并进行相应处理
        struct epoll_event eventItem;
        memset(&eventItem, 0, sizeof(eventItem));
        eventItem.events = EPOLLIN;
        eventItem.data.u32 = EPOLL_ID_INOTIFY;
        //将对mINotifyFd的监听注册到epoll对象中
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    
        //创建一个名为wakeFds的匿名管道
        int wakeFds[2];
        result = pipe(wakeFds);
    
        mWakeReadPipeFd = wakeFds[0];
        mWakeWritePipeFd = wakeFds[1];
    
        result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
        result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
        eventItem.data.u32 = EPOLL_ID_WAKE;
        //将管道读取端的描述符的可读事件注册到epoll对象中。因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在
        //epoll_wait()的调用里,然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。此时只需向wakeFds管道的写入端写入任意数据,
        //此时读取端有数据可读,使得epoll_wait()得以返回,从而达到唤醒InputReader线程的目的
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    }
    

           在上面的构造方法内,对Epoll和INotify进行处理,后续有事件到来时就可以获取了,接下来看一下getEvents()的逻辑处理:

    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        .......
        for (;;) {
            .......
            //处理未被InputReader取走的输入事件与设备事件。epoll_wait()所取出的epoll_event存储在mPendingEventItems中,
            //mPendingEventCount指定mPendingEventItems数组所存储的事件个数。而mPendingEventIndex指定尚未处理的epoll_event的索引
            while (mPendingEventIndex < mPendingEventCount) {
                const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
                //在这里分析每一个epoll_event 如果表示设备节点可读,则读取原始事件并放置到buffer中。
                //如果表示mINotifyEd可读,则设置mPendingINotify为true,当InputReader将现有的输入事件都取出后读取mINotifyEd中的事件,并加装与卸载相应的设备。
                //另外,如果此epoll_event表示wakeFds的读取端有数据可读,则设置awake标志为true,
                //此时无论此次getEvents()调用是否取到事件,都不会调用epoll_wait()进行事件等待。
                if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                    if (eventItem.events & EPOLLIN) {
                        mPendingINotify = true;
                    }
                    continue;
                }
    
                if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                    if (eventItem.events & EPOLLIN) {
                        char buffer[16];
                        ssize_t nRead;
                        do {
                            nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                        } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                    }
                }
        }
           .....
           //如果此次getEvents()调用成功获取了一些事件,或者要求唤醒InputReader,则退出循环并结束getEvents()的调用,
           //使InputReader可以立刻处理事件
           if (event != buffer || awoken) {
               break;
           }
    
            //如果此次getEvents()调用没能获取事件,说明mPendingEventItems中没有事件可用。
            //于是执行epoll_wait()函数等待新的事件到来,将结果存储到mPendingEventItems里,并重置mPendingEventIndex为0
            mPendingEventIndex = 0;
            int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    
            if (pollResult == 0) {
                // Timed out.
                mPendingEventCount = 0;
                break;
            }
    
            if (pollResult < 0) {
                // An error occurred.
                mPendingEventCount = 0;
                if (errno != EINTR) {
                    ALOGW("poll failed (errno=%d)\n", errno);
                    usleep(100000);
                }
            } else {
                //从epoll_wait()中得到新的事件后,重新循环,对新事件进行处理
                mPendingEventCount = size_t(pollResult);
            }
        }
    
        //返回本次getEvents()调用所读取的事件数量
        return event - buffer;
    }
    

           getEvents()函数的本质就是读取并处理Epoll事件与INotify事件:包括设备插拔及各种触摸、按钮事件等,可以看做是一个不同设备的集线器,主要面向的是/dev/input目录下的设备节点,比如说/dev/input/event0上的事件就是输入事件,通过EventHub的getEvents()就可以监听并获取该事件,getEvents()将它们封装为RawEvent结构体,并放入buffer中供InputReader进行处理。

    2.processEventsLocked()

           当通过EventHub获取到事件即count>0时,对事件进行加工处理,此时调用到processEventsLocked()方法:

    void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
        for (const RawEvent* rawEvent = rawEvents; count;) {
            int32_t type = rawEvent->type;
            size_t batchSize = 1;
            if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
                int32_t deviceId = rawEvent->deviceId;
                while (batchSize < count) {
                    if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                            || rawEvent[batchSize].deviceId != deviceId) {
                        break;
                    }
                    batchSize += 1;
                }
                processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
            } else {
                switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
                }
            }
            count -= batchSize;
            rawEvent += batchSize;
        }
    }
    

           在方法内部调用了processEventsForDeviceLocked()处理来自同一输入设备的一批事件:

    void InputReader::processEventsForDeviceLocked(int32_t deviceId,
            const RawEvent* rawEvents, size_t count) {
        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
        if (deviceIndex < 0) {
            ALOGW("Discarding event for unknown deviceId %d.", deviceId);
            return;
        }
    
        InputDevice* device = mDevices.valueAt(deviceIndex);
        if (device->isIgnored()) {
            //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
            return;
        }
    
        device->process(rawEvents, count);
    }
    

           processEventsForDeviceLocked()再将事件列表交给InputDevice::process()处理:

    void InputDevice::process(const RawEvent* rawEvents, size_t count) {
        size_t numMappers = mMappers.size();
        for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
    
            if (mDropUntilNextSync) {
                if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                    mDropUntilNextSync = false;
                } else {
                }
            } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
                ALOGI("Detected input event buffer overrun for device %s.", getName().string());
                mDropUntilNextSync = true;
                reset(rawEvent->when);
            } else {
                for (size_t i = 0; i < numMappers; i++) {
                    InputMapper* mapper = mMappers[i];
                    mapper->process(rawEvent);
                }
            }
        }
    }
    

           可以看到,InputDevice::process()将事件逐个交给每一个InputMapper的process()事件处理。此处就不展开分析了,在调用对应InputMapper实现的process()方法后,会最终调用到getListener()->notifyMotion(&releaseArgs)、getListener()->notifyKey(&args)等等,看一下getListener():

    InputListenerInterface* InputReader::ContextImpl::getListener() {
        return mReader->mQueuedListener.get();
    }
    

           返回的就是mQueuedListener,是QueuedInputListener实例,此处先不分析notifyxx()逻辑,稍后一起分析:

    3.mQueuedListener->flush()

           在porceeEventsLocked()后,接下来会执行mQueuedListener->flush()来结束loopOnce()这个方法,mQueuedListener是QueuedInputListener实例,QueuedInputListener位于进入InputListener.cpp里面,一起看一下具体实现逻辑:

    void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
        mArgsQueue.push(new NotifyKeyArgs(*args));
    }
    
    void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
        listener->notifyKey(this);
    }
    
    void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
        mArgsQueue.push(new NotifyMotionArgs(*args));
    }
    
    void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
        listener->notifyMotion(this);
    }
    
    void QueuedInputListener::flush() {
        size_t count = mArgsQueue.size();
        for (size_t i = 0; i < count; i++) {
            NotifyArgs* args = mArgsQueue[i];
            args->notify(mInnerListener);
            delete args;
        }
        mArgsQueue.clear();
    }
    
    QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
            mInnerListener(innerListener) {
    }
    

           可以看到,在第二项工作中调用notifyMotion()后,执行了队列的push,只是将该事件args压入队列中;最后通过flush()来遍历队列,执行对应args的notify()方法,在notify()方法内部执行listener->notifyXX(),该listener是mInnerListener,而mInnerListener是InputListenerInterface,是在创建QueuedInputListener对象时传入的。
           通过前面的代码分析可以知道,QueuedInputListener是在创建InputReader时就创建了,而传入的参数是mInputDispatcher,通过Android IMS原理解析IMS成员关系图可以看到,InputDispatcher继承了InputListenerInterface,所以mInnerListener就是mInputDispatcher,因此在执行flush()后,最终调用到InputDispatcher的逻辑。
           QueuedInputListener避免了在原始事件的加工过程中向InputDispatcher进行事件提交,而是将事件信息缓存起来,在InputReader::loopOnce()函数的末尾,也就是InputReader处理完抽取自EventHub的所有原始输入事件之后,QueuedInputListener::flush()函数的调用将缓存的事件信息取出,并提交给InputDispatcher,事件派发就由此开始了。
           接下来请看Android IMS原理解析之事件派发

    相关文章

      网友评论

        本文标题:Android IMS原理解析之InputReader

        本文链接:https://www.haomeiwen.com/subject/fdeuiltx.html