Android Input的流程和原理

Android Input事件机制

Android系统是由事件驱动的,而Input是最常见的事件之一,用户的点击、滑动、长按等操作,都属于Input事件驱动,其中的核心就是InputReader和InputDispatcher。InputReader和InputDispatcher是跑在system_server进程中的两个Native循环线程,负责读取和分发Input事件。整体处理流程大致如下:

  1. 触摸屏会按照屏幕硬件的触控采样率周期,每隔几秒扫描一次,如果有触控时间就会上报到对应设备驱动;系统封装了一个叫EventHub的对象,它利用INotify和epoll机制监听/dev/input目录下的Input设备驱动节点,通过EventHug的getEvents接口就可以监听并获取到Input事件;

  2. InputReader负责从EventHub里面把Input事件读取出来,然后交给InputDispatcher进行事件分发;

  3. InputDispatcher在拿到InputReader获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;

  4. InboundQueue队列("iq")中放着InputDispatcher从InputReader中拿到的Input事件;

  5. OutboundQueue队列("oq")里面放的是即将要指派给各个目标窗口App的事件;

  6. WaitQueue队列("wq")里面记录的是已经派发给App,但是App还在处理没有返回处理成功的事件;

  7. PendingInputEventQueue("aq")中记录的是应用需要处理的Input事件,这里可以看到Input事件已经传递到应用进程;

  8. deliverInputEvent标识App UI Thread被Input事件唤醒;

  9. InputResponse标识Input事件区域,这里可以看到一个Input_Down事件 + 若干个Input_Move + 一个Input_Up事件的处理阶段都被算到了这里;

  10. App响应处理Input事件,内部会在其界面View数中逐层分发和处理。

 

结合Systrace分析

从上面的系统机制的分析可以看出,整个Input触控时间的分发与处理主要涉及到两个进程:一个是system_server系统进程,另一个是当前焦点窗口所属的Setting应用进程。

system_server进程的处理过程

  1. 当用户手指在Setting应用界面滑动时,系统system_server进程中的Native线程InputReader会从EventHub中读取,利用Linux的epoll机制监听到的屏幕驱动上报的Input触控事件,然后唤醒另一条Native线程InputDIspatcher负责进行事件的进一步分发处理。

  2. InputDispatcher被唤醒后会先将事件放到InboundQueue队列("iq")中,然后找到具体处理此Input事件的应用目标窗口,并将Input事件放入对应的应用目标窗口的OutboundQueue队列("oq")中,等待进一步通过SocketPair双工信道发送Input事件到应用目标窗口中。

  3. 最后当事件发送给具体的应用目标窗口后,会将事件移动到WaitQueue队列("wq")中,一直等待收到目标应用处理Input事件完成后反馈后再从队列中移除,如果5秒钟没有收到目标应用窗口处理完成此次Input事件的反馈,就会报该应该ANR异常事件。以上整个过程在Android系统AOSP源码中都加有相应的Systrace tag,如下图Systrace所示:

应用进程的处理过程

当Input触控时间通过socket传递到Setting应用进程这边后,会唤醒应用的UI线程在ViewRootImpl#deliverInputEvent的流程中进行Input事件的具体分发与处理。具体的处理流程:

  1. 先交给之前在添加应用PhoneWindow窗口时的ViewRootImpl#setView流程中创建的多个不同类型的InputUsage中依次进行处理(比如对输入法处理逻辑的封装ImeInputUsage,某些key类型的Input事件会由它先交给输入法进程处理完成后再交给应用窗口的InputUsage处理),整个处理流程是按照责任链的设计模式进行;

  2. 然后会交给负责应用窗口Input事件分发处理的ViewPostImeInputUsage中具体处理,这里面会从View布局树的根节点DecorView开始遍历整个View树上的每一个子View或ViewGroup控件执行事件的分发、拦截、处理的逻辑;

  3. 最后触控时间处理完成后会调用finishInputEvent结束应用对触控事件处理逻辑,这里面会通过JNI调用到Native层InputConsumer的sendFinishedSignal函数中通过socket消息通知系统框架中的InputDIspatcher该Input事件处理完成,触发从"wq"队列中及时移除待处理事件以免报ANR异常。

一次滑动过程的触控交互的InputResponse区域中一般会包含一个Input的ACTION_DOWN事件 + 多个ACTION_MOVE事件 + 一个ACTION_UP事件,Setting应用界面中的相关View控件在收到多个ACTION_MOVE触控事件后,经过判断用户手指滑动行为,一般会调用View#invalidate等相关接口触发UI线程的绘制上帧更新画面的操作。

Android Input初始化及InputReader流程

概述

当输入设备可用时,Linux内核会在/dev/input/下创建对应名为event0~n或其他名称的设备节点。而当输入设备不可用时,则会将对应的节点删除。

Android输入系统的工作原理概括来说,就是监控/dev/input/下的所有设备节点,当某个节点有数据可读时,将数据读出并进行一系列的翻译加工,然后再所有的窗户中寻找合适的事件接收者,并派发给它。

getevent与sendevent

