android12 WindowManagerService窗口的布局过程

18 阅读21分钟

WindowManagerService的窗口布局过程,是在窗口完成“添加”addWindow 之后,确定其最终在屏幕上的尺寸、位置和层级(Z-Order)的关键环节。这个过程主要由应用进程的 ViewRootImpl 触发,核心实现在WMS的 relayoutWindow 和 performLayout 等方法中。

触发时机:何时开始布局?

窗口布局的触发时机很灵活,每当窗口属性发生变化时都可能触发。最常见的场景是:

  • 应用首次添加窗口后:在 ViewRootImpl.setView() 中会调用 requestLayout(),通过 Choreographer 发起一个异步遍历任务 mTraversalRunnable
  • 窗口属性变化时:比如应用因键盘弹出、屏幕旋转等调用 requestLayout() 重新布局。
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
              int userId) {
        synchronized (this) {
                 ...
                 //请求首次布局
                 requestLayout();
                 ...
           
              }
         }
}

//frameworks/base/core/java/android/view/ViewRootImpl.java
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        ...
        scheduleTraversals();
    }
}

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //MessageQueue 中插入一个同步屏障 。这个屏障的作用是:
        //所有同步消息都不会被执行,只有异步消息可以绕过屏障继续执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //注册一个执行 View 的测量、布局、绘制的CallBack
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
    }
}

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        ...
        //核心绘制流程
        performTraversals();
        ...
    }
}

跨进程请求:从应用到WMS (relayoutWindow)

mTraversalRunnable 的核心是执行 performTraversals(),其中会通过 mWindowSession.relayout() 跨进程调用WMS的 relayoutWindow 方法,携带应用期望的窗口尺寸等参数。

//frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
    ...
    final boolean wasReportNextDraw = mReportNextDraw;
    if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
            || mForceNextWindowRelayout) {
        mForceNextWindowRelayout = false;
        ...
        try {
            ...
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            ...
        
    } else {
        ...
        maybeHandleWindowMove(frame);
    }
    ...
    mIsInTraversal = false;
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    ...
    //调用WMS.relayoutWindow()
    int relayoutResult = mWindowSession.relayout(mWindow, params,
         (int) (mView.getMeasuredWidth() * appScale + 0.5f),
         (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
         insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,   frameNumber,
        mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
        mTempControls, mSurfaceSize);
    mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
    //relayoutWindow之后填充Surface
    if (mSurfaceControl.isValid()) {
         if (!useBLAST()) {
              mSurface.copyFrom(mSurfaceControl);
          } else {
             if (blastSurface != null) {
               mSurface.transferFrom(blastSurface);
              }
         }
     }
   ... 
   
}
 

WMS核心处理:计算与分配

relayoutWindow 是WMS处理布局请求的入口,主要完成:

  1. 查找窗口状态:通过 windowForClientLocked() 找到对应的 WindowState 对象。

  2. 触发全面布局计算:调用 WindowSurfacePlacer.performSurfacePlacement()。这是核心步骤,内部会:

    • 执行 performLayout() :遍历所有窗口,分别处理非附加窗口附加窗口(如PopupWindow),通过 WindowManagerPolicy.layoutWindowLw() 为每个窗口计算并保存最终的 mFrame 等坐标信息。
    • 更新窗口层级:通过 RootWindowContainer 管理所有窗口(DisplayContent)的层级关系。
  3. 创建或更新SurfacerelayoutWindow 会为可见窗口调用 WindowStateAnimator.createSurfaceLocked() 创建 SurfaceControl。最终通过 mSurface.copyFrom(mSurfaceControl) 将 Surface 传回给应用进程。

  4. 返回计算结果:将计算好的窗口大小、位置等数据通过 relayout() 的 out 参数传回给应用。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
              int requestedWidth, int requestedHeight, int viewVisibility, int flags,
              long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
              SurfaceControl outSurfaceControl, InsetsState outInsetsState,
              InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
          ...
          synchronized (mGlobalLock) {
          //根据 `client` 查找对应的 `WindowState`,若找不到则直接返回
              final WindowState win = windowForClientLocked(session, client, false);
              if (win == null) {
                  return 0;
              }
              ...
  
              WindowStateAnimator winAnimator = win.mWinAnimator;
              if (viewVisibility != View.GONE) {
              //设置窗口大小为客户端需要的大小
                  win.setRequestedSize(requestedWidth, requestedHeight);
              }
              //`frameNumber` 用于同步帧的序列号(与 BLAST 同步机制相关)
              win.setFrameNumber(frameNumber);
  
              int attrChanges = 0;
              int flagChanges = 0;
              int privateFlagChanges = 0;
              if (attrs != null) {
                  //根据系统状态(如是否锁屏、是否在沉浸模式)调整窗口参数
                  displayPolicy.adjustWindowParamsLw(win, attrs);
                  win.mToken.adjustWindowParams(win, attrs);
                  int disableFlags =
                          (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
                  if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
                      disableFlags = 0;
                  }
                  win.mDisableFlags = disableFlags;
                  
               ...
 
              //记录可见性变化,用于后续判断是否需要移动IME或壁纸
              final int oldVisibility = win.mViewVisibility;
  
             
              final boolean becameVisible =
                      (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                              && viewVisibility == View.VISIBLE;
              boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                      || becameVisible;
              boolean focusMayChange = win.mViewVisibility != viewVisibility
                      || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                      || (!win.mRelayoutCalled);
  
              boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                      && win.hasWallpaper();
              wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
              if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
                  winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
              }
  
              win.mRelayoutCalled = true;
              win.mInRelayout = true;
  
              win.setViewVisibility(viewVisibility);
           
  
  
              win.setDisplayLayoutNeeded();
              win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
  
              
      
     //只有当窗口可见且关联的 Activity 可见时,才真正执行relayout(创建/更新 Surface)
              final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                      (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                              || win.mActivityRecord.isClientVisible());
  
              
              if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
                  
                  }
                  result |= RELAYOUT_RES_SURFACE_CHANGED;
                  if (!win.mWillReplaceWindow) {
                     
                      if (wallpaperMayMove) {
                          displayContent.mWallpaperController.adjustWallpaperWindows();
                      }

         //如果窗口不可见但已有 Surface,且没有正在播放退出动画,则尝试启动退出动画
         //启动退出动画后,Surface 会在动画结束后被释放
                      focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
                  }
              }
  
     
              if (shouldRelayout) {
                  try {
                  //创建 Surface
                      result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                  } catch (Exception e) {
                     ...
                      return 0;
                  }
              }
  
              // We may be deferring layout passes at the moment, but since the client is interested
              // in the new out values right now we need to force a layout.
              
               //####重点
              //遍历整个窗口树,进行最终的层级计算、位置确定和 Surface 显示
              mWindowPlacerLocked.performSurfacePlacement(true /* force */);
  
              if (shouldRelayout) {
                  
   //标记 `mHasSurface = true`
   //如果窗口之前是隐藏的,设置 `mFirstTimeVisible = true`
   //更新窗口的显示状态
   //如果是输入法窗口且尚未注册,立即注册
                  result = win.relayoutVisibleWindow(result);
  
                  if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                      focusMayChange = true;
                  }
                  if (win.mAttrs.type == TYPE_INPUT_METHOD
                          && displayContent.mInputMethodWindow == null) {
                      displayContent.setInputMethodWindowLocked(win);
                      imMayMove = true;
                  }
                  win.adjustStartingWindowFlags();
                  Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
              } else {
                 ...
  
                  if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
                  //可见但没有执行 relayout(如 Activity 不可见),返回现有 Surface
                      winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
                    
                  } else {
                     
                        ...
                      try {
                         
                        // 窗口不可见,释放 Surface
                          outSurfaceControl.release();
                      } finally {
                          ...
                      }
                  }
  
                 
              }
  
              if (focusMayChange) {
                  //更新焦点窗口
                  if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                      imMayMove = false;
                  }
              }
  
              
              boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
              //更新输入法窗口
              if (imMayMove) {
                  displayContent.computeImeTarget(true /* updateImeTarget */);
                  if (toBeDisplayed) {
                      //IME 目标窗口可能因可见性或焦点变化而改变
                      displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                  }
              }
  
              if (wallpaperMayMove) {
              //壁纸可能需要因窗口显示/隐藏而重新调整层级
                  displayContent.pendingLayoutChanges |=
                          WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
              }
  
              if (win.mActivityRecord != null) {
                  displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
              }
  
             ...
          }
  
          Binder.restoreCallingIdentity(origId);
          return result;
}
   
  

