美文网首页
[017]Input子系统-上篇

[017]Input子系统-上篇

作者: 王小二的技术栈 | 来源:发表于2019-11-01 10:19 被阅读0次

    前言

    还得当年我刚接触触摸屏手机的时候,我就得非常好奇,为什么我触摸屏幕会产生屏幕上UI的变化,感觉非常神奇。在进入这个行业之后,我才发现原来屏幕分触控层和显示层,我们触摸屏幕的事件会通过"驱动-系统-应用-应用的某个UI控件"这一个完整流程。

    Input子系统的流程图


    从图中可以看到一次完整的事件传递包含两个进程,system_server和app进程,我们这一篇先来分析一下左边部分,也就是system_server。

    知识准备-epoll

    epoll有关的知识可以看我好友的博客Linux基础知识之IO多路复用epoll
    简单解释一下epoll的作用,类似于java中某个锁的wait,可以让线程block,并不占用cpu,只是epoll可以监听多个fd的状态并block,这个epoll wait之后,notify的条件是fd的内容有变化。其实Looper就是基于epoll机制,有兴趣可以看我好友博客Android P源码分析之Looper(Native)

    InputManagerService启动

    InputManagerService(初始化)
        nativeInit
            NativeInputManager
                EventHub
                InputManager
                    InputDispatcher
                        Looper
                    InputReader
                        QueuedInputListener
                    InputReaderThread
                    InputDispatcherThread
    IMS.start(启动)
        nativeStart
            InputManager.start
                InputReaderThread->run
                InputDispatcherThread->run
    

    简单理解就是system_server进程中的InputManagerService初始化运行以后,会启动两个线程InputReader和InputDispatcher

    Input事件的设备节点

    我们可以通过adb shell getevent指令看到手机上所有的input事件的设备节点,驱动层会把从屏幕上采集到触摸的事件写到 /dev/input/event1这个设备节点,其他设备节点用于处理其他事件,例如按键,摇杆。

    kobewang@KobedeMacBook-Pro:~$ adb shell getevent
    add device 1: /dev/input/event5
      name:     "sm6150-t1-snd-card Button Jack"
    add device 2: /dev/input/event4
      name:     "sm6150-t1-snd-card Headset Jack"
    add device 3: /dev/input/event3
      name:     "ff_key"
    add device 4: /dev/input/event2
      name:     "gpio-keys"
    add device 5: /dev/input/event0
      name:     "qpnp_pon"
    add device 6: /dev/input/event1
      name:     "fts_ts"
    

    InputReader的主要工作

    InputReader通过调用EventHub的getEvents方法监听Input事件的设备节点,在getEvents方法中就会采用epoll机制进行监听,然后发送event给InputDispatcher。

    InputReader.cpp

    void InputReader::loopOnce() {
        ...
        //从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256
        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
        { // acquire lock
            AutoMutex _l(mLock);
             mReaderIsAliveCondition.broadcast();
            if (count) { //处理事件
                processEventsLocked(mEventBuffer, count);
            }
            if (oldGeneration != mGeneration) {
                inputDevicesChanged = true;
                getInputDevicesLocked(inputDevices);
            }
            ...
        } // release lock
    
        //发送事件到nputDispatcher
        mQueuedListener->flush();
    }
    
    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        AutoMutex _l(mLock); //加锁
    
        struct input_event readBuffer[bufferSize];
        RawEvent* event = buffer; //原始事件
        size_t capacity = bufferSize; //容量大小为256
        bool awoken = false;
        for (;;) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            ...
            bool deviceChanged = false;
            while (mPendingEventIndex < mPendingEventCount) {
                //从mPendingEventItems读取事件项
                const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
                ...
                //获取设备ID所对应的device
                ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
                Device* device = mDevices.valueAt(deviceIndex);
                if (eventItem.events & EPOLLIN) {
                    //从设备不断读取事件,放入到readBuffer
                    int32_t readSize = read(device->fd, readBuffer,
                            sizeof(struct input_event) * capacity);
    
                    if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                        deviceChanged = true;
                        closeDeviceLocked(device);//设备已被移除则执行关闭操作
                    } else if (readSize < 0) {
                        ...
                    } else if ((readSize % sizeof(struct input_event)) != 0) {
                        ...
                    } 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++) {
                            //获取readBuffer的数据
                            struct input_event& iev = readBuffer[i];
                            //将input_event信息, 封装成RawEvent
                            event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                    + nsecs_t(iev.time.tv_usec) * 1000LL;
                            event->deviceId = deviceId;
                            event->type = iev.type;
                            event->code = iev.code;
                            event->value = iev.value;
                            event += 1;
                            capacity -= 1;
                        }
                        if (capacity == 0) {
                            mPendingEventIndex -= 1;
                            break;
                        }
                    }
                }
                ...
            }
            ...
            mLock.unlock(); //poll之前先释放锁
            //利用epoll机制监听input设备节点,等待input事件的到来
            int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
            ...
            mLock.lock(); //poll之后再次请求锁
            ...
        }
    
        return event - buffer; //返回所读取的事件个数
    }
    

    InputDispatcher的主要工作

    InputDispatcher接收来自InputReader的输入事件,并记录WMS的窗口信息,用于派发事件到合适的窗口。具体的代码我就不贴了,可以看一下[015]ANR视角看InputDispatcher来理解一下InputDispatcher。

    总结

    一个event时间的传递的前半段旅程
    第一步:驱动将屏幕的event写到了/dev/input/event1
    第二步:InputReader线程通过EventHub的getEvents方法获得event事件,并通知InputDispatcher线程
    第三步: InputDispatcher将这个event通过InputChannel跨进程发给App进程

    InputChannel是个什么?

    这个问题我们将会在[018]Input子系统-下篇中讲解

    参考文章

    Input系统—启动篇
    Input系统—InputReader线程
    Input系统—InputDispatcher线程

    相关文章

      网友评论

          本文标题:[017]Input子系统-上篇

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