WMS轉屏流程

WMS轉屏流程

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

從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發(fā)送新的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. 停止分發(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.結束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. 創(chuàng)建并且設置轉屏動畫,因為轉屏動畫和凍屏動畫同時執(zhí)行
           screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                   mPolicy.isDefaultOrientationForced(), isSecure,
                   this);
           mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                   screenRotationAnimation);
       }
   }

在ScreenRotationAnimation構造方法中創(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)建個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();
   }

到這已經凍屏,停止輸入事件分發(fā),并設置了截圖Surface顯示在最上面,設置了轉屏動畫,但還沒有開始執(zhí)行,現(xiàn)在回到WindowManagerService.updateRotationUnchecked繼續(xù)往下分析,通知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期間繪制窗口,因為調整完還有更新窗口,所以是為了優(yōu)化性能
              mWindowManager.deferSurfaceLayout();
          }
          try {
              if (values != null) {
                  //AMS發(fā)轉屏廣播,通知應用進程,調整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上面說過會觸發(fā)繪制動畫,最后調用到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這里判斷,如果窗口正處于凍結狀態(tài)那么doRequest為true ,需要再次調用requestTraversal重新繪制解凍,mService.mWindowsFreezingScreen
                  doRequest = mService.mRoot.copyAnimToLayoutParams();
              }
  
              if (hasPendingLayoutChanges || doRequest) {
                  //這里解凍
                  mService.mWindowPlacerLocked.requestTraversal();
              }
  
              ... ...
          }
      }

開始繪制轉屏動畫,然后發(fā)現(xiàn)屏幕還是凍著的,所以再次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截圖后開始執(zhí)行動畫
           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 分發(fā)事件時,如果發(fā)現(xiàn)凍屏標志為true,那么不繼續(xù)分發(fā)該事件,而ANR是因為已經找到處理事件窗口,但是窗口沒有反饋處理結果,超過5s則觸發(fā)ANR

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市指郁,隨后出現(xiàn)的幾起案子故觅,更是在濱河造成了極大的恐慌剃袍,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辛润,死亡現(xiàn)場離奇詭異抠忘,居然都是意外死亡,警方通過查閱死者的電腦和手機放仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撬碟,“玉大人诞挨,你說我怎么就攤上這事∧馗颍” “怎么了惶傻?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長其障。 經常有香客問我银室,道長,這世上最難降的妖魔是什么励翼? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任蜈敢,我火速辦了婚禮,結果婚禮上汽抚,老公的妹妹穿的比我還像新娘抓狭。我一直安慰自己,他們只是感情好造烁,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布否过。 她就那樣靜靜地躺著午笛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苗桂。 梳的紋絲不亂的頭發(fā)上药磺,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音誉察,去河邊找鬼。 笑死惹谐,一個胖子當著我的面吹牛持偏,可吹牛的內容都是我干的。 我是一名探鬼主播氨肌,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼鸿秆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怎囚?” 一聲冷哼從身側響起卿叽,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恳守,沒想到半個月后考婴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡催烘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年沥阱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伊群。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡考杉,死狀恐怖,靈堂內的尸體忽然破棺而出舰始,到底是詐尸還是另有隱情崇棠,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布丸卷,位于F島的核電站枕稀,受9級特大地震影響,放射性物質發(fā)生泄漏谜嫉。R本人自食惡果不足惜抽莱,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骄恶。 院中可真熱鬧食铐,春花似錦、人聲如沸僧鲁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斟叼,卻和暖如春偶惠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朗涩。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工忽孽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谢床。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓兄一,卻偏偏與公主長得像,于是被迫代替她去往敵國和親识腿。 傳聞我的和親對象是個殘疾皇子出革,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345