WindowSurfacePlacer.performSurfacePlacement() 是 Android 窗口管理服务(WindowManagerService)中最核心的方法之一。它的主要职责是计算并应用所有窗口的最终状态——包括它们的位置、大小、可见性以及 Z 轴顺序,并将这些变更通过一次原子操作提交给底层渲染系统 SurfaceFlinger,最终完成屏幕的刷新。

这个过程可以理解为一次全局的窗口布局“提交”(commit),其核心流程大致如下:

//frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
          // 1. 检查是否处于延迟布局状态,如果是则记录请求并返回
          //`mDeferDepth`:表示延迟布局的嵌套深度,用于合并多次请求
          if (mDeferDepth > 0 && !force) {
              mDeferredRequests++;
              return;
          }
          // 2. 执行一个循环,最多6次,用于处理递归或依赖性的布局请求
         // `6次循环`:这是一个保护机制,用于处理布局过程中可能产生的新的布局请求。
         //Google工程师的经验值认为6次足以处理所有正常的嵌套场景,超过则可能有问题,
         //会停止循环并报错
          int loopCount = 6;
          do {
         
              mTraversalScheduled = false;
              performSurfacePlacementLoop(); // 核心布局逻辑
              ...
              loopCount--;
          } while (mTraversalScheduled && loopCount > 0);
           ...
 }

performSurfacePlacementLoop() 这个方法承担了具体的布局工作,其核心是调用 RootWindowContainer.performSurfacePlacement() 开始遍历整个窗口树。

private void performSurfacePlacementLoop() {
         //如果正在布局中,则直接返回,避免递归调用导致崩溃或死锁
         if (mInLayout) {
             return;
         }
         
         final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
          //系统配置(如屏幕方向)还在变更中,直接返回
         if (defaultDisplay.mWaitingForConfig) {
             return;
         }
 
         if (!mService.mDisplayReady) {
             return;
         }
 
         mInLayout = true;
 
 
         //在开始布局之前删除僵尸窗口。僵尸窗口是
         //指持有者已经异常退出或处于隐藏状态却拥有surface窗口
         if (!mService.mForceRemoves.isEmpty()) {
             //mForceRemoves 僵尸窗口
             while (!mService.mForceRemoves.isEmpty()) {
                 final WindowState ws = mService.mForceRemoves.remove(0);
                 //立即移除,不经过常规的动画和清理流程
                 ws.removeImmediately();
             }
             
             Object tmp = new Object();
             synchronized (tmp) {
                 try {
                     tmp.wait(250);
                 } catch (InterruptedException e) {
                 }
             }
         }
 
         try {
            //`RootWindowContainer`会遍历DisplayContent下的所有窗口,
            //根据最新的状态(尺寸、层级、动画等)重新计算它们的位置和大小,
            //并提交相应的 `Transaction` 给 SurfaceFlinger 更新显示
             mService.mRoot.performSurfacePlacement();
 
             mInLayout = false;
 
             if (mService.mRoot.isLayoutNeeded()) {
                 if (++mLayoutRepeatCount < 6) {
                     //继续请求下一轮布局,设置mTraversalScheduled为true
                     requestTraversal();
                 } else {
                     mLayoutRepeatCount = 0;
                 }
             } else {
                 mLayoutRepeatCount = 0;
             }
 
             //通知监听者,窗口布局发生了变化
             if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
                 mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
                 mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
             }
         } catch (RuntimeException e) {
             mInLayout = false;
            
         }
}


