InputDispatcher中窗口列表的更新逻辑

前言

        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      }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值