InputSystem(IMS)摘要

 

 总体框架:

在这里插入图片描述

 实现:

void InputReader::loopOnce() {
    //关键点1 通过EventHub获取事件列表
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    mReaderIsAliveCondition.broadcast();
    //关键点2 对事件进行 加工处理
    processEventsLocked(mEventBuffer, count);//处理事件【见小节3.1】
    ...
    //关键点3 发布事件到InputDispatcher中
    mQueuedListener->flush();
}

数据流程:eventhub接收驱动层event(触屏,键盘等) -->inputreader线程(for+epoll监听eventhub)---唤醒looper.wakeup-->inputdispatch 线程----wms socketpair分配socket通信--->UI主线程
事件分发线程如何获取聚焦窗口 ?
查找目标:向WindowManagerService查询当前window,获得对应的connection
把事件放入到connection的outboundQueue队列当中
从队列中逐个把事件写入fd,取出并构造InputMessage最后发送。

IMS与window:
到底是第一次IMS-WMS-APP,然后IMS-APP;还是每次都是IMS-WMS-APP

只有WMS知道哪个window是activity的。所以应该还是一直IMS-WMS-APP.
对IMS来说,更换APP不关心.

在InputDispatcher内部,socketpair被封装成了inputchannel,inputchannel又被封装成了Connection进而和应用层之间进行联系
在WindowManagerService内部,WindowState通过创建socketpair来得到fd0和fd1,将fd0封装成inputchannel注册到InputDispatcher中,
将fd1通过binder通信机制传递给应用端,应用端将fd1封装成InputChannel,
通过WindowInputEventReceiver最后将fd1放入到应用层这边的looper中进行监听
第一次ims-wms-app,之后ims-app socketot
notes:socketpair可以同一台机器进程通信。(原理是给一台机器的两个fd).

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

一 概述

当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原始的内核事件.要说明的是 Android 的输入设备有很多种,比如屏幕、键盘、鼠标、游戏手柄、操纵杆等等,其中应用开发接触最多的屏幕。
1 输入设备可用时,Linux 内核会在 /dev/input 中创建对应的设备节点,每种不同的输入设备对应不同的设备节点。
Android 提供了 getevent 和 sendevent 两个工具帮助开发者从设备节点读取输入事件和模拟写入输入事件。
2 输入系统从这些设备节点中取出原始的输入事件,经过层层封装后成为 KeyEvent 或者 MotionEvent.
3 交付给相应的目标窗口 (Window) 来消费该输入事件。
可见,输入系统在整个输入事件处理过程中起到了承上启下的衔接作用。

下图是整个输入事件处理的详细过程,我们先看个大概,以后会详细介绍参与其中的各个组件:

在这里插入图片描述

说明:

图中3个红色虚线框:分别表示:IMS 中事件的读取和派发;WMS中Window获取事件和传递;View中事件的传递和处理
图中2种颜色区域: 表示两个进程:system_server 和应用进程。
图中红色实线箭头: 表示事件传递的主要过程。
图中2个红色虚线箭头: 列出了两个比较常见的 policy 拦截的大致阶段 (当然不止这两个),说明了最终如何回调到 PhoneWindowManager 的同名方法。

IMS 系统分层: java层IMS,C++层NIM->IM. C++层分成两个线程,一个输入一个输出.
数据流程:输入是EventHub->Inputreader thread->inputDispatch thread->inputEventReceiver->viewRootImpl->DectorView->Activity->viewGroup->view
仅仅是数据转发,没有业务逻辑处理

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

二  各个模块-线程-类介绍

IMS Service存在于System_server 进程中Service的Native的InputManager:创建InputReaderThread和InputDispatcherThread两个线程.
notes: 一个service不止一个线程

2个线程.5个对象NativeInputManager,EventHub,InputManager, InputDispatcher,InputReader.

  • EventHu:从系统的输入设备获取事件,管理输入设备
  • Native层的"InputReader"线程负责从硬件获取输入,转换成事件(Event), 并分发给Input Dispatcher.
  • Native层的InputDispatcher接收来自InputReader的输入事件Event,并记录WMS的窗口信息,用于派发事件到合适的窗口;
  • NativeInputManager
    • InputManagerService的JNI实现
    • InputManagerService与C++层的InputManager之间的桥梁
  • Java层的InputManagerService跟WMS交互,WMS记录所有窗口信息,并同步更新到IMS,为InputDispatcher正确派发事件到ViewRootImpl提供保障;
  • Input Manager Service: 负责Input Reader 和 Input Dispatchor的创建,并提供Policy 用于Events的预处理。
  • Window Manager Service:管理Input Manager 与 View(Window) 以及 ActivityManager 之间的通信。
  • View and Activity:接收按键并处理。
  • ActivityManager Service:ANR 处理。

模块    简介说明
Linux内核    接受输入设备中断,将原始事件数据写入到设备节点
设备节点    IMS可以从中读取事件
InputManagerService    分为Java层和Native层两部分,Java层负责与WMS的通信;Native层是InputReader和InputDispatcher两个输入系统关键组件的运行容器。
EventHub    直接访问所有的设备节点。通过一个名为getEvents()的函数将所有输入系统相关的待处理底层事件返回给使用者。
InputReader    运行于独立线程,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。通过线程循环不断地通过getEvents()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,它会更新输入设备列表于配置。对于原始输入事件,InputReader对其进行翻译、组装、封装为包含了更多信息、更具可读性的输入事件,然后交给InputDispatcher进行派发。
InputReaderPolicy    为InputReader事件加工处理提供策略配置。
InputDispatcher    是IMS中的另一个关键组件。它也运行于一个独立的线程中。InputDispatcher中保管了来自WMS的所有窗口的信息,收到来自InputReader的输入事件后,会寻找合适的窗口,将事件派发给它。
InputDispatcherPolicy    它为InputDispatcher的派发过程提供策略控制。
WMS    对InputDispatcher的正常工作起到了至关重要的作用。新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有窗口的信息,包括窗口的可点击区域,焦点窗口等信息,实时地更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派发到指定的窗口。
ViewRootImpl    对于某些窗口,如壁纸窗口、SurfaceView的窗口来说,窗口即是输入事件派发的终点。而对于其他的如Activity、对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件(View)。ViewRootImpl将窗口所接收到的输入事件沿着控件树将事件派发给对应的控件。

三  IMS service创建(启动): 时序图

从SystemServer的启动开始逐步分析:systemserver->startOtherServices

addService&startService

private void startOtherServices() {
    inputManager = new InputManagerService(context);//初始化IMS对象【见小节2.1】
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    ...
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());//将InputMonitor对象保持到IMS对象  
    inputManager.start();//[见小节2.9]
}

IMS服务是伴随着system_server进程的启动而启动,整个调用过程:

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

四 使用

它继承于IInputManager.Stub, 作为Binder服务端.
Client位于InputManager的内部,通过IInputManager.Stub.asInterface() 获取Binder代理端.
C/S两端通信的协议是由IInputManager.aidl来定义的。

五  流程各部分详解

1) Android输入事件模型
输入事件肯定要先捕获才能传给窗口,因此,首先应该有一个线程在不断的监听屏幕或其它输入端,一旦有触摸或其它输入事件,就将事件捕获;
其次,还应该存在某种手段可以找到目标窗口,因为可能有多个 APP 的多个界面为用户可见,必须确定这个事件究竟通知那个窗口;
最后才是目标窗口如何消费事件的问题。

在这里插入图片描述

InputManagerService 是 Android 为了处理各种用户输入操作而抽象的一个服务,自身可以看做是一个 Binder 服务实体,在 SystemServer 进程启动的时候实例化,并注册到 ServiceManager 中去.
不过这个服务对外主要是用来提供一些输入设备信息的作用,作为 Binder 服务的作用比较小:

private void startOtherServices() {
        ......
        InputManagerService inputManager = null;
        inputManager = new InputManagerService(context);
        wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
        ServiceManager.addService(Context.WINDOW_SERVICE, wm);
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
       ......
}

