美文网首页
WMS转屏流程

WMS转屏流程

作者: 米豆同学 | 来源:发表于2019-11-22 18:11 被阅读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

    相关文章

      网友评论

          本文标题:WMS转屏流程

          本文链接:https://www.haomeiwen.com/subject/osbqwctx.html