Android | InputManagerService与输入事件采集

前言

  • 事件分发机制是Android中的基础而重要的知识,一般认为Activity#dispatchKeyEvent()或者Activity#dispatchTouchEvent()是分发的起点。那么问题来了,是谁调用了Activity的方法呢?输入事件是如何产生的?
  • Android系统有一整套从Linux内核应用框架层应用层的事件处理机制
  • 本文将以InputManagerService为线索,分析输入事件的产生-采集-分发流程,希望能帮上忙
Android | InputManagerService与输入事件采集_第1张图片
思维导图
Android | InputManagerService与输入事件采集_第2张图片
输入事件 示意图

1. 启动服务

Android系统启动后,系统进程SystemServer.java将依次启动各个系统服务,我们搜索下InputManagerService,不会匹配到太多东西,梳理一下有关的代码:

关于系统服务的更多介绍参考:[Android | 系统启动过程]

// /frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
    InputManagerService inputManager = null;
    WindowManagerService wm = null;
        
    // ...
        
    // 实例化InputManagerService
    inputManager = new InputManagerService(context);
    // 实例化WindowManagerService
    wm = WindowManagerService.main(context,inputManager,...);
    // 添加到ServiceManager统一管理
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, ...);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,...);
        
    // ...
        
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    // 启动InputManager服务
    inputManager.start();
}
Android | InputManagerService与输入事件采集_第3张图片
IMS与WMS UML类图

可以看出,系统进程分别实例化了InputManagerServiceWindowManagerService,前者的实例直接传入后者,而后者又传递了一个InputMonitor对象给前者,这是为什么呢?暂时跳过,继续往下看InputManagerService.java:

// /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);

private static native void nativeStart(long ptr);

public InputManagerService(Context context) {
    // ...
    // 调用了native层
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    // ...
}

public void start() {
    // 调用了native层
    nativeStart(mPtr);
    // ...
}

这里只是调用到native层的两个静态方法,继续往下看com_android_server_input_InputManagerService.cpp:

// /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    // ...
    // 实例化NativeInputManager对象
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast(im);
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast(ptr);
    // 调用到InputManager#start()
    status_t result = im->getInputManager()->start();
}

NativeInputManager::NativeInputManager(jobject contextObj, 
        jobject serviceObj, const sp& looper) : 
    mLooper(looper), mInteractive(true) {
    // ...
    // 实例化EventHub对象
    sp eventHub = new EventHub();
    // 实例化InputManager对象
    mInputManager = new InputManager(eventHub, this, this);
}

public: 
inline sp getInputManager() const { return mInputManager; }

可以看出,在native层实例化了NativeInputManager,其构造方法里还实例化了InputManagerEventHub对象,后者是干什么的呢?暂时跳过,继续往下看InputManager.cpp:

// /frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
        const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    // 实例化InputDispatcher与InputReader
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    // InputDispatcher实例与EventHub实例传递给InputReader
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    // 实例化两个线程
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
    // 运行两个线程
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    // ...
}

可以看出,InputManager的构造方法中实例化了InputDispatcherInputReader,随后start()中启动了InputDispatcherThread线程与InputReaderThread线程(继承于Thread.cpp)。

Android | InputManagerService与输入事件采集_第4张图片
Timethreads图 - 01

这两个线程分别做什么事呢?继续往下看InputReder.cpp与InputDispatcher.cpp:

// /frameworks/native/services/inputflinger/InputReader.cpp

InputReaderThread::InputReaderThread(const sp& reader) : 
// 继承于Thread
Thread(/*canCallJava*/ true), mReader(reader) {
}

/**
 * true:循环执行threadLoop()直到调用requireExit()退出循环
 **/
bool InputReaderThread::threadLoop() {
    // 读取/采集一次事件
    mReader->loopOnce();
    return true;
}
// /frameworks/native/services/inputflinger/InputDispatcher.cpp

InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

bool InputDispatcherThread::threadLoop() {
    // 分发一次事件
    mDispatcher->dispatchOnce();
    return true;
}

可以看到,到这里InputManagerService的启动就完成了,提炼出关键点:

  • InputManagerServiceWindowManagerService运行在系统进程SystemServer
  • InputManagerService启动了InputReaderThread线程与InputDispatcherThread线程,分别死循环调用InputReader#loopOnce()InputDispatcher#dispatchOnce()