InputManagerService 跟 WindowManagerService 几乎同时被添加,从一定程度上也能说明两者几乎是相生的关系,而输入事件的处理也确实同时涉及这两个服务,最好的证据就是 WindowManagerService 直接握着 InputManagerService 的引用,如果对照上面的处理模型,InputManagerService 主要负责触摸事件的采集,而 WindowManagerService 负责找到目标窗口.
接下来,先看看 InputManagerService 是如何完成触摸事件采集的。

2) 输入事件的读取-- InputReaderThread 线程

InputManagerService 会单独开一个线程(InputReaderThread)专门用来读取触摸事件:framework/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    //生成一个InputReader,构造函数中new了一个EventHub
    mReader = createInputReader(readerPolicy, mClassifier);
    initialize();
}
void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
sp<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) {
    return new InputReader(new EventHub(), policy, listener);
}

唤醒InputReader&indispatch

这两个thread会loop起来,其中InputReaderThread会wait在EventHub->getEvents这里等待下面上报输入事件;
而DispatcherThread会mLooper->pollOnce(),睡眠在这里,等待被InputReaderThread唤醒mLooper->wake().

 

Eventhub

每个输入设备都有自己的驱动程序,数据接口也不尽相同,如何在一个线程里(上面说过只有一个InputReader Thread)把所有的用户输入都给捕捉到? 
这首先要归功于Linux 内核的输入子系统(Input Subsystem), 它在各种各样的设备驱动程序上加了一个抽象层,只要底层的设备驱动程序按照这层抽象接口来实现,
上层应用就可以通过统一的接口来访问所有的输入设备,InputChannel 的 sendMessage 函数,最会通过 socket 发送到 APP 端.

EventHub负责打开/dev/input/目录下的所有设备,然后为每一个设备创建一个Device,并把这个Device放入EventHub所定义的数组们Device中。
之后把这个设备纳入监视范围。
接下来等待事件发生,一旦有事件发生,就从产生input_event事件的设备中读取出这些设备,把这些事件转化为RawEvent类型放入InputReader提供的事件数组中

Ioctl打开设备. 然后EventHub,它主要是利用 Linux 的 inotify 和 epoll 机制,监听设备事件:包括设备插拔及触摸、按钮等各种输入事件,可以看做是一个不同设备的集线器,主要面向的是 /dev/input 目录下的设备节点,比如说 /dev/input/event0 上的事件就是输入事件,通过 EventHub 的 getEvents 就可以监听并获取该事件(经过EventHub将input_event结构体 + deviceId 转换成RawEvent结构体 ):

在这里插入图片描述

在 new InputManager 时候,会新建一个 InputReader 对象及 InputReaderThread 线程,这个线程的主要作用就是通过 EventHub 的 getEvents 获取 Input 事件。

在这里插入图片描述

framework/native/services/inputflinger/InputReaderBase.cpp
framework/native/services/inputflinger/InputReader.cpp

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}
void InputReader::loopOnce() {
    //关键点1 通过EventHub获取事件列表
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    mReaderIsAliveCondition.broadcast();
    //关键点2 对事件进行 加工处理
    processEventsLocked(mEventBuffer, count);//处理事件【见小节3.1】
    ...
    //关键点3 发布事件到InputDispatcher中
    mQueuedListener->flush();
}
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    for (;;) {
            //从mPendingEventItems读取事件项
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.events & EPOLLIN) {
                //从设备不断读取事件,放入到readBuffer
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                }
            }
            ...
        //等待input事件的到来
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        ...
    }
    return event - buffer; //返回所读取的事件个数
}

1) 从EventHub中获取事件列表。事件分为两类:设备节点中读取的原始输入事件,输入设备可用性变化事件(设备事件)。
2) 通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。
对于原始输入事件,则在进行转译、封装与加工后将结果存储到mQueuedListener中。
3) 所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

3) 输入事件的派发---- InputDispatch 线程

在新建 InputManager 的时候,不仅创建了一个事件读取线程,还创建了一个事件派发线程.因此 InputManager 的模型设计为如下样式:
在这里插入图片描述

InputReader 中的 mQueuedListener 其实就是 InputDispatcher 对象,所以 mQueuedListener->flush() 就是通知 InputDispatcher 事件读取完毕,可以派发事件了.
InputDispatcherThread 是一个典型的 Looper 线程,基于 native 的 Looper 实现了 Hanlder 消息处理模型,如果有 Input 事件到来就被唤醒处理事件,处理完毕后继续睡眠等待,简化代码如下:
framework/native/services/inputflinger/InputDispatcher.cpp

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {   ......
        //被唤醒 ,处理Input消息
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
       ......
    } 
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    //睡眠等待input事件
    mLooper->pollOnce(timeoutMillis);
}

以上就是派发线程的模型,dispatchOnceInnerLocked 是具体的派发处理逻辑,这里看其中一个分支,触摸事件:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ......
    switch (mPendingEvent->type) {
        ......
        case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        ......
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
        ......
    }......
}
bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
        nsecs_t* nextWakeupTime) {
    ......
    std::vector<InputTarget> inputTargets;
    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {
    //关键点1 找到目标Window
    injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    ......
    //关键点2  派发
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

从以上代码可以看出,对于触摸事件会首先通过 findTouchedWindowTargetsLocked 找到目标 Window,进而通过 dispatchEventLocked 将消息发送到目标窗口.
下面研究一下如何找到目标窗口,以及这个窗口列表是如何维护的。

4) 为输入事件找目标窗口(没看懂)

Android 系统能够同时支持多块屏幕,每块屏幕被抽象成一个 DisplayContent 对象,内部维护一个 WindowList 列表对象,用来记录当前屏幕中的所有窗口,包括状态栏、导航栏、应用窗口、子窗口(layer?)等。
对于输入事件中的触摸事件来说,我们比较关心可见窗口,用 adb shell dumpsys SurfaceFlinger 看一下可见窗口的组织形式:
那么,如何找到触摸事件对应的窗口呢,是状态栏、导航栏还是应用窗口呢,这个时候 DisplayContent 的 WindowList 就发挥作用了,DisplayContent 握着所有窗口的信息,因此,可以根据触摸事件的位置及窗口的属性来确定将事件发送到哪个窗口,当然其中的细节比一句话复杂的多,跟窗口的状态、透明、分屏等信息都有关系,下面简单看一下,达到主观理解就可以了:
framework/native/services/inputflinger/InputDispatcher.cpp

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, Vector<InputTarget>& inputTargets,
        nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
        ......
        sp<InputWindowHandle> newHoverWindowHandle;
        bool isTouchModal = false;
        ......
        if (newGesture || (isSplit &&
                maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
            int32_t pointerIndex = getMotionEventActionPointerIndex(action);
            int32_t x = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_X));
            int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_Y));
            bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
            //继续调用findTouchedWindowAtLocked寻找接收触屏事件的Window
    sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
       displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
        }
        ......        
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
    // Traverse windows from front to back to find touched window.
    //windowHandles保存了对应displayId的所有窗口的信息
    const std::vector<sp<InputWindowHandle>> windowHandles =
        getWindowHandlesLocked(displayId);
    //遍历所有窗口
    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)) {
                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                    //找到目标窗口
                if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                      int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                      if (portalToDisplayId != ADISPLAY_ID_NONE
                                && portalToDisplayId != displayId) {
                            if (addPortalWindows) {
                                // For the monitoring channels of the display.
                                mTempTouchState.addPortalWindow(windowHandle);
                            }
                            return findTouchedWindowAtLocked(
                            portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
                      }
                      // Found window.
                      //返回目标窗口
                      return windowHandle;
                  }}......}} }
    return nullptr;
}
//返回对应displayId中的所有窗口
std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
        int32_t displayId) const {
    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it =
            mWindowHandlesByDisplay.find(displayId);
    if(it != mWindowHandlesByDisplay.end()) {
        return it->second;
    }
    // Return an empty one if nothing found.
    return std::vector<sp<InputWindowHandle>>();
}
//mWindowHandlesByDisplay的定义
//第一个域表示displayID,第二个域表示对应displayID下所有的窗口
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
            GUARDED_BY(mLock);
