WMS转屏流程

2019-11-22  本文已影响0人  米豆同学

WMS转屏流程

PhoneWindowManager会通过WindowOrientaionListener监听传感器数据,判断是否需要转屏,如果需要转屏,冻屏截屏,设置转屏动画,然后通知AMS更新configuration,AMS发转屏广播通知应用进程,调整Activity task ,然后调用WMS.continueSurfaceLayout这里调用WindowSurfacePlacer.performSurfacePlacement重新绘制窗口和执行窗口动画,继而像Choregrapher发送绘制动画请求,开始执行动画,执行完转屏动画后,解冻窗口

从WMS.updateRotationUnchecked开始处理转屏分析:

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

     private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
         if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
                 + " alwaysSendConfiguration=" + alwaysSendConfiguration
                 + " forceRelayout=" + forceRelayout);
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
 
         long origId = Binder.clearCallingIdentity();
 
         try {
             // TODO(multi-display): Update rotation for different displays separately.
             final boolean rotationChanged;
             final int displayId;
             synchronized (mWindowMap) {
                 final DisplayContent displayContent = getDefaultDisplayContentLocked();
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
                 //1. 开始冻屏
                 rotationChanged = displayContent.updateRotationUnchecked();
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 if (!rotationChanged || forceRelayout) {
                     displayContent.setLayoutNeeded();
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                             "updateRotation: performSurfacePlacement");
                     mWindowPlacerLocked.performSurfacePlacement();
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
                 displayId = displayContent.getDisplayId();
             }
             
             if (rotationChanged || alwaysSendConfiguration) {
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
                 //2. 给AMS发送新的Configuration信息
                 sendNewConfiguration(displayId);
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }

displayContent.updateRotationUnchecked()方法主要调用startFreezingDisplayLocked 开始冻屏

/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

   void startFreezingDisplayLocked(int exitAnim, int enterAnim,
           DisplayContent displayContent) {
       ... ...
       mScreenFrozenLock.acquire();

       mDisplayFrozen = true;
       mDisplayFreezeTime = SystemClock.elapsedRealtime();
       mLastFinishedFreezeSource = null;

       // {@link mDisplayFrozen} prevents us from freezing on multiple displays at the same time.
       // As a result, we only track the display that has initially froze the screen.
       mFrozenDisplayId = displayContent.getDisplayId();
       //1. 停止分发输入事件,mInputMonitor用于WMS和InputManagerService 通信
       mInputMonitor.freezeInputDispatchingLw();

       // Clear the last input window -- that is just used for
       // clean transitions between IMEs, and if we are freezing
       // the screen then the whole world is changing behind the scenes.
       mPolicy.setLastInputMethodWindowLw(null, null);
       //2.结束app动画
       if (mAppTransition.isTransitionSet()) {
           mAppTransition.freeze();
       }

       if (PROFILE_ORIENTATION) {
           File file = new File("/data/system/frozen");
           Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
       }

       mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
       // TODO(multidisplay): rotation on non-default displays
       if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
           mExitAnimId = exitAnim;
           mEnterAnimId = enterAnim;
           //3. 停止窗口动画
           ScreenRotationAnimation screenRotationAnimation =
                   mAnimator.getScreenRotationAnimationLocked(mFrozenDisplayId);
           if (screenRotationAnimation != null) {
               screenRotationAnimation.kill();
           }

           // Check whether the current screen contains any secure content.
           boolean isSecure = displayContent.hasSecureWindowOnScreen();

           displayContent.updateDisplayInfo();
           //4. 创建并且设置转屏动画,因为转屏动画和冻屏动画同时执行
           screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                   mPolicy.isDefaultOrientationForced(), isSecure,
                   this);
           mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                   screenRotationAnimation);
       }
   }