Android | InputManagerService与输入事件采集_第5张图片
Timethreads图 - 02

2. 采集事件

上一节讲到,InputReaderThread线程死循环执行InputReader#loopOnce(),用于采集事件,简化代码如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() {
    // ...
    // 从EventHub中读取事件,存储在指针mEventBuffer中
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    if (count) {
        // 处理读取到的事件
        processEventsLocked(mEventBuffer, count);
    }
    // ...
    // 这一行是干什么的?稍后介绍
    mQueuedListener->flush();
}

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    // 遍历每个事件
    for (const RawEvent* rawEvent = rawEvents; count;) {
        // ...
        int32_t type = rawEvent->type;
        if(type <  EventHubInterface::FIRST_SYNTHETIC_EVENT){
            // 分支1:输入事件
            // ...
                        
            // 处理每一个输入事件
            int32_t deviceId = rawEvent->deviceId;
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        }else{
            // 分支2:设备事件
            switch(rawEvent->type){
                    case EventHubInterface::DEVICE_ADDED:
                        addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                        break;
                       // ...
            }
        }
    }
    // ...
}

可以看到,loopOnce()EventHub实例中获取到原始事件,并依次处理每个事件,分为两种:

  • 输入事件(分支1)
  • 设备事件(分支2)

我们先看分支1的processEventsForDeviceLocked(),简化代码如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

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) {
    size_t numMappers = mMappers.size();
    // 遍历每个事件
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
            // 依次交给每个InputMapper处理
            for (size_t i = 0; i < numMappers; i++) {
                    InputMapper* mapper = mMappers[i];
                    mapper->process(rawEvent);
            }
    }
}

可以看到,输入事件交给了InputDevice处理,在内部实际上是执行了多次InputMapper#process(),这里的InputDeviceInputMapper是什么呢,还记得分支2的addDeviceLocked()吗?简化代码如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    // ...
    // 创建InputDevice实例
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    // ...
    // 添加到mDevices列表 
    // InputReader.h:KeyedVector mDevices;
    mDevices.add(deviceId, device);
    // ...
}

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

    // ...
        
    // Scroll wheel-like devices. - 滚轮式
    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
        device->addMapper(new RotaryEncoderInputMapper(device));
    }
        
    // ...
        
    // Touchscreens and touchpad devices. - 触屏式
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        // 多点触控设备
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        // 单点触控设备
        device->addMapper(new SingleTouchInputMapper(device));
    }
        
    // Joystick-like devices. - 操纵杆式
    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
        device->addMapper(new JoystickInputMapper(device));
    }
        // ...
}

可以看到分支2里,先创建了InputDevice实例,随后根据不同的设备事件的类型,又添加了不同InputMapper,比如滚轮式,触屏式,操纵杆式,这也就说明了,一个Android设备上是支持同时接入多个输入设备的,当然触屏设备才是本文分析的重点。

让我们回到分支1的InputMapper#process(),因为我们的重点是触屏设备,所以我们只关心MultiTouchInputMapperSingleTouchInputMapper,代码如下:

// /frameworks/native/services/inputflinger/InputReader.cpp

// 多点触控设备
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    // ...
}

// 单点触控设备
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    // ...
}

void TouchInputMapper::process(const RawEvent* rawEvent) {
    // ...
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}

void TouchInputMapper::sync(nsecs_t when) {
    // ...
    processRawTouches(false /*timeout*/);
}

void TouchInputMapper::processRawTouches(bool timeout) {
    // ...
    cookAndDispatch(mCurrentRawState.when);
}

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    // ...
        
    // 按键事件
    if (consumeRawTouches(when, policyFlags)) {
        mCurrentRawState.rawPointerData.clear();
    }
    // ...
    // 触点事件
    dispatchPointerUsage(when, policyFlags, pointerUsage);
    // ...
}

从简化的代码可以看出,不论是MultiTouchInputMapper还是SingleTouchInputMapper,最终都会走到cookAndDispatch()这个方法,分别处理了两类事件:

  • 按键事件
  • 触点事件
// /frameworks/native/services/inputflinger/InputReader.cpp

bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
    // ...
    // 处理虚拟按键事件
    dispatchVirtualKey(...)
    // ...
}