//为了便于理解看下面这个函数,根据window的token查找对应的window
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
        const sp<IBinder>& windowHandleToken) const {
    for (auto& it : mWindowHandlesByDisplay) {
        const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
        for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
            if (windowHandle->getToken() == windowHandleToken) {
                return windowHandle;
            }
    }}
    return nullptr;
}

mWindowHandlesByDisplay 记录着所有 displayId 的所有窗口,findTouchedWindowTargetsLocked 就是从 mWindowHandlesByDisplay 中找到目标窗口,规则太复杂,总之就是根据点击位置和窗口的 Z-order 之类的特性去确定,有兴趣可以自行分析。
不过这里需要关心的是 mWindowHandlesByDisplay,它是怎么来的,另外窗口增删的时候如何保持最新的呢?这里就牵扯到跟 WindowManagerService 交互的问题了,mWindowHandlesByDisplay 的值是在 InputDispatcher::setInputWindows 中设置的:

void InputDispatcher::setInputWindows(
const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) {
        ......
        if (inputWindowHandles.empty()) {
            // Remove all handles on a display if there are no windows left.
            mWindowHandlesByDisplay.erase(displayId);
        } else {
            // Since we compare the pointer of input window handles 
            // across window updates, we need to make sure the handle object
            // for the same window stays unchanged across updates.
            const std::vector<sp<InputWindowHandle>>& oldHandles =
                    mWindowHandlesByDisplay[displayId];
           ......
           //定义newHandles用来获取
            std::vector<sp<InputWindowHandle>> newHandles;
            for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
                ......
                if (oldHandlesByTokens.find(handle->getToken()) !=
                    oldHandlesByTokens.end()) {
                    const sp<InputWindowHandle> oldHandle =
                            oldHandlesByTokens.at(handle->getToken());
                    oldHandle->updateFrom(handle);
                    newHandles.push_back(oldHandle);
                } else {
                  //把有效的handle添加到newHandles中
                    newHandles.push_back(handle);
                }
            }
            ......
            // Insert or replace
            //完成对mWindowHandlesByDisplay的赋值或更新
            mWindowHandlesByDisplay[displayId] = newHandles;
        }
       ......

谁会调用这个函数呢? 真正的入口在 WindowManagerService 中,在 WMS 对窗口进行更新的时候会调用 InputMonitor 进而调用 InputDispatcher::setInputWindows,这个时机主要是跟窗口增改删除等逻辑相关,以 addWindow 为例:
在这里插入图片描述

从上面流程可以理解为什么说 WindowManagerService 跟 InputManagerService 是相辅相成的了,到这里,如何找到目标窗口已经解决了,下面就是如何将事件发送到目标窗口的问题了。

5) 输入事件发送到目标窗口

简单说明下架构图的流程:
在InputDispatcher内部,socketpair被封装成了inputchannel,inputchannel又被封装成了Connection进而和应用层之间进行联系
在WindowManagerService内部,WindowState通过创建socketpair来得到fd0和fd1,将fd0封装成inputchannel注册到InputDispatcher中,
将fd1通过binder通信机制传递给应用端,应用端将fd1封装成InputChannel,
通过WindowInputEventReceiver最后将fd1放入到应用层这边的looper中进行监听

第一次ims-wms-app,之后ims-app socketot
notes:socketpair可以同一台机器进程通信。(原理是给一台机器的两个fd).

这就是整个事件传输的宏观流程,接下来我们从3个方面对 输入事件与APP之间建立联系,做一个分析:
WindowManager注册窗口焦点
server端消息注册通道
client端注册消息接收通道

找到了目标窗口,同时也将事件封装好了,剩下的就是通知目标窗口,可是有个最明显的问题就是,目前所有的逻辑都是在 SystemServer 进程,而要通知的窗口位于 APP 端的用户进程,那么如何通知呢?
下意识的可能会想到 Binder 通信,毕竟 Binder 在 Android 中是使用最多的 IPC 手段了,不过 Input 事件处理采用的却不是 Binder:高版本的采用的都是 Socket 的通信方式,而比较旧的版本采用的是 Pipe 管道的方式。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
    ......
    pokeUserActivityLocked(eventEntry);
    for (const InputTarget& inputTarget : inputTargets) {
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
}}}

代码逐层往下看会发现最后会调用到 InputChannel 的 sendMessage 函数,最会通过 socket 发送到 APP 端(Socket 怎么来的接下来会分析)
(App和SystemServer中的IMS,WMS通信使用socket)

在这里插入图片描述

这个 Socket 是怎么来的呢?或者说两端通信的一对 Socket 是怎么来的呢?
其实还是要牵扯到 WindowManagerService,在 APP 端向 WMS 请求添加窗口的时候,会伴随着 Input 通道的创建(inputchannel 就是socketpair).窗口的添加一定会调用 ViewRootImpl 的 setView 函数: 

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 
    //关键点1:调用requestLayout来通知InputManagerService当前的窗口是激活的窗口
    requestLayout();
    mInputChannel = new InputChannel();
//关键点2,bindr通信,后面会创建一对InputChannel(server/client)
   //函数调用结束后,mInputChannel就变成了client端的对象。
    res = mWindowSession.addToDisplay(mWindow...mInputChannel);
    //关键点3 创建并初始化WindowInputEventReceiver,按键消息会从native层传到该对象的onInputEvent函数
   //说明:onInputEvent函数是按键在应用端java层分发的起始端
   mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                    Looper.myLooper());
}

//1 requestLayout是通知IMS这个Activity窗口是当前被激活的窗口,同时将所有的窗口注册到InputDispatcher中。
2 mWindowSession.addToDisplay是把键盘消息接收通道的server端注册端注册到CPP层的IMS中,client端注册到本应用程序的消息循环Looper中,
当IMS监控到有键盘消息的时候,就会找到当前被激活的窗口,进而找到其在IMS中对应的键盘消息接收通道(InputChannel),
通过这个通道在IMS的server端来通知应用程序消息循环的client端,这样就把键盘消息分发给当前激活的Activity窗口了 
3 应用程序这一侧注册消息接收通道

updateInputWindowsLw  {
  addInputWindowHandleLw(u.mInputWindowHandle,...);
  // Send windows to native code.
        mService.mInputManager.setInputWindows(mInputWindowHandles);
}
这里将带有InputChannel的Activity窗口都设置为IMS的输入窗口,最后执行

在 IWindowSession.aidl 定义中 InputChannel 是 out 类型,也就是说需要服务端进行填充,那么接着看服务端 WMS 如何填充的呢?

public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {         
        ......
        final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
        ......
}
void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        //关键点1创建通信信道
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];//本地用
        mClientChannel = inputChannels[1];//APP端用
        mInputWindowHandle.token = mClient.asBinder();
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        //注册信道与窗口
        mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}

WMS 首先创建 socketpair 作为全双工通道,并分别填充到 Client 与 Server 的 InputChannel 中去
之后让 InputManager将Input通信信道与当前的窗口绑定,这样就能知道哪个窗口用哪个信道通信了
最后通过 Binder 将 outInputChannel 回传到 APP 端,下面是 SocketPair 的创建代码:

public static InputChannel[] openInputChannelPair(String name) {
        return nativeOpenInputChannelPair(name);
}
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    ScopedUtfChars nameChars(env, nameObj);
    std::string name = nameChars.c_str();

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    ......
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    ......
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    //填充到server inputchannel
    std::string serverChannelName = name;
    serverChannelName += " (server)";
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
    //填充到client inputchannel
    std::string clientChannelName = name;
    clientChannelName += " (client)";
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

这里 socketpair 的创建与访问其实还是借助文件描述符的,WMS 需要借助 Binder 通信向 APP 端回传文件描述符 fd,这部分只是可以参考 Binder 知识,主要是在内核层面实现两个进程 fd 的转换,窗口添加成功后,socketpair 被创建,被传递到了 APP 端,但是信道并未完全建立,因为还需要一个主动的监听,毕竟消息到来是需要通知的,先看一下信道模型:
在这里插入图片描述
APP 端的监听消息的手段是:将 socket 添加到 Looper 线程的 epoll 数组中去,一有消息到来 Looper 线程就会被唤醒,并获取事件内容,从代码上来看,通信信道的打开是伴随 WindowInputEventReceiver 的创建来完成的。

