[017]Input子系统-上篇

前言

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

Input子系统的流程图

[017]Input子系统-上篇_第1张图片

从图中可以看到一次完整的事件传递包含两个进程,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子系统-上篇)