Android输入系统(二)IMS的启动过程和输入事件的处理

  • Android框架层
  • Android输入系统
  • Android框架层

本文首发于微信公众号「刘望舒」

基于Android 8.1

前言

在上一篇文章中,我们学习了IMS的诞生(创建),IMS创建后还会进行启动,这篇文章我们来学习IMS的启动过程和输入事件的处理。

1.IMS的启动过程

IMS的创建在SystemServer的startOtherServices方法中,不了解请查看Android输入系统(一)输入事件传递流程和InputManagerService的诞生这篇文章。 frameworks/base/services/java/com/android/server/SystemServer.java

1 private void startOtherServices() { 2 ... 3 traceBeginAndSlog("StartInputManagerService"); 4 inputManager = new InputManagerService(context); 5 traceEnd(); 6 ... 7 traceBeginAndSlog("StartInputManager"); 8 inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); 9 inputManager.start(); 10 traceEnd(); 11 12}

创建IMS后就会紧接着执行IMS的启动。IMS的start方法如下所示。 frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

1 public void start() { 2 Slog.i(TAG, "Starting input manager"); 3 nativeStart(mPtr); 4 // Add ourself to the Watchdog monitors. 5 Watchdog.getInstance().addMonitor(this); 6 ... 7 }

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

1static const JNINativeMethod gInputManagerMethods[] = { 2... 3 { "nativeStart", "(J)V", 4 (void*) nativeStart }, 5... 6}

nativeStart方法对应的JNI函数为nativeStart: frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

1static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { 2 NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); 3 status_t result = im->getInputManager()->start();//1 4 if (result) { 5 jniThrowRuntimeException(env, "Input manager could not be started."); 6 } 7}

用reinterpret_cast操作符将jlong类型的ptr强制转换为原类型(NativeInputManager指针类型)。注释1处会调用InputManager的start函数。 frameworks/native/services/inputflinger/InputManager.cpp

1status_t InputManager::start() { 2 status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); 3 if (result) { 4 ALOGE("Could not start InputDispatcher thread due to error %d.", result); 5 return result; 6 } 7 result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); 8 if (result) { 9 ALOGE("Could not start InputReader thread due to error %d.", result); 10 mDispatcherThread->requestExit(); 11 return result; 12 } 13 return OK; 14}

可以看到InputManager的start函数运行了InputReaderThread和InputDispatcherThread,这两个线程在Android输入系统(一)输入事件传递流程和InputManagerService的诞生提到过,它们在InputManager的构造函数中被创建,其中InputReaderThread中运行了InputReader, InputDispatcherThread中运行了InputDispatcher。

2.InputDispatcher的启动过程

先来回顾下InputDispatcher和InputReader是在哪创建的,InputManager的构造函数如下所示。 frameworks/native/services/inputflinger/InputManager.cpp

1InputManager::InputManager( 2 const sp<EventHubInterface>& eventHub, 3 const sp<InputReaderPolicyInterface>& readerPolicy, 4 const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { 5 mDispatcher = new InputDispatcher(dispatcherPolicy); 6 mReader = new InputReader(eventHub, readerPolicy, mDispatcher); 7 initialize(); 8}

可以看到InputDispatcher和InputReader是有关联的,InputDispatcher会作为一个参数传入到InputReader中。 InputDispatcher是在InputReader之前创建的,这个顺序不能改变,因为要确保InputReader将加工后的输入事件交给InputDispatcher时,InputDispatcher已经被创建。 InputDispatcher的定义如下所示。 frameworks/native/services/inputflinger/InputDispatcher.h

1class InputDispatcherThread : public Thread { 2public: 3 explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher); 4 ~InputDispatcherThread(); 5private: 6 virtual bool threadLoop(); 7 sp<InputDispatcherInterface> mDispatcher; 8}; 9}

InputDispatcher.h中定义了threadLoop纯虚函数,InputDispatcher继承了Thread。native的Thread内部有一个循环,当线程运行时,会调用threadLoop函数,如果它返回true并且没有调用requestExit函数,就会接着循环调用threadLoop函数。 查看InputDispatcherThread的threadLoop函数是如何实现的。 frameworks/native/services/inputflinger/InputDispatcher.cpp