在这里插入图片描述

信息到来,Looper 根据 fd 找到对应的监听器:NativeInputEventReceiver,并调用 handleEvent 处理对应事件.
之后会进一步读取事件,并封装成 Java 层对象,传递给 Java 层,进行相应的回调处理:

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ......
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    ......
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ......
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        //获取事件
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        ......
        switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;
             ......
         }
         if (inputEventObj) {//回调处理函数
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                env->DeleteLocalRef(inputEventObj);
         } ......}......
}

 最后就是触摸事件被封装成了 inputEvent,并通过 InputEventReceiver的dispatchInputEvent进行处理,这里就返回到我们常见的Java世界了.

 6) 目标窗口中的事件处理

最后简单看一下事件的处理流程,Activity 或者 Dialog 等是如何获得 Touch 事件的呢?如何处理的呢?直白的说就是将监听事件交给 ViewRootImpl 中的 rootView,让它自己去负责完成事件的消费。
究竟最后被哪个 View 消费了要看具体实现了,而对于 Activity 与 Dialog 中的 DecorView 重写了 View 的事件分配函数 dispatchTouchEvent,将事件处理交给了 CallBack 对象处理,至于 View 及 ViewGroup 的消费,算 View 自身的逻辑了。
在这里插入图片描述

六  总结

现在把所有的流程跟模块串联起来,流程大致如下:

  • 点击屏幕
  • InputManagerService 的 Read 线程捕获事件,预处理后发送给 Dispatcher 线程
  • Dispatcher 找到目标窗口
  • 通过 Socket 将事件发送到目标窗口
  • APP 端被唤醒
  • 找到目标窗口处理事件

在这里插入图片描述

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

源码分析设计

Inputreader thread
InputReader读取的输入事件作为参数,通过InputListenerInterface的接口函数被InputDispatcher接收。
接收的事件构造为EventEntry对象放入InputDispatcher对象的EventEntry类型的队列mInboundQueue中,并唤醒InputDispatcherThread线程。
InputDispatcherThread线程不断从该队列中读取输入事件。
首先调用InputDispatcher对象的dispatchOnce函数;dispatchOnce函数又调用dispatchOnceInnerLocked函数,在dispatchOnceInnerLocked函数中进行一系列判断后若是按键事件则调用dispatchKeyLocked函数;
在dispatchKeyLocked函数中使用findFocusedWindowTargetsLocked函数寻找焦点窗口,并把找到的焦点窗口通过调用函数addWindowTargetLocked放入mCurrentInputTargets数组中,
然后调用addMonitoringTargetsLocked为刚才找到的焦点窗口绑定一个inputChannel通道,
接着调用dispatchEventLocked函数;
在dispatchEventLocked函数中首先根据刚才焦点窗口绑定的inputChannel找到对应的一个Connection对象,然后调用prepareDispatchCycleLocked;
在prepareDispatchCycleLocked函数中调用enqueueDispatchEntryLocked函数,在enqueueDispatchEntryLocked函数中根据传进来的事件对应的对象eventEntry构造一个 DispatchEntry对象,DispatchEntry对象中包含inputTarget的信息,并放入connection对象outboundQueue队列;
接着又调用startDispatchCycleLocked;

InputDispatch thread:
从outboundQueue中取出需要处理的事件,交给connection的inputPublisher去分发,将事件加入到connection的waitQueue中。
同时这里 分发事件是通过InputPublisher的publishKeyEvent来完成
这里通过send函数往socket的server端写入InputMessage对象,应用程序正睡眠在client端的fd上,此时client端就会收到该InputMessage,client被唤醒后会进行按键事件的分发。
最后dispatcher线程通过connection对象和APP之间建立连接。
最后简单总结下:事件派发流程:
查找目标:向WindowManagerService查询当前window,获得对应的connection
把事件放入到connection的outboundQueue队列当中
从队列中逐个把事件写入fd,取出并构造InputMessage最后发送。


,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

InputReader 详细源码分析

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) {
            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;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

设备增加 

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    mDevices.add(deviceId, device); //添加设备到mDevices
    ...
}
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    //创建InputDevice对象
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
    ...
    //获取键盘源类型
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
     ....
    //添加键盘类设备InputMapper
    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    //添加鼠标类设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new CursorInputMapper(device));
    }
    //添加触摸屏设备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));
    }
    ...
    return device;
}

该方法主要功能:

  • 创建InputDevice对象,将InputReader的mContext赋给InputDevice对象所对应的变量
  • 根据设备类型来创建并添加相对应的InputMapper,同时设置mContext.

input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:

  • 键盘类设备:KeyboardInputMapper
  • 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
  • 鼠标类设备:CursorInputMapper

介绍完设备增加过程,继续回到[小节3.1]除了设备的增删,更常见事件便是数据事件,那么接下来介绍数据事件的 处理过程。

事件处理 

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    ...
    InputDevice* device = mDevices.valueAt(deviceIndex);
    device->process(rawEvents, count);
}
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
     for (size_t i = 0; i < numMappers; i++) {
        InputMapper* mapper = mMappers[i];
        mapper->process(rawEvent);//调用具体mapper来处理
     }
}
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        if (isKeyboardOrGamepadKey(scanCode)) {
            if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {//获取所对应的KeyCode
                keyCode = AKEYCODE_UNKNOWN;
                flags = 0;
            }
            processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
        }
        break;
    }
    case EV_MSC: ...
    case EV_SYN: ...
    }
}
status_t EventHub::mapKey(int32_t deviceId...) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId); //获取设备对象
    status_t status = NAME_NOT_FOUND;

    sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
    kcm->mapKey(scanCode, usageCode, outKeycode);//根据scanCode找到keyCode
    ...
}
status_t KeyCharacterMap::mapKey(...) const {
    ...
     ssize_t index = mKeysByScanCode.indexOfKey(scanCode);     
     *outKeyCode = mKeysByScanCode.valueAt(index);//根据scanCode找到keyCode
}
void KeyboardInputMapper::processKey(...) {
        keyCode = rotateKeyCode(keyCode, mOrientation);
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {  
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;//mKeyDowns记录着所有按下的键
        } else {
            ...
            mKeyDowns.push(); //压入栈顶
            KeyDown& keyDown = mKeyDowns.editTop();
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
        }
        mDownTime = when; //记录按下时间点
    ...
    //创建NotifyKeyArgs对象, when记录eventTime, downTime记录按下时间;
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);//通知key事
}
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

mapper is keyboard;处理键盘事件

createDeviceLocked创建设备并添加InputMapper,提到会有多种InputMapper。 这里以KeyboardInputMapper(按键事件)为例来展开说明.
按键事件处理 :KeyboardInputMapper.process.
将事件的扫描码(scanCode)转换成键盘码(Keycode) 
参数说明:

  • mKeyDowns记录着所有按下的键;
  • mDownTime记录按下时间点;
  • 此处KeyboardInputMapper的mContext指向InputReader,getListener()获取的便是mQueuedListener。 接下来调用该对象的notifyKey.

 mArgsQueue的数据类型为Vector<NotifyArgs*>,将该key事件压人该栈顶。 到此,整个事件加工完成, 再然后就是将事件发送给InputDispatcher线程.
接下来,再回调小节[1.2] InputReader的loopOnce过程, 可知当执行完processEventsLocked()过程, 然后便开始执行mQueuedListener->flush()过程, 如下文.

 QueuedListener

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
    }
}
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this); // this是指NotifyKeyArgs【见小节4.3】
}

遍历整个mArgsQueue数组, 在input架构中NotifyArgs的实现子类主要有以下几类:

NotifyConfigurationChangedArgs;NotifyKeyArgs;NotifyMotionArgs;NotifySwitchArgs;NotifyDeviceResetArgs
紧接着上述的小节[3.4.5], 可知此处是NotifyKeyArgs对象. 从InputManager对象初始化的过程可知,mInnerListener便是InputDispatcher对象。

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ... 
    event.initialize(args->deviceId...);//初始化KeyEvent对象
    //mPolicy是指NativeInputManager对象。【小节4.3.1】
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
      policyFlags |= POLICY_FLAG_FILTERED;
            //当inputEventObj不为空, 则事件被filter所拦截【见小节4.3.2】
        mPolicy->filterInputEvent(&event, policyFlags);
        //创建KeyEntry对象
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source...);
        //将KeyEntry放入队列【见小节4.3.3】
        needWake = enqueueInboundEventLocked(newEntry);
    }
    if (needWake) {
        //唤醒InputDispatcher线程【见小节4.3.5】
        mLooper->wake();
    }
}
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent...) {
            // 调用Java层的IMS.interceptKeyBeforeQueueing
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
    jobject inputEventObj;JNIEnv* env = jniEnv();
    switch (inputEvent->getType()) {
    case AINPUT_EVENT_TYPE_KEY:
        inputEventObj = android_view_KeyEvent_fromNative(env,
                static_cast<const KeyEvent*>(inputEvent));
        break;
    case AINPUT_EVENT_TYPE_MOTION:
        inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
                static_cast<const MotionEvent*>(inputEvent));
        break;
    default:
        return true; // 走事件正常的分发流程
    }
    //当inputEventObj不为空,则调用Java层的IMS.filterInputEvent()
    jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent,
            inputEventObj, policyFlags);
}

该方法的主要功能:

  1. 调用NativeInputManager.interceptKeyBeforeQueueing,加入队列前执行拦截动作,但并不改变流程,调用链:
    • IMS.interceptKeyBeforeQueueing
    • InputMonitor.interceptKeyBeforeQueueing (继承IMS.WindowManagerCallbacks)
    • PhoneWindowManager.interceptKeyBeforeQueueing (继承WindowManagerPolicy)
  2. 当mInputFilterEnabled=true(该值默认为false,可通过setInputFilterEnabled设置),则调用NativeInputManager.filterInputEvent过滤输入事件;
    • 当返回值为false则过滤该事件,不再往下分发;
  3. 生成KeyEvent,并调用enqueueInboundEventLocked,将该事件加入到InputDispatcherd的成员变量mInboundQueue。

该方法会调用Java层的InputManagerService的interceptKeyBeforeQueueing()方法。

当inputEventObj不为空,则调用Java层的IMS.filterInputEvent(). 经过层层调用后, 最终会再调用InputDispatcher.injectInputEvent(),该基本等效于该方法的后半段:

  • enqueueInboundEventLocked
  • wakeup
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry); //将该事件放入mInboundQueue队列尾部
    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
          ......
    case EventEntry::TYPE_MOTION: {
        //当前App无响应且用户希望切换到其他应用窗口,则drop该窗口事件,并处理其他窗口事件
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN...) {
            //查询可触摸的窗口【见小节4.3.4】
            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
        }
        break;
}}}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId...) { 
    size_t numWindows = mWindowHandles.size();
    for (size_t i = 0; i < numWindows; i++) {//从前台到后台来遍历查询可触摸的窗口
        sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
            if (windowInfo->visible) {
                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                        return windowHandle; //找到目标窗口
}} }
void Looper::wake() {
    write(mWakeEventFd, &inc, sizeof(uint64_t));
}

 此处mWindowHandles的赋值过程是由Java层的InputMonitor.setInputWindows(),经过JNI调用后进入InputDispatcher::setInputWindows()方法完成.
进一步说, 就是WMS执行addWindow()过程或许UI改变等场景,都会触发该方法的修改

[小节4.3]的过程会调用enqueueInboundEventLocked()方法来决定是否需要将数字1写入句柄mWakeEventFd来唤醒InputDispatcher线程. 满足唤醒的条件:

  1. 执行enqueueInboundEventLocked方法前,mInboundQueue队列为空,执行完必然不为空,则需要唤醒分发线程
  2. 当事件类型为key事件,且发生一对按下和抬起操作,则需要唤醒;
  3. 当事件类型为motion事件,且当前可触摸的窗口属于另一个应用,则需要唤醒.

介绍了EventHub从设备节点获取事件的流程,当收到事件后接下里便开始处理事件.

input_reader_seq

InputReader的主要工作分两部分:

  1. 调用EventHub的getEvents()读取节点/dev/input的input_event结构体转换成RawEvent结构体,RawEvent根据不同InputMapper来转换成相应的EventEntry,比如按键事件则对应KeyEntry,触摸事件则对应MotionEntry。
    • 转换结果:input_event -> EventEntry;
  2. 将事件添加到mInboundQueue队列尾部,加入该队列前有以下两个过滤:
    • IMS.interceptKeyBeforeQueueing:事件分发前可增加业务逻辑;
    • IMS.filterInputEvent:可拦截事件,当返回值为false的事件都直接拦截,没有机会加入mInboundQueue队列,不会再往下分发;否则进入下一步;
    • enqueueInboundEventLocked:该事件放入mInboundQueue队列尾部;
    • mLooper->wake:并根据情况来唤醒InputDispatcher线程.
  3. KeyboardInputMapper.processKey()的过程, 记录下按下down事件的时间点.

input_reader

说明:

  • 节点/dev/input的event事件所对应的输入设备信息位于/proc/bus/input/devices,也可以通过getevent来获取事件. 不同的input事件所对应的物理input节点,比如常见的情形:
    • 屏幕触摸和(MENU,HOME,BACK)3按键:对应同一个input设备节点;
    • POWER和音量(下)键:对应同一个input设备节点;
    • 音量(上)键:对应同一个input设备节点;

InputReader整个过程涉及多次事件封装转换,其主要工作核心是以下三大步骤:

  • getEvents:通过EventHub(监听目录/dev/input)读取事件放入mEventBuffer,而mEventBuffer是一个大小为256的数组, 再将事件input_event转换为RawEvent; [见小节2.1]
  • processEventsLocked: 对事件进行加工, 转换RawEvent -> NotifyKeyArgs(NotifyArgs) 
  • QueuedListener->flush:将事件发送到InputDispatcher线程, 转换NotifyKeyArgs -> KeyEntry(EventEntry) [见小节4.1]

InputReader线程不断循环地执行InputReader.loopOnce(), 每次处理完生成的是EventEntry(比如KeyEntry, MotionEntry), 接下来的工作就交给InputDispatcher线程。

InputDispatcher线程详解

介绍InputReader利用EventHub获取数据后生成EventEntry事件,加入到InputDispatcher的mInboundQueue队列,再唤醒InputDispatcher线程。本文将介绍InputDispatcher,同样从threadLoop为起点开始分析。

threadLoop

整个过程不断循环地调用InputDispatcher的dispatchOnce()来分发事件.整个过程不断循环地调用InputDispatcher的dispatchOnce()来分发事件

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce(); //【见小节1.2】
}
void InputDispatcher::dispatchOnce() {  
    dispatchOnceInnerLocked(&nextWakeupTime);//当mCommandQueue不为空时处理
    runCommandsLockedInterruptible();
    mLooper->pollOnce(timeoutMillis); //进入epoll_wait
}

 InputDispatcher

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    switch (mPendingEvent->type) {
      case EventEntry::TYPE_KEY: {
          // 分发按键事件[见小节2.2]
          done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
          break;
      }...}...
}
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    addMonitoringTargetsLocked(inputTargets);
    //只有injectionResult是成功,才有机会执行分发事件【见小节2.4】
    dispatchEventLocked(currentTime, entry, inputTargets);
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    //【见小节2.4.1】向mCommandQueue队列添加doPokeUserActivityLockedInterruptible命令
    pokeUserActivityLocked(eventEntry);
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
    }
}
void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
    ...
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doPokeUserActivityLockedInterruptible);
}
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command); 
    mCommandQueue.enqueueAtTail(commandEntry);// 将命令加入mCommandQueue队尾
    return commandEntry;
}
void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,int32_t dispatchMode) {
    //生成新的事件, 加入connection的outbound队列
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry...);
    switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
            connection->inputState.trackKey(keyEntry,dispatchEntry->resolvedAction...);
            break;
        }
        ...
    }
    ...
    //添加到outboundQueue队尾
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    //当Connection状态正常,且outboundQueue不为空
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
          case EventEntry::TYPE_KEY: {
              KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
              //发布Key事件 [见小节2.9]
              status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                      keyEntry->deviceId, keyEntry->source...);
              break;
          }
          ...
        }
        //从outboundQueue中取出事件,重新放入waitQueue队列
        connection->outboundQueue.dequeue(dispatchEntry);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
    }
}
status_t InputPublisher::publishKeyEvent(...) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
        ......
    //通过InputChannel来发送消息
    return mChannel->sendMessage(&msg);
}