//遍历DisplayContent下的所有窗口,根据最新的状态(尺寸、层级、动画等)
//重新计算它们的位置和大小,并提交相应的 `Transaction` 给 SurfaceFlinger 更新显示
//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java 
void performSurfacePlacement() {
         try {
             performSurfacePlacementNoTrace();
         } finally {
             
         }
}
     

void performSurfacePlacementNoTrace() {
 
         int i;
 
         //更新处于焦点状态的窗口
         if (mWmService.mFocusMayChange) {
             mWmService.mFocusMayChange = false;
             mWmService.updateFocusedWindowLocked(
                     UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
         }
 
         ...
         //递增布局序列号
         mWmService.mTransactionSequence++;
         //获取手机屏幕的宽高尺寸
         final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
         final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
         ...
         //使用Transaction,保证所有窗口的更新是一个原子操作,避免屏幕出现撕裂或闪烁。
         mWmService.openSurfaceTransaction();
         try {
            //核心:计算并应用所有窗口的Surface变更
             applySurfaceChangesTransaction();
         } catch (RuntimeException e) {
            
         } finally {
          //关闭事务
         // 这一步会将当前累积的所有变更通过 `SurfaceControl` 的 `apply` 方法,
         //一次性发送给 `SurfaceFlinger` 进程,由它来完成真正的屏幕合成与显示
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
            
             }
         }
 
         ...
         
 
         if (mWmService.mFocusMayChange) {
             mWmService.mFocusMayChange = false;
            // 再次更新焦点窗口mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                     false /*updateInputWindows*/);
         }
 
         if (isLayoutNeeded()) {
             defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
             
         }
         //遍历所有需要调整大小的窗口,
         //并通过 Binder 异步通知对应的应用进程(`IWindow.resized()`),
         //让应用重新绘制内容。
         handleResizingWindows();
 
         if (mWmService.mDisplayFrozen) {
         //如果屏幕旋转等操作完成,解除冻结状态
             
         }
         if (mOrientationChangeComplete) {
             if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                 mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                 mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                 mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
             }
             mWmService.stopFreezingDisplayLocked();
         }
 
         i = mWmService.mDestroySurface.size();
         if (i > 0) {
             do {
                 i--;
                 WindowState win = mWmService.mDestroySurface.get(i);
                 win.mDestroying = false;
                 final DisplayContent displayContent = win.getDisplayContent();
                 if (displayContent.mInputMethodWindow == win) {
                     displayContent.setInputMethodWindowLocked(null);
                 }
                 if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
                     displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 }
                  //销毁不可见窗口的 Surface
                 win.destroySurfaceUnchecked();
             } while (i > 0);
             mWmService.mDestroySurface.clear();
         }
 
         // Time to remove any exiting tokens?
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             //移除过期的 Token,清理已退出的组件
             displayContent.removeExistingTokensIfPossible();
         }
 
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             //pendingLayoutChanges是一个位掩码,
             //用于记录在当前布局循环中发生的特殊事件(如壁纸变化、需要重新布局)
             if (displayContent.pendingLayoutChanges != 0) {
                 displayContent.setLayoutNeeded();
             }
         }
 
         ...
         final int N = mWmService.mPendingRemove.size();
         if (N > 0) {
               //批量移除除待删除窗口,并重新分配层级
             if (mWmService.mPendingRemoveTmp.length < N) {
                 mWmService.mPendingRemoveTmp = new WindowState[N + 10];
             }
             mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
             mWmService.mPendingRemove.clear();
             ArrayList<DisplayContent> displayList = new ArrayList();
             for (i = 0; i < N; i++) {
                 final WindowState w = mWmService.mPendingRemoveTmp[i];
                 w.removeImmediately();
                 final DisplayContent displayContent = w.getDisplayContent();
                 if (displayContent != null && !displayList.contains(displayContent)) {
                     displayList.add(displayContent);
                 }
             }
 
             for (int j = displayList.size() - 1; j >= 0; --j) {
                 final DisplayContent dc = displayList.get(j);
                 //遍历列表并最终分配唯一的 `mLayer` 值,
                 //这个值才真正决定了窗口在屏幕上的 Z 轴顺序
                 dc.assignWindowLayers(true /*setLayoutNeeded*/);
             }
         }
 
         forAllDisplays(dc -> {
           //通知 IMS 最新的窗口布局,以便正确分发触摸事件
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             dc.updateSystemGestureExclusion();
             dc.updateTouchExcludeRegion();
         });
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
         mWmService.enableScreenIfNeededLocked();
 
         
         //向 `Choreographer` 注册一个回调,
         请求在下一个 VSYNC(垂直同步)信号到来时执行动画。
         mWmService.scheduleAnimationLocked();
  
        
     }
  