void TouchInputMapper::dispatchVirtualKey(...) {
    // ...
    // 实例化一个NotifyKeyArgs
    NotifyKeyArgs args(...);
    // 回调
    getListener()->notifyKey(&args);
}
// /frameworks/native/services/inputflinger/InputReader.cpp

void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage) {
    // ...
    // 处理触点事件
    dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
    // ...
}

void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
    // ...
    // 处理触点事件
    dispatchMotion(...)
    // ...
}

void TouchInputMapper::dispatchMotion(...){
    // ...
    // 实例化一个NotifyMotionArgs
    NotifyMotionArgs args(...);
    // 回调
    getListener()->notifyMotion(&args);
}

从简化代码可以看出,对于按键事件和触点事件,分别实例化了NotifyKeyArgsNotifyMotionArgs,随后分别调用了getListener()->notifyKey()getListener()->notifyMotion()

Android | InputManagerService与输入事件采集_第6张图片
事件采集 UML时序图 - 01

getListener()->notifyKey()看起来很像是告知监听器已经采集到事件了,是不是这样呢?找一下getListener()相关代码:

// /frameworks/native/services/inputflinger/InputReader.cpp

InputListenerInterface* InputReader::ContextImpl::getListener() {
    // 弱指针的用法,简单理解为返回了mQueuedListener就好
    return mReader->mQueuedListener.get();
}

// --- InputReader ---

InputReader::InputReader(const sp& eventHub, const sp& policy, const sp& listener) {
    // 实例化QueuedInputListener
    mQueuedListener = new QueuedInputListener(listener);
}

可以看出,getListener()的返回值是mQueuedListener,返回值类型是InputListenerInterface,它在InputReader的构造方法中创建,并包装了InputReader构造方法的第三个参数listener,这个listener又是什么呢?让我们回去找到实例化InputReader的地方:

// /frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(...) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    // InputDispatcher实例与EventHub实例传递给InputReader
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

原来如此,InputDispatcher实例就是这第三个参数listener,我们查看InputDispatcherInputListenerInterface的定义:InputDispatcher.h与InputListener.h

// /frameworks/native/services/inputflinger/InputDispatcher.h

class InputDispatcher : public InputDispatcherInterface {
    // ...
}

class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
    //...
}
// /frameworks/native/services/inputflinger/InputListener.h

/**
 * 基类
 **/
struct NotifyArgs {
    virtual ~NotifyArgs() { }
    virtual void notify(const sp& listener) const = 0;
};

/**
 * 触点事件
 **/
struct NotifyMotionArgs : public NotifyArgs {
    // ...
}

/**
 * 按键事件
 **/
struct NotifyKeyArgs : public NotifyArgs {
    // ...
}

class InputListenerInterface : public virtual RefBase {/**弱指针**/
    // ...
public:
    virtual void notifyKey(const NotifyKeyArgs* args) = 0;
    virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
        
    // ...  
};

class QueuedInputListener : public InputListenerInterface {
    // ...
public:
    explicit QueuedInputListener(const sp& innerListener);
        
    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args);
        
    void flush();
        
    // ...

private:
    sp mInnerListener;
    Vector mArgsQueue;
};

可以看到,QueuedInputListenerInputDispatcher都是实现了InputListenerInterface,前者其实就是后者的包装类。

Android | InputManagerService与输入事件采集_第7张图片
InputDispatcher UML类图

我们看看包装类QueuedInputListener都做了什么,简化代码如下InputListener.cpp:

// /frameworks/native/services/inputflinger/InputListener.cpp

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    // push到队列中
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    // push到队列中
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

void NotifyMotionArgs::notify(const sp& listener) const {
    listener->notifyMotion(this);
}

void NotifyKeyArgs::notify(const sp& listener) const {
    listener->notifyKey(this);
}

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    // 遍历每个NotifyArgs
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // mInnerListener就是InputDispatcher
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

可以看到,之前的getListener()->notifyMotion()或者getListener()->notifyKey()都是把事件pushmArgsQueue队列中。那么队列中的这些事件到底什么时候才被处理呢?还记得InputReader#loopOnce()的最后一步吗?