getevent与sendevent两个工具可以从设备节点中直接读取输入事件或写入输入事件。

Getevent

由于getevent不会对事件数据做任何加工,因此其输出的内容是由内核提供的最原始的事件,输出是十六进制的:

adb shell getevent [-选项] [device_path]。

adb shell getevent –t 查看当前按下按键的值,值0x01表示按下,0x00则表示抬起。

按下返回

[1556162527.777123] /dev/input/event6: 0001 009e 00000001

[1556162527.777123] /dev/input/event6: 0000 0000 00000000

送开返回

[1556162530.504152] /dev/input/event6: 0003 0030 00000000

[1556162530.504152] /dev/input/event6: 0003 0032 00000000

[1556162530.504152] /dev/input/event6: 0000 0002 00000000

[1556162530.504152] /dev/input/event6: 0000 0000 00000000

[1556162530.525527] /dev/input/event6: 0001 009e 00000000

[1556162530.525527] /dev/input/event6: 0000 0000 00000000

事件类型(0001),事件代码(009e)以及事件的值(00000001)。

Sendevent

实现模拟用户输入的功能,sendevent的参数为十进制。

sendevent <节点路径> <类型><代码> <值>

adb shell sendevent /dev/input/event0 1 116 1 #按下电源键

adb shell sendevent /dev/input/event0 1 116 0 #抬起电源键

adb shell input keyevent 4 # 返回按键

Android系统Input事件处理流程

内核将原始事件写入到设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDIspatcher。InputDIspatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件做出响应,更新自己的画面、执行特定的动作。

  1. Linux内核,接收输入设备的中断,并将原始事件的数据写入到设备节点中。

  2. 设备节点,作为内核的IMS的桥梁,它将原始事件的数据暴露给用户控件,以便IMS可以从中读取事件。

  3. InputManagerService,一个Android系统服务,它分为Java层和Native层两个部分。Java层负责与WMS的通信。而Native层则是InputReader和InputDIspatcher两个输入系统关键组件的运行容器。

  4. EventHub,直接访问所有的设备节点。并且正如其名字所描述的,它通过一个名为getEvents()函数将所有输入系统相关的待处理的底层事件返回给使用者。这些事件包括原始输入事件、设备节点的增删等。

  5. InputReader,是IMS中的关键组件之一。它运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。它通过其线程循环不断地通过getEvent()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,它会更新输入设备列表于配置。对于原始输入事件,InputReader对其进行翻译、组装、封装为包含了更多信息、更具可读性的输入事件,然后交给InputDIspatcher进行派发。

  6. InputReaderPolicy,它为InputReader的事件加工处理提供一些策略配置,例如键盘布局信息等。

  7. InputDispatcher,是IMS中另一个关键组件。它也运行于一个独立的线程中。InputDIspatcher中保管了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口。

  8. InputDIspatcherPolicy,它为InputDIspatcher的派发过程提供策略控制。例如截取某些特定的输入事件来作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是HOME键被InputDIspatcherPolicy截取到PhoneWindowManager中进行处理,并阻止窗口收到HOME键按下的事件。

  9. WMS,虽然不是输入系统中的一员,但是它却对InputDIspatcher的正常工作起到了至关重要的作用。当新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有窗口的信息,包括窗口的可点击区域,焦点窗口等信息,实时地更新到IMS的InputDIspatcher中,使得InputDIspatcher可以正确的将事件派发到指定的窗口。

  10. ViewRootImpl,对于某些窗口,如壁纸窗口、SurfaceView的窗口来说,窗口即是输入事件派发的终点。而对于其他的如Activity、对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件(View)。ViewRootImpl将窗口所收到的输入事件沿着控件树将事件派发给感兴趣的控件。

初始化

InputManagerService初始化

这段代码是Android系统中InputManagerService的启动过程。首先,在SystemServer服务的startOtherServices()方法中,创建并启动了InputManagerService实例。然后,在InputManagerService的构造函数中,初始化了Native层和Java层的通信,并将Java层的InputManagerService对象和消息队列的Looper传递给Native层。最后,在nativeInit方法中,创建了一个NativeInputManager对象,这个对象是Java层和Native层之间的桥梁,并将这个对象的指针返回给Java层的InputManagerService,保存在mPtr成员变量中。

具体来说,startOtherServices()方法中:

  1. 创建了InputManagerService实例。

  2. 调用了InputManagerService的setWindowManagerCallbacks()方法,将WindowManager的回调传递给InputManagerService。

  3. 调用了InputManagerService的start()方法,启动了InputManagerService。

InputManagerService的构造函数中:

  1. 保存了上下文信息。

  2. 创建了一个名为InputManagerHandler的Handler,用于处理输入事件。

  3. 调用了native方法nativeInit(),传入了上下文对象、InputManagerService对象和消息队列的Looper。

  4. 将InputManagerService注册为InputManagerInternal服务的本地服务。

nativeInit()方法中:

  1. 从Java层的消息队列对象中获取了消息队列。

  2. 创建了一个NativeInputManager对象,这个对象是Java层和Native层之间的桥梁。

  3. NativeInputManager对象的指针转换为jlong类型,并返回给Java层的InputManagerService,保存在mPtr成员变量中。