在ScreenRotationAnimation构造方法中创建截图Surface

   public ScreenRotationAnimation(Context context, DisplayContent displayContent,
           boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) {
       ... ...

       final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
       try {
           mSurfaceControl = displayContent.makeOverlay()
                   .setName("ScreenshotSurface")
                   .setSize(mWidth, mHeight)
                   .setSecure(isSecure)
                   .build();
            //创建个Surface并将截图显示在上面, Surface layer非常高
           // capture a screenshot into the surface we just created
           // TODO(multidisplay): we should use the proper display
           final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
           final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
           // This null check below is to guard a race condition where WMS didn't have a chance to
           // respond to display disconnection before handling rotation , that surfaceflinger may
           // return a null handle here because it doesn't think that display is valid anymore.
           if (displayHandle != null) {
               Surface sur = new Surface();
               sur.copyFrom(mSurfaceControl);
               SurfaceControl.screenshot(displayHandle, sur);
               t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
               t.setAlpha(mSurfaceControl, 0);
               t.show(mSurfaceControl);
               sur.destroy();
           } else {
               Slog.w(TAG, "Built-in display " + displayId + " is null.");
           }
       } catch (OutOfResourcesException e) {
           Slog.w(TAG, "Unable to allocate freeze surface", e);
       }

       if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
               "  FREEZE " + mSurfaceControl + ": CREATE");
        //设置旋转动画矩阵
       setRotation(t, originalRotation);
       t.apply();
   }

到这已经冻屏,停止输入事件分发,并设置了截图Surface显示在最上面,设置了转屏动画,但还没有开始执行,现在回到WindowManagerService.updateRotationUnchecked继续往下分析,通知AMS转屏

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

      private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
              boolean initLocale, boolean persistent, int userId, boolean deferResume,
              UpdateConfigurationResult result) {
          int changes = 0;
          boolean kept = true;
  
          if (mWindowManager != null) {
              //延迟绘制窗口,应该是防止调整Activity期间绘制窗口,因为调整完还有更新窗口,所以是为了优化性能
              mWindowManager.deferSurfaceLayout();
          }
          try {
              if (values != null) {
                  //AMS发转屏广播,通知应用进程,调整Activity
                  changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
                          deferResume);
              }
  
              kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
          } finally {
              if (mWindowManager != null) {
                  //最后再回到WMS中调整窗口,重新绘制窗口
                  mWindowManager.continueSurfaceLayout();
              }
          }
  
          if (result != null) {
              result.changes = changes;
              result.activityRelaunched = !kept;
          }
          return kept;
      }

WMS.continueSurfaceLayout上面说过会触发绘制动画,最后调用到WindowAnimator.animate

frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java

 /**
       * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
       * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
       * sure other threads can make progress if this happens.
       */
      private void animate(long frameTimeNs) {
  
          synchronized (mService.mWindowMap) {
              if (!mInitialized) {
                  return;
              }
  
              // Schedule next frame already such that back-pressure happens continuously
              scheduleAnimation();
          }
  
          synchronized (mService.mWindowMap) {
              mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
              //WindowSurfacePlacer.performSurfacePlacement中会根据这个值调用stopFreezingDisplayLocked解冻
              mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
              mAnimating = false;
              if (DEBUG_WINDOW_TRACE) {
                  Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
              }
  
              if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
              mService.openSurfaceTransaction();
              try {
                  final AccessibilityController accessibilityController =
                          mService.mAccessibilityController;
                  final int numDisplays = mDisplayContentsAnimators.size();
                  for (int i = 0; i < numDisplays; i++) {
                      final int displayId = mDisplayContentsAnimators.keyAt(i);
                      final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
                      DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
  
                      final ScreenRotationAnimation screenRotationAnimation =
                              displayAnimator.mScreenRotationAnimation;
                      if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                          //开始屏幕旋转动画
                          if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
                              setAnimating(true);
                          } else {
                              mBulkUpdateParams |= SET_UPDATE_ROTATION;
                              screenRotationAnimation.kill();
                              displayAnimator.mScreenRotationAnimation = null;
  
                              //TODO (multidisplay): Accessibility supported only for the default
                              // display.
                              if (accessibilityController != null && dc.isDefaultDisplay) {
                                  // We just finished rotation animation which means we did not
                                  // announce the rotation and waited for it to end, announce now.
                                  accessibilityController.onRotationChangedLocked(
                                          mService.getDefaultDisplayContentLocked());
                              }
                          }
                      }
  
                      // Update animations of all applications, including those
                      // associated with exiting/removed apps
                      ++mAnimTransactionSequence;
                      //开始窗口动画
                      dc.updateWindowsForAnimator(this);
                      //开始壁纸动画
                      dc.updateWallpaperForAnimator(this);
                      //将变换矩阵设置在Surface上
                      dc.prepareSurfaces();
                  }
  
                  ... ...
  
                  SurfaceControl.mergeToGlobalTransaction(mTransaction);
              } catch (RuntimeException e) {
                  Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
              } finally {
                  mService.closeSurfaceTransaction("WindowAnimator");
                  if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
              }
  
              boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
              boolean doRequest = false;
              if (mBulkUpdateParams != 0) {
                  //copyAnimToLayoutParams这里判断,如果窗口正处于冻结状态那么doRequest为true ,需要再次调用requestTraversal重新绘制解冻,mService.mWindowsFreezingScreen
                  doRequest = mService.mRoot.copyAnimToLayoutParams();
              }
  
              if (hasPendingLayoutChanges || doRequest) {
                  //这里解冻
                  mService.mWindowPlacerLocked.requestTraversal();
              }
  
              ... ...
          }
      }