findFocusedWindowTargetsLocked->addWindowTargetLocked()将该事件添加到inputTargets。将当前聚焦窗口mFocusedWindowHandle的inputChannel传递到inputTargets。
该方法主要功能是将eventEntry发送到目标inputTargets.
根据inputChannel的fd从mConnectionsByFd队列中查询目标connection.

这里InputDispatcher的成员变量 mFocusedWindowHandle 就代表当前激活的窗口
这个函数遍历inputWindowHandles,获取获得焦点的窗口,并赋值给mFocusedWindowHandle 。
这样,IMS就把当前激活的窗口保存在InputDispatcher中,后面就可以把键盘消息分发给它来处理
这里根据java层的InputWindowHandle获得native层的InputWindowHandle对象,根据java层InputChannel获得native层InputChannel对象,
最后调用NativeInputManager的resgiterInputChannel,进而调用InputDispatcher的registerInputChannel
InputConsumer::consume {
     ...
       status_t result = mChannel->receiveMessage(&mMsg);
}   

该方法主要功能:

  • 根据dispatchMode来分别执行DispatchEntry事件加入队列的操作。
  • 当起初connection.outboundQueue等于空, 经enqueueDispatchEntryLocked处理后, outboundQueue不等于空情况下, 则执行startDispatchCycleLocked()方法.

该方法主要功能:

  • 根据dispatchMode来决定是否需要加入outboundQueue队列;
  • 根据EventEntry,来生成DispatchEntry事件;
  • 将dispatchEntry加入到connection的outbound队列.

执行到这里,其实等于由做了一次搬运的工作,将InputDispatcher中mInboundQueue中的事件取出后, 找到目标window后,封装dispatchEntry加入到connection的outbound队列.

startDispatchCycleLocked的主要功能: 从outboundQueue中取出事件,重新放入waitQueue队列

  • startDispatchCycleLocked触发时机:当起初connection.outboundQueue等于空, 经enqueueDispatchEntryLocked处理后, outboundQueue不等于空。
  • startDispatchCycleLocked主要功能: 从outboundQueue中取出事件,重新放入waitQueue队列

 InputChannel通过socket向远端的socket发送消息。socket通道是如何建立的呢? InputDispatcher又是如何与前台的window通信的呢? 见下一篇文章Input系统—进程交互, 从文章的小节2.1开始继续往下说.

处理Comand

bool InputDispatcher::runCommandsLockedInterruptible() {
    do {
        //从mCommandQueue队列的头部取出第一个元素
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
        Command command = commandEntry->command;
        //此处调用的命令隐式地包含'LockedInterruptible'
        (this->*command)(commandEntry);
    } while (! mCommandQueue.isEmpty());
}
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);;
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
    android_server_PowerManagerService_userActivity(eventTime, eventType);
}
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
        gPowerModule->powerHint(gPowerModule, POWER_HINT_INTERACTION, NULL);
        env->CallVoidMethod(gPowerManagerServiceObj,
                gPowerManagerServiceClassInfo.userActivityFromNative,
                nanoseconds_to_milliseconds(eventTime), eventType, 0);
}
private void userActivityFromNative(long eventTime, int event, int flags) {
    userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
}
private void userActivityInternal(long eventTime, int event, int flags, int uid) {
    synchronized (mLock) {
        if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
            updatePowerStateLocked();
        }
    }
}

通过循环方式处理完mCommandQueue队列的所有命令,处理过程从mCommandQueue中取出CommandEntry.
runCommandsLockedInterruptible是不断地从mCommandQueue队列取出命令,然后执行直到全部执行完成。

总结

流程图

input_dispatcher_seq

核心方法

用一张图来整体概况InputDispatcher线程的主要工作:

input_dispatcher

图解:

  1. dispatchOnceInnerLocked(): 从InputDispatcher的mInboundQueue队列,取出事件EventEntry。另外该方法开始执行的时间点(currentTime)便是后续事件dispatchEntry的分发时间(deliveryTime)
  2. dispatchKeyLocked():满足一定条件时会添加命令doInterceptKeyBeforeDispatchingLockedInterruptible;
  3. enqueueDispatchEntryLocked():生成事件DispatchEntry并加入connection的outbound队列
  4. startDispatchCycleLocked():从outboundQueue中取出事件DispatchEntry, 重新放入connection的waitQueue队列.
  5. InputChannel.sendMessage通过socket方式将消息发送给远程进程;
  6. runCommandsLockedInterruptible():通过循环遍历地方式,依次处理mCommandQueue队列中的所有命令。而mCommandQueue队列中的命令是通过postCommandLocked()方式向该队列添加的。

UI线程

一. 概述

前面文章都是介绍了两个线程InputReader和InputDispatcher的工作过程。在InputDispatcher的过程讲到 调用InputChanel通过socket与远程进程通信,本文便展开讲解这个socket是如何建立的。
对于InputReader和InputDispatcher都是运行在system_server进程;
用户点击的界面往往可能是某一个app,而每个app一般地都运行在自己的进程,这里就涉及到跨进程通信,app进程是如何与system进程建立通信。
要解答这些问题,从Activity最基本的创建过程开始说起。我们都知道一般地Activity对应一个应用窗口, 每一个窗口对应一个ViewRootImpl。窗口是如何添加到Activity的,从Activity.onCreate()为起点讲解。

UI线程

UI线程属于app. UI线程可以理解为Activitythread 线程,applicationThread其一个对象.
Activity的生命周期的回调方法都是运行在主线程,也称之为UI线程,所有UI相关的操作都需要运行在该线程。
本文虽然是UI线程,但并非只介绍所有运行在UI线程的流程,文中还涉及binder thread。

protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_account_bind);
    ...
}
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    root.setView(view, wparams, panelParentView);
    ...
}
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    //获取IWindowSession的代理类【见小节2.3.1】
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mDisplay = display;
    mThread = Thread.currentThread(); //主线程
    mWindow = new W(this);
    mChoreographer = Choreographer.getInstance();
    ...
}
public static IWindowSession getWindowSession() {
  //获取IMS的代理类
  InputMethodManager imm = InputMethodManager.getInstance();                
  IWindowManager windowManager = getWindowManagerService();//获取WMS的代理类
  //经过Binder调用,最终调用WMS[见小节2.3.2]
  sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {...},
                        imm.getClient(), imm.getInputContext());
}
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) {
    //创建Session对象
    Session session = new Session(this, callback, client, inputContext);
    return session;
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      mInputChannel = new InputChannel(); //创建InputChannel对象
      //通过Binder调用,进入system进程的Session[见小节2.4]
      res = mWindowSession.addToDisplay(mWindow, mSeq...);
      mInputQueue = new InputQueue();
      mInputQueueCallback.onInputQueueCreated(mInputQueue);
     //创建WindowInputEventReceiver对象[见3.1]
     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    public int addToDisplay(IWindow window, int seq...) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}
public int addWindow(Session session, IWindow client, int seq...) {
    //创建WindowState【见小节2.5.1】
    WindowState win = new WindowState(this, session, client, token...);           
         //根据WindowState的HashCode以及title来生成InputChannel名称
        String name = win.makeInputChannelName();    
        //创建一对InputChannel[见小节2.6]
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        //将socket服务端保存到WindowState的mInputChannel
        win.setInputChannel(inputChannels[0]);      
        //socket客户端传递给outInputChannel [见小节2.7]
        inputChannels[1].transferTo(outInputChannel);
        //利用socket服务端作为参数[见小节2.8]
        mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
        //新添加window能接收按下操作,则更新聚焦窗口。
        focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                false /*updateInputWindows*/);
           ...
    if (focusChanged) {
        mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
    }
    //设置当前聚焦窗口【见小节2.5.2】
    mInputMonitor.updateInputWindowsLw(false /*force*/);
}