这样,Java层的InputManagerService就可以通过mPtr成员变量与Native层的NativeInputManager进行通信了。这个过程是Android系统启动过程中的一部分,用于初始化和管理输入事件,包括触摸、按键等输入事件的处理。

* frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
        ...
         traceBeginAndSlog("StartInputManagerService");
     inputManager = new InputManagerService(context);
     traceEnd();
     ...
     traceBeginAndSlog("StartInputManager");
     inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
     inputManager.start();
     traceEnd();
     ...      
}

* frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
                ...
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
                ...
        LocalServices.addService(InputManagerInternal.class, new LocalService());
}

* frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
   ...
   /* 新建了一个NativeInputManager对象,NativeInputManager,
   此对象将是Native层组件与Java层IMS进行通信的桥梁 */
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,  messageQueue->getLooper());
    im->incStrong(0);
    //返回了NativeInputManager对象的指针给Java层的IMS,IMS将其保存在mPtr成员变量中
    return reinterpret_cast<jlong>(im);
}

在nativeInit函数中,将Java层的MessageQueue转换为native层的MessageQueue,然后再取出Looper用于NativeInputManager的初始化。NativeInputManager是Java层与Native层互相通信的桥梁,它实现了InputReaderPolicyInterface与InputDispatcherPolicyInterface两个接口,通过JNI回调Java层的IMS,由它完成决策。

NativeInputManager的初始化

这个过程做了以下事情:

将Java层的InputManagerService转换为native层的InputManagerService存储在mServiceObj中 -创建InputManager。

这段代码是Android系统中InputManager的初始化过程。在InputManager的构造函数中,首先创建了InputDispatcher和InputClassifier对象,这两个对象分别负责分发输入事件和输入事件的分类。然后,创建了InputReader对象,这个对象负责从设备读取输入事件。最后,创建了InputReaderThread和InputDispatcherThread两个线程,分别用于处理输入事件的读取和分发。

具体来说:

  1. InputManager的构造函数中:

    1. 创建了InputDispatcher对象,用于分发输入事件。

    2. 创建了InputClassifier对象,用于对输入事件进行分类。

    3. 调用createInputReader()方法创建了InputReader对象,这个对象负责从设备读取输入事件。

    4. 调用initialize()方法,创建了InputReaderThreadInputDispatcherThread两个线程。

  2. createInputReader()方法中:

    1. 创建了InputReader对象,这个对象负责从设备读取输入事件。

  3. initialize()方法中:

    1. 创建了InputReaderThread对象,这个线程用于处理输入事件的读取。

    2. 创建了InputDispatcherThread对象,这个线程用于处理输入事件的分发。

这样,InputManager就可以通过这两个线程来处理输入事件了。

这个过程是Android系统启动过程中的一部分,用于初始化和管理输入事件,包括触摸、按键等输入事件的处理。

* frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    mReader = createInputReader(readerPolicy, mClassifier);
    initialize();
}

sp<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) {
    return new InputReader(new EventHub(), policy, listener);
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager的初始化

InputManager的构造函数也比较简洁,它创建了四个对象,分别为IMS的核心参与者InputReader与InputDispatcher,以及它们所在的线程InputReaderThread与InputDispatcherThread。注意InputManager的构造函数的参数readerPolicy与dispatcherPolicy,它们都是NativeInputManager。

InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,并创建一个EventHub。

启动

system_server执行InputManagerService.start()函数以启动IMS,在start()方法中,做了以下事情:

  1. 调用nativeStart方法,其实就是调用InputManager的start()方法。

  2. 将InputManagerService交给WatchDog监控。

  3. 注册触控点速度、显示触控的观察者,并注册广播监控它们。

  4. 主动调用updateXXX方法更新(初始化)。

InputManager的start()启动InputDispatcherThread和InputReaderThread开始监听。

当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。

这段代码是Android系统中InputManagerService的启动过程。在Java层的start()方法中,调用了native方法nativeStart(),传入了mPtr成员变量,这个变量是Java层和Native层之间的桥梁。

在Native层的InputManagerstart()方法中,首先启动了InputDispatcherThread线程,这个线程负责分发输入事件。然后,启动了InputReaderThread线程,这个线程负责从设备读取输入事件。

具体来说:

  1. InputManagerServicestart()方法中:

    1. 调用了native方法nativeStart(),传入了mPtr成员变量,这个变量是Java层和Native层之间的桥梁。

  2. InputManagerstart()方法中:

    1. 调用mDispatcherThread->run()方法,启动了InputDispatcherThread线程,这个线程负责分发输入事件。

    2. 调用mReaderThread->run()方法,启动了InputReaderThread线程,这个线程负责从设备读取输入事件。

这样,InputManager就可以通过这两个线程来处理输入事件了。

这个过程是Android系统启动过程中的一部分,用于初始化和管理输入事件,包括触摸、按键等输入事件的处理。

* frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
 public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);
        ...
        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
        updateAccessibilityLargePointerFromSettings();
}
    
* frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    ...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
   ...
}

InputReaderThread

        启动后循环执行mReader->loopOnce(),loopOnce中会调用mEventHub->getEvents读取事件,读取的结果存储在参数mEventBuffer中,返回值表示事件的个数,当EventHub中无事件可以抽取时,此函数的调用将会阻塞直到事件到来或者超时。

        读取到了事件就会调用processEventsLocked处理事件,对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中,处理完成后调用getInputDevicesLocked获取输入设备信息。

        调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化。

        最后调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDIspatcher。

        注意 C++层的Thread类与Java层的Thread类有着一个显著的不同。C++层Thread类内建了线程循环,threadLoop()就是一次循环而已,只要返回值为true,threadLoop()将会不断地被内建的循环调用。这也是InputReader.loopOnce()函数名称的由来。而Java层Thread类的run()函数则是整个线程的全部,一旦其退出,线程也便完结。

这段代码是Android系统中InputReader的循环处理过程。在Native层的InputReaderThreadthreadLoop()方法中,调用了InputReaderloopOnce()方法,这个方法会循环读取输入事件。

InputReaderloopOnce()方法中,首先从EventHub中读取输入事件,然后处理这些事件。如果输入设备发生了变化,就通知InputReaderPolicy。最后,调用mQueuedListenerflush()方法,将处理过的输入事件发送给监听者。

具体来说:

  1. InputReaderThreadthreadLoop()方法中:

    1. 调用了mReaderloopOnce()方法,这个方法会循环读取输入事件。

  2. InputReaderloopOnce()方法中:

    1. EventHub中读取输入事件。

    2. 处理这些事件。

    3. 如果输入设备发生了变化,就通知InputReaderPolicy

    4. 调用mQueuedListenerflush()方法,将处理过的输入事件发送给监听者。

这样,InputReader就可以通过这个循环来处理输入事件了。

这个过程是Android系统启动过程中的一部分,用于初始化和管理输入事件,包括触摸、按键等输入事件的处理。

* frameworks/native/services/inputflinger/InputReaderBase.cpp
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

* frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::loopOnce() {
    ...
    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

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();
}

EventHub

InputReader在其线程循环中的第一个工作便是从EventHub中读取一批未处理的事件。 EventHub的直译是事件集线器,顾名思义,它将所有的输入事件通过一个接口getEvents()将从多个输入设备节点中读取的事件交给InputReader,是输入系统最底层的一个组件。

这段代码是Android系统中EventHub的初始化过程。在EventHub的构造函数中,首先创建了一个epoll对象和inotify对象,用于监听设备节点的增删事件。然后,将设备节点的路径/dev/input作为监听对象添加到inotify对象中,当此文件夹下的设备节点发生创建与删除事件时,都可以通过inotify对象读取事件的详细信息。接着,将inotify对象和名为wakeFds的匿名管道(用于唤醒InputReader线程)作为epoll的监控对象,并注册到epoll对象中。

具体来说:

  1. EventHub的构造函数中:

    1. 创建了一个epoll对象。

    2. 创建了一个inotify对象,用于监听设备节点的增删事件。

    3. 将设备节点的路径/dev/input作为监听对象添加到inotify对象中。

    4. 创建了一个名为wakeFds的匿名管道,并将管道读取端的描述符的可读事件注册到epoll对象中。

  2. 在epoll对象中注册了inotify对象和wakeFds管道,用于监听设备节点的增删事件和唤醒InputReader线程。

这样,EventHub就可以通过epoll和inotify来监听设备节点的变化,并及时处理这些事件了。

这个过程是Android系统启动过程中的一部分,用于初始化和管理输入事件,包括触摸、按键等输入事件的处理。

* frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(nullptr), mClosingDevices(nullptr),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //创建一个epoll对象
    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    //创建一个inotify对象。这个inotify对象将被用来监听设备节点的增删事件
    mINotifyFd = inotify_init();
    //将存储设备节点的路径/dev/input作为监听对象添加到inotify对象中。当此文件夹下的设备节点
    //发生创建与删除事件时,都可以通过mINotifyFd读取事件的详细信息
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
   ...
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mINotifyFd;
    //将mINotifyFd作为epoll的一个监控对象。当inotify事件到来时,epoll_wait()将
    //立刻返回,EventHub便可从mINotifyFd中读取设备节点的增删信息,并作相应处理
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
        
        /*创建了一个名为wakeFds的匿名管道,并将管道读取端的描述符的可读事件注册到epoll对象中。
        因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在epoll_wait()的调用里,
        然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。
        此时只需向wakeFds管道的写入端写入任意数据,此时读取端有数据可读,使得epoll_wait()得以返回,
        从而达到唤醒InputReader线程的目的*/

    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
        ...
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ...
}

INotify