遍历并应用变更 (applySurfaceChangesTransaction)

  • 遍历窗口树:系统会从 DisplayContent(代表一个逻辑显示屏)开始,递归遍历其下的所有 WindowState(代表一个窗口)。

  • 执行布局和绘制状态变更:对于每个窗口,系统会:

    • 根据最新的布局参数,计算并更新其 Surface 的大小和位置(layoutWindow 流程)。
    • 检查窗口的绘制状态,例如,当窗口的 Surface 已完成绘制,其状态会被更新为 READY_TO_SHOW,进而变为 HAS_DRAW,最终被设置为可见

//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private void applySurfaceChangesTransaction() {
    mHoldScreenWindow = null;
    mObscuringWindow = null;
    final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
    final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
    final int defaultDw = defaultInfo.logicalWidth;
    final int defaultDh = defaultInfo.logicalHeight;
    ...

    final int count = mChildren.size();
    for (int j = 0; j < count; ++j) {
        final DisplayContent dc = mChildren.get(j);
         //递归处理所有显示屏(包括主屏、外接屏、虚拟屏等),每个DisplayContent会:
        //调整窗口层级(Z-order)
        //更新窗口位置、大小、透明度等属性
       //处理动画
       //应用窗口的Surface操作
        dc.applySurfaceChangesTransaction();
    }
    
    //给DisplayManagerService一个机会调整显示属性(如旋转角度),确保与窗口系统协调
    mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
    //将`mDisplayTransaction`中的所有Surface操作合并到全局事务中
    //最终提交给SurfaceFlinger进行合成
    SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}


//执行布局和绘制状态变更
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
    final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;

    mTmpUpdateAllDrawn.clear();

    int repeats = 0;
    do {
    
        repeats++;
        //最多重试6次(防止无限循环)
        if (repeats > 6) {
            clearLayoutNeeded();
            break;
        }

       
         //壁纸需要重新调整
        if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
            mWallpaperController.adjustWallpaperWindows();
        }
        //配置需要更新(如屏幕旋转)
        if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
            if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
            if (updateOrientation()) {
                setLayoutNeeded();
                sendNewConfiguration();
            }
        }

         //需要重新布局
        if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            setLayoutNeeded();
        }

        // FIRST LOOP: Perform a layout, if needed.
        //小于4,如果循环四次以后还无法满足,则不再尝试对窗口布局,
        //仅处理pendingLayoutChanges,如果6次尝试仍无法满足,则放弃尝试
        if (repeats < LAYOUT_REPEAT_THRESHOLD) {
        // 执行布局
            performLayout(repeats == 1, false /* updateInputWindows */);
        } else {
            
        }
        
        pendingLayoutChanges = 0;

        try {
            //DisplayPolicy修改一些属性状态
            mDisplayPolicy.beginPostLayoutPolicyLw();
            //mApplyPostLayoutPolicy 记录可能影响系统窗口或锁屏界面可见性窗口
            //调用DisplayPolicy.applyPostLayoutPolicyLw()
            forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
            //修改系统窗口或者锁屏界面的可见效
            pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        
        mInsetsStateController.onPostLayout();
    } while (pendingLayoutChanges != 0); //PhoneWindowManager认为布局结果仍需
       进行调整,则重新再来一遍

    mTmpApplySurfaceChangesTransactionState.reset();

    try {
            //遍历所有窗口,执行实际的Surface操作
           //设置窗口位置、大小、透明度
          //应用动画变换(旋转、缩放)
         //设置Z-order(层级顺序)
        //更新窗口裁剪区域
        forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
    } finally {
       
    }
    //提交Surface事务到SurfaceFlinger
   //处理窗口绘制完成的通知
    prepareSurfaces();

    
    //检查是否需要显示输入法,在布局完成后触发
    mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();

    mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
    if (!mWmService.mDisplayFrozen) {
         //更新显示属性
        mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                mLastHasContent,
                mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
                mTmpApplySurfaceChangesTransactionState.preferredModeId,
                mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,
                mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
                mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
                true /* inTraversal, must call performTraversalInTrans... below */);
    }

    final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
    if (wallpaperVisible != mLastWallpaperVisible) {
        mLastWallpaperVisible = wallpaperVisible;
       
       //壁纸可见性通知mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
    }

  //检查Activity的所有窗口是否都已绘制完成,如果是,则触发显示逻辑(如启动过渡动画结束
    while (!mTmpUpdateAllDrawn.isEmpty()) {
        final ActivityRecord activity = mTmpUpdateAllDrawn.removeLast();
        // See if any windows have been drawn, so they (and others associated with them)
        // can now be shown.
        activity.updateAllDrawn();
    }
}


//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void performLayout(boolean initial, boolean updateInputWindows) {
    try {
        performLayoutNoTrace(initial, updateInputWindows);
    } finally {
       
    }
}


//先对顶级窗口进行布局,然后对子窗口布局
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
   //检查是否需要布局
    if (!isLayoutNeeded()) {
        return;
    }
    //清除布局标志,避免重复执行
    clearLayoutNeeded();
   
   //布局序列号更新
   //布局序列号递增,用于:
   //调试和日志追踪
   //检测布局变化的顺序
   //防止并发问题(每次布局有唯一ID)
    int seq = mLayoutSeq + 1;
    if (seq < 0) seq = 0;
    mLayoutSeq = seq;

    //initial表示是否为首次布局通常用于动画初始状态的判断)
    mTmpInitial = initial;


    
    //对所有顶级窗口进行布局
    forAllWindows(mPerformLayout, true /* traverseTopToBottom */);

    //对非顶级窗口布局
    forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);

   //布局完成后窗口的尺寸和位置可能发生变化,此时需要输入系统更新窗口状态
    mInputMonitor.setUpdateInputWindowsNeededLw();
    if (updateInputWindows) {
    //非强制更新(有变化才更新)
        mInputMonitor.updateInputWindowsLw(false /*force*/);
    }
}