1bool InputDispatcherThread::threadLoop() { 2 mDispatcher->dispatchOnce(); 3 return true; 4}

threadLoop函数中只调用了InputDispatcher的dispatchOnce函数: frameworks/native/services/inputflinger/InputDispatcher.cpp

1void InputDispatcher::dispatchOnce() { 2 nsecs_t nextWakeupTime = LONG_LONG_MAX; 3 { // acquire lock 4 AutoMutex _l(mLock); 5 mDispatcherIsAliveCondition.broadcast(); 6 if (!haveCommandsLocked()) {//1 7 dispatchOnceInnerLocked(&nextWakeupTime);//2 8 } 9 if (runCommandsLockedInterruptible()) { 10 nextWakeupTime = LONG_LONG_MIN; 11 } 12 } // release lock 13 nsecs_t currentTime = now();//3 14 int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//4 15 mLooper->pollOnce(timeoutMillis); 16}

注释1处用于检查InputDispatcher的缓存队列中是否有等待处理 的命令,如果没有就会执行注释2处的dispatchOnceInnerLocked函数,用来将输入事件分发给合适的Window。注释3处获取当前的时间,结合注释4处,得出InputDispatcherThread需要睡眠的时间为timeoutMillis。最后调用Looper的pollOnce函数使InputDispatcherThread进入睡眠状态,并将它的最长的睡眠的时间设置为timeoutMillis。当有输入事件产生时,InputReader就会将睡眠状态的InputDispatcher 唤醒,InputDispatcher会重新开始分发输入事件。 那么InputReader是如何唤醒InputDispatcherThread的呢? 我们接着往下看。

3.InputReader处理事件过程

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

1bool InputReaderThread::threadLoop() { 2 mReader->loopOnce(); 3 return true; 4}

threadLoop函数中只调用了InputReader的loopOnce函数: frameworks/native/services/inputflinger/InputReader.cpp

1void InputReader::loopOnce() { 2 ... 3 //通过EventHub的getEvents函数获取事件信息存在mEventBuffer中 4 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1 5 { // acquire lock 6 AutoMutex _l(mLock); 7 mReaderIsAliveCondition.broadcast(); 8 if (count) { 9 //如果有事件信息,调用processEventsLocked函数对事件进行加工处理 10 processEventsLocked(mEventBuffer, count);//2 11 } 12 ... 13}

注释1处调用EventHub的getEvents函数来获取设备节点的事件信息到mEventBuffer中,事件信息主要有两种,一种是设备节点的增删事件(设备事件),一种是原始输入事件。注释2处的processEventsLocked函数用于对mEventBuffer中的原始输入事件信息进行加工处理,加工后的输入事件会交由InputDispatcher来处理,processEventsLocked函数如下所示。 frameworks/native/services/inputflinger/InputReader.cpp

1void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { 2 //遍历所有的事件 3 for (const RawEvent* rawEvent = rawEvents; count;) { 4 int32_t type = rawEvent->type; 5 size_t batchSize = 1; 6 //事件类型分为原始输入事件和设备事件,这个条件语句对原始输入事件进行处理 7 if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { 8 int32_t deviceId = rawEvent->deviceId; 9 while (batchSize < count) { 10 if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT 11 || rawEvent[batchSize].deviceId != deviceId) { 12 break; 13 } 14 batchSize += 1; 15 } 16#if DEBUG_RAW_EVENTS 17 ALOGD("BatchSize: %d Count: %d", batchSize, count); 18#endif 19 //处理deviceId所对应的设备的原始输入事件 20 processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//1 21 } else { 22 //对设备事件进行处理 23 switch (rawEvent->type) { 24 case EventHubInterface::DEVICE_ADDED: 25 addDeviceLocked(rawEvent->when, rawEvent->deviceId); 26 break; 27 case EventHubInterface::DEVICE_REMOVED: 28 removeDeviceLocked(rawEvent->when, rawEvent->deviceId); 29 break; 30 case EventHubInterface::FINISHED_DEVICE_SCAN: 31 handleConfigurationChangedLocked(rawEvent->when); 32 break; 33 default: 34 ALOG_ASSERT(false); // can't happen 35 break; 36 } 37 } 38 count -= batchSize; 39 rawEvent += batchSize; 40 } 41}

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

1void InputReader::processEventsForDeviceLocked(int32_t deviceId, 2 const RawEvent* rawEvents, size_t count) { 3 ssize_t deviceIndex = mDevices.indexOfKey(deviceId);//1 4 if (deviceIndex < 0) { 5 ALOGW("Discarding event for unknown deviceId %d.", deviceId); 6 return; 7 } 8 InputDevice* device = mDevices.valueAt(deviceIndex);//2 9 if (device->isIgnored()) { 10 //ALOGD("Discarding event for ignored deviceId %d.", deviceId); 11 return; 12 } 13 device->process(rawEvents, count); 14}

注释1处根据deviceId从mDevices中获取对应的deviceIndex,注释2处再根据这个deviceIndex从mDevices中获取对应的InputDevice。最后会调用InputDevice的process函数: frameworks/native/services/inputflinger/InputReader.cpp

1void InputDevice::process(const RawEvent* rawEvents, size_t count) {* 2 size_t numMappers = mMappers.size(); 3 //遍历处理该InputDevice所有的事件 4 for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { 5#if DEBUG_RAW_EVENTS 6 ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld", 7 rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, 8 rawEvent->when); 9#endif 10 //mDropUntilNextSync的值默认为false,如果设备的输入事件缓冲区溢出,这个值会置为true。 11 if (mDropUntilNextSync) { 12 ... 13 } else { 14 for (size_t i = 0; i < numMappers; i++) {//1 15 InputMapper* mapper = mMappers[i]; 16 mapper->process(rawEvent);//2 17 } 18 } 19 } 20}

首先会遍历InputDevice中的所有的事件,真正加工原始输入事件的是InputMapper对象,由于原始输入事件的类型很多,因此在InputMapper有很多子类,用于加工不同的原始输入事件,比如KeyboardInputMapper用于处理键盘输入事件,TouchInputMapper用于处理触摸输入事件。 注释1处遍历所有的InputMapper,在注释2处将原始输入事件交由这些InputMapper来处理,至于是哪个InputMapper来处理,InputReader并不关心。 这里就以处理键盘输入事件为例,KeyboardInputMapper的process函数如下所示。 frameworks/native/services/inputflinger/InputReader.cpp

1void KeyboardInputMapper::process(const RawEvent* rawEvent) { 2 switch (rawEvent->type) { 3 case EV_KEY: {//1 4 int32_t scanCode = rawEvent->code; 5 int32_t usageCode = mCurrentHidUsage; 6 mCurrentHidUsage = 0; 7 if (isKeyboardOrGamepadKey(scanCode)) { 8 processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);//2 9 } 10 break; 11 } 12 ... 13 } 14}

注释1处,如果事件的类型为按键类型的事件,就会调用注释2处的KeyboardInputMapper的processKey函数。 frameworks/native/services/inputflinger/InputReader.cpp

1void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, 2 int32_t usageCode) { 3 ... 4 NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags, 5 down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 6 AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); 7 getListener()->notifyKey(&args);//1 8}

processKey函数会将加工后的键盘输入事件封装为NotifyKeyArgs,将NotifyKeyArgs通知给InputListenerInterface。 InputDispatcher继承了InputDispatcherInterface,而InputDispatcherInterface继承了InputListenerInterface,因此注释1处实际上是调用了InputDispatcher的notifyKey函数,将NotifyKeyArgs交给InputDispatcher处理。 frameworks/native/services/inputflinger/InputDispatcher.cpp

1void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 2 ... 3 bool needWake; 4 { // acquire lock 5 mLock.lock(); 6 if (shouldSendKeyToInputFilterLocked(args)) { 7 mLock.unlock(); 8 policyFlags |= POLICY_FLAG_FILTERED; 9 if (!mPolicy->filterInputEvent(&event, policyFlags)) { 10 return; // event was consumed by the filter 11 } 12 mLock.lock(); 13 } 14 int32_t repeatCount = 0; 15 KeyEntry* newEntry = new KeyEntry(args->eventTime, 16 args->deviceId, args->source, policyFlags, 17 args->action, flags, keyCode, args->scanCode, 18 metaState, repeatCount, args->downTime);//1 19 needWake = enqueueInboundEventLocked(newEntry);//2 20 mLock.unlock(); 21 } // release lock 22 if (needWake) { 23 mLooper->wake(); 24 } 25}

代码块中采用Mutex互斥锁的形式,在注释1处根据NotifyKeyArgs,重新封装一个KeyEntry对象,代表一次按键数据。注释2处根据KeyEntry,来判断是否需要将睡眠中的InputDispatcherThread唤醒,如果需要,就调用Looper的wake函数进行唤醒,InputDispatcherThread被唤醒后就会重新对输入事件的分发,具体的回头查看第2小节。

总结

本文涉及到了四个关键的类,分别是IMS、EventHub、InputDispatcher和InputReader,它们做了如下的工作:

  1. IMS启动了InputDispatcherThread和InputReaderThread,分别用来运行InputDispatcher和InputReader。
  2. InputDispatcher先于InputReader被创建,InputDispatcher的dispatchOnceInnerLocked函数用来将事件分发给合适的Window。InputDispatcher没有输入事件处理时会进入睡眠状态,等待InputReader通知唤醒。
  3. InputReader通过EventHub的getEvents函数获取事件信息,如果是原始输入事件,就将这些原始输入事件交由不同的InputMapper来处理,最终交由InputDispatcher来进行分发。
  4. InputDispatcher的notifyKey函数中会根据按键数据来判断InputDispatcher是否要被唤醒,InputDispatcher被唤醒后,会重新调用dispatchOnceInnerLocked函数将输入事件分发给合适的Window。

点赞
收藏

评论区

加载中...

相关推荐

Android系统启动流程(四)Launcher启动过程与系统启动流程

Android框架层Android系统启动categories:Android框架层本文首发于微信公众号「刘望舒」前言此前的文章我们学习了init进程、Zygote进程和SyetemServer进程的启动过程,这一篇文章我们就来学习Android系统启动流程的最后一步:Launcher的启动流程,并结合本系列的前三

Android系统启动流程(三)解析SyetemServer进程启动过程

上一篇我们学习了Zygote进程,并且知道Zygote进程启动了SyetemServer进程,那么这一篇我们就来学习Android7.0版本的SyetemServer进程的启动过程。"tag:Android框架层Android系统启动categories:Android框架层本文首发于微信公众号「刘望舒」前言上一

Android解析WindowManager(三)Window的添加过程

Android框架层Android系统服务WindowManagercategories:Android框架层本文首发于微信公众号「刘望舒」前言在此前的系列文章中我们学习了WindowManager体系和Window的属性,这一篇我们接着来讲Window的添加过程。建议阅读此篇文章前先阅读本系列的前两篇文章。<!more1.概述WindowMana

Android系统启动流程(二)解析Zygote进程启动过程

上一篇文章我们分析了init进程的启动过程,启动过程中主要做了三件事,其中一件就是创建了Zygote进程,那么Zygote进程是什么,它做了哪些事呢?这篇文章会给你这些问题的答案。"tag:Android框架层Android系统启动categories:Android框架层本文首发于微信公众号「刘望舒」前言上一篇

Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程

Android框架层Android输入系统Android框架层本文首发于微信公众号「刘望舒」前言在上一篇文章中,我们学习了输入事件的处理,输入事件会交由InputDispatcher进行分发,那么InputDispatcher是如何进行分发的?这篇文章会给你答案。1.InputReader的加工类型在这篇文章中,我们知道InputReader会对原始

Android包管理机制(三)PMS处理APK的安装

Android框架层Android包管理机制Android框架层本文首发于微信公众号「刘望舒」前言在上一篇文章中,我们学习了PackageInstaller是如何安装APK的,最后会将APK的信息交由PMS处理。那么PMS是如何处理的呢?这篇文章会给你答案。1.PackageHandler处理安装消息APK的信息交由PMS后,PMS通过向Packag