INotify是一个Linux内核所提供的一个文件系统变化通知机制。它可以为应用程序监控文件系统的变化,如文件的创建、删除、读写等。INotify机制有两个基本对象,分别为inotify对象与watch对象,都使用文件描述符表示。

  • inotify对象对应了一个队列,应用程序可以向inotify对象添加多个监听。当被监听的事件发生时,可以通过read()函数从inotify对象中将事件信息读取出来。Inotify对象可以通过inotify_init创建。

  • watch对象则用来描述文件系统的变化事件的监听。它是一个二元组,包括监听目标和事件掩码两个元素。监听目标是文件系统的一个路径,可以是文件也可以是文件夹。而事件掩码则表示了需要需要监听的事件类型,掩码中的每一位代表一种事件。可以监听的事件种类很多,其中就包括文件的创建(IN_CREATE)与删除(IN_DELETE)。以下代码即可将一个用于监听输入设备节点的创建与删除的watch对象添加到inotify对象中:

int wd = inotify_add_watch (inotifyFd, “/dev/input”,IN_CREATE | IN_DELETE);

完成上述watch对象的添加后,当/dev/input/下的设备节点发生创建与删除操作时,都会将相应的事件信息写入到inotifyFd所描述的inotify对象中,此时可以通过read()函数从inotifyFd描述符中将事件信息读取出来。

事件信息使用结构体inotify_event进行描述:

struct inotify_event {
       __s32           wd;             /* 事件对应的Watch对象的描述符 */
       __u32           mask;           /* 事件类型,例如文件被删除,此处值为IN_DELETE */
       __u32           cookie;
       __u32           len;            /* name字段的长度 */
       char            name[0];        /* 可变长的字段,用于存储产生此事件的文件路径*/
};

当有监听事件发生时,可以通过如下方式将一个或多个未读取的事件信息读取出来:

size_t len = read (inotifyFd, events_buf,BUF_LEN);

其中events_buf是inotify_event的数组指针,能够读取的事件数量由取决于数组的长度。成功读取事件信息后,便可根据inotify_event结构体的字段判断事件类型以及产生事件的文件路径了。

INotify机制的使用过程

  • 通过inotify_init()创建一个inotify对象。

  • 通过inotify_add_watch将一个或多个监听添加到inotify对象中。

  • 通过read()函数从inotify对象中读取监听事件。当没有新事件发生时,inotify对象中无任何可读数据。

通过INotify机制避免了轮询文件系统的麻烦,但是还有一个问题,INotify机制并不是通过回调的方式通知事件,而需要使用者主动从INotify对象中进行事件读取,这里借助Linux的Epoll机制。

Epoll

Epoll可以使用一次等待监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符或自定义的数据,使用者可以据此读取所需的数据后可以再次进入等待。因此不需要为每个描述符创建独立的线程进行阻塞读取,避免了资源浪费的同时又可以获得较快的响应速度。

epoll_create(int max_fds):创建一个epoll对象的描述符,之后对epoll的操作均使用这个描述符完成。max_fds参数表示了此epoll对象可以监听的描述符的最大数量。

int epoll_create1(int flag);

当flag是0时,表示和epoll_create函数完全一样,不需要size的提示了

当flag = EPOLL_CLOEXEC,创建的epfd会设置FD_CLOEXEC

当flag = EPOLL_NONBLOCK,创建的epfd会设置为非阻塞

FD_CLOEXEC是fd的一个标识说明,用来设置文件close-on-exec状态的。当close-on-exec状态为0时,调用exec时,fd不会被关闭;状态非零时则会被关闭,这样做可以防止fd泄露给执行exec后的进程

epoll_ctl (int epfd, int op,int fd, struct epoll_event *event):用于管理注册事件的函数。这个函数可以增加/删除/修改事件的注册。

op表示了何种操作,包括EPOLL_CTL_ADD/DEL/MOD三种,分别表示增加/删除/修改注册事件。

int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout):用于等待事件的到来。当此函数返回时,events数组参数中将会包含产生事件的文件描述符。函数返回值表示获取了多少个事件。

Epoll机制的使用过程

创建epoll对象:

Int epfd = epoll_create(MAX_FDS)

填充epoll_event结构体,以描述监控事件。

通过epoll_ctl()函数将此描述符与epoll_event结构体注册进epoll对象,重复这个步骤可以将多个文件描述符的多种事件监听注册到epoll对象中。

使用epoll_wait()函数等待事件,会使调用者陷入等待状态,直到其注册的事件之一发生之后才会返回,并且携带了刚刚发生的事件的详细信息,在如getEvents中调用。

EventHub的创建过程
  • 创建mEpollFd用于监听是否有数据(有无事件)可读。
  • 创建mINotifyFd将它注册到DEVICE_PATH(这里路径就是/dev/input)节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得EventHub能收到来自系统的通知,并获取事件的详细信息。
  • 调用epoll_ctl函数将mEpollFd和mINotifyFd注册到epoll中。
  • 定义int wakeFd[2]作为事件传输管道的读写两端,并将读端注册到epoll中让mEpollFd监听。

EventHub 是 Android 系统中负责处理输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。这个类的主要职责是监听和管理 /dev/input 目录下的所有输入设备,并将这些设备的事件转换为 Android 系统可以理解的事件格式。

