前言和概述
Android的输入设备,最常用的就是 触摸屏和按键 了。当然还有其他方式,比如游戏手柄,比如支持OTG设备,则可以链接鼠标、键盘等。
那么这些设备的操作 是如何传递到系统 并 控制界面的呢?系统如何知道是如何知道点击了某个界面按钮,按了某个按键,知道交给哪个应用处理的?
该篇主要介绍这些,即 输入事件从生成(存于设备节点中) 传递到目标View的过程。 在进入输入事件传递机制之前,首先了解一个东西---设备节点。
了解设备节点
当有输入事件时,Linux内核会将事件数据写入 设备节点 中,供上层读取最终传递到具体的View中。 该备节点 位于/dev/input/。
1. 查看:输入事件相关设备信息
与事件相关的设备信息位于:/proc/bus/input/devices。
下面是一部分,大致了解下。Name对应的Handlers注意下。
//查看所有事件相关设备信息$ adb shell cat /proc/bus/input/devices I: Bus=0019 Vendor=0000 Product=0000 Version=0000 N: Name="ACCDET" P: Phys= S: Sysfs=/devices/virtual/input/input0 U: Uniq= H: Handlers=event0 B: PROP=0 B: EV=23 B: KEY=40 0 0 0 0 0 0 0 0 0 0 0 0 10 0 c0000 0 0 0 B: SW=d4 I: Bus=0019 Vendor=2454 Product=6500 Version=0010 N: Name="mtk-kpd" P: Phys= S: Sysfs=/devices/platform/10010000.kp/input/input1 U: Uniq= H: Handlers=event1 B: PROP=0 B: EV=3 B: KEY=1000000 0 0 0 0 0 0 0 0 1c0000 0 0 0 ...
2. 命令:getevent 和 sendenvent
2.1 getevent
通过设备的getevent命令,可以查看输入事件的信息。
//获取输入事件,这里是按了下电源键$ adb shell getevent add device 1: /dev/input/event0 name: "ACCDET" add device 2: /dev/input/event2 name: "sf-keys" add device 3: /dev/input/event3 name: "mtk-tpd" add device 4: /dev/input/event1 name: "mtk-kpd" /dev/input/event1: 0001 0074 00000001 /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0001 0074 00000000 /dev/input/event1: 0000 0000 00000000//从/proc/bus/input/devices获取到要关注的设备的节点,可以单独获取//下面是获取也是按的电源键获取到的$ adb shell getevent /dev/input/event1 0001 0074 00000001 0000 0000 00000000 0001 0074 00000000 0000 0000 00000000//多了解参数,这个-l就很清楚了//-l: label event types and names in plain text$ adb shell getevent -l /dev/input/event1 EV_KEY KEY_POWER DOWN EV_SYN SYN_REPORT 00000000 EV_KEY KEY_POWER UP EV_SYN SYN_REPORT 00000000
上面列出的3种,第一种没有参数 获取所有输入事件。加上 -l参数 的结果就很清晰了。
事件类型: 0001 即 EV_KEY,按键
事件代码: 0074 即 KEY_POWER,电源键
事件的值: 00000001 即 DOWN,按下;00000000 即 UP,抬起。
/dev/input/event1: 0001 0074 00000001 就是 电源键按下了。
/dev/input/event1: 0001 0074 00000000 就是 电源键抬起了。
注意:这里的值 都是 16进制的。
触摸屏幕也一样:
//触摸屏幕截取/dev/input/event3: EV_ABS ABS_MT_TOUCH_MAJOR 0000001e /dev/input/event3: EV_ABS ABS_MT_TRACKING_ID 00000000 /dev/input/event3: EV_ABS ABS_MT_POSITION_X 000001b5 /dev/input/event3: EV_ABS ABS_MT_POSITION_Y 000001e1 /dev/input/event3: EV_SYN SYN_MT_REPORT 00000000 /dev/input/event3: EV_SYN SYN_REPORT 00000000
2.2 sendenvent
输入事件 设备节点也是可写的,通过sendevent可模拟用户输入。
但 sendevent 的参数是 十进制。
格式:sendevent <设备节点> <事件类型> <事件代码> <事件的值>
所以getevent中,电源按下/抬起的:事件类型即1,事件代码即116,事件的值即1/0。
//电源键按下$ adb shell sendevent /dev/input/event1 1 116 1//电源键抬起$ adb shell sendevent /dev/input/event1 1 116 0//由上述getevent个人理解,0 0 0上报后生效,同按一次电源键操作$ adb shell sendevent /dev/input/event1 0 0 0
概述
该篇也是基于Android10的代码分析。 该篇写时后期调整过几次标题编号,如果文中有参考的编号不对应,请指出。下面图片由于博客显示不是原图,可能部分不清晰,可以单独查看图片原图。
文章很长,但分的3个模块比较清晰,可以根据需要查看。
好,这里正式开始了。
下面是画的一张图,即本章的大致内容。也是方便自己查阅,主要介绍了 输入事件是如何从 设备节点中 传递到具体的View的。
整篇文章比较长,需要耐心。
若有不对,欢迎指出:
说明:
图中3个红色虚线框: 即下面输入事件传递 介绍的3部分内容。IMS中事件的读取和派发;WMS中Window获取事件和传递;View中事件的传递和处理。
图中2种颜色区域: 标识2个进程。system_server 和 应用进程。
图中红色实线箭头: 文章介绍的 事件传递的 主要过程。
图中2个红色虚线箭头: 列出了 两个比较常见的policy拦截的大致阶段 (当然不止这两个),说明了最终如何回调到PhoneWindowManager的同名方法。
输入事件的传递
输入事件的传递过程,如概述中所述,这里分成了3个部分来说明。
IMS中事件的读取和派发
WMS中Window获取事件和传递
View中事件的传递和处理
下面来具体看看。
1. IMS中事件的读取和派发
我们从IMS(InputManagerService)的创建和启动开始看。
IMS是在SystemServer的 startOtherServices() 方法中启动。(之前总结了AMS/PMS/WMS等,这里类似)
//SystemServer.javaprivate void startOtherServices() { WindowManagerService wm = null; InputManagerService inputManager = null; try { //参考1.1,创建IMS对象 inputManager = new InputManagerService(context); wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); //注册服务:"input" ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); //参考1.4,设置回调 inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); //参考1.5, 启动 inputManager.start(); } final InputManagerService inputManagerF = inputManager; mActivityManagerService.systemReady(() -> { try { if (inputManagerF != null) { inputManagerF.systemRunning(); } } }, BOOT_TIMINGS_TRACE_LOG); }
SystemServer中 关于IMS主要看3个内容:
new InputManagerService(),最后进入native 最终创建了InputManager及一系列相关内容。
inputManager.setWindowManagerCallbacks(),设置了回调,这里说明了 最终如何回调到PhoneWindowManager。
inputManager.start(),主要是IntputManager中两个线程运行起来。InputReaderThread读取和处理,InputDispatcherThread派发。
1.1 创建了InputManager
先看IMS的构造方法:
//InputManagerService.javapublic class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor { private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); public InputManagerService(Context context) { this.mContext = context; //创建了InputManagerHandler,其Looper是DisplayThead的Looper this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); //进入native,并返回了mPtr mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); LocalServices.addService(InputManagerInternal.class, new LocalService()); } private final class InputManagerHandler extends Handler { public InputManagerHandler(Looper looper) { super(looper, null, true /*async*/); } } }
看到 this.mHandler 的Looper 是 DisplayThread的Looper。 这个Looper的消息队列 作为参数 传入到 nativeInit() 方法中。
下面进入 nativeInit() 。
//com_android_server_input_InputManagerService.cppstatic const JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J", (void*) nativeInit }, }static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); ... //创建NativeInputManager NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); //system/core/libutils/RefBase.cpp查看 im->incStrong(0); //返回给IMS,IMS后续会用到。IMS保存在mPtr。 return reinterpret_cast<jlong>(im); }//com_android_server_input_InputManagerService.cpp//NativeInputManager的构造方法:NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { //创建InputManager mInputManager = new InputManager(this, this); defaultServiceManager()->addService(String16("inputflinger"), mInputManager, false); }
这里看到,nativeInit() 中创建NativeInputManager。 返回给IMS的是 reinterpret_cast<jlong>(im) ,这是某种转换,可以看作是将NativeInputManager返回给了java层。
NativeInputManager中又创建了 InputManager。接着看InputManager的创建:
//InputManager.cppInputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { //创建InputDispatcher mDispatcher = new InputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); //创建InputReader mReader = createInputReader(readerPolicy, mClassifier); //创建了两个线程 InputReaderThread和InputDispatcherThread initialize(); }void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }//InputReaderFactory.cppsp<InputReaderInterface> createInputReader( const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) { //EventHub作为参数 传入InputReader return new InputReader(new EventHub(), policy, listener); }
上述代码,可以看到,InputManager中基本都是创建操作,创建了InputDispatcher、InputClassifier、InputReader、EventHub、InputReaderThread、InputDispatcherThread。
下面会逐步看到他们的作用 以及如何运行的。 这里先简单说明下其中几个主要的部分, 先有个大致了解。
EventHub:创建InputReader时 可以看到先创建了EventHub作为参数。
EventHub 通过Linux内核的INotify与Epoll机制 监听设备,可直接访问 设备节点。通过 getEvents() 方法 读取设备节点的原始输入事件 数据。
关于 EventHub的创建 这里不讨论了,这里只需简单了解它上面一点就可以了。它涉及内核和一些机制,暂时我也还不熟悉,哈哈。
InputReader:负责输入事件的获取。在独立线程(InputReaderThread)中 循环执行,有以下几个功能:
功能1-通过 EventHub 不断 获取设备节点的 原始输入数据 。
功能2-然后 进行加工处理后 交由 InputDispatcher分派 。
功能3-它还有 管理 输入设备列表和配置 。
InputDispatcher:负责输入事件的派发。在独立线程(InputDispatcherThread)中运行,其保存有WMS的所有窗口信息。
在接收到 InputReader 的输入事件后,会在窗口信息中找到合适的 窗口 并 派发消息。
InputReaderThread、InputDispatcherThread:因为InputReader 和 InputDispatcher都是耗时操作,因此创建 单独线程 来运行他们。这就是他们运行的线程。
创建完成后,他们是如何联系 并 运行的?
这个下面从 InputReaderThread和InputDispatcherThread两个线程的运行起来 理一下就可以大致了解。
1.2 InputReaderThread的运行:InputReader读取和处理事件
这里从InputReaderThread的运行开始介绍。
关于InputReaderThread和InputDispatcherThread 是如何运行起来,如何执行的threadLoop() ,后面也介绍了,请参考1.5
1.2.1 InputReader 获取输入事件
这里从 InputReaderThread::threadLoop() 开始跟踪:
//InputReaderBase.cppbool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }//InputReader.cppvoid InputReader::loopOnce() { ... //获取事件 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { //处理输入事件,参考1.2.2 processEventsLocked(mEventBuffer, count); } ... } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked // on another thread similarly waiting to acquire the InputReader lock thereby // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. //将事件 推送给 InputDispatcher 进行处理。参考1.2.2.3 mQueuedListener->flush(); ... }
线程 运行起来后,会执行threadLoop,这里返回true,会循环执行该threadLoop方法。
threadLoop中调用 loopOnce,通过3步将消息 发送到InputDispatcher:
通过 mEventHub->getEvents()获取所有 输入事件的原始数据。 这部分该篇不讨论
通过 processEventsLocked() 处理输入事件。 参考1.2.2
通过 mQueuedListener->flush() 推送到InputDispatcher。 参考1.2.2.3
1.2.2 InputReader 处理输入事件
看下处理输入事件的方法:processEventsLocked()
//InputReader.cppvoid 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; } //处理 真正的输入事件,参考 1.2.2.2 processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { //处理 设备增加、删除、扫描更新。参考1.2.2.1 switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId); break; ... } } count -= batchSize; rawEvent += batchSize; } }
前面讲了InputReader有3个功能,这里可以看到功能2和3:对输入事件进行加工处理后 交由InputDispatcher;对输入设备 列表的管理和配置。先看功能3,在看功能2。
1.2.2.1 了解输入设备的管理
先看下功能3:对输入设备 列表的管理和配置。
这里以增加设备 为例,看下addDeviceLocked():
//InputReader.cppvoid InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { ssize_t deviceIndex = mDevices.indexOfKey(deviceId); if (deviceIndex >= 0) { ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); return; } InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); uint32_t classes = mEventHub->getDeviceClasses(deviceId); int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); //创建InputDevice InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); device->configure(when, &mConfig, 0); device->reset(when); ... //加入mDevices mDevices.add(deviceId, device); ... }//mDevices 和 InputDevice定义(截取部分)//InputReader.hclass InputReader : public InputReaderInterface { KeyedVector<int32_t, InputDevice*> mDevices; }class InputDevice { int32_t mId;//通过mId从EventHub中找到对应的输入设备 std::vector<InputMapper*> mMappers;//处理上报的事件}
这里创建了一个InputDevice,然后将其加入到mDevices。mDevices 中保存了 设备的id 以及 对应的InputDevice。
EventHub 中也有个 mDevices,保存了 设备的id 和 对应的Device信息。 如下(截取部分):
//EventHub.hclass EventHub : public EventHubInterface { KeyedVector<int32_t, Device*> mDevices; struct Device { Device* next; int fd; // may be -1 if device is closed //设备节点 的文件句柄 const int32_t id; const std::string path; const InputDeviceIdentifier identifier; //记录设备信息,设备的名称、供应商、型号等等 std::unique_ptr<TouchVideoDevice> videoDevice; uint32_t classes; std::unique_ptr<VirtualKeyMap> virtualKeyMap; KeyMap keyMap; } }
看下 创建InputDevice的过程,createDeviceLocked():
//InputReader.cppInputDevice* 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); // External devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { device->setExternal(true); } // Keyboard-like devices. ... if (keyboardSource != 0) { device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); } // 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)); } return device; }void InputDevice::addMapper(InputMapper* mapper) { mMappers.push_back(mapper); }
创建了InputDevice后,进行一些设置。值得关注的是 一个InputDevice保存了多个 InputMapper,这些InputMapper保存在mMappers。
简单理一下:InputReader添加设备,首先创建了一个InputDevice,然后加入到mDevices中。而根据设备类型,可以创建多个InputMapper,这多个InputMapper保存在InputDevice中的mMappers中。
1.2.2.2 输入事件处理
接着看功能2,对输入事件进行处理 processEventsForDeviceLocked() :
//InputReader.cppvoid InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { ssize_t deviceIndex = mDevices.indexOfKey(deviceId); //最终根据deviceId获得设备对应的InputDevice InputDevice* device = mDevices.valueAt(deviceIndex); //事件交由 对应InputDevice处理。rawEvents是一组事件,可以注意下来源。 device->process(rawEvents, count); }//InputReader.cppvoid InputDevice::process(const RawEvent* rawEvents, size_t count) { // Process all of the events in order for each mapper. // We cannot simply ask each mapper to process them in bulk because mappers may // have side-effects that must be interleaved. For example, joystick movement events and // gamepad button presses are handled by different mappers but they should be dispatched // in the order received. for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { ... if (mDropUntilNextSync) { if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { mDropUntilNextSync = false; ... } ... } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); mDropUntilNextSync = true; reset(rawEvent->when); } else { //将InputDvices对象中的mMappers依次取出来,调用process()进行处理 for (InputMapper* mapper : mMappers) { mapper->process(rawEvent); } } --count; } }
InputReader 获得某设备相关一组事件,然后找到对应InputDevice进行处理,执行 InputDevice::process() 。
InputDevice则将InputDvices对象中的mMappers依次取出来,调用process()进行处理。各个 InputMapper 对事件进行判断,若是属于自己处理的类型 再进行不同的处理。
下面 以键盘事件 为例说明,则InputMapper是KeyboardInputMapper:
//InputReader.cppvoid KeyboardInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_KEY: { int32_t scanCode = rawEvent->code; int32_t usageCode = mCurrentHidUsage; mCurrentHidUsage = 0; if (isKeyboardOrGamepadKey(scanCode)) { //处理事件,这里即处理按键的方法 processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); } break; } ... } }//InputReader.cpp//内核上报的扫描码(scanCode),转换成Android系统使用的按键码(keyCode),重构NotifyArgs,加入 mArgsQueue队列void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { int32_t keyCode; int32_t keyMetaState; uint32_t policyFlags; ... //重构args NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); //插入到 mArgsQueue 队列中 getListener()->notifyKey(&args); }
这个处理过程 主要是 封装各个参数,重新构造成 NotifyKeyArgs ,然后 将构造的 NotifyKeyArgs对象加入 mArgsQueue队列。
加入到 mArgsQueue的过程, getListener()->notifyKey(&args):
//InputReader.cppInputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); } InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : ... { mQueuedListener = new QueuedInputListener(listener); }//InputListener.cppQueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) : mInnerListener(innerListener) { }void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { mArgsQueue.push_back(new NotifyKeyArgs(*args));//push_back() 在Vector尾部插入}//InputListener.hclass QueuedInputListener : public InputListenerInterface { std::vector<NotifyArgs*> mArgsQueue; };
在1.1中 创建InputReader时已经知道(可以回去看下),InputReader中的lister是InputClassifier对象,所以 QueuedInputListener中的innerListener 也就是 InputClassifier。
到这里再理一下:事件先交由了对应的InputDevice,然后找对处理该事件类型的InputMapper 进行处理。InputMapper 将事件等信息 构造了NotifyArgs,然后加入到了mArgsQueue中。
1.2.2.3 输入事件传入到 InputDispatcher
看 InputReader::loopOnce() 的最后一句:mQueuedListener->flush();
//InputListener.cppvoid QueuedInputListener::flush() { size_t count = mArgsQueue.size(); //依次从mArgsQueue中取出NotifyArgs for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; //mInnerListener是InputClassifier,上面(1.2.2.2最后)已经特意指出 args->notify(mInnerListener); delete args; } mArgsQueue.clear(); }
如注释所说。接着看 args->notif(),接下来都是以键盘事件为例:
//NotifyArgs是所有args的超类。 以键盘为例,args即NotifyKeyArgsvoid NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyKey(this); }//InputManager.cppInputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); }//InputClassifier.cpp//mListener是InputDispatcherInputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}void InputClassifier::notifyKey(const NotifyKeyArgs* args) { // pass through mListener->notifyKey(args); }
很清楚列出了,这里的mListener即InputDispatcher。所以最终走到了 InputDispatcher::notifyKey():
//InputDispatcher.cppvoid InputDispatcher::notifyKey(const NotifyKeyArgs* args) { ... int32_t keyCode = args->keyCode; //Meta + Backspace -> generate BACK; Meta + Enter -> generate HOME accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); android::base::Timer t; //mPolicy是NativeInputManager,最终回调到PhoneWindowManager的同名方法。参考1.2.2.4 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); ... bool needWake; { // acquire lock mLock.lock(); ... KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, args->displayId, policyFlags, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); } // release lock if (needWake) { mLooper->wake(); } } bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); //entry加入到mInboundQueue mInboundQueue.enqueueAtTail(entry); ... return needWake; }
最终,输入事件 由InputReader 获取处理后,传递到InputDispatcher,封装成EventEntry并加入mInboundQueue 队列了。
1.2.2.4 事件入队前的拦截:interceptKeyBeforeQueueing()
注意上面 InputDispatcher::notifyKey 中有 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); 这句话,对PhoneWindowManager有过了解的,应该比较清楚。
这里就是 policy拦截的 比较常见的一处,从这最终回调 的是 PhoneWindowManager中的方法。
这个大致看下,这里mPolicy即 NativeInputManager ,所以直接看NativeInputManager::interceptKeyBeforeQueueing():
//com_android_server_input_InputManagerService.cppvoid NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) { ... wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeQueueing, keyEventObj, policyFlags); ... }//InputManagerService.javaprivate int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); }
这个跟踪 就是执行了IMS中的interceptKeyBeforeQueueing()方法。
最终是如何调用到 PhoneWindowManager 中的方法的?
这里的mWindowManagerCallbacks是 wms中创建的InputManagerCallback对象。这个如何来的 参考1.4。
所以 mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); 即:
//InputManagerCallback.javapublic InputManagerCallback(WindowManagerService service) { mService = service; }@Overridepublic int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); }
这里的mService.mPolicy就是PhoneWindowManager对象,在WMS创建时设置的。所以最终 回调的 PhoneWindowManager 中的 interceptKeyBeforeQueueing() 方法。 PhoneWindowManager 是 WindowManagerPolicy 的实现类。
1.3 InputDispatcherThread运行:InputDispatcher派发事件
前面讲到,输入事件 在InputDispatcher中 封装成EventEntry并加入mInboundQueue 队列了。接着看 InputDispatcher是如何继续处理 派发的。
如同InputReaderThread中介绍,这里直接看threadLoop()。
//frameworks/native/services/inputflinger/InputDispatcher.cppbool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; }void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock std::scoped_lock _l(mLock); mDispatcherIsAlive.notify_all(); // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. //若mCommandQueue为空 if (!haveCommandsLocked()) { //参考1.3.1 dispatchOnceInnerLocked(&nextWakeupTime); } // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. //参考1.3.4,运行 mCommandQueue 中命令 if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis); }//InputDispatcher.hEventEntry* mPendingEvent GUARDED_BY(mLock); Queue<EventEntry> mInboundQueue GUARDED_BY(mLock); Queue<EventEntry> mRecentQueue GUARDED_BY(mLock); Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);//InputDispatcher.cppbool InputDispatcher::haveCommandsLocked() const { return !mCommandQueue.isEmpty(); }
1.3.1 InputDispatcher 取得输入事件
1.2.2.3讲到:输入事件 由InputReader 获取处理后,加入到了InputDispatcher中的 mInboundQueue 队列了。
事件派发 首先从 mInboundQueue队列中 取出输入事件,然后进行处理。
//InputDispatcher.cppvoid InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { ... // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { if (mInboundQueue.isEmpty()) { ... } else { // Inbound queue has at least one entry. //从mInboundQueue中 出队 一个元素 mPendingEvent = mInboundQueue.dequeueAtHead(); traceInboundQueueLengthLocked(); } // Poke user activity for this event. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(mPendingEvent); } // Get ready to dispatch the event. //这里注意下,ANR相关。这里先mark下,这篇不说明 resetANRTimeoutsLocked(); } // Now we have an event to dispatch. // All events are eventually dequeued and processed this way, even if we intend to drop them. ... switch (mPendingEvent->type) { ... case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); ... //分派事件 done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } ... }
取出事件后,然后分派,这里同样以 键盘按键事件为例,直接看 dispatchKeyLocked():
1.3.2 分派事件前处理
直接看 dispatchKeyLocked():
//InputDispatcher.cppbool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ... // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { //派发给用户,参考1.3.2.1 if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { //将InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible函数指针作为参数 //执行postCommandLocked(),执行后 该函数被封装到CommandEntry 加入到 mCommandQueue队列 CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); //InputWindowHandle保存了窗口相关信息,由java层而来 //获取焦点窗口的InputWindowHandle sp<InputWindowHandle> focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); if (focusedWindowHandle != nullptr) { //InputChannel也是一种跨进程 commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); } commandEntry->keyEntry = entry; entry->refCount += 1; return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } ... // Identify targets. //参考 1.3.2.3 std::vector<InputTarget> inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); ... // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // Dispatch the key. //参考1.3.3 dispatchEvenfentLocked(currentTime, entry, inputTargets); return true; } InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { CommandEntry* commandEntry = new CommandEntry(command); mCommandQueue.enqueueAtTail(commandEntry); return commandEntry; }
1.3.2.1 事件分派前的拦截:interceptKeyBeforeDispatching()
通过postCommandLocked() 将 doInterceptKeyBeforeDispatchingLockedInterruptible 函数作为参数,封装到CommandEntry 最后加入到 mCommandQueue队列。这个函数并没有马上运行。
这个doInterceptKeyBeforeDispatchingLockedInterruptible():
//InputDispatcher.cppvoid InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; ... sp<IBinder> token = commandEntry->inputChannel != nullptr ? commandEntry->inputChannel->getToken() : nullptr; nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); ... entry->release(); }
interceptKeyBeforeDispatching() 类似1.2.2.4。最终也是 调用到PhoneWindowManager 中的同名方法。
1.3.2.2 了解InputWindowHandle和InputChannel
<360>InputWindowHandle:InputWindowHandle保存了窗口相关信息,由java层而来。
关于InputWindowHandle 知道这大概是什么,不影响此篇理解,就没有完全跟踪下去。下面个人查看的路径开始,内容挺多,也没跟踪完全。先记录下,后续再看。
//WindowManagerService.javapublic int addWindow(Session session, IWindow client, int seq, ...) { displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); }//InputMonitor.java/* Updates the cached window information provided to the input dispatcher. */void updateInputWindowsLw(boolean force) { if (!force && !mUpdateInputWindowsNeeded) { return; } scheduleUpdateInputWindows(); }
InputChannel:
InputChannel也是一种跨进程, 本质也是socket。是一对创建的。
WindowState创建了一对InputChannel。server端注册到InputDispatcher,建立了Connect。client端返回给应用进程的窗口,ViewRootImpl.setView()时传入的参数mInputChannel。
InputDispatcher向其InputChannel中写入事件,窗口就可以从InputChannel中读取了。
简单列出下相关代码:
//WindowManagerService.javapublic 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) { //outInputChannel来自 ViewRootImpl.setView()时创建的 win.openInputChannel(outInputChannel); } }//WindowState.javavoid openInputChannel(InputChannel outInputChannel) { if (mInputChannel != null) { throw new IllegalStateException("Window already has an input channel."); } String name = getName(); //创建一对InputChannel,创建过程该篇不说明。 &
发表评论 取消回复