// /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() {
    // ...
    // 从EventHub中读取事件,存储在指针mEventBuffer中
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    if (count) {
        // 处理读取到的事件
        processEventsLocked(mEventBuffer, count);
    }
    // ...
    mQueuedListener->flush();
}

原来如此,所有事件被pushmArgsQueue队列之后,会调用mQueuedListener->flush(),最终会调用到InputDispatcher#notifyKey()InputDispatcher#notifyMotion(),归纳时序图如下:

Android | InputManagerService与输入事件采集_第8张图片
事件采集 UML时序图 - 02

继续查看InputDispatcher#notifyKey()InputDispatcher#notifyMotion()的简化代码:

// /frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    // ...
        
    // 实例化KeyEntry
    KeyEntry* newEntry = new KeyEntry(...);
    // 加入事件队列
    enqueueInboundEventLocked(newEntry);
    // 唤醒在mLooper上等待的线程
    mLooper->wake();
}

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    // ...
        
    // 实例化MotionEntry
    MotionEntry* newEntry = new MotionEntry(...);
    // 加入事件队列
    enqueueInboundEventLocked(newEntry);
    // 唤醒在mLooper上等待的线程
    mLooper->wake();
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    // 加入队列尾部
    mInboundQueue.enqueueAtTail(entry);
    // ...
}

可以看到,InputDispatcher#notifyKey()InputDispatcher#notifyMotion()基本类似,都是将事件KeyEntryMotionEntry加入队列mInboundQueue尾部,随后唤醒在mLooper(Looper.h)上等待的线程。

Android | InputManagerService与输入事件采集_第9张图片
Timethreads图 - 03

记得我们熟知的生产者-消费者模型吗?

到这里,我们就完整地在InputReaderThread线程中执行了一次loopOnce(),总结一下关键点:

  • InputReaderEventHub中读取事件,其中一部分是输入事件,我们关心的是按键事件触点事件
  • 事件最终被加入mInboundQueue队列,随即唤醒在mLooper上等待的线程

那么,唤醒的是哪个线程呢?会不会就是InputDispatcherThread线程呢?相关代码如下:

// /frameworks/native/services/inputflinger/InputDispatcher.cpp

// --- InputDispatcherThread ---

InputDispatcherThread::InputDispatcherThread(...) {
    bool InputDispatcherThread::threadLoop() {
        mDispatcher->dispatchOnce();
        return true;
    }
}
// --- InputDispatcher ---

InputDispatcher::InputDispatcher(...){
    mLooper = new Looper(false);
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    // ...
    // 分发一次事件
    dispatchOnceInnerLocked(&nextWakeupTime);
    // ...
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    // 在mLooper上等待一段时间
    mLooper->pollOnce(timeoutMillis);
}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    // ...
    // 获取mInboundQueue队列头部的事件
    mPendingEvent = mInboundQueue.dequeueAtHead();
        
    //...
        
    switch (mPendingEvent->type) {
        // ...
        case EventEntry::TYPE_KEY: {
                // ...
                // 分发按键事件
                done = dispatchKeyLocked(...);
                break;
        }
        case EventEntry::TYPE_MOTION: {
                // ...
                // 分发触点事件
                done = dispatchMotionLocked(...);
                break;
        }
    }
    // ...
}

可以看出,InputDispatcherThread线程中死循环运行dispatchOnce(),每一次都从mInboundQueue队列中获取头部的事件,并执行一次事件分发。每分发一次事件后都会调用mLooper->pollOnce()等待一段时间。等待的过程中,可能被InputReaderThread线程中执行的mLooper.wake()提前唤醒,从而触发下一次事件分发。


到这里,我们就完整分析了InputManagerService的事件采集流程,下一篇文章我们将讨论事件分发过程,欢迎关注彭旭锐的!


延伸阅读

  • [Android | 系统启动过程]
  • [Android | InputManagerService与输入事件分发]

推荐阅读

  • Android | 自定义属性
  • Android | 文件存储
  • Android | 再按一次返回键退出
  • 设计模式 | 静态代理与动态代理
  • 笔记 | 使用Markdown高效率编写文档
  • 笔记 | Android Studio极速编译
  • 笔记 | 使用Keytool管理密钥和证书

感谢喜欢!你的点赞是对我最大的鼓励!欢迎关注彭旭锐的!

你可能感兴趣的:(Android | InputManagerService与输入事件采集)