getEvents 方法中,它首先检查是否需要扫描设备,如果需要,它会调用 scanDevicesLocked 方法来遍历 /dev/input 目录下的所有设备,打开这些设备并存储到 Device 结构体中,同时将这些设备的文件描述符的可读事件注册到 Epoll 中。当设备的输入事件到来时,Epoll 会在 getEvents 函数的调用中产生一条 epoll 事件。

然后,它遍历 mClosingDevices 链表,为每一个已卸载的设备生成 DEVICE_REMOVED 事件。

接着,它通过 Epoll 事件的 data 字段确定此事件表示了 mINotifyFd 可读,即 Epoll 事件的处理。如果 mPendingINotify 事件待处理,它会调用 readNotifyLocked 函数来读取并处理存储在 mINotifyFd 中的 INotify 事件。

这个过程会一直重复,直到满足某个条件(例如超时或者缓冲区满)。

这个过程的关键点在于 scanDevicesLockedreadNotifyLocked 方法,它们分别负责扫描设备和处理 INotify 事件。这两个方法的具体实现细节没有在提供的代码片段中展示,但它们应该是用来打开设备、注册 Epoll 事件、处理设备卸载和 INotify 事件的。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、线程同步、设备状态管理等。

* frameworks/native/services/inputflinger/EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        ...
        for (;;) {
                ...
                if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            //遍历/dev/input下所有可用的输入设备打开并存储到Device结构体
            //将设备节点的描述符的可读事件注册到Epoll中,当此设备的输入事件到来时,Epoll会在getEvents()函数的调用中产生一条epoll事件
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
         //遍历mClosingDevices链表,为每一个已卸载的设备生成DEVICE_REMOVED事件
        while (mClosingDevices) {
        ...
        }
        // 通过Epoll事件的data字段确定此事件表示了mINotifyFd可读,及Epoll事件的处理
         while (mPendingEventIndex < mPendingEventCount) {
         }
         //如果INotify事件待处理
          if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            //调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件
            readNotifyLocked();
            deviceChanged = true;
        }
    }
}

getEvents()通过Epoll事件的data.u32字段在mDevices列表中查找已加载的设备,并从设备的文件描述符中读取原始输入事件列表。从文件描述符中读取的原始输入事件存储在input_event结构体中,这个结构体的四个字段存储了事件的事件戳、类型、代码与值四个元素。然后逐一将input_event的数据转存到RawEvent中并保存至buffer以返回给调用者。

InputReader

InputReader是在InputReaderThread中启动的,InputReaderThread和InputDispatcherThread的定义是类似的,也是继承了Thread并定义了threadLoop纯虚函数。如果处理的事件为键盘输入事件,则调用时序图如下所示。

原始事件的入口函数是ProcessEventsLocked,根据事件类型区分原始输入事件还是设备增删事件,对于原始输入事件,EventHub会讲属于同意输入设备的原始输入事件放在一起,因此使用processEventsForDeviceLocked同时处理来自同意输入设备的一批事件。

InputReader 是 Android 系统中负责处理输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。这个类的主要职责是接收和处理来自 EventHub 的原始输入事件。

processEventsLocked 方法中,它遍历所有的原始事件。对于每一条原始事件,根据事件类型,它会调用 processEventsForDeviceLocked 方法来处理。如果事件类型是原始输入事件,那么它会调用 InputDeviceprocess 方法来处理。如果事件类型是设备增删事件,那么它会处理设备增删事件。

processEventsForDeviceLocked 方法中,它首先检查设备是否存在,如果设备被忽略,那么它会忽略这条事件。如果设备存在,那么它会调用 InputDeviceprocess 方法来处理事件。

这个过程的关键点在于 InputDeviceprocess 方法,它是用来处理输入事件的。这个方法的具体实现细节没有在提供的代码片段中展示,但它是用来处理输入事件的。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、设备状态管理等。

* frameworks/native/services/inputflinger/InputReader.cpp
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;
                   ...
                   //数据事件的处理,batchSize表示属于此设备的输入事件个数
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
                //处理设备增删事件
            ...
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    //InputReader保存了mDevices字典,以设备ID为键值存储了一系列的InputDevice对象
    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;
    }
    //调用InputDevice的process处理事件
    device->process(rawEvents, count);
}

processEventsForDeviceLocked中,InputReader保存了mDevices字典,在processEventsLocked的处理设备增删阶段初始化,然后调用InputDevice::process处理事件。

InputDevice

InputReader的InputDevice存储输入设备的信息,与EventHub一样,InputDevice描述了一个输入设备,并且以设备ID为键保存在mDevices中,InputReader::InputDevice和EventHub::Device结构体类似,也保存了设备的ID、厂商信息、设备所属类别。只是InputDevice多了一个InputMapper列表。

InputMapper

InputMapper是InputReader中实际进行原始输入事件加工的场所,有一系列子类,分别用于加工不同类型的原始输入事件。这样使得InputDevice不需要知道哪一个InputMapper可以处理一个原始输入事件,只须将一个事件逐一交给每一个InputMapper尝试处理,如果InputMapper可以接受这个事件则处理,否则什么都不做。

InputReader 类是 Android 系统中负责管理输入设备和处理输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。

