在WMS启动的时候,就创建了WindowSurfacePlacer对象,该对象专门用来执行窗口Surface的“摆放”操作。
![](https://i-blog.csdnimg.cn/blog_migrate/9ced05fe6bb51d0980e6098e62aa20e6.png)
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId, ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
Bundle outSyncIdBundle) {
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
}
performSurfacePlacement
WindowSurfacePlacer#performSurfacePlacement()
方法,可以说是WMS中最核心的方法,负责所有窗口的Surface的摆放工作,如何显示、显示在什么位置、显示区域多大,都将通过这个方法,完成确认并下发给SurfaceFlinger中进行显示。当WMS中任何状态发生变化,都会触发该方法的执行,对整个WMS树结构进行遍历,确定所有的Surface的可见与否。下面就从该方法开始,详细分析下整个流程
frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement(boolean force) {
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
if (mInLayout) {
if (DEBUG) {
throw new RuntimeException("Recursive call!");
}
Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
+ Debug.getCallers(3));
return;
}
// TODO(multi-display):
final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
if (defaultDisplay.mWaitingForConfig) {
// Our configuration has changed (most likely rotation), but we
// don't yet have the complete configuration to report to
// applications. Don't do any window layout until we have it.
return;
}
if (!mService.mDisplayReady) {
// Not yet initialized, nothing to do.
return;
}
mInLayout = true;
if (!mService.mForceRemoves.isEmpty()) {
// Wait a little bit for things to settle down, and off we go.
while (!mService.mForceRemoves.isEmpty()) {
final WindowState ws = mService.mForceRemoves.remove(0);
Slog.i(TAG, "Force removing: " + ws);
ws.removeImmediately();
}
Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
Object tmp = new Object();
synchronized (tmp) {
try {
tmp.wait(250);
} catch (InterruptedException e) {
}
}
}
try {
mService.mRoot.performSurfacePlacement();
mInLayout = false;
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
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;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
}
这个方法中:
首先,通过WMS#openSurfaceTransaction()方法,获取一个SurfaceControl#sGlobalTransaction对象;
然后执行applySurfaceChangesTransaction()进行所有SurfaceControl的遍历;
最后通过WMS#closeSurfaceTransaction()方法,对sGlobalTransaction上的一系列SurfaceControl进行提交,发送给SurfaceFlinger。整个过程中,所有SurfaceControl的原子操作就是通过sGlobalTransaction来进行。
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// "Something has changed! Let's make it correct now."
// TODO: Super long method that should be broken down...
void performSurfacePlacementNoTrace() {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
+ Debug.getCallers(3));
}
int i;
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
mWmService.mTransactionSequence++;
// TODO(multi-display): recents animation & wallpaper need support multi-display.
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
mWmService.mAtmService.mBackNavigationController
.checkAnimationReady(defaultDisplay.mWallpaperController);
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
displayContent.pendingLayoutChanges);
}
}
}
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
if (isLayoutNeeded()) {
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
defaultDisplay.pendingLayoutChanges);
}
}
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
mOrientationChangeComplete);
}
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();
}
// Destroy the surface of any windows that are no longer visible.
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;
}
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
}
}
if (!mWmService.mDisplayFrozen) {
final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
|| mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
// Post these on a handler such that we don't call into power manager service while
// holding the window manager lock to avoid lock contention with power manager lock.
mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
0).sendToTarget();
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
}
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
mWmService.mPowerManagerInternal.setPowerMode(
Mode.SUSTAINED_PERFORMANCE,
mSustainedPerformanceModeEnabled);
}
if (mUpdateRotation) {
ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");
mUpdateRotation = updateRotationUnchecked();
}
if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()
|| (mOrientationChangeComplete && !isLayoutNeeded()
&& !mUpdateRotation)) {
mWmService.checkDrawnWindowsLocked();
}
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
dc.updateKeepClearAreas();
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();
mWmService.scheduleAnimationLocked();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
}
我们接着看applySurfaceChangesTransaction()方法,这里会每个DisplayContent进行遍历:
可以看到,在RootWindowContainer内部的performSurfacePlacement()会顺序执行以下方法:
1.mService.openSurfaceTransaction(),通过SurfaceControl来通知native开始一个Transaction;
2.applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh)来处理Transaction;
3.mService.closeSurfaceTransaction(),通过SurfaceControl来通知native(SurfaceFlinger)关闭一个Transaction最终来执行合成显示等工作;
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private void applySurfaceChangesTransaction() {
// TODO(multi-display): Support these features on secondary screens.
final DisplayContent defaultDc = mDefaultDisplay;
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
final SurfaceControl.Transaction t = defaultDc.getSyncTransaction();
if (mWmService.mWatermark != null) {
mWmService.mWatermark.positionSurface(defaultDw, defaultDh, t);
}
if (mWmService.mStrictModeFlash != null) {
mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, t);
}
if (mWmService.mEmulatorDisplayOverlay != null) {
mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
defaultDc.getRotation(), t);
}
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
mWmService.mDisplayManagerInternal.performTraversal(t);
if (t != defaultDc.mSyncTransaction) {
SurfaceControl.mergeToGlobalTransaction(t);
}
}
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
beginHoldScreenUpdate();
mTmpUpdateAllDrawn.clear();
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
pendingLayoutChanges);
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();
}
// Perform a layout, if needed.
performLayout(true /* initial */, false /* updateInputWindows */);
pendingLayoutChanges = 0;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
try {
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
mDisplayPolicy.finishPostLayoutPolicyLw();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
mInsetsStateController.onPostLayout();
mTmpApplySurfaceChangesTransactionState.reset();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
try {
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
prepareSurfaces();
// This should be called after the insets have been dispatched to clients and we have
// committed finish drawing windows.
mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
mTmpApplySurfaceChangesTransactionState.disableHdrConversion,
true /* inTraversal, must call performTraversalInTrans... below */);
}
// If the display now has content, or no longer has content, update recording.
updateRecording();
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
}
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();
}
finishHoldScreenUpdate();
}
分为三部分:
1. 执行 performLayout
各个WindowState的WindowFrame由此步进行确认;
2.mDisplayPolicy.beginPostLayoutPolicyLw()
进行Display policy和Window policy的应用对各个WindowState应用当前的一些策略,并策略用于控制窗口的显示和隐藏状态中去。
3.mApplySurfaceChangesTransaction
遍历各个WindowState,根据携带参数确定是否修改逻辑屏显示参数、对WindowState状态进行由COMMIT_DRAW_PENDING到READY_TO_SHOW的转变;
4.prepareSurfaces
对每个Surface的位置、大小等做最后的准备,并提交给sGlobalTransation对象等待进行show或hide。
1.performLayout
可以看到,DisplayContent#mLayoutNeeded属性作为控制该操作是否执行的关键。这个方法中也分两个步骤:
- 对所有WindowState进行layout操作,mPerformLayout是一个函数接口Consumer的实例,通过forAllWindows()完成所有WindowState的遍历,完成对WindowFrame的确定;
- 对非TYPE_APPLICATION_ATTACHED_DIALOG类型的依附窗口进行额外的处理,WindowState#mPerformLayoutAttached属性表示依附窗口,即属于SubWindow类型的窗口。
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void performLayout(boolean initial, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
try {
performLayoutNoTrace(initial, updateInputWindows);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
}
clearLayoutNeeded();
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: dw=" + mDisplayInfo.logicalWidth
+ " dh=" + mDisplayInfo.logicalHeight);
}
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
mTmpInitial = initial;
// First perform layout of any root windows (not attached to another window).
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// Now perform layout of attached windows, which usually depend on the position of the
// window they are attached to. XXX does not deal with windows that are attached to windows
// that are themselves attached.
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
// Window frames may have changed. Tell the input dispatcher about it.
mInputMonitor.setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
}
// 进行非子窗口的layout
mLayoutNeeded
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mPerformLayout = w -> {
if (w.mLayoutAttached) {
return;
}
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
final boolean gone = w.isGoneForLayout();
if (DEBUG_LAYOUT) {
Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
+ " config reported=" + w.isLastConfigReportedToClient());
final ActivityRecord activity = w.mActivityRecord;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
+ " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
+ " visibleRequested=" + (activity != null && activity.isVisibleRequested())
+ " parentHidden=" + w.isParentWindowHidden());
else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility
+ " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
+ " visibleRequested=" + (activity != null && activity.isVisibleRequested())
+ " parentHidden=" + w.isParentWindowHidden());
}
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
if (!gone || !w.mHaveFrame || w.mLayoutNeeded) {
if (mTmpInitial) {
w.resetContentChanged();
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
final boolean firstLayout = !w.isLaidOut();
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
// If this is the first layout, we need to initialize the last frames and inset values,
// as otherwise we'd immediately cause an unnecessary resize.
if (firstLayout) {
// The client may compute its actual requested size according to the first layout,
// so we still request the window to resize if the current frame is empty.
if (!w.getFrame().isEmpty()) {
w.updateLastFrames();
}
w.onResizeHandled();
}
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
+ " mParentFrame=" + w.getParentFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
};
然后,当WindowState可见时,执行DisplayPolicy#layoutWindowLw()方法,这里将会根据DisplayFrame对象,对WindowFrame中的各个Rect对象属性进行确定。确定WindowFrame的过程
计算窗口位置大小
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win.skipLayout()) {
return;
}
// This window might be in the simulated environment.
// We invoke this to get the proper DisplayFrames.
displayFrames = win.getDisplayFrames(displayFrames);
final WindowManager.LayoutParams attrs = win.mAttrs.forRotation(displayFrames.mRotation);
sTmpClientFrames.attachedFrame = attached != null ? attached.getFrame() : null;
// If this window has different LayoutParams for rotations, we cannot trust its requested
// size. Because it might have not sent its requested size for the new rotation.
final boolean trustedSize = attrs == win.mAttrs;
final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibleTypes(), win.mGlobalScale, sTmpClientFrames);
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
computeFrames
frameworks/base/core/java/android/view/WindowLayout.java
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
float compatScale, ClientWindowFrames frames) {
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final Rect attachedWindowFrame = frames.attachedFrame;
final Rect outDisplayFrame = frames.displayFrame;
final Rect outParentFrame = frames.parentFrame;
final Rect outFrame = frames.frame;
// Compute bounds restricted by insets
final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
attrs.isFitInsetsIgnoringVisibility());
final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
windowBounds.right - right, windowBounds.bottom - bottom);
if (attachedWindowFrame == null) {
outParentFrame.set(outDisplayFrame);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
final InsetsSource source = state.peekSource(ID_IME);
if (source != null) {
outParentFrame.inset(source.calculateInsets(
outParentFrame, false /* ignoreVisibility */));
}
}
} else {
outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame);
}
// Compute bounds restricted by display cutout
final int cutoutMode = attrs.layoutInDisplayCutoutMode;
final DisplayCutout cutout = state.getDisplayCutout();
final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
frames.isParentFrameClippedByDisplayCutout = false;
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
// Ensure that windows with a non-ALWAYS display cutout mode are laid out in
// the cutout safe zone.
final Rect displayFrame = state.getDisplayFrame();
if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
if (displayFrame.width() < displayFrame.height()) {
displayCutoutSafeExceptMaybeBars.top = MIN_Y;
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
} else {
displayCutoutSafeExceptMaybeBars.left = MIN_X;
displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
}
final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0;
if (layoutInScreen && layoutInsetDecor
&& (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
|| cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, systemBars(), requestedVisibleTypes);
if (systemBarsInsets.left >= cutout.getSafeInsetLeft()) {
displayCutoutSafeExceptMaybeBars.left = MIN_X;
}
if (systemBarsInsets.top >= cutout.getSafeInsetTop()) {
displayCutoutSafeExceptMaybeBars.top = MIN_Y;
}
if (systemBarsInsets.right >= cutout.getSafeInsetRight()) {
displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
if (systemBarsInsets.bottom >= cutout.getSafeInsetBottom()) {
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
if (type == TYPE_INPUT_METHOD
&& displayCutoutSafeExceptMaybeBars.bottom != MAX_Y
&& state.calculateInsets(displayFrame, navigationBars(), true).bottom > 0) {
// The IME can always extend under the bottom cutout if the navbar is there.
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen;
// TYPE_BASE_APPLICATION windows are never considered floating here because they don't
// get cropped / shifted to the displayFrame in WindowState.
final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
&& type != TYPE_BASE_APPLICATION;
// Windows that are attached to a parent and laid out in said parent already avoid
// the cutout according to that parent and don't need to be further constrained.
// Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
mTempRect.set(outParentFrame);
outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
frames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
}
outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
}
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
final boolean inMultiWindowMode = WindowConfiguration.inMultiWindowMode(windowingMode);
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
outDisplayFrame.left = MIN_X;
outDisplayFrame.top = MIN_Y;
outDisplayFrame.right = MAX_X;
outDisplayFrame.bottom = MAX_Y;
}
final boolean hasCompatScale = compatScale != 1f;
final int pw = outParentFrame.width();
final int ph = outParentFrame.height();
final boolean extendedByCutout =
(attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
int rw = requestedWidth;
int rh = requestedHeight;
float x, y;
int w, h;
// If the view hierarchy hasn't been measured, the requested width and height would be
// UNSPECIFIED_LENGTH. This can happen in the first layout of a window or in the simulated
// layout. If extendedByCutout is true, we cannot use the requested lengths. Otherwise,
// the window frame might be extended again because the requested lengths may come from the
// window frame.
if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
rw = attrs.width >= 0 ? attrs.width : pw;
}
if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
rh = attrs.height >= 0 ? attrs.height : ph;
}
if ((attrs.flags & FLAG_SCALED) != 0) {
if (attrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
w = (int) (attrs.width * compatScale + .5f);
} else {
w = attrs.width;
}
if (attrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
h = (int) (attrs.height * compatScale + .5f);
} else {
h = attrs.height;
}
} else {
if (attrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
w = (int) (rw * compatScale + .5f);
} else {
w = rw;
}
if (attrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
h = (int) (rh * compatScale + .5f);
} else {
h = rh;
}
}
if (hasCompatScale) {
x = attrs.x * compatScale;
y = attrs.y * compatScale;
} else {
x = attrs.x;
y = attrs.y;
}
if (inMultiWindowMode
&& (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) == 0) {
// Make sure window fits in parent frame since it is in a non-fullscreen task as
// required by {@link Gravity#apply} call.
w = Math.min(w, pw);
h = Math.min(h, ph);
}
// We need to fit it to the display if either
// a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
// for the taskless windows)
// b) If it's a secondary app window, we also need to fit it to the display unless
// FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
// screen, but SurfaceViews want to be always at a specific location so we don't fit it to
// the display.
final boolean fitToDisplay = !inMultiWindowMode
|| ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
// Set mFrame
Gravity.apply(attrs.gravity, w, h, outParentFrame,
(int) (x + attrs.horizontalMargin * pw),
(int) (y + attrs.verticalMargin * ph), outFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
if (extendedByCutout) {
extendFrameByCutout(displayCutoutSafe, outDisplayFrame, outFrame,
mTempRect);
}
if (DEBUG) Log.d(TAG, "computeFrames " + attrs.getTitle()
+ " frames=" + frames
+ " windowBounds=" + windowBounds.toShortString()
+ " requestedWidth=" + requestedWidth
+ " requestedHeight=" + requestedHeight
+ " compatScale=" + compatScale
+ " windowingMode=" + WindowConfiguration.windowingModeToString(windowingMode)
+ " displayCutoutSafe=" + displayCutoutSafe
+ " attrs=" + attrs
+ " state=" + state
+ " requestedInvisibleTypes=" + WindowInsets.Type.toString(~requestedVisibleTypes));
}
setFrames
void setFrames(ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
final WindowFrames windowFrames = mWindowFrames;
mTmpRect.set(windowFrames.mParentFrame);
windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
windowFrames.mFrame.set(clientWindowFrames.frame);
windowFrames.mCompatFrame.set(windowFrames.mFrame);
if (mInvGlobalScale != 1f) {
// Also, the scaled frame that we report to the app needs to be adjusted to be in
// its coordinate space.
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
windowFrames.setParentFrameWasClippedByDisplayCutout(
clientWindowFrames.isParentFrameClippedByDisplayCutout);
// Calculate relative frame
windowFrames.mRelFrame.set(windowFrames.mFrame);
WindowContainer<?> parent = getParent();
int parentLeft = 0;
int parentTop = 0;
if (mIsChildWindow) {
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) {
final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
windowFrames.mFrame.top - parentTop);
if (requestedWidth != mLastRequestedWidth || requestedHeight != mLastRequestedHeight
|| !mTmpRect.equals(windowFrames.mParentFrame)) {
mLastRequestedWidth = requestedWidth;
mLastRequestedHeight = requestedHeight;
windowFrames.setContentChanged(true);
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
}
}
if (mIsWallpaper) {
final Rect lastFrame = windowFrames.mLastFrame;
final Rect frame = windowFrames.mFrame;
if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
mDisplayContent.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
}
}
updateSourceFrame(windowFrames.mFrame);
if (mActivityRecord != null && !mIsChildWindow) {
mActivityRecord.layoutLetterbox(this);
}
mSurfacePlacementNeeded = true;
mHaveFrame = true;
}
2. mApplyPostLayoutPolicy
接下来是WindowState对policy的应用,这一操作也分三步进行:
- DisplayPolicy.beginPostLayoutPolicy()对DisplayPolicy中相关参数进行重置;
- 函数接口mApplyPostLayoutPolicy对每个WindowState进行policy应用;
- DisplayPolicy.finishPostLayoutPolicyLw()进行完成policy应用后的操作;
这里也只对最关键的一部分——WindowState对policy的应用——进行分析,这一步通过函数接口对象mApplyPostLayoutPolicy
进行,mApplyPostLayoutPolicy收到回调后,将直接调用DisplayPolicy#applyPostLayoutPolicyLw()
方法,在applyPostLayoutPolicyLw()中,最终通过WindowManagerPolicy#applyKeyguardPolicy()对keyguard相关的policy进行了应用:
beginPostLayoutPolicy
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void beginPostLayoutPolicyLw() {
mLeftGestureHost = null;
mTopGestureHost = null;
mRightGestureHost = null;
mBottomGestureHost = null;
mTopFullscreenOpaqueWindowState = null;
mNavBarColorWindowCandidate = null;
mNavBarBackgroundWindowCandidate = null;
mStatusBarAppearanceRegionList.clear();
mLetterboxDetails.clear();
mStatusBarBackgroundWindows.clear();
mStatusBarColorCheckedBounds.setEmpty();
mStatusBarBackgroundCheckedBounds.setEmpty();
mSystemBarColorApps.clear();
mAllowLockscreenWhenOn = false;
mShowingDream = false;
mIsFreeformWindowOverlappingWithNavBar = false;
mForciblyShownTypes = 0;
}
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplyPostLayoutPolicy =
w -> getDisplayPolicy().applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(),
mImeLayeringTarget);
applyPostLayoutPolicyLw
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
if (attrs.type == TYPE_NAVIGATION_BAR) {
// Keep mNavigationBarPosition updated to make sure the transient detection and bar
// color control is working correctly.
final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
mNavigationBarPosition = navigationBarPosition(displayFrames.mRotation);
}
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
applyKeyguardPolicy(win, imeTarget);
// Check if the freeform window overlaps with the navigation bar area.
if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()
&& win.mActivityRecord != null && isOverlappingWithNavBar(win)) {
mIsFreeformWindowOverlappingWithNavBar = true;
}
if (win.hasInsetsSourceProvider()) {
final SparseArray<InsetsSourceProvider> providers = win.getInsetsSourceProviders();
final Rect bounds = win.getBounds();
for (int index = providers.size() - 1; index >= 0; index--) {
final InsetsSourceProvider provider = providers.valueAt(index);
final InsetsSource source = provider.getSource();
if ((source.getType()
& (Type.systemGestures() | Type.mandatorySystemGestures())) == 0) {
continue;
}
if (mLeftGestureHost != null && mTopGestureHost != null
&& mRightGestureHost != null && mBottomGestureHost != null) {
continue;
}
final Insets insets = source.calculateInsets(bounds, false /* ignoreVisibility */);
if (mLeftGestureHost == null && insets.left > 0) {
mLeftGestureHost = win;
}
if (mTopGestureHost == null && insets.top > 0) {
mTopGestureHost = win;
}
if (mRightGestureHost == null && insets.right > 0) {
mRightGestureHost = win;
}
if (mBottomGestureHost == null && insets.bottom > 0) {
mBottomGestureHost = win;
}
}
}
if (win.mSession.mCanForceShowingInsets) {
mForciblyShownTypes |= win.mAttrs.forciblyShownTypes;
}
if (!affectsSystemUi) {
return;
}
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
if (mTopFullscreenOpaqueWindowState == null) {
final int fl = attrs.flags;
if (win.isDreamWindow()) {
// If the lockscreen was showing when the dream started then wait
// for the dream to draw before hiding the lockscreen.
if (!mDreamingLockscreen || (win.isVisible() && win.hasDrawn())) {
mShowingDream = true;
appWindow = true;
}
}
if (appWindow && attached == null && attrs.isFullscreen()
&& (fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
mAllowLockscreenWhenOn = true;
}
}
// Check the windows that overlap with system bars to determine system bars' appearance.
if ((appWindow && attached == null && attrs.isFullscreen())
|| attrs.type == TYPE_VOICE_INTERACTION) {
// Record the top-fullscreen-app-window which will be used to determine system UI
// controlling window.
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
}
// Cache app windows that is overlapping with the status bar to determine appearance
// of status bar.
if (mStatusBar != null
&& sTmpRect.setIntersect(win.getFrame(), mStatusBar.getFrame())
&& !mStatusBarBackgroundCheckedBounds.contains(sTmpRect)) {
mStatusBarBackgroundWindows.add(win);
mStatusBarBackgroundCheckedBounds.union(sTmpRect);
if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
mStatusBarAppearanceRegionList.add(new AppearanceRegion(
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
new Rect(win.getFrame())));
mStatusBarColorCheckedBounds.union(sTmpRect);
addSystemBarColorApp(win);
}
}
// Cache app window that overlaps with the navigation bar area to determine opacity
// and appearance of the navigation bar. We only need to cache one window because
// there should be only one overlapping window if it's not in gesture navigation
// mode; if it's in gesture navigation mode, the navigation bar will be
// NAV_BAR_FORCE_TRANSPARENT and its appearance won't be decided by overlapping
// windows.
if (isOverlappingWithNavBar(win)) {
if (mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
addSystemBarColorApp(win);
}
if (mNavBarBackgroundWindowCandidate == null) {
mNavBarBackgroundWindowCandidate = win;
}
}
// Check if current activity is letterboxed in order create a LetterboxDetails
// component to be passed to SysUI for status bar treatment
final ActivityRecord currentActivity = win.getActivityRecord();
if (currentActivity != null) {
final LetterboxDetails currentLetterboxDetails = currentActivity
.mLetterboxUiController.getLetterboxDetails();
if (currentLetterboxDetails != null) {
mLetterboxDetails.add(currentLetterboxDetails);
}
}
} else if (win.isDimming()) {
if (mStatusBar != null) {
// If the dim window is below status bar window, we should update the appearance
// region if needed. Otherwise, leave it as it is.
final int statusBarLayer = mStatusBar.mToken.getWindowLayerFromType();
final int targetWindowLayer = win.mToken.getWindowLayerFromType();
if (targetWindowLayer < statusBarLayer
&& addStatusBarAppearanceRegionsForDimmingWindow(
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
mStatusBar.getFrame(), win.getBounds(), win.getFrame())) {
addSystemBarColorApp(win);
}
}
if (isOverlappingWithNavBar(win) && mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
addSystemBarColorApp(win);
}
} else if (appWindow && attached == null
&& (mNavBarColorWindowCandidate == null || mNavBarBackgroundWindowCandidate == null)
&& win.getFrame().contains(
getBarContentFrameForWindow(win, Type.navigationBars()))) {
if (mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
addSystemBarColorApp(win);
}
if (mNavBarBackgroundWindowCandidate == null) {
mNavBarBackgroundWindowCandidate = win;
}
}
}
applyKeyguardPolicy()方法中,将根据当前是否锁屏和WindowState是否能在锁屏上显示,对WindowState设置相应的可见标记:
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
private void applyKeyguardPolicy(WindowState win, WindowState imeTarget) {
if (win.canBeHiddenByKeyguard()) {
final boolean shouldBeHiddenByKeyguard = shouldBeHiddenByKeyguard(win, imeTarget);
if (win.mIsImWindow) {
// Notify IME insets provider to freeze the IME insets. In case when turning off
// the screen, the IME insets source window will be hidden because of keyguard
// policy change and affects the system to freeze the last insets state. (And
// unfreeze when the IME is going to show)
mDisplayContent.getInsetsStateController().getImeSourceProvider().setFrozen(
shouldBeHiddenByKeyguard);
}
if (shouldBeHiddenByKeyguard) {
win.hide(false /* doAnimation */, true /* requestAnim */);
} else {
win.show(false /* doAnimation */, true /* requestAnim */);
}
}
}
首先会判断当前WindowState是否能够被keyguard隐藏,这将直接影响到窗口的显示。如果能够被隐藏,则接下来会判断是否应该进行隐藏,如果需要进行隐藏,则调用WindowState#hide()进行隐藏,否则调用WindowState#show()进行显示。下面依次看下这个过程。
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) {
if (!mDisplayContent.isDefaultDisplay || !isKeyguardShowing()) {
return false;
}
// Show IME over the keyguard if the target allows it.
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisible()
&& win.mIsImWindow && (imeTarget.canShowWhenLocked()
|| !imeTarget.canBeHiddenByKeyguard());
if (showImeOverKeyguard) {
return false;
}
// Show SHOW_WHEN_LOCKED windows if keyguard is occluded.
final boolean allowShowWhenLocked = isKeyguardOccluded()
// Show error dialogs over apps that are shown on keyguard.
&& (win.canShowWhenLocked()
|| (win.mAttrs.privateFlags & LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR) != 0);
return !allowShowWhenLocked;
}
3. mApplySurfaceChangesTransaction
遍历各个WindowState,根据携带参数确定是否修改逻辑屏显示参数、对WindowState状态进行由COMMIT_DRAW_PENDING到READY_TO_SHOW的转变;
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
final RootWindowContainer root = mWmService.mRoot;
// Update effect.
// 确定当前WindowState是否被上面的WindowState遮挡
// 确定是否会遮挡之后进行遍历的WindowState
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
if (!mTmpApplySurfaceChangesTransactionState.obscured) {
final boolean isDisplayed = w.isDisplayed();
if (isDisplayed && w.isObscuringDisplay()) {
// This window completely covers everything behind it, so we want to leave all
// of them as undimmed (for performance reasons).
mObscuringWindow = w;
mTmpApplySurfaceChangesTransactionState.obscured = true;
}
final boolean displayHasContent = root.handleNotObscuredLocked(w,
mTmpApplySurfaceChangesTransactionState.obscured,
mTmpApplySurfaceChangesTransactionState.syswin);
if (!mTmpApplySurfaceChangesTransactionState.displayHasContent
&& !getDisplayPolicy().isWindowExcludedFromContent(w)) {
mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent;
}
if (w.mHasSurface && isDisplayed) {
if ((w.mAttrs.flags & FLAG_KEEP_SCREEN_ON) != 0) {
mTmpHoldScreenWindow = w;
} else if (w == mLastWakeLockHoldingWindow) {
ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
"handleNotObscuredLocked: %s was holding screen wakelock but no longer "
+ "has FLAG_KEEP_SCREEN_ON!!! called by%s",
w, Debug.getCallers(10));
}
final int type = w.mAttrs.type;
if (type == TYPE_SYSTEM_DIALOG
|| type == TYPE_SYSTEM_ERROR
|| (type == TYPE_NOTIFICATION_SHADE
&& mWmService.mPolicy.isKeyguardShowing())) {
mTmpApplySurfaceChangesTransactionState.syswin = true;
}
if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0
&& w.mAttrs.preferredRefreshRate != 0) {
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
= w.mAttrs.preferredRefreshRate;
}
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing
|= w.mAttrs.preferMinimalPostProcessing;
mTmpApplySurfaceChangesTransactionState.disableHdrConversion
|= !(w.mAttrs.isHdrConversionEnabled());
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
if (w.getWindowingMode() != WINDOWING_MODE_PINNED
&& mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
&& preferredModeId != 0) {
mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
}
final float preferredMinRefreshRate = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredMinRefreshRate(w);
if (mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate == 0
&& preferredMinRefreshRate != 0) {
mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate =
preferredMinRefreshRate;
}
final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredMaxRefreshRate(w);
if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0
&& preferredMaxRefreshRate != 0) {
mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate =
preferredMaxRefreshRate;
}
}
}
if (obscuredChanged && w.isVisible() && mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
// current wallpaper's visibility has been updated accordingly.
mWallpaperController.updateWallpaperTokens(mDisplayContent.isKeyguardLocked());
}
w.handleWindowMovedIfNeeded();
final WindowStateAnimator winAnimator = w.mWinAnimator;
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
w.resetContentChanged();
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
// Take care of the window being ready to display.
final boolean committed = winAnimator.commitFinishDrawingLocked();
if (isDefaultDisplay && committed) {
if (w.hasWallpaper()) {
ProtoLog.v(WM_DEBUG_WALLPAPER,
"First draw done in potential wallpaper target %s", w);
mWallpaperMayChange = true;
pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats(
"wallpaper and commitFinishDrawingLocked true",
pendingLayoutChanges);
}
}
}
}
final ActivityRecord activity = w.mActivityRecord;
if (activity != null && activity.isVisibleRequested()) {
activity.updateLetterboxSurface(w);
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
mTmpUpdateAllDrawn.add(activity);
}
}
w.updateResizingWindowIfNeeded();
};
commitFinishDrawingLocked
接下来执行mApplySurfaceChangesTransaction
函数接口,在该接口中,会遍历各个WindowState,根据携带参数确定是否修改逻辑屏显示参数,并且对WindowState状态进行由COMMIT_DRAW_PENDING
到READY_TO_SHOW
的转变:
frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked() {
if (DEBUG_STARTING_WINDOW_VERBOSE &&
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState="
+ drawStateToString());
}
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
mSurfaceController);
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
// This must be called while inside a transaction.
boolean performShowLocked() {
if (!showToCurrentUser()) {
if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
return false;
}
logPerformShow("performShow on ");
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}
}
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
return false;
}
logPerformShow("Showing ");
mWmService.enableScreenIfNeededLocked();
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
if (mHidden) {
mHidden = false;
final DisplayContent displayContent = getDisplayContent();
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
if (c.mWinAnimator.mSurfaceController != null) {
c.performShowLocked();
// It hadn't been shown, which means layout not performed on it, so now we
// want to make sure to do a layout. If called from within the transaction
// loop, this will cause it to restart with a new layout.
if (displayContent != null) {
displayContent.setLayoutNeeded();
}
}
}
}
return true;
}
经过这个方法,窗口的绘制状态将由COMMIT_DRAW_PENDING
变为READY_TO_SHOW
状态,进入这个状态,表示窗口已经绘制完成并且完成提交,接下来将等待同一WindowToken的窗口完成绘制后,进行最终的show操作。
窗口的显示过程共有五个状态:
- NO_SURFACE:在创建WindowState后的默认状态,表示当前窗口还创没有执行relayout()方法创建Surface;
- DRAW_PENDING:执行relayout()方法后,创建完成Surface后的状态,表示等待绘制;
- COMMIT_DRAW_PENDING:窗口Surface上完成绘制后的状态,执行WindowStateAnimator#finishDrawingLocked()方法设置,表示已经完成绘制,等待下次刷帧进行提交;
- READY_TO_SHOW:表示窗口已经绘制完成并且完成提交,此时如果该窗口的兄弟窗口全部完成绘制且满足显示要求,则直接进行HAS_DRAWN的转变完成显示,否则等待其他兄弟窗口完成绘制后,再进行HAS_DRAWN转变;
- HAS_DRAWN:表示该窗口正式显示;
至此,mApplySurfaceChangesTransaction中的内容全部完毕。
4.prepareSurfaces
接下来执行prepareSurfaces
prepareSurfaces()对所有SurfaceControl进行最后的准备
prepareSurfaces()方法将会对每个WindowContainer的Surface做最后的准备工作,各个Surface位置、大小、是否显示在屏幕上等,都将通过这个方法来进行设置,并在关闭事务后,提交给SurfaceFlinger进行显示。
相比前面几个函数接口都是针对WindowState进行遍历,该方法则针对所有的WindowContainer进行遍历,遍历方式是由父容器→ 子容器进行,
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
@Override
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
final Transaction transaction = getPendingTransaction();
super.prepareSurfaces();
// TODO: Once we totally eliminate global transaction we will pass transaction in here
// rather than merging to global.
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
后面可以看到,所有的prepareSurfaces()都是将SurfaceControl提交在了mPendingTransaction上。然后完成遍历后,将mPendingTransaction合并到全局Transaction对象上提交给SurfaceFlinger。
在ActivityRecord中,将会根据其mVisible属性,对它的mSurfaceControl进行show或hide操作,同时遍历子容器prepareSurfaces操作:
在这个方法中,通过getSyncTransaction()方法得到Transaction对象,并在该Transaction上对该容器的mSurfaceControl进行show或hide的操作。这个Transaction对象其实就是DisplayContent#mPendingTransaction对象。
对于所有DisplayContent以下的WindowContainer来说,它的mSurfaceControl对象并非是直接进行内容显示的Surface载体,真正的载体是在relayout()过程中由WindowSurfaceController管理并创建的对于由ActivityRecord管理的mSurfaceControl对象,而且WindowContainer#mSurfaceControl作为WindowSurfaceController#mSurfaceControl的父SurfaceControl。对于SurfaceFlinger来说,WindowContainer#mSurfaceControl是一个ContainerLayer,WindowSurfaceController#mSurfaceControl才真正进行绘制的BufferQueueLayer。
这也是为何对于由ActivityRecord来管理的WindowState而言,其可见状态由ActivityRecord控制(canBeHiddenByKeyguardLw()第一个return false)。
frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void prepareSurfaces() {
final boolean show = isVisible() || isAnimating(PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
| ANIMATION_TYPE_PREDICT_BACK);
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
getSyncTransaction().show(mSurfaceControl);
} else if (!show && mLastSurfaceShowing) {
getSyncTransaction().hide(mSurfaceControl);
}
// Input sink surface is not a part of animation, so just apply in a steady state
// (non-sync) with pending transaction.
if (show && mSyncState == SYNC_STATE_NONE) {
mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getPendingTransaction());
}
}
if (mThumbnail != null) {
mThumbnail.setShowing(getPendingTransaction(), show);
}
mLastSurfaceShowing = show;
super.prepareSurfaces();
}
WindowState#prepareSurfaces()方法,是所有prepareSurfaces()方法中最重要的,在这个方法中,将会设置SurfaceControl的位置、Alpha值、Matrix、Crop等各种属性,下面来看这个方法:
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Override
void prepareSurfaces() {
mIsDimming = false;
if (mHasSurface) {
// 更新dim
applyDims();
//更新位置
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
//更新scale
updateScaleIfNeeded();
// 进入WindowStateAnimator中进行实际Surface的准备工作
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
}
super.prepareSurfaces();
}
更新位置 updateSurfacePositionNonOrganized
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
final void updateSurfacePositionNonOrganized() {
// Avoid fighting with the organizer over Surface position.
if (isOrganized()) return;
updateSurfacePosition(getSyncTransaction());
}
/**
* Only for use internally (see PROTECTED annotation). This should only be used over
* {@link #updateSurfacePositionNonOrganized} when the surface position needs to be
* updated even if organized (eg. while changing to organized).
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
return;
}
if (isClosingWhenResizing()) {
// This container is closing while resizing, keep its surface at the starting position
// to prevent animation flicker.
getRelativePosition(mDisplayContent.mClosingChangingContainers.get(this), mTmpPos);
} else {
getRelativePosition(mTmpPos);
}
final int deltaRotation = getRelativeDisplayRotation();
if (mTmpPos.equals(mLastSurfacePosition) && deltaRotation == mLastDeltaRotation) {
return;
}
t.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
// set first, since we don't want rotation included in this (for now).
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
if (mTransitionController.isShellTransitionsEnabled()
&& !mTransitionController.useShellTransitionsRotation()) {
if (deltaRotation != Surface.ROTATION_0) {
updateSurfaceRotation(t, deltaRotation, null /* positionLeash */);
getPendingTransaction().setFixedTransformHint(mSurfaceControl,
getWindowConfiguration().getDisplayRotation());
} else if (deltaRotation != mLastDeltaRotation) {
t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
getPendingTransaction().unsetFixedTransformHint(mSurfaceControl);
}
}
mLastDeltaRotation = deltaRotation;
}
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
return;
}
if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
&& !mSurfacePlacementNeeded) {
// Since this relies on mWindowFrames, changes made while layout is deferred are
// likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
// up-to-date and thus can't be relied on.
return;
}
mSurfacePlacementNeeded = false;
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
mSurfacePosition);
if (mWallpaperScale != 1f) {
final Rect bounds = getParentFrame();
Matrix matrix = mTmpMatrix;
matrix.setTranslate(mXOffset, mYOffset);
matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
bounds.exactCenterY());
matrix.getValues(mTmpMatrixArray);
mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
} else {
mSurfacePosition.offset(mXOffset, mYOffset);
}
// Freeze position while we're unrotated, so the surface remains at the position it was
// prior to the rotation.
if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
&& !mLastSurfacePosition.equals(mSurfacePosition)) {
final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported();
final boolean surfaceInsetsChanged = surfaceInsetsChanging();
final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
if (surfaceInsetsChanged) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
}
final boolean surfaceResizedWithoutMoveAnimation = surfaceSizeChanged
&& mWinAnimator.getShown() && !canPlayMoveAnimation() && okToDisplay()
&& mSyncState == SYNC_STATE_NONE;
final ActivityRecord activityRecord = getActivityRecord();
// If this window belongs to an activity that is relaunching due to an orientation
// change then delay the position update until it has redrawn to avoid any flickers.
final boolean isLetterboxedAndRelaunching = activityRecord != null
&& activityRecord.areBoundsLetterboxed()
&& activityRecord.mLetterboxUiController
.getIsRelaunchingAfterRequestedOrientationChanged();
if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) {
applyWithNextDraw(mSetSurfacePositionConsumer);
} else {
mSetSurfacePositionConsumer.accept(t);
}
}
}
在这个方法中,首先会使用WindowState的mWindowFrames.mFrame获得SurfaceControl的位置坐标点,然后通过Transaction#setPosition()方法完成设置。因此,每个窗口的WindowFrame对象的mFrame属性,就决定了该窗口要具体显示的位置。
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
// Only apply the position to the surface when there's no leash created.
if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
}
};
然后看/进入WindowStateAnimator中进行实际Surface的准备工作
frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
if (!hasSurface()) {
// There is no need to wait for an animation change if our window is gone for layout
// already as we'll never be visible.
if (w.getOrientationChanging() && w.isGoneForLayout()) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w);
w.setOrientationChanging(false);
}
return;
}
computeShownFrameLocked();
if (!w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
// If we are waiting for this window to handle an orientation change. If this window is
// really hidden (gone for layout), there is no point in still waiting for it.
// Note that this does introduce a potential glitch if the window becomes unhidden
// before it has drawn for the new orientation.
if (w.getOrientationChanging() && w.isGoneForLayout()) {
w.setOrientationChanging(false);
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation change skips hidden %s", w);
}
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
mLastAlpha = mShownAlpha;
ProtoLog.i(WM_SHOW_TRANSACTIONS,
"SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);
boolean prepared =
mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
mSurfaceController.showRobustly(t);
mLastHidden = false;
final DisplayContent displayContent = w.getDisplayContent();
if (!displayContent.getLastHasContent()) {
// This draw means the difference between unique content and mirroring.
// Run another pass through performLayout to set mHasContent in the
// LogicalDisplay.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
"showSurfaceRobustlyLocked " + w,
displayContent.pendingLayoutChanges);
}
}
}
}
}
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
if (w.mDisplayContent.shouldSyncRotationChange(w)) {
w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
} else {
w.setOrientationChanging(false);
ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
}
}
}
计算要显示的alpha
void computeShownFrameLocked() {
if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
} else if (mWin.isDragResizeChanged()) {
// This window is awaiting a relayout because user just started (or ended)
// drag-resizing. The shown frame (which affects surface size and pos)
// should not be updated until we get next finished draw with the new surface.
// Otherwise one or two frames rendered with old settings would be displayed
// with new geometry.
return;
}
if (DEBUG) {
Slog.v(TAG, "computeShownFrameLocked: " + this
+ " not attached, mAlpha=" + mAlpha);
}
mShownAlpha = mAlpha;
}
WindowSurfaceController#prepareToShowInTransaction():设置SurfaceControl的Alpha
boolean prepareToShowInTransaction(SurfaceControl.Transaction t, float alpha) {
if (mSurfaceControl == null) {
return false;
}
mSurfaceAlpha = alpha;
t.setAlpha(mSurfaceControl, alpha);
return true;
}
-
对SurfaceControl进行显示或隐藏;
- WindowSurfaceController#showRobustly():
frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java
void showRobustly(SurfaceControl.Transaction t) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ " during relayout");
if (mSurfaceShown) {
return;
}
setShown(true);
t.show(mSurfaceControl);
if (mAnimator.mIsWallpaper) {
final DisplayContent dc = mAnimator.mWin.getDisplayContent();
EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
dc.mDisplayId, 1 /* request shown */,
String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
}
}
当所有WindowContainer#prepareSurfaces()完成后,DisplayContent#applySurfaceChangesTransaction()大部分逻辑执行完毕。
回到RootWindowContainer#performSurfacePlacementNoTrace()方法中,将会通过SurfaceControl.closeTransaction()方法对事务进行提交,此时该Transaction对象上的所有show的SurfaceControl,都会送到SurfaceFlinger中,由SurfaceFlinger进一步处理,进行合成,最终显示在界面上。这个方法中,事务提交之后的流程就暂且略过。
4.总结
以上就是对WindowManagerService中对所有窗口Surface固定工作流程的一些分析,主要还是对事务内的一些逻辑进行了重点分析。在整个过程中,充分利用了各个容器之间的关系和函数接口,从DisplayContent到叶子节点WindowState,进行了完整的遍历,完成了对所有Surface的摆放工作。