开始绘制转屏动画,然后发现屏幕还是冻着的,所以再次WindowSurfacePlacer.requestTraversal重新绘制窗口->Choregrapher回调->WindowSurfacePlacer.performSurfacePlacement->RootWindowContainer.performSurfacePlacement

frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    void performSurfacePlacement(boolean recoveringMemory) {
     ... ...
          //mOrientationChangeComplete根据mBulkUpdateParams& SET_ORIENTATION_CHANGE_COMPLETE判断,所以这里为true
          if (mOrientationChangeComplete) {
              if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                  mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                  mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                  mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
              }
              mService.stopFreezingDisplayLocked();
          }
          ... ...
    }

最后冻屏结束处理

   void stopFreezingDisplayLocked() {
       ... ...

       final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);

       // We must make a local copy of the displayId as it can be potentially overwritten later on
       // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
       // of update rotation, but we reference the frozen display after that call in this method.
       final int displayId = mFrozenDisplayId;
       mFrozenDisplayId = INVALID_DISPLAY;
       mDisplayFrozen = false;
       //通知InputManagerService 解除冻屏
       mInputMonitor.thawInputDispatchingLw();
       ... ...

       boolean updateRotation = false;

       ScreenRotationAnimation screenRotationAnimation =
               mAnimator.getScreenRotationAnimationLocked(displayId);
       if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
               && screenRotationAnimation.hasScreenshot()) {
           if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
           // TODO(multidisplay): rotation on main screen only.
           DisplayInfo displayInfo = displayContent.getDisplayInfo();
           // Get rotation animation again, with new top window
           if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
               mExitAnimId = mEnterAnimId = 0;
           }
           //dismiss截图后开始执行动画
           if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
                   getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                       displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
               mTransaction.apply();
               scheduleAnimationLocked();
           } else {
               screenRotationAnimation.kill();
               mAnimator.setScreenRotationAnimationLocked(displayId, null);
               updateRotation = true;
           }
       } else {
           if (screenRotationAnimation != null) {
               screenRotationAnimation.kill();
               mAnimator.setScreenRotationAnimationLocked(displayId, null);
           }
           updateRotation = true;
       }

       boolean configChanged;

       // While the display is frozen we don't re-compute the orientation
       // to avoid inconsistent states.  However, something interesting
       // could have actually changed during that time so re-evaluate it
       // now to catch that.
       configChanged = updateOrientationFromAppTokensLocked(displayId);

       ... ...
       //释放weak lock
       mScreenFrozenLock.release();

       ... ...
   }

需要注意的是冻屏不会导致ANR,冻屏只是在InputDispatcher 分发事件时,如果发现冻屏标志为true,那么不继续分发该事件,而ANR是因为已经找到处理事件窗口,但是窗口没有反馈处理结果,超过5s则触发ANR

上一篇下一篇

猜你喜欢

热点阅读