本文首发于 刘望舒的博客
地址:
关联系列
解析 WMS 系列 http://liuwangshu.cn/tags/WindowManagerService/
深入理解 JNI 系列
输入系统系列
前言
在上一篇文章中, 我们学习了 IMS 的诞生(创建),IMS 创建后还会进行启动, 这篇文章我们来学习 IMS 的启动过程和输入事件的处理.
1.IMS 的启动过程
IMS 的创建在 SystemServer 的 startOtherServices 方法中, 不了解请查看 Android 输入系统 (一) 输入事件传递流程和 InputManagerService 的诞生这篇文章. frameworks/base/services/java/com/Android/server/SystemServer.java
- private void startOtherServices() {
- ...
- traceBeginAndSlog("StartInputManagerService");
- inputManager = new InputManagerService(context);
- traceEnd();
- ...
- traceBeginAndSlog("StartInputManager");
- inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
- inputManager.start();
- traceEnd();
- }
创建 IMS 后就会紧接着执行 IMS 的启动. IMS 的 start 方法如下所示. frameworks/base/services/core/java/com/Android/server/input/InputManagerService.java
- public void start() {
- Slog.i(TAG, "Starting input manager");
- nativeStart(mPtr);
- // Add ourself to the Watchdog monitors.
- Watchdog.getInstance().addMonitor(this);
- ...
- }
IMS 的 start 方法中, 会将自身添加到 Watchdog 中进行监控, 用于定时检测系统关键服务 (AMS 和 WMS 等) 是否可能发生死锁. nativeStart 方法对应的 JNI 层的函数是什么呢? 查看 com_android_server_input_InputManagerService 的 gInputManagerMethods 数组, 不理解 JNI 的可以查看深入理解 JNI 系列文章. frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
- static const JNINativeMethod gInputManagerMethods[] = {
- ...
- { "nativeStart", "(J)V",
- (void*) nativeStart },
- ...
- }
nativeStart 方法对应的 JNI 函数为 nativeStart: frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
- static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- status_t result = im->getInputManager()->start();//1
- if (result) {
- jniThrowRuntimeException(env, "Input manager could not be started.");
- }
- }
用 reinterpret_cast 操作符将 jlong 类型的 ptr 强制转换为原类型(NativeInputManager 指针类型). 注释 1 处会调用 InputManager 的 start 函数. frameworks/native/services/inputflinger/InputManager.cpp
- status_t InputManager::start() {
- status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
- if (result) {
- ALOGE("Could not start InputDispatcher thread due to error %d.", result);
- return result;
- }
- result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
- if (result) {
- ALOGE("Could not start InputReader thread due to error %d.", result);
- mDispatcherThread->requestExit();
- return result;
- }
- return OK;
- }
可以看到 InputManager 的 start 函数运行了 InputReaderThread 和 InputDispatcherThread, 这两个线程在 Android 输入系统 (一) 输入事件传递流程和 InputManagerService 的诞生提到过, 它们在 InputManager 的构造函数中被创建, 其中 InputReaderThread 中运行了 InputReader, InputDispatcherThread 中运行了 InputDispatcher.
2.InputDispatcher 的启动过程
先来回顾下 InputDispatcher 和 InputReader 是在哪创建的, InputManager 的构造函数如下所示. frameworks/native/services/inputflinger/InputManager.cpp
- InputManager::InputManager(
- const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
- mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- initialize();
- }
可以看到 InputDispatcher 和 InputReader 是有关联的, InputDispatcher 会作为一个参数传入到 InputReader 中. InputDispatcher 是在 InputReader 之前创建的, 这个顺序不能改变, 因为要确保 InputReader 将加工后的输入事件交给 InputDispatcher 时, InputDispatcher 已经被创建. InputDispatcher 的定义如下所示. frameworks/native/services/inputflinger/InputDispatcher.h
- class InputDispatcherThread : public Thread {
- public:
- explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
- ~InputDispatcherThread();
- private:
- virtual bool threadLoop();
- sp<InputDispatcherInterface> mDispatcher;
- };
- }
InputDispatcher.h 中定义了 threadLoop 纯虚函数, InputDispatcher 继承了 Thread.native 的 Thread 内部有一个循环, 当线程运行时, 会调用 threadLoop 函数, 如果它返回 true 并且没有调用 requestExit 函数, 就会接着循环调用 threadLoop 函数. 查看 InputDispatcherThread 的 threadLoop 函数是如何实现的. frameworks/native/services/inputflinger/InputDispatcher.cpp
- bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
- }
threadLoop 函数中只调用了 InputDispatcher 的 dispatchOnce 函数: frameworks/native/services/inputflinger/InputDispatcher.cpp
- void InputDispatcher::dispatchOnce() {
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- mDispatcherIsAliveCondition.broadcast();
- if (!haveCommandsLocked()) {//1
- dispatchOnceInnerLocked(&nextWakeupTime);//2
- }
- if (runCommandsLockedInterruptible()) {
- nextWakeupTime = LONG_LONG_MIN;
- }
- } // release lock
- nsecs_t currentTime = now();//3
- int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//4
- mLooper->pollOnce(timeoutMillis);
- }
注释 1 处用于检查 InputDispatcher 的缓存队列中是否有等待处理 的命令, 如果没有就会执行注释 2 处的 dispatchOnceInnerLocked 函数, 用来将输入事件分发给合适的 Windows. 注释 3 处获取当前的时间, 结合注释 4 处, 得出 InputDispatcher 需要睡眠的时间为 timeoutMillis. 最后调用 Looper 的 pollOnce 函数使 InputDispatcher 进入睡眠状态, 并将它的最长的睡眠的时间设置为 timeoutMillis. 当有输入事件产生时, InputReader 就会将睡眠状态的 InputDispatcher 唤醒, InputDispatcher 会重新开始分发输入事件. 那么 InputReader 是如何唤醒 InputDispatcher 的呢? 我们接着往下看.
3.InputReader 处理事件过程
InputReader 是在 InputReaderThread 中启动的, InputReaderThread 和 InputDispatcherThread 的定义是类似的, 也是继承了 Thread 并定义了 threadLoop 纯虚函数. 如果处理的事件为键盘输入事件, 则调用时序图如下所示.
InputReaderThread 的 threadLoop 函数如下所示. frameworks/native/services/inputflinger/InputReader.cpp
- bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
- }
threadLoop 函数中只调用了 InputReader 的 loopOnce 函数: frameworks/native/services/inputflinger/InputReader.cpp
- void InputReader::loopOnce() {
- ...
- // 通过 EventHub 的 getEvents 函数获取事件信息存在 mEventBuffer 中
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1
- { // acquire lock
- AutoMutex _l(mLock);
- mReaderIsAliveCondition.broadcast();
- if (count) {
- // 如果有事件信息, 调用 processEventsLocked 函数对事件进行加工处理
- processEventsLocked(mEventBuffer, count);//2
- }
- ...
- }
注释 1 处调用 EventHub 的 getEvents 函数来获取设备节点的事件信息到 mEventBuffer 中, 事件信息主要有两种, 一种是设备节点的增删事件(设备事件), 一种是原始输入事件. 注释 2 处的 processEventsLocked 函数用于对 mEventBuffer 中的原始输入事件信息进行加工处理, 加工后的输入事件会交由 InputDispatcher 来处理, processEventsLocked 函数如下所示. 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;
- while (batchSize <count) {
- if (rawEvent[batchSize].type>= EventHubInterface::FIRST_SYNTHETIC_EVENT
- || rawEvent[batchSize].deviceId != deviceId) {
- break;
- }
- batchSize += 1;
- }
- #if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %d Count: %d", batchSize, count);
- #endif
- // 处理 deviceId 所对应的设备的原始输入事件
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//1
- } 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;
- default:
- ALOG_ASSERT(false); // can't happen
- break;
- }
- }
- count -= batchSize;
- rawEvent += batchSize;
- }
- }
InputReader 的 processEventsLocked 函数首先遍历了所有的事件, 这些事件用 RawEvent 对象来表示, 将原始 输入事件和设备事件分开处理, 其中设备事件分为 DEVICE_ADDED,DEVICE_REMOVED 和 FINISHED_DEVICE_SCAN, 这些事件是在 EventHub 的 getEvent 函数中生成的. 如果是 DEVICE_ADDED 事件(设备添加事件),InputReader 会新建 InputDevice 对象, 用来存储设备信息, 并且会将 InputDevice 存储在 KeyedVector 类型的容器 mDevices 中. 同一个设备的输入事件交给 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);//1
- if (deviceIndex <0) {
- ALOGW("Discarding event for unknown deviceId %d.", deviceId);
- return;
- }
- InputDevice* device = mDevices.valueAt(deviceIndex);//2
- if (device->isIgnored()) {
- //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
- return;
- }
- device->process(rawEvents, count);
- }
注释 1 处根据 deviceId 从 mDevices 中获取对应的 deviceIndex, 注释 2 处再根据这个 deviceIndex 从 mDevices 中获取对应的 InputDevice. 最后会调用 InputDevice 的 process 函数: frameworks/native/services/inputflinger/InputReader.cpp
- void InputDevice::process(const RawEvent* rawEvents, size_t count) {*
- size_t numMappers = mMappers.size();
- // 遍历处理该 InputDevice 所有的事件
- for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
- #if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
- rawEvent->when);
- #endif
- //mDropUntilNextSync 的值默认为 false, 如果设备的输入事件缓冲区溢出, 这个值会置为 true.
- if (mDropUntilNextSync) {
- ...
- } else {
- for (size_t i = 0; i <numMappers; i++) {//1
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);//2
- }
- }
- }
- }
首先会遍历 InputDevice 中的所有的事件, 真正加工原始输入事件的是 InputMapper 对象, 由于原始输入事件的类型很多, 因此在 InputMapper 有很多子类, 用于加工不同的原始输入事件, 比如 KeyboardInputMapper 用于处理键盘输入事件, TouchInputMapper 用于处理触摸输入事件. 注释 1 处遍历所有的 InputMapper, 在注释 2 处将原始输入事件交由这些 InputMapper 来处理, 至于是哪个 InputMapper 来处理, InputReader 并不关心. 这里就以处理键盘输入事件为例, KeyboardInputMapper 的 process 函数如下所示. frameworks/native/services/inputflinger/InputReader.cpp
- void KeyboardInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_KEY: {//1
- int32_t scanCode = rawEvent->code;
- int32_t usageCode = mCurrentHidUsage;
- mCurrentHidUsage = 0;
- if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);//2
- }
- break;
- }
- ...
- }
- }
注释 1 处, 如果事件的类型为按键类型的事件, 就会调用注释 2 处的 KeyboardInputMapper 的 processKey 函数. frameworks/native/services/inputflinger/InputReader.cpp
- void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
- int32_t usageCode) {
- ...
- NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
- getListener()->notifyKey(&args);//1
- }
processKey 函数会将加工后的键盘输入事件封装为 NotifyKeyArgs, 将 NotifyKeyArgs 通知给 InputListenerInterface. InputDispatcher 继承了 InputDispatcherInterface, 而 InputDispatcherInterface 继承了 InputListenerInterface, 因此注释 1 处实际上是调用了 InputDispatcher 的 notifyKey 函数, 将 NotifyKeyArgs 交给 InputDispatcher 处理. frameworks/native/services/inputflinger/InputDispatcher.cpp
- void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
- ...
- bool needWake;
- { // acquire lock
- mLock.lock();
- if (shouldSendKeyToInputFilterLocked(args)) {
- mLock.unlock();
- policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
- return; // event was consumed by the filter
- }
- mLock.lock();
- }
- int32_t repeatCount = 0;
- KeyEntry* newEntry = new KeyEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
- args->action, flags, keyCode, args->scanCode,
- metaState, repeatCount, args->downTime);//1
- needWake = enqueueInboundEventLocked(newEntry);//2
- mLock.unlock();
- } // release lock
- if (needWake) {
- mLooper->wake();
- }
- }
代码块中采用 Mutex 互斥锁的形式, 在注释 1 处根据 NotifyKeyArgs, 重新封装一个 KeyEntry 对象, 代表一次按键数据. 注释 2 处根据 KeyEntry, 来判断是否需要将睡眠中的 InputDispatcher 唤醒, 如果需要, 就调用 Looper 的 wake 函数进行唤醒, InputDispatcher 被唤醒后就会重新对输入事件的分发, 具体的回头查看第 2 小节.
总结
本文涉及到了四个关键的类, 分别是 IMS,EventHub,InputDispatcher 和 InputReader, 它们做了如下的工作:
IMS 启动了 InputDispatcherThread 和 InputReaderThread, 分别用来运行 InputDispatcher 和 InputReader.
InputDispatcher 先于 InputReader 被创建, InputDispatcher 的 dispatchOnceInnerLocked 函数用来将事件分发给合适的 Windows.InputDispatcher 没有输入事件处理时会进入睡眠状态, 等待 InputReader 通知唤醒.
InputReader 通过 EventHub 的 getEvents 函数获取事件信息, 如果是原始输入事件, 就将这些原始输入事件交由不同的 InputMapper 来处理, 最终交由 InputDispatcher 来进行分发.
InputDispatcher 的 notifyKey 函数中会根据按键数据来判断 InputDispatcher 是否要被唤醒, InputDispatcher 被唤醒后, 会重新调用 dispatchOnceInnerLocked 函数将输入事件分发给合适的 Windows.
来源: https://juejin.im/post/5bf41fac5188257a5a24df1d