addDeviceLocked 方法中,它首先检查是否已经添加了相同的设备,如果已经添加了,那么它会忽略这条设备添加事件。然后,它获取设备标识符、设备类和设备控制器编号,并调用 createDeviceLocked 方法来创建一个新的 InputDevice 对象。

createDeviceLocked 方法中,它创建一个新的 InputDevice 对象,并根据设备类添加相应的 InputMapper。例如,如果设备是键盘类设备,它会添加 KeyboardInputMapper;如果设备是触摸屏设备,它会添加 MultiTouchInputMapperSingleTouchInputMapper

这个过程的关键点在于 InputDevice 的创建和 InputMapper 的添加。InputDevice 是用来管理设备和处理事件的,而 InputMapper 是用来处理特定类型的输入事件的。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、设备状态管理等。

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {//已添加的相同设备则不再添加
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);

    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
...
    mDevices.add(deviceId, device);
    ...
}

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
   ...
   //添加键盘类设备InputMapper
   if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    ...
    //添加触摸屏设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ...
}

InputDevice::process首先会遍历InputDevice中的所有的事件,真正加工原始输入事件的是InputMapper对象,由于原始输入事件的类型很多,因此在InputMapper有很多子类,用于加工不同的原始输入事件,比如KeyboardInputMapper用于处理键盘输入事件,TouchInputMapper用于处理触摸输入事件。

InputDevice 类是 Android 系统中负责处理输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。

process 方法中,它遍历所有的原始事件。如果 mDropUntilNextSync 的值为 true,那么它会忽略当前事件。如果 mDropUntilNextSync 的值为 false,那么它会遍历所有的 InputMapper,并调用它们的 process 方法来处理事件。

这个过程的关键点在于 InputMapperprocess 方法,它是用来处理输入事件的。这个方法的具体实现细节没有在提供的代码片段中展示,但它是用来处理输入事件的。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、设备状态管理等。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        ...
         //mDropUntilNextSync的值默认为false,如果设备的输入事件缓冲区溢出,这个值会置为true。
        if (mDropUntilNextSync) {
        }else{
            for (InputMapper* mapper : mMappers) {
                mapper->process(rawEvent);
            }
        }
        ...
        --count;
    }
}

KeyboardInputMapper 是 Android 系统中负责处理键盘输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。

process 方法中,它根据事件类型来处理键盘事件。当事件类型为 EV_KEY 时,它会调用 processKey 方法来处理。在 processKey 方法中,它会根据扫描码和使用码来获取按键码、元状态和策略标志。

如果按键被按下,它会根据屏幕的方向对按键的虚拟键值进行旋转转换,并检查是否应该丢弃虚拟键。如果应该丢弃,它会取消触摸并返回。然后,它会生成一个 KeyDown 结构体并将其添加到集合中。

如果按键被抬起,它会从集合中移除对应的 KeyDown 对象。

最后,它会生成一个 NotifyKeyArgs 对象,并调用 getListener 方法的 notifyKey 方法来通知按键事件。

mapKey 方法中,它首先获取设备,然后检查键字符映射。如果键字符映射存在,它会尝试根据扫描码和使用码来映射按键码。

这个过程的关键点在于键字符映射和按键码的映射,以及按键事件的处理。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、设备状态管理等。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {//表示为Keyboard事件
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
        //排除对鼠标按键的处理
        if (isKeyboardOrGamepadKey(scanCode)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    ...
    }
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;

    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }
    if (down) {
        // 当按下时,首先需要根据屏幕的方向对按键的虚拟键值进行旋转转换
        if (mParameters.orientationAware) {
            keyCode = rotateKeyCode(keyCode, getOrientation());
        }
       // KeyboardInputMapper维护了一个mKeyDowns集合
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            //对于重复按下的按键,需要确保后续的处理过程中的虚拟键值与第一次按下时的一致,以免重复过程中屏幕的方向的变化导致虚拟键值的变化,使得后续InputDispatch无法正常识别重复按键的动作
            keyCode = mKeyDowns[keyDownIndex].keyCode;
        } else {
            //生成keydown结构体并添加到集合中
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }
            if (policyFlags & POLICY_FLAG_GESTURE) {
                mDevice->cancelTouch(when);
            }
            KeyDown keyDown;
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
            mKeyDowns.push_back(keyDown);
        }

        mDownTime = when;
    } else {
        ssize_t keyDownIndex = findKeyDown(scanCode);
        //对于抬起的按键,则将对应的keydown对象从集合中移除
        if (keyDownIndex >= 0) {
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
        } else {
            return;
        }
    }
    ...
    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

status_t EventHub::mapKey(int32_t deviceId,
        int32_t scanCode, int32_t usageCode, int32_t metaState,
        int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    status_t status = NAME_NOT_FOUND;

    if (device) {
        // Check the key character map first.
        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
        if (kcm != nullptr) {
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                *outFlags = 0;
                status = NO_ERROR;
            }
        }
}

EventHub::openDeviceLocked会为此设备加载键盘布局配置文件,键盘布局配置文件的路径位于/system/usr/keylayout下的.lk文件。

