From db407ef12de94280658d22835856377cc6b6b96a Mon Sep 17 00:00:00 2001 From: Tiger Huang Date: Mon, 21 Mar 2022 22:06:07 +0800 Subject: [PATCH] Introduce updateVisibility and updateLayout This is a step to let the client layout its window locally. The ultimate goal is to reduce the jank while laying out the window. With the new AIDL methods, we can divide IWindowSession.relayout into: - IWindowSession#updateVisibility (synchronized binder call) - To get or update the surface - To fetch the latest factors about window-layout - Only called when the view visibility is changed - WindowLayout#computeFrames (local function call) - To compute the window frames - IWindowSession#updateLayout (one way binder call) - To report the result of layout to the server In this way, if the view visibility is not changed, the UI thread of the client won't be blocked by the binder call during relayout. The local layout project won't be done in a single CL. In order not to break the existing logic, this CL introduces a flag: LOCAL_LAYOUT. The flag will be enabled when the logic of local layout is ready. Bug: 161810301 Bug: 175861051 Test: presubmit (no behavior change) Change-Id: Ic4b2fc78a318f3a68e1ef8a35d8f3ab705856702 --- .../service/wallpaper/WallpaperService.java | 35 ++++++-- core/java/android/view/IWindowSession.aidl | 35 ++++++++ core/java/android/view/ViewRootImpl.java | 88 ++++++++++++++----- .../android/view/WindowlessWindowManager.java | 15 ++++ .../java/com/android/server/wm/Session.java | 25 ++++++ .../server/wm/WindowManagerService.java | 13 +++ 6 files changed, 184 insertions(+), 27 deletions(-) diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 317408387526f..c20fc75a51f36 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -24,6 +24,7 @@ import static android.graphics.Matrix.MSKEW_X; import static android.graphics.Matrix.MSKEW_Y; import static android.view.SurfaceControl.METADATA_WINDOW_TYPE; import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; +import static android.view.ViewRootImpl.LOCAL_LAYOUT; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import android.animation.Animator; @@ -39,6 +40,7 @@ import android.app.Service; import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; +import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -257,6 +259,8 @@ public abstract class WallpaperService extends Service { private final Point mLastSurfaceSize = new Point(); private final Matrix mTmpMatrix = new Matrix(); private final float[] mTmpValues = new float[9]; + private final WindowLayout mWindowLayout = new WindowLayout(); + private final Rect mTempRect = new Rect(); final WindowManager.LayoutParams mLayout = new WindowManager.LayoutParams(); @@ -1091,7 +1095,8 @@ public abstract class WallpaperService extends Service { | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; final Configuration config = mMergedConfiguration.getMergedConfiguration(); - final Rect maxBounds = config.windowConfiguration.getMaxBounds(); + final WindowConfiguration winConfig = config.windowConfiguration; + final Rect maxBounds = winConfig.getMaxBounds(); if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) { mLayout.width = myWidth; @@ -1149,10 +1154,28 @@ public abstract class WallpaperService extends Service { mLayout.surfaceInsets.set(0, 0, 0, 0); } - final int relayoutResult = mSession.relayout( - mWindow, mLayout, mWidth, mHeight, - View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl, - mInsetsState, mTempControls, mSyncSeqIdBundle); + int relayoutResult = 0; + if (LOCAL_LAYOUT) { + if (!mSurfaceControl.isValid()) { + relayoutResult = mSession.updateVisibility(mWindow, mLayout, + View.VISIBLE, mMergedConfiguration, mSurfaceControl, + mInsetsState, mTempControls); + } + + final Rect displayCutoutSafe = mTempRect; + mInsetsState.getDisplayCutoutSafe(displayCutoutSafe); + mWindowLayout.computeFrames(mLayout, mInsetsState, displayCutoutSafe, + winConfig.getBounds(), winConfig.getWindowingMode(), mWidth, + mHeight, mRequestedVisibilities, null /* attachedWindowFrame */, + 1f /* compatScale */, mWinFrames); + + mSession.updateLayout(mWindow, mLayout, 0 /* flags */, mWinFrames, mWidth, + mHeight); + } else { + relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight, + View.VISIBLE, 0, mWinFrames, mMergedConfiguration, + mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle); + } final int transformHint = SurfaceControl.rotationToBufferTransform( (mDisplayInstallOrientation + mDisplay.getRotation()) % 4); @@ -1202,7 +1225,7 @@ public abstract class WallpaperService extends Service { null /* ignoringVisibilityState */, config.isScreenRound(), false /* alwaysConsumeSystemBars */, mLayout.softInputMode, mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type, - config.windowConfiguration.getWindowingMode(), null /* typeSideMap */); + winConfig.getWindowingMode(), null /* typeSideMap */); if (!fixedSize) { final Rect padding = mIWallpaperEngine.mDisplayPadding; diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index fd8690009a6d2..70986024bc5bc 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -107,6 +107,41 @@ interface IWindowSession { out InsetsState insetsState, out InsetsSourceControl[] activeControls, out Bundle bundle); + /** + * Changes the view visibility and the attributes of a window. This should only be called when + * the visibility of the root view is changed. This returns a valid surface if the root view is + * visible. This also returns the latest information for the caller to compute its window frame. + * + * @param window The window being updated. + * @param attrs If non-null, new attributes to apply to the window. + * @param viewVisibility Window root view's visibility. + * @param outMergedConfiguration New config container that holds global, override and merged + * config for window, if it is now becoming visible and the merged configuration has changed + * since it was last displayed. + * @param outSurfaceControl Object in which is placed the new display surface. + * @param outInsetsState The current insets state in the system. + * @param outActiveControls The insets source controls for the caller to override the insets + * state in the system. + * + * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}. + */ + int updateVisibility(IWindow window, in WindowManager.LayoutParams attrs, int viewVisibility, + out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl, + out InsetsState outInsetsState, out InsetsSourceControl[] outActiveControls); + + /** + * Reports the layout results and the attributes of a window to the server. + * + * @param window The window being reported. + * @param attrs If non-null, new attributes to apply to the window. + * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}. + * @param clientFrames the window frames computed by the client. + * @param requestedWidth The width the window wants to be. + * @param requestedHeight The height the window wants to be. + */ + oneway void updateLayout(IWindow window, in WindowManager.LayoutParams attrs, int flags, + in ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight); + /* * Notify the window manager that an application is relaunching and * windows should be prepared for replacement. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 172cd03900e7e..2613c1a2992fc 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -78,11 +78,13 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER; @@ -278,6 +280,13 @@ public final class ViewRootImpl implements ViewParent, */ public static final boolean CAPTION_ON_SHELL = false; + /** + * Whether the client should compute the window frame on its own. + * @hide + */ + public static final boolean LOCAL_LAYOUT = + SystemProperties.getBoolean("persist.debug.local_layout", false); + /** * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. @@ -8083,17 +8092,68 @@ public final class ViewRootImpl implements ViewParent, final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f); final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f); - int relayoutResult = mWindowSession.relayout(mWindow, params, - requestedWidth, requestedHeight, viewVisibility, - insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, - mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, - mTempControls, mRelayoutBundle); - mSyncSeqId = mRelayoutBundle.getInt("seqid"); + mWillMove = false; + mWillResize = false; + int relayoutResult = 0; + WindowConfiguration winConfig = getConfiguration().windowConfiguration; + if (LOCAL_LAYOUT) { + if (mFirst || viewVisibility != mViewVisibility) { + relayoutResult = mWindowSession.updateVisibility(mWindow, params, viewVisibility, + mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls); + if (mTranslator != null) { + mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); + mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); + } + mInsetsController.onStateChanged(mTempInsets); + mInsetsController.onControlsChanged(mTempControls); + + mPendingAlwaysConsumeSystemBars = + (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; + } + final InsetsState state = mInsetsController.getState(); + final Rect displayCutoutSafe = mTempRect; + state.getDisplayCutoutSafe(displayCutoutSafe); + if (mWindowAttributes.type == TYPE_APPLICATION_STARTING) { + // TODO(b/210378379): Remove the special logic. + // Letting starting window use the window bounds from the pending config is for the + // fixed rotation, because the config is not overridden before the starting window + // is created. + winConfig = mPendingMergedConfiguration.getMergedConfiguration() + .windowConfiguration; + } + mWindowLayout.computeFrames(mWindowAttributes, state, displayCutoutSafe, + winConfig.getBounds(), winConfig.getWindowingMode(), requestedWidth, + requestedHeight, mInsetsController.getRequestedVisibilities(), + getAttachedWindowFrame(), 1f /* compatScale */, mTmpFrames); + + mWindowSession.updateLayout(mWindow, params, + insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames, + requestedWidth, requestedHeight); + + } else { + relayoutResult = mWindowSession.relayout(mWindow, params, + requestedWidth, requestedHeight, viewVisibility, + insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, + mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, + mTempControls, mRelayoutBundle); + mSyncSeqId = mRelayoutBundle.getInt("seqid"); + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); + mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame); + mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); + mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); + } + mInsetsController.onStateChanged(mTempInsets); + mInsetsController.onControlsChanged(mTempControls); + + mPendingAlwaysConsumeSystemBars = + (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; + } final int transformHint = SurfaceControl.rotationToBufferTransform( (mDisplayInstallOrientation + mDisplay.getRotation()) % 4); - final WindowConfiguration winConfig = getConfiguration().windowConfiguration; WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth, requestedHeight, mTmpFrames.frame, mPendingDragResizing, mSurfaceSize); @@ -8142,24 +8202,10 @@ public final class ViewRootImpl implements ViewParent, destroySurface(); } - mPendingAlwaysConsumeSystemBars = - (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; - if (restore) { params.restore(); } - - if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); - mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame); - mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); - mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); - } setFrame(mTmpFrames.frame); - mWillMove = false; - mWillResize = false; - mInsetsController.onStateChanged(mTempInsets); - mInsetsController.onControlsChanged(mTempControls); return relayoutResult; } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 06588b2cbb6d0..385a80d7c32cf 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -335,6 +335,21 @@ public class WindowlessWindowManager implements IWindowSession { return 0; } + @Override + public int updateVisibility(IWindow window, WindowManager.LayoutParams inAttrs, + int viewVisibility, MergedConfiguration outMergedConfiguration, + SurfaceControl outSurfaceControl, InsetsState outInsetsState, + InsetsSourceControl[] outActiveControls) { + // TODO(b/161810301): Finish the implementation. + return 0; + } + + @Override + public void updateLayout(IWindow window, WindowManager.LayoutParams inAttrs, int flags, + ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) { + // TODO(b/161810301): Finish the implementation. + } + @Override public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) { } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 9ad25ac8876fe..f75a06d99d6cc 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -116,6 +116,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private float mLastReportedAnimatorScale; private String mPackageName; private String mRelayoutTag; + private String mUpdateViewVisibilityTag; + private String mUpdateWindowLayoutTag; private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities(); private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0]; final boolean mSetsUnrestrictedKeepClearAreas; @@ -222,6 +224,27 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { mService.removeWindow(this, window); } + @Override + public int updateVisibility(IWindow client, WindowManager.LayoutParams attrs, + int viewVisibility, MergedConfiguration outMergedConfiguration, + SurfaceControl outSurfaceControl, InsetsState outInsetsState, + InsetsSourceControl[] outActiveControls) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateViewVisibilityTag); + int res = mService.updateViewVisibility(this, client, attrs, viewVisibility, + outMergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + return res; + } + + @Override + public void updateLayout(IWindow window, WindowManager.LayoutParams attrs, int flags, + ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateWindowLayoutTag); + mService.updateWindowLayout(this, window, attrs, flags, clientFrames, requestedWidth, + requestedHeight); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + @Override public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) { mService.setWillReplaceWindows(appToken, childrenOnly); @@ -689,6 +712,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { if (wpc != null) { mPackageName = wpc.mInfo.packageName; mRelayoutTag = "relayoutWindow: " + mPackageName; + mUpdateViewVisibilityTag = "updateVisibility: " + mPackageName; + mUpdateWindowLayoutTag = "updateLayout: " + mPackageName; } else { Slog.e(TAG_WM, "Unknown process pid=" + mPid); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 29e292da5697e..6f07f0f945e3e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2619,6 +2619,19 @@ public class WindowManagerService extends IWindowManager.Stub return result; } + int updateViewVisibility(Session session, IWindow client, LayoutParams attrs, + int viewVisibility, MergedConfiguration outMergedConfiguration, + SurfaceControl outSurfaceControl, InsetsState outInsetsState, + InsetsSourceControl[] outActiveControls) { + // TODO(b/161810301): Finish the implementation. + return 0; + } + + void updateWindowLayout(Session session, IWindow client, LayoutParams attrs, int flags, + ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) { + // TODO(b/161810301): Finish the implementation. + } + public boolean outOfMemoryWindow(Session session, IWindow client) { final long origId = Binder.clearCallingIdentity();