private final Consumer<WindowState> mPerformLayout = w -> {
    ...
    if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
        ...
        //准备布局所需的临时数据
       //处理窗口装饰(标题栏、边框)的尺寸计算
       //更新窗口状态标志
        //w = WindowState
        w.prelayout();
        ...
          //根据窗口类型(Activity、Dialog、System等)计算位置
         //应用Gravity(对齐方式)
         // 考虑系统Insets(状态栏、导航栏、手势区域)
        //处理窗口缩放、旋转
       // 计算最终的Frame矩形
       //DisplayPolicy
        getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
        ...

     
    }
};

private final Consumer<WindowState> mPerformLayoutAttached = w -> {
    if (w.mLayoutAttached) {
       
        if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
                || w.mLayoutNeeded) {
            ...
            //w = WindowState
            w.prelayout();
            //DisplayPolicy
            getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
            w.mLayoutSeq = mLayoutSeq;
           
        }
    }
};


DisplayPolicy.layoutWindowLw() 是 Android 窗口策略中为单个窗口计算最终位置和大小的核心方法。它在 DisplayContent.applySurfaceChangesTransaction() 的迭代布局阶段被调用,将布局策略“翻译”成每个窗口具体的矩形区域。

layoutWindowLw() 接收一个 WindowState 对象,综合其布局参数(如gravityflags)、窗口类型(普通应用、系统窗口、输入法等)以及屏幕装饰区域(状态栏、导航栏等),计算出该窗口在屏幕上的精确位置和大小,并将结果保存到窗口的 WindowFrames 中。你可以把它看作是整个窗口布局流水线上的“最后一道精准工序”。

//frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
 //DisplayFrames包含屏幕各种预定义区域(如安全区域、稳定区域、系统UI占据区域)的数据对象。
    //如果当前窗口是系统导航栏,调用专门的 `layoutNavigationBar` 方法计算其位置。
    //这个方法会返回导航栏的位置(底部/左侧/右侧),并保存到 `mNavigationBarPosition` 
    //中,然后直接返回。导航栏不参与后续的通用布局逻辑。
    if (win == mNavigationBar) {
        mNavigationBarPosition = layoutNavigationBar(displayFrames,
                mBarContentFrames.get(TYPE_NAVIGATION_BAR));
        return;
    }
    //如果当前窗口是状态栏(`mStatusBar`),并且**不能**接收输入事件
    //(`!canReceiveInput(win)`),则调用专门的 `layoutStatusBar` 计算其位置后返回。
    //为什么判断条件里要加上“不能接收输入”?因为状态栏在某些场景下(如锁屏时)可能不接收
    //输入,但它仍然需要被布局
    if ((win == mStatusBar && !canReceiveInput(win))) {
        layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
        return;
    }
    //快速路径:画中画(PiP)过渡。如果该窗口所属的 Activity 正在等待进入画中画模式
    //(`mWaitForEnteringPinnedMode` 为 `true`),则跳过布局,直接返回。这是因为 
    //PiP 过渡期间窗口的布局由动画系统单独控制,不应该被这里的逻辑干扰。
    if (win.mActivityRecord != null && win.mActivityRecord.mWaitForEnteringPinnedMode) {
        return;
    }
    //从 `WindowState` 中获取 `LayoutParams`,然后提取出:
    final WindowManager.LayoutParams attrs = win.getAttrs();
    //`type`:窗口类型(如 `TYPE_APPLICATION`、`TYPE_SYSTEM_ERROR` 等)。
    final int type = attrs.type;
    //`fl`:公共标志位(如 `FLAG_LAYOUT_IN_SCREEN`)。
    final int fl = attrs.flags;
    //`pfl`:私有标志位(如 `PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME`)。
    final int pfl = attrs.privateFlags;
    //`sim`:软输入模式(Soft Input Mode),控制窗口与输入法如何交互。
    final int sim = attrs.softInputMode;

    //**获取窗口自己的显示框架和窗口帧对象**。


  //允许每个窗口返回自己独立的 `DisplayFrames`。对于自由窗口或分屏窗口,它们可能有
  //不同于整个屏幕的显示区域。如果窗口没有自定义的,则直接返回传入的 `displayFrames`。
    displayFrames = win.getDisplayFrames(displayFrames); 
    //windowFrames`:这是 `WindowState` 中保存所有帧(`mFrame`、`mParentFrame`、
    //`mDisplayFrame` 等)的容器对象。
    final WindowFrames windowFrames = win.getWindowFrames();

 
    //将当前的 `mParentFrame` 保存到临时变量 `sTmpLastParentFrame` 中,
    //用于后续比较是否发生了变化
    sTmpLastParentFrame.set(windowFrames.mParentFrame);
    // 创建 `pf` 和 `df` 引用,分别指向 `WindowFrames` 中
    //的 `mParentFrame` 和 `mDisplayFrame`。注意,这里是对引用的操作,后续
    //对 `pf` 和 `df` 的修改会直接影响 `windowFrames` 中的原始矩形。
    final Rect pf = windowFrames.mParentFrame;
    final Rect df = windowFrames.mDisplayFrame;
    //重置一个标志,表示父帧尚未因屏幕挖孔而被裁剪。
    windowFrames.setParentFrameWasClippedByDisplayCutout(false);

    //窗口是否被允许在屏幕范围内布局(包括系统UI覆盖的区域)。
    final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
    //窗口是否希望自动处理系统UI(状态栏/导航栏)的占位。
    //这两个标志的组合决定了窗口如何与系统UI交互。
    final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;

   //**计算窗口的显示边界 (`df`)** 。
    //获取窗口当前的边衬区(Insets)状态 `state`。
    final InsetsState state = win.getInsetsState();
    //根据窗口的布局参数、边衬区状态和窗口令牌的边界(`win.mToken.getBounds()`),
    //**初步设置 `df`**。对于普通应用窗口,`df` 通常被设为整个屏幕;对于自由窗口或分屏窗
    //口,`df` 会被设为该窗口的边界  
    computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
    //计算父帧 (`pf`)
    //如果窗口没有父窗口
    if (attached == null) {
        pf.set(df);
        //输入法避让
        if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
         //表示希望父帧能避开输入法区域。于是会从 `state` 中获取输入法(IME)的边衬区源
         //(`InsetsSource`),计算输入法在 `pf` 上占据的矩形区域,然后从 `pf` 中减去
       //(`inset`)这部分区域。这样,当输入法弹出时,窗口的父帧会相应缩小,从而让窗口内容
         //自动上移,避免被输入法遮挡。
            final InsetsSource source = state.peekSource(ITYPE_IME);
            if (source != null) {
            // 减去输入法区域
                pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
            }
        }
    } else {
    //如果窗口有父窗口
    //如果子窗口没有设置 `FLAG_LAYOUT_IN_SCREEN`,那么它的父帧等于**父窗口的帧**
    //(`attached.getFrame()`),意味着它被限制在父窗口内部。
    //如果子窗口设置了 `FLAG_LAYOUT_IN_SCREEN`,那么它的父帧等于 `df`,意味着它
    //可以在全屏范围内布局(但受到后续裁剪的限制)。,
    除非明确要求"布局在屏幕中"(`FLAG_LAYOUT_IN_SCREEN`)
        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
    }

    final int cutoutMode = attrs.layoutInDisplayCutoutMode;
    
    //**开始处理屏幕挖孔(DisplayCutout)** 。
    //获取窗口的 `layoutInDisplayCutoutMode` 属性,它有三个可能的值:
    // `DEFAULT`:默认模式,窗口内容避开挖孔区域。
    // `NEVER`:窗口内容永远不能与挖孔重叠。
    // `ALWAYS`:窗口内容允许延伸到挖孔区域。
    //只有当模式不是 `ALWAYS` 时,才需要执行裁剪逻辑(因为 `ALWAYS` 模式不限制窗口
    //扩展到挖孔区域)。
    if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
    //窗口是否附加在父窗口中并且没有设置 `FLAG_LAYOUT_IN_SCREEN`(即它是在父窗口
    //内布局的)。
        final boolean attachedInParent = attached != null && !layoutInScreen;
        //窗口是否**没有**请求隐藏状态栏
        //(即 `getRequestedVisibility(ITYPE_STATUS_BAR)`
        //返回 `false`,表示状态栏可见)。注意方法名 `getRequestedVisibility` 返
        //回 `true` 表示请求**隐藏**,所以取反后表示“希望全屏”或“不隐藏状态栏”。
        final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
        //窗口是否**没有**请求隐藏导航栏(即导航栏可见)。
        final boolean requestedHideNavigation =
                !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);

        //判断窗口是否为“浮动的全屏窗口”。一个窗口同时满足以下条件时被认为是浮动的:
        //不是全屏窗口(`!attrs.isFullscreen()`)、设置
       // 了 `FLAG_LAYOUT_IN_SCREEN`,并且不是基础应用窗口
    // (`TYPE_BASE_APPLICATION`)。这种窗口会在后续被裁剪或移位到 `displayFrame内。
        final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
                && type != TYPE_BASE_APPLICATION;
         //创建一个临时矩形 `displayCutoutSafeExceptMaybeBars`,
         //其初始值为 `displayFrames.mDisplayCutoutSafe`(即屏幕的安全区域,完全
         //避开了所有挖孔区域)
        final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
        displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
        //**处理“短边”模式**。
        //如果窗口的 `cutoutMode` 是 `SHORT_EDGES`,表示窗口可以在屏幕的“短边”
        //方向扩展到挖孔区域。
        if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {

            if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
               // //“短边”是上下两条边。因此将临时矩形的 `top` 和 `bottom` 设为极大/
               //极小值,即移除上下边界限制。
                displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
            } else {
                 //“短边”是左右两条边。因此将临时矩形的 `left` 和 `right` 设为极大
                 ///极小值。
                displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
                displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
            }
        }

       ...

    
    //如果窗口设置了 `FLAG_LAYOUT_NO_LIMITS` 标志(允许超出屏幕显示),
    //并且不是系统错误窗口 (`TYPE_SYSTEM_ERROR`),也不在多窗口模式下,
    //则将其显示帧设为极大的范围(`-10000` 到 `10000`),
    //从而实现“可以绘制到屏幕外”的效果,常用于一些特殊动画或全屏效果
    if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
            && !win.inMultiWindowMode()) {
        df.left = df.top = -10000;
        df.right = df.bottom = 10000;
    }

   // 如果父帧在此次计算中发生了变化,会标记窗口内容已改变
    if (!sTmpLastParentFrame.equals(pf)) {
        windowFrames.setContentChanged(true);
    }

    //根据计算好的父帧和显示帧,最终确定窗口的精确坐标和大小,
    //并更新相关的输入法源帧信息
    win.computeFrameAndUpdateSourceFrame();
}

