前言
InputDispatcher中派发input事件时会打印tag为input_interaction的log,这句log主要包含了一个connection列表,有助于理解当前InputDispatcher把input事件究竟派发给了谁。一般情况下,这个connection列表中会包含若干个InputMonitor、一个或者多个窗口。对于touch事件,首先会通过findTouchedWindowTargetsLocked方法来确定一个主要的目标派发窗口,这是必需的一个窗口且必然会出现在connection列表中,用于连续接收touch事件并能对用户操作做出响应。当满足一些额外条件时,connection列表中也可能会出现多个窗口。比如,在Launcher界面点击时,connection列表中会包含Launcher、Wallaper和NavigationBar三个窗口,但只有Launcher是通过findTouchedWindowTargetsLocked方法来确定的。而findTouchedWindowTargetsLocked方法是通过从mWindowHandlesByDisplay获取到对应dispalyId的窗口列表并按照顺序从前到后去遍历确认的目标窗口。当排在前面的窗口被遍历到并被确认为目标派发窗口时,目标派发窗口的寻找流程到此结束,不会再遍历后面的窗口了。
举一个例子,当窗口列表中同时包含NotificationShade和Incallui这两个窗口时,由于NotificationShade为系统窗口、其z-order会更大,因此会先被遍历,如果此时NotificationShade被确认为目标派发窗口,那么排在后面的Incallui窗口就失去了机会,input事件将会派发给NotificationShade而非Incallui,此时可能会出现Incallui界面点击、滑动无响应的情况。因此,InputDispatcher中的窗口列表会影响到后续目标派发窗口的确认。
那么,InputDispatcher中的窗口列表是如何确认的呢?
SurfaceFlinger更新窗口信息
上层WMS中的窗口信息是会同步到SurfaceFlinger侧的,每次vSync信号来临时都会调用 updateInputFlinger方法,若满足一定条件则会根据上层WMS传来的信息来更新SurfaceFlinger侧的窗口信息。因此,底层InputDispatcher中的窗口列表信息是从SurfaceFlinger处更新而来的。
SurfaceFlinger中通过调用buildWindowInfo来更新Layer信息并构建新的窗口列表,然后通过WindowInfosListener的windowInfosChanged方法将新的窗口列表信息同步至InputDispatcher中。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp 3212 void SurfaceFlinger::updateInputFlinger() { ... 3218 std::vector<WindowInfo> windowInfos; 3219 std::vector<DisplayInfo> displayInfos; 3220 bool updateWindowInfo = false; 3221 if (mVisibleRegionsDirty || mInputInfoChanged) { 3222 mInputInfoChanged = false; 3223 updateWindowInfo = true; >>构建窗口列表信息 3224 buildWindowInfos(windowInfos, displayInfos); 3225 } ... 3229 BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo, 3230 windowInfos = std::move(windowInfos), 3231 displayInfos = std::move(displayInfos), 3232 inputWindowCommands = 3233 std::move(mInputWindowCommands), 3234 inputFlinger = mInputFlinger, this]() { 3235 ATRACE_NAME("BackgroundExecutor::updateInputFlinger"); 3236 if (updateWindowInfo) { >>通知InputDispatcher更新窗口列表信息 3237 mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, 3238 inputWindowCommands.syncInputWindows); 3239 } else if (inputWindowCommands.syncInputWindows) {
需要出现在InputDispatcher的窗口列表中,在SurfaceFlinger这里需要满足两个条件:一是对应Layer存在于SurfaceFlinger所维护的Layer列表中,二是对应Layer需要满足needsInputInfo条件。对于第一个条件,当Layer对应Surface没有被上层destroy时,才会存在于SurfaceFlinger的Layer列表中。对于第二个条件,需要对应窗口的inputChannelToken建立且没有被移除。
3281 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, 3282 std::vector<DisplayInfo>& outDisplayInfos) { 3283 ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos; ... >>遍历Layer列表 3310 mDrawingState.traverseInReverseZOrder([&](Layer* layer) { >>如果对应Layer不满足needsInputInfo条件,则return 3311 if (!layer->needsInputInfo()) return; 3312 3313 // Do not create WindowInfos for windows on displays that cannot receive input. 3314 if (const auto opt = displayInputInfos.get(layer->getLayerStack())) { 3315 const auto& info = opt->get(); >>先通过fillInputinfo填充窗口信息至对应Layer >>再将返回的WindowInfo放入outWindowInfos列表中 3316 outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure)); 3317 } 3318 });
/frameworks/native/services/surfaceflinger/Layer.h >>返回的是hasInputInfo的执行结果 608 virtual bool needsInputInfo() const { return hasInputInfo(); } /frameworks/native/services/surfaceflinger/Layer.cpp 2446 bool Layer::hasInputInfo() const { >>当对应inputChannelToken不为null时返回true 2447 return mDrawingState.inputInfo.token != nullptr || 2448 mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); 2449 }
上层销毁Surface时机
上层移除Surface的时机一般为对应窗口退出动画执行完毕时,由relayoutWindow流程发起。对应代码流程为:WMS.relayoutWindow->tryStartExitingAnimation->WindowState.destroySurface->destroySurfaceUnchecked->WindowStateAnimator.destroySurfaceLocked->destroySurface->WindowSurfaceController.destroy->SurfaceControl.Transaction.remove。
上层移除token时机
上层会将相关信息填充至InputWindowHandle,然后再通过Transaction将信息同步至SurfaceFlinger侧。因此,WMS最终会通过InputWindowHandleWrapper.setToken方法将inputChannelToken给设置为null。
frameworks/base/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java 101 void setToken(IBinder token) { 102 if (mHandle.token == token) { 103 return; 104 } >>给InputWindowHandle设置token 105 mHandle.token = token; 106 mChanged = true; 107 }
643 @VisibleForTesting 644 static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc, 645 InputWindowHandleWrapper inputWindowHandle) { 646 if (DEBUG_INPUT) { 647 Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle); 648 } >>当窗口信息发生变化时,调用applyChangesToSurface方法去更新 649 if (inputWindowHandle.isChanged()) { 650 inputWindowHandle.applyChangesToSurface(t, sc); 651 } 652 }
63 void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) { >>通过Transation将窗口信息更新至SurfaceFlinger 64 t.setInputWindowInfo(sc, mHandle); 65 mChanged = false; 66 }
同步窗口信息至InputDispatcher
前面提到SurfaceFlinger在vSync信号到来时会更新Layer信息(假如有必要的话),同时构建临时窗口列表并将其通过WindowInfosListener通知更新至InputDispatcher。具体流程如下:
注册WindowInfosListener
InputDispatcher在初始化时就会去向SurfaceComposerClient注册WindowInfosListener,便于后面在窗口信息发生变化时InputDispatcher能够同步接收到更新后的窗口列表信息。注册listener的相关代码流程为:SurfaceComposerClient.addwindowInfosListener->WindowInfosListenerReporter.addwindowInfosListener
->SurfaceFlinger.addWindowInfosListener(向SurfaceFlinger注册WindowInfosListenerReporter)->WindowInfosListenerInvoker.addWindowInfosListener。可以看到,InputDispatcher其实是通过SurfaceComposerClient最终向WindowInfosListenerReporter去注册WindowInfosListener类型的listener,而WindowInfosListenerReporter会向SurfaceFlinger注册自己。
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
530 // --- InputDispatcher --- 531 532 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) 533 : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {} 534 535 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy, 536 std::chrono::nanoseconds staleEventTimeout) 537 : mPolicy(policy), 538 mPendingEvent(nullptr), 539 mLastDropReason(DropReason::NOT_DROPPED), 540 mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), ... >>初始化mWindowInfoListener 561 mWindowInfoListener = new DispatcherWindowListener(*this); >>向SurfaceComposerClient注册mWindowInfoListener 562 SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); 563 564 mKeyRepeatState.lastKeyEntry = nullptr; 565 566 policy->getDispatcherConfiguration(&mConfig); 567 }
回调WindowInfosListener
当窗口信息变化时,Surfaceflinger会回调前面所注册listener的onWindowInfosChanged方法并最终通知到InputDispatcher,相关代码流程如下:SurfaceFlinger.updateInputFlinger-> WindowInfosListenerInvoker.windowInfosChanged->WindowInfosListenerReporter.onWindowInfosChanged
->WindowInfosListener.onWindowInfosChanged->InputDispatcher.DispatcherwindowListener.onWindowInfosChanged
->InputDispatcher.onWindowInfosChanged->setInputwindowsLocked
当InputDispatcher接收到更新后的窗口列表信息时,会打印"setInputWindows ..."字样的log,从log中可以清晰看到后面InputDispatcher在确认目标派发窗口时所要遍历的窗口列表具体信息。
4665 void InputDispatcher::setInputWindowsLocked( 4666 const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) { 4667 if (DEBUG_FOCUS) { 4668 std::string windowList; 4669 for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) { 4670 windowList += iwh->getName() + " "; 4671 } 4672 ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); 4673 }