WMS轉(zhuǎn)屏流程

WMS轉(zhuǎn)屏流程

PhoneWindowManager會(huì)通過WindowOrientaionListener監(jiān)聽傳感器數(shù)據(jù),判斷是否需要轉(zhuǎn)屏,如果需要轉(zhuǎn)屏,凍屏截屏,設(shè)置轉(zhuǎn)屏動(dòng)畫,然后通知AMS更新configuration,AMS發(fā)轉(zhuǎn)屏廣播通知應(yīng)用進(jìn)程,調(diào)整Activity task ,然后調(diào)用WMS.continueSurfaceLayout這里調(diào)用WindowSurfacePlacer.performSurfacePlacement重新繪制窗口和執(zhí)行窗口動(dòng)畫,繼而像Choregrapher發(fā)送繪制動(dòng)畫請(qǐng)求,開始執(zhí)行動(dòng)畫,執(zhí)行完轉(zhuǎn)屏動(dòng)畫后,解凍窗口

從WMS.updateRotationUnchecked開始處理轉(zhuǎn)屏分析:

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發(fā)送新的Configuration信息
                 sendNewConfiguration(displayId);
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }

displayContent.updateRotationUnchecked()方法主要調(diào)用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. 停止分發(fā)輸入事件,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.結(jié)束app動(dòng)畫
       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. 停止窗口動(dòng)畫
           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. 創(chuàng)建并且設(shè)置轉(zhuǎn)屏動(dòng)畫,因?yàn)檗D(zhuǎn)屏動(dòng)畫和凍屏動(dòng)畫同時(shí)執(zhí)行
           screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                   mPolicy.isDefaultOrientationForced(), isSecure,
                   this);
           mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                   screenRotationAnimation);
       }
   }

在ScreenRotationAnimation構(gòu)造方法中創(chuàng)建截圖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();
            //創(chuàng)建個(gè)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");
        //設(shè)置旋轉(zhuǎn)動(dòng)畫矩陣
       setRotation(t, originalRotation);
       t.apply();
   }

到這已經(jīng)凍屏,停止輸入事件分發(fā),并設(shè)置了截圖Surface顯示在最上面,設(shè)置了轉(zhuǎn)屏動(dòng)畫,但還沒有開始執(zhí)行,現(xiàn)在回到WindowManagerService.updateRotationUnchecked繼續(xù)往下分析,通知AMS轉(zhuǎn)屏

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) {
              //延遲繪制窗口,應(yīng)該是防止調(diào)整Activity期間繪制窗口,因?yàn)檎{(diào)整完還有更新窗口,所以是為了優(yōu)化性能
              mWindowManager.deferSurfaceLayout();
          }
          try {
              if (values != null) {
                  //AMS發(fā)轉(zhuǎn)屏廣播,通知應(yīng)用進(jìn)程,調(diào)整Activity
                  changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
                          deferResume);
              }
  
              kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
          } finally {
              if (mWindowManager != null) {
                  //最后再回到WMS中調(diào)整窗口,重新繪制窗口
                  mWindowManager.continueSurfaceLayout();
              }
          }
  
          if (result != null) {
              result.changes = changes;
              result.activityRelaunched = !kept;
          }
          return kept;
      }

WMS.continueSurfaceLayout上面說過會(huì)觸發(fā)繪制動(dòng)畫,最后調(diào)用到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中會(huì)根據(jù)這個(gè)值調(diào)用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()) {
                          //開始屏幕旋轉(zhuǎn)動(dòng)畫
                          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;
                      //開始窗口動(dòng)畫
                      dc.updateWindowsForAnimator(this);
                      //開始壁紙動(dòng)畫
                      dc.updateWallpaperForAnimator(this);
                      //將變換矩陣設(shè)置在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這里判斷,如果窗口正處于凍結(jié)狀態(tài)那么doRequest為true ,需要再次調(diào)用requestTraversal重新繪制解凍,mService.mWindowsFreezingScreen
                  doRequest = mService.mRoot.copyAnimToLayoutParams();
              }
  
              if (hasPendingLayoutChanges || doRequest) {
                  //這里解凍
                  mService.mWindowPlacerLocked.requestTraversal();
              }
  
              ... ...
          }
      }

開始繪制轉(zhuǎn)屏動(dòng)畫,然后發(fā)現(xiàn)屏幕還是凍著的,所以再次WindowSurfacePlacer.requestTraversal重新繪制窗口->Choregrapher回調(diào)->WindowSurfacePlacer.performSurfacePlacement->RootWindowContainer.performSurfacePlacement

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

    void performSurfacePlacement(boolean recoveringMemory) {
     ... ...
          //mOrientationChangeComplete根據(jù)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();
          }
          ... ...
    }

最后凍屏結(jié)束處理

   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截圖后開始執(zhí)行動(dòng)畫
           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();

       ... ...
   }

需要注意的是凍屏不會(huì)導(dǎo)致ANR,凍屏只是在InputDispatcher 分發(fā)事件時(shí),如果發(fā)現(xiàn)凍屏標(biāo)志為true,那么不繼續(xù)分發(fā)該事件,而ANR是因?yàn)橐呀?jīng)找到處理事件窗口,但是窗口沒有反饋處理結(jié)果,超過5s則觸發(fā)ANR

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評(píng)論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,947評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,094評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評(píng)論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,268評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,817評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,039評(píng)論 0 285
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,551評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,502評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,662評(píng)論 1 366
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評(píng)論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,907評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,255評(píng)論 3 389
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,637評(píng)論 2 370