WindowState.computeFrame() 方法。这是窗口几何计算的最终执行者,将 layoutWindowLw() 中确定的约束(pf 和 df)转换为窗口的精确坐标和大小。

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void computeFrameAndUpdateSourceFrame() {
    //核心计算
    computeFrame();
    //通常,只有输入法(IME)窗口、某些系统窗口或实现了 `InsetsControlTarget` 的窗口
    //才拥有这个对象。它负责向系统提供窗口的边衬区信息,从而影响其他窗口的布局。例如,输入法
    //窗口弹出时,它会向系统报告自己占据的区域,系统会据此调整其他应用窗口的大小。
    if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {    //计算窗口当前的 `mSourceFrame`,并将其报告给 `InsetsStateController`。
    //其他窗口(尤其是应用窗口)在布局时,会查询 `InsetsState` 来获取输入法、状态栏等系统
    //窗口占据的区域,从而调整自己的内容区域,避免被遮挡
        mControllableInsetProvider.updateSourceFrame();
    }
}


void computeFrame() {
    //如果此窗口即将被另一个窗口替换,窗口正在执行退出动画,尚未收到移除请求,直接返回
    //不更新帧。这防止了在替换过程中对旧窗口应用新的布局,导致视觉闪烁
    if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
        return;
    }
    // 标记 `mHaveFrame = true`,表示窗口已有有效的帧数据
    mHaveFrame = true;
    //获取窗口所属的任务(Task)
    final Task task = getTask();
    //窗口是否全屏且填充显示区域(不在多窗口模式,且匹配显示区域边界)
    final boolean isFullscreenAndFillsArea = !inMultiWindowMode() && matchesDisplayAreaBounds();
    final boolean windowsAreFloating = task != null && task.isFloating();
    //显示内容(DisplayContent
    final DisplayContent dc = getDisplayContent();
    //显示设备的逻辑信息(宽度、高度等)
    final DisplayInfo displayInfo = getDisplayInfo();
    //窗口帧
    final WindowFrames windowFrames = getLayoutingWindowFrames();
    //用于边衬区计算的帧,初始设为窗口令牌的边界
    mInsetFrame.set(getBounds());

    
    final Rect layoutContainingFrame;
    final Rect layoutDisplayFrame;

    
    final int layoutXDiff;
    final int layoutYDiff;
    final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
    final InsetsControlTarget imeTarget = dc.getImeTarget(IME_TARGET_LAYERING);
    //判断当前窗口是否受输入法(IME)影响,以及是否需要进行位置调整
    //imeWin:当前可见的输入法窗口
    //imeTarget:当前接收输入法边衬区的目标窗口
    //windowsAreFloating:窗口是否属于浮动任务(如自由窗口、画中画)
    //判断当前窗口是否是输入法调整的目标
   //对于浮动窗口:如果输入法目标存在,且与当前窗口属于同一个任务,则为目标
   //对于非浮动窗口:调用 `isImeLayeringTarget()` 判断(通常是最顶层的应用窗口)。
    final boolean isInputMethodAdjustTarget = windowsAreFloating
            ? imeTarget != null && task == imeTarget.getWindow().getTask()
            : isImeLayeringTarget();
    //只有当输入法窗口可见,且当前窗口是调整目标时,才为 `true`
    final boolean isImeTarget =
            imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
            
    //这是 `computeFrame()` 中最复杂的部分,决定了窗口的 `mContainingFrame`——
    //即窗口内容可以放置的“容器”边界。
    if (isFullscreenAndFillsArea || layoutInParentFrame()) {
        //全屏窗口,子窗口直接使用 `mParentFrame`
        windowFrames.mContainingFrame.set(windowFrames.mParentFrame);
        layoutDisplayFrame = windowFrames.mDisplayFrame;
        layoutContainingFrame = windowFrames.mParentFrame;
        //偏移量 `layoutXDiff` 和 `layoutYDiff` 均为 0,表示不需要额外偏移
        layoutXDiff = 0;
        layoutYDiff = 0;
    } else {
        //非全屏窗口(含输入法调整)
        //**初始包含帧**设为窗口令牌的边界(即任务边界)。
        windowFrames.mContainingFrame.set(getBounds());
        
        if (isImeTarget) {
            //**自由窗口模式**
            if (inFreeformWindowingMode()) {
                //计算窗口的可见帧(排除系统栏、输入法和挖孔)
               
                final InsetsState state = dc.getInsetsStateController().getRawInsetsState();
                final Rect visibleFrame = mTmpRect;
                visibleFrame.set(state.getDisplayFrame());
                visibleFrame.inset(state.calculateInsets(visibleFrame,
                        systemBars() | ime() | displayCutout(), false /* ignoreVisibility */));
                final int bottomOverlap =
                        windowFrames.mContainingFrame.bottom - visibleFrame.bottom;
                //如果窗口底部超出了可见帧底部
                if (bottomOverlap > 0) {
                //则将窗口向上平移(`offset(0, -offs)`),使窗口内容不被输入法遮挡。
                //但最多平移到顶部(`distanceToTop` 限制)。
                    final int distanceToTop = Math.max(windowFrames.mContainingFrame.top
                            - visibleFrame.top, 0);
                    int offs = Math.min(bottomOverlap, distanceToTop);
                    windowFrames.mContainingFrame.offset(0, -offs);
                    mInsetFrame.offset(0, -offs);
                }
                //**分屏/多窗口模式(非画中画)**
            } else if (!inPinnedWindowingMode() && windowFrames.mContainingFrame.bottom
                    > windowFrames.mParentFrame.bottom) {
                  
                //如果包含帧底部超出了父帧底部,则将包含帧底部设为父帧底部,使得窗口在
                //父帧内重新布局,避免被输入法推出父帧范围。
                windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
            }
        }

        //浮动窗口(画中画/自由窗口)
        if (windowsAreFloating) {
            如果包含帧为空,则设为 `mDisplayFrame`
            if (windowFrames.mContainingFrame.isEmpty()) {
                windowFrames.mContainingFrame.set(windowFrames.mDisplayFrame);
            }
        }
        //**设置布局相关帧和偏移**
        //保存原始的 `mDisplayFrame` 备份。
        layoutDisplayFrame = mTmpRect2;
        //将 `mDisplayFrame` 设为 `mContainingFrame`(显示帧被限制在包含帧内)。
        layoutDisplayFrame.set(windowFrames.mDisplayFrame);
        windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
        //计算 `layoutXDiff` 和 `layoutYDiff`:`mInsetFrame` 相对
        //于 `mContainingFrame` 的偏移量。
        layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
        layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
        //布局包含帧设为 `mInsetFrame`
        layoutContainingFrame = mInsetFrame;
        mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
        //减去边衬区(`subtractInsets`)
        //从 `mDisplayFrame` 中减去系统UI和边衬区占据的区域
        subtractInsets(windowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
                mTmpRect);
         //对于非子窗
        if (!layoutInParentFrame()) {
           //从 `mContainingFrame` 和 `mInsetFrame` 中减去父帧边衬区的影响
            subtractInsets(windowFrames.mContainingFrame, layoutContainingFrame,
                    windowFrames.mParentFrame, mTmpRect);
            subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
                    mTmpRect);
        }
        //确保布局显示帧在布局包含帧内。
        layoutDisplayFrame.intersect(layoutContainingFrame);
    }

    final int pw = windowFrames.mContainingFrame.width();
    final int ph = windowFrames.mContainingFrame.height();

    if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
        mLastRequestedWidth = mRequestedWidth;
        mLastRequestedHeight = mRequestedHeight;
        windowFrames.setContentChanged(true);
    }

    final int fw = windowFrames.mFrame.width();
    final int fh = windowFrames.mFrame.height();
    //根据窗口的 `gravity`、`width`、`height` 等属性,
    //在 `layoutContainingFrame` 和 `layoutDisplayFrame` 的约束下,
    //计算出 `mFrame` 的最终位置和大小
    applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
    //`TYPE_DOCK_DIVIDER`分屏分割线
    if (mAttrs.type == TYPE_DOCK_DIVIDER) {
         //如果帧发生了变化
        if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
            mMovedByResize = true;
        }
    }

  //应用偏移并设置兼容帧
  //将所有帧按 `-layoutXDiff` 和 `-layoutYDiff` 反向偏移,以补偿之
  //前 `mInsetFrame` 相对于 `mContainingFrame` 的偏移。这确保了最终帧在正确的位置。
    windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);

    windowFrames.mCompatFrame.set(windowFrames.mFrame);
    if (hasCompatScale()) {
    //为支持屏幕兼容模式(如低分辨率应用在高分辨率屏幕上显示),创建 `mCompatFrame`。
    //如果窗口有兼容缩放(`hasCompatScale()`),则将 `mCompatFrame` 乘以逆缩放因子,
    //使其回到应用自身的坐标系。
        windowFrames.mCompatFrame.scale(mInvGlobalScale);
    }

     //如果是壁纸窗口,且帧大小发生变化,则更新壁纸偏移量(`updateWallpaperOffset`),
     //确保壁纸随着窗口大小变化正确滚动。
    if (mIsWallpaper && (fw != windowFrames.mFrame.width()
            || fh != windowFrames.mFrame.height())) {
        dc.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
    }

    //计算相对帧,保存窗口相对于父窗口(或父容器)的坐标
    windowFrames.mRelFrame.set(windowFrames.mFrame);
    WindowContainer parent = getParent();
    int parentLeft = 0;
    int parentTop = 0;
    //对于子窗口
    if (mIsChildWindow) {
        //父窗口是 `WindowState`,取其 `mFrame` 的左上角。
        parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
        parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
        //对于非子窗口
    } else if (parent != null) {
        //父容器是 `Task` 或 `DisplayContent`,取其 `getBounds()` 的左上角。
        final Rect parentBounds = parent.getBounds();
        parentLeft = parentBounds.left;
        parentTop = parentBounds.top;
    }
    //最终 `mRelFrame` 的左上角 = `mFrame.left - parentLeft`,
    //`mFrame.top - parentTop`。
    windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
            windowFrames.mFrame.top - parentTop);

    
}

应用进程接收:执行View绘制

应用进程的 ViewRootImpl 收到WMS返回的结果后,会继续执行 performTraversals() 的后续步骤:

  • performMeasure() :根据WMS计算的尺寸,测量View树中每个View的大小。
  • performLayout() :根据测量结果,为View树中的每个View确定在窗口内的位置。
  • performDraw() :利用WMS返回的 Surface,将绘制内容渲染到缓冲区。

总而言之,WMS窗口布局的本质是一个从应用到系统服务,再回到应用的闭环过程。应用发起请求,WMS集中计算和分配资源,最后应用根据结果完成UI的绘制与渲染。