processKey函数会将加工后的键盘输入事件封装为NotifyKeyArgs,将NotifyKeyArgs通知给InputListenerInterface。

mArgsQueue的数据类型为Vector<NotifyArgs*>,将该key事件压人该vector中。

InputReader::loopOnce的最后调用mQueuedListener->flush()遍历整个mArgsQueue数组。

InputDispatcher继承了InputDispatcherInterface,而InputDispatcherInterface继承了InputListenerInterface,因此实际上是调用了InputDispatcher的notifyKey函数,将NotifyKeyArgs交给InputDispatcher处理。

QueuedInputListener 类是 Android 系统中负责接收和处理输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。

notifyKey 方法中,它将接收到的按键事件参数添加到队列中。

flush 方法中,它遍历队列中的所有事件,调用每个事件的 notify 方法来处理事件,然后删除这些事件。最后,它清空队列。

这个过程的关键点在于事件的接收和处理,以及事件队列的管理。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、事件状态管理等。

* frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push_back(new NotifyKeyArgs(*args));
}

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

Android Input初始化及InputDIspatcher流程

唤醒InputDIspatcher

以Motion事件的分化过来进行举例。

InputDispatcher 类是 Android 系统中负责分发输入事件的类,它位于 frameworks/native/services/inputflinger 目录下。

notifyMotion 方法中,它首先检查 Motion 事件的参数是否有效,然后通过 interceptMotionBeforeQueueing 方法来校验 Motion 事件的触点数量和 ID 是否在合理范围内。如果 Motion 事件需要通过输入过滤器,它会创建一个 MotionEntry 对象并将其添加到入队列中。如果事件被过滤器消费,它会返回而不进行分发。

enqueueInboundEventLocked 方法中,它将事件添加到入队列中,并根据事件类型进行优化处理。如果当前应用无响应且用户希望切换到其他应用窗口,它会查找触摸窗口。

findTouchedWindowAtLocked 方法中,它遍历所有的窗口,检查坐标是否落在窗口之上。如果窗口是可触摸的,它会返回该窗口。

这个过程的关键点在于事件的分发、过滤和窗口的查找。

请注意,这是一个简化的解释,实际的实现可能会更复杂,包括错误处理、事件状态管理等。

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
        //检查Motion事件的参数是否有效,对于motion事件,主要时校验触控点的数量与ID是否在合理范围
    if (!validateMotionEvent(args->action, args->actionButton,
                args->pointerCount, args->pointerProperties)) {
        return;
    }
    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;

    android::base::Timer t;
    //mPolicy是指NativeInputManager对象,这里policyFlags为引用,会修改policyFlags的值
    //如果处理时间大于50ms则,输出警告
    mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
                std::to_string(t.duration().count()).c_str());
    }

    bool needWake;
    { // acquire lock
        mLock.lock();
                //Motion事件是否需要交由InputFilter过滤
        if (shouldSendMotionToInputFilterLocked(args)) {
            mLock.unlock();
                        //初始化MotionEvent,将NotifyMotionArgs中的参数信息赋值给MotionEvent中的参数
         
            MotionEvent event;
            event.initialize(args->deviceId, args->source, args->displayId,
                    args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    args->classification, 0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);

            policyFlags |= POLICY_FLAG_FILTERED;
            //开始过滤,如果返回值为false,就会直接return,这次事件不再进行分发,直接忽略
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }
            mLock.lock();
        }
         //创建KeyEntry对象
        MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState, args->classification,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
                //将KeyEntry放入队列,返回true,则派发线程处于休眠,需要唤醒
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
        //mInboundQueue为空,则表示派发线程处于休眠
    bool needWake = mInboundQueue.isEmpty();
    //将事件放入到队列尾部
    mInboundQueue.enqueueAtTail(entry);
    //事件响应速度优化处理
    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
       ...
        break;
    }
    case EventEntry::TYPE_MOTION: {
     //当前App无响应且用户希望切换到其他应用窗口,则drop该窗口事件,并处理其他窗口事件
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                && mInputTargetWaitApplicationToken != nullptr) {
            ...
            //查询可触摸的窗口
             sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
            ...
        }
        break;
    }
    }

    return needWake;
}

sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
   //从前到后浏览窗口,找到触摸窗口和外部目标。
   // mWindowHandles中保存的InputWindowHandler类保存了窗口的InputChannel以及InputWindowInfo结构体
   // InputWindowInfo结构体保存了窗口的各种布局信息,包括可见性、位置、尺寸、flag等。
    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
    // 递归遍历mWindowHandles中的所有WindowHandle,检查时间坐标是否落在其上
    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo->displayId == displayId) {
            int32_t flags = windowInfo->layoutParamsFlags;
            if (windowInfo->visible) {
                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                // 如果窗口是focusable或者flag不为FLAG_NOT_FOCUSABLE,则说明该窗口是”可触摸模式“
                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                    //如果窗口是”可触摸模式或者坐标
上一篇:C语言 | Leetcode C语言题解之第502题IPO-题解:


下一篇:用扣子模板,走AI捷径,这个双11,大模型要发威了?