Activity启动是由system进程控制:

  1. handleLaunchActivity():会调用Activity.onCreate(), 该方法内再调用setContentView(),经过AMS与WMS的各种交互,层层调用后,进入step2
  2. handleResumeActivity():会调用Activity.makeVisible(),该方法继续调用便会执行到WindowManagerImpl.addView(), 该方法内部再调用WindowManagerGlobal.addView()

setInputWindows的调用链:(最终设置mFocusedWindowHandle值)

-> IMS.setInputWindows 
  -> NativeInputManager::setInputWindows
    -> InputDispatcher::setInputWindows

dragWindowHandle的初始化过程:

View.startDrag
    Session.prepareDrag
      WMS.prepareDragSurface
        mDragState = new DragState(...);
    Session.performDrag   
      DragState.register
        mDragWindowHandle = new InputWindowHandle(...);  
public static InputChannel[] openInputChannelPair(String name) {
    return nativeOpenInputChannelPair(name);
}
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    //创建一对socket[见小节2.6.2]
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    //创建Java数组
    jobjectArray channelPair = env->NewObjectArray(2,gInputChannelClassInfo.clazz, NULL);
    //创建NativeInputChannel对象[见小节2.6.3]
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
    //创建NativeInputChannel对象[见小节2.6.3]
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
    ...
    //将client和server 两个插入到channelPair
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}
status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    //真正创建socket对的地方【核心】
    socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);
    int bufferSize = SOCKET_BUFFER_SIZE; //32k
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    ......
    //创建InputChannel对象
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
    //创建InputChannel对象
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}
InputChannel::InputChannel(const String8& name,int fd):mName(name), mFd(fd) {
    //将socket设置成非阻塞方式
    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
}
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
  mInputChannel(inputChannel), mDisposeCallback(NULL) {
}
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, NativeInputChannel* nativeInputChannel) {
    //创建Java的InputChannel
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);
    if (inputChannelObj) {
        //将nativeInputChannel保存到Java层的InputChannel的成员变量mPtr
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    }
    return inputChannelObj;
}
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj, NativeInputChannel* nativeInputChannel) {
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}
public void transferTo(InputChannel outParameter) {    
    nativeTransferTo(outParameter);
}
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
        jobject otherObj) { 
    android_view_InputChannel_getNativeInputChannel(env, otherObj);
    //将当前inputChannels[1]的mPtr赋值给nativeInputChannel
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    // 将该nativeInputChannel保存到outInputChannel的参数
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
public void registerInputChannel(InputChannel inputChannel, InputWindowHandle inputWindowHandle) {
    nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    ...
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    {        
        //创建Connection[见小节2.8.4]
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        ...
        //将该fd添加到Looper监听[见小节2.8.5]
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    }
    mLooper->wake(); //connection改变, 则唤醒looper
}
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
    // 此处的callback为handleReceiveCallback 
    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
    {       
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            //通过epoll监听fd
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ...
            mRequests.add(fd, request); //该fd的request加入到mRequests队列
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            ...
            mRequests.replaceValueAt(requestIndex, request);
        }
}

该方法主要功能:

  1. 创建socket pair; (非阻塞式的socket)
  2. 设置两个socket的接收和发送的buffer上限为32KB;
  3. 创建client和server的Native层InputChannel对象;
    • sockets[0]所对应的InputChannel名称的后缀为(server);
    • sockets[1]所对应的InputChannel名称的后缀为(client)

就是将socket客户端inputChannels[1]传递给outInputChannel;

  • inputChannel是指inputChannels[0],即socket服务端;
  • inputWindowHandle是指WindowState.mInputWindowHandle;

将新创建的connection保存到mConnectionsByFd成员变量,“InputDispatcher”线程的Looper添加对socket服务端的监听功能; 当该socket有消息时便会唤醒该线程工作。
此处inputChannel是指前面openInputChannelPair创建的socket服务端,将其同时保存到Connection.inputChannel和InputPublisher.mChannel。
此处Loop便是“InputDispatcher”线程的Looper,将socket服务端的fd采用epoll机制注册监听.

小节

虽然本文介绍的UI线程的工作,

  • [小节2.1 ~ 2.3]: 运行在UI线程;
  • [小节2.4 ~ 2.8]:通过IWindowSession的Binder IPC调用,运行在system_server的binder thread;

ViewRootImpl的setView()过程:

  • 创建socket pair,作为InputChannel:
    • socket服务端保存到system_server(IMS)中的WindowState的mInputChannel;
    • socket客户端通过binder传回到远程进程的UI主线程ViewRootImpl的mInputChannel;
  • IMS.registerInputChannel()注册InputChannel,监听socket服务端:
    • Loop便是“InputDispatcher”线程的Looper;
    • 回调方法handleReceiveCallback。

notes: socket服务端保存到system_server中,client 在app中.IMS在system_server中,监听socket 服务器端

三. WindowInputEventReceiver

创建WindowInputEventReceiver对象

final class WindowInputEventReceiver extends InputEventReceiver {
    //inputChannel是指socket客户端,Looper是指UI线程的Looper
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper); //【见小节3.2】
    }
    ...
}
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
     ...
     mInputChannel = inputChannel;
     mMessageQueue = looper.getQueue(); //UI线程消息队列
     mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
             inputChannel, mMessageQueue);
 }
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    //获取UI主线程的消息队列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    //创建NativeInputEventReceiver对象【见小节3.4】
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    //【见小节3.5】
    status_t status = receiver->initialize();
    ...
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); 
    return reinterpret_cast<jlong>(receiver.get());
}
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);  //【见小节3.6】
    return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
  if (mFdEvents != events) {
      mFdEvents = events;
      int fd = mInputConsumer.getChannel()->getFd();
      if (events) {
          //将socket客户端的fd添加到主线程的消息池【见小节3.6.1】
          mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
      } else {
          mMessageQueue->getLooper()->removeFd(fd);
      }
  }
}  
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
        if (requestIndex < 0) {
            //通过epoll监听fd
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ...
            mRequests.add(fd, request); //该fd的request加入到mRequests队列
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            ...
            mRequests.replaceValueAt(requestIndex, request);
        }
}

此处的Looper便是UI主线程的Looper,将socket客户端的fd添加到UI线程的Looper来监听,回调方法为NativeInputEventReceiver。

总结

input_ui

首先,通过openInputChannelPair来创建socket pair,作为InputChannel:

  • socket服务端保存到system_server中的WindowState的mInputChannel;
  • socket客户端通过binder传回到远程进程的UI主线程ViewRootImpl的mInputChannel;

紧接着,完成了两个线程的epoll监听工作:

  • [小节2.8]IMS.registerInputChannel(): “InputDispatcher”线程监听socket服务端,收到消息后回调InputDispatcher.handleReceiveCallback();
  • [小节3.6]setFdEvents(): UI主线程监听socket客户端,收到消息后回调NativeInputEventReceiver.handleEvent().

有了这些“InputDispatcher”和“UI”主线程便可以进行跨进程通信与交互。

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

Input系统—事件处理全过程

(举例子:将上面讲的所有的内容连接到一起)

一. 概述

先简单总结和回顾以下前几篇文章的内容:

  • Input系统—InputReader线程:通过EventHub从/dev/input节点获取事件,转成EventEntry事件加入到InputDispatcher的mInboundQueue.
  • Input系统—InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connection的outboundQueue队列.再然后开始处理分发事件,取出outbound队列,放入waitQueue.
  • Input系统—UI线程:创建socket pair,分别位于”InputDispatcher”线程和focused窗口所在进程的UI主线程,可相互通信。
    • UI主线程:通过setFdEvents(),监听socket客户端,收到消息后回调NativeInputEventReceiver();【见小节2.1】
    • “InputDispatcher”线程:通过IMS.registerInputChannel(),监听socket服务端,收到消息后回调handleReceiveCallback;[见小节3.1]

接下来,以按键事件为例,说一说一次事件处理过程是如何完成。按键事件经过InputReader,再到InputDispatcher的startDispatchCycleLocked()过程会调用publishKeyEvent(),从该方法说起

二. InputDispatcher线程

status_t InputPublisher::publishKeyEvent(...) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    ....
    //通过InputChannel来发送消息
    return mChannel->sendMessage(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
    do {
        //向目标mFd写入消息,采用异步非阻塞方式
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
}

Input系统—UI线程讲解了会创建socket pair,用于两个进程的线程间相互通信。当mFd写入消息后,此时会唤醒处于epoll_wait状态的应用进程的UI线程,见下文。

三. UI主线程

当收到消息的处理过程,Android消息机制在获取下一条消息的时候,会调用lnativePollOnce(),最终进入到Looper::pollInner()过程,如下

int Looper::pollInner(int timeoutMillis) {
    struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //fd最大个数为16
    //等待事件发生或者超时,在nativeWake()方法,向管道写端写入字符;
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //循环遍历,处理所有的事件
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken(); //已唤醒则读取并清空管道数据
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                //处理request,生成对应的reponse对象,push到mResponses数组
                pushResponse(events, mRequests.valueAt(requestIndex));
            }
        }
    }
Done: ;
    //处理带有Callback()方法的Response事件,执行Reponse相应的回调方法
    for (size_t i = 0; i < mResponses.size(); i++) {
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            // 处理请求的回调方法;response.request.callback是指NativeInputEventReceiver
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
 }}       }
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & ALOOPER_EVENT_INPUT) {
        //UI线程收到Key事件后,开始处理该事件。
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    if (events & ALOOPER_EVENT_OUTPUT) {
        ......
        setFdEvents(ALOOPER_EVENT_INPUT);
    }
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    for (;;) {
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (!skipCallbacks) {
            jobject inputEventObj;
            switch (inputEvent->getType()) {
                case AINPUT_EVENT_TYPE_KEY:
                    //由Native的inputEvent来生成Java层的事件
                    inputEventObj = android_view_KeyEvent_fromNative(env,
                            static_cast<KeyEvent*>(inputEvent));
                    break;
                ...
            }
            if (inputEventObj) {
                //执行Java层的InputEventReceiver.dispachInputEvent【见小节3.3.3】
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
}}}}
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
    //循环遍历所有的Event
    while (!*outEvent) {
            //收到新消息【见小节3.3.2】
            status_t result = mChannel->receiveMessage(&mMsg);
            if (result) {
                    result = consumeBatch(factory, frameTime, outSeq, outEvent);
                    break;
            }
        }
        switch (mMsg.header.type) {
          case InputMessage::TYPE_KEY: {
              //从mKeyEventPool池中取出KeyEvent
              KeyEvent* keyEvent = factory->createKeyEvent();
              //将msg封装成KeyEvent
              initializeKeyEvent(keyEvent, &mMsg);
              *outSeq = mMsg.body.key.seq;
              *outEvent = keyEvent;
              break;
          }...
}}       }
status_t InputChannel::receiveMessage(InputMessage* msg) {
    do {
        //读取InputDispatcher发送过来的消息
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);
}
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event); //[见小节3.3.4]
}
final class WindowInputEventReceiver extends InputEventReceiver {
    public void onInputEvent(InputEvent event) {
       enqueueInputEvent(event, this, 0, true); //【见小节3.3.5】
    }
    ...
}
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    if (processImmediately) {
        doProcessInputEvents(); //【见小节3.3.6】
    } else {
        scheduleProcessInputEvents();
    }
}
void doProcessInputEvents() {
    while (mPendingInputEventHead != null) {
       QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        deliverInputEvent(q);
    }
}
private void deliverInputEvent(QueuedInputEvent q) {
     mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
     if (stage != null) {
         stage.deliver(q);
     } else {
         finishInputEvent(q); //[见小节3.4]
     }
 }

UI线程收到Key事件后,开始处理该事件。 
经过一系列的InputStage调用, 最终会分发到真正需要处理该时间的窗口. 当处理完后会调用finishInputEvent(), 见小节3.4

finishInputEvent --q.mReceiver.finishInputEvent--nativeFinishInputEvent
sendFinishedSignal--sendUnchainedFinishedSignal--mChannel->sendMessage(&msg);
通过InputChannel->sendMessage,将TYPE_FINISHED类型的消息,发送回InputDispatcher线程

四. InputDispatcher线程

int Looper::pollInner(int timeoutMillis) {
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...
Done:
    ...
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            // 处理请求的回调方法【见小节4.2】
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            ...
        }
    }
    return result;
}
int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
    //handleReceiveCallback()【见小节4.3】
    return mCallback(fd, events, data);
}
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
   .....
  d->runCommandsLockedInterruptible(); //执行命令【见小节4.6】
}
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
    InputMessage msg;
    status_t result = mChannel->receiveMessage(&msg); //接收消息
}

finishDispatchCycleLocked--onDispatchCycleFinishedLocked--runCommandsLockedInterruptible--doDispatchCycleFinishedLockedInterruptible

五. 总结

5.1 整体框架图

input_summary

5.2 交互过程

用一张图来总结交互过程,主要是通过一对socket方式来通信。
当input分发到app端, 那么便进入来了InputEventReceiver.dispatchInputEvent()过程.

input_socket

图解:

  1. InputDispatcher线程调用InputPublisher的publishKeyEvent向UI主线程发送input事件;
  2. UI主线程接收到该事件后,调用InputConsumer的consumeEvents来处理该事件, 一路执行到ViewRootImpl.deliverInputEvent()方法;
  3. UI主线程经过一系列的InputStage来处理, 当事件分发完成,则会执行finishInputEvent()方法.再进一步调用InputConsumer::sendFinishedSignal 告知InputDispatcher线程该时事件已处理完成.
  4. InputDispatcher线程收到该事件后, 执行InputDispatcher::handleReceiveCallback();最终会调用doDispatchCycleFinishedLockedInterruptible()方法 ,将dispatchEntry事件从等待队列(waitQueue)中移除.

附录:硬件输入

input_dev 代表底层驱动。
input_handler 代表某类输入设备的处理方法,相当于一个上层驱动。
(一个input_dev 可以有多个input_handler,同样,一个input_handler 可以用于多种输入设备。)
input handle 用来关联某个input_dev 和 某个 input_handler, 它对应上图中的紫色的原点。每个input handle 都会生成一个文件节点。

/dev/input $ ls                                                                                                                                       

event0 event1 event2 event3 event4 event5 event6

比如图中4个evdev的handle就对应与 /dev/input/下的四个文件"event0 - 3". 通过input handle, 可以找到对应的input_handler 和 input_dev.

通过Linux input system获取用户输入的流程简单如下:

  1. 设备通过input_register_dev 将自己的驱动注册到Input 系统。
  2. 各种Handler 通过 input_register_handler将自己注册到Input系统中。
  3. 每一个注册进来的input_dev 或 Input_handler 都会通过input_connect() 寻找对方,生成对应的 input_handle,并在/dev/input/下产成一个设备节点文件.
  4. 应用程序通过打开(Open)Input_handle对应的文件节点,打开其对应的input_dev 和 input_handler的驱动。这样,当用户按键时,底层驱动就能捕捉到,并交给对应的上层驱动(handler)进行处理,然后返回给应用程序。

所以,只要打开 /dev/input/ 下的所有 event* 设备文件,我们就可以有办法获取所有输入设备的输入事件,不管它是触摸屏,还是一个USB 设备,还是一个红外遥控器。Android中完成这个工作的就是EventHub。它在NativeInputManager的初始化中被创建。

other:map类型:KeyboadInputMapper对象,TouchInputMapper、CursorInputMapper、VibratorInputMapper、SwitchInputMapper、JoystickInputMapper等映射对象
Inputdispatch里面还分成3个queue,不需要这么详细

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值