diff options
4 files changed, 163 insertions, 12 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 57812c1d604c..2b5648f39d2c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2962,6 +2962,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId, mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight); mDisplayRotation.physicalDisplayChanged(); + mDisplayPolicy.physicalDisplayChanged(); } // If there is an override set for base values - use it, otherwise use new values. @@ -2993,6 +2994,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp reconfigureDisplayLocked(); if (physicalDisplayChanged) { + mDisplayPolicy.physicalDisplayUpdated(); mDisplaySwitchTransitionLauncher.onDisplayUpdated(currentRotation, getRotation(), getDisplayAreaInfo()); } @@ -3042,7 +3044,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + mBaseDisplayHeight + " on display:" + getDisplayId()); } } - if (mDisplayReady) { + if (mDisplayReady && !mDisplayPolicy.shouldKeepCurrentDecorInsets()) { mDisplayPolicy.mDecorInsets.invalidate(); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index be52e5a4566b..0dac7d6cb546 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -173,6 +173,10 @@ public class DisplayPolicy { private static final int INSETS_OVERRIDE_INDEX_INVALID = -1; + // TODO(b/266197298): Remove this by a more general protocol from the insets providers. + private static final boolean USE_CACHED_INSETS_FOR_DISPLAY_SWITCH = + SystemProperties.getBoolean("persist.wm.debug.cached_insets_switch", false); + private final WindowManagerService mService; private final Context mContext; private final Context mUiContext; @@ -217,6 +221,8 @@ public class DisplayPolicy { private final SystemGesturesPointerEventListener mSystemGestures; final DecorInsets mDecorInsets; + /** Currently it can only be non-null when physical display switch happens. */ + private DecorInsets.Cache mCachedDecorInsets; private volatile int mLidState = LID_ABSENT; private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; @@ -1695,11 +1701,7 @@ public class DisplayPolicy { * Called when the configuration has changed, and it's safe to load new values from resources. */ public void onConfigurationChanged() { - final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); - final Resources res = getCurrentUserResources(); - final int portraitRotation = displayRotation.getPortraitRotation(); - mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode); mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res); mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res); @@ -1870,10 +1872,11 @@ public class DisplayPolicy { @Override public String toString() { - return "{nonDecorInsets=" + mNonDecorInsets - + ", configInsets=" + mConfigInsets - + ", nonDecorFrame=" + mNonDecorFrame - + ", configFrame=" + mConfigFrame + '}'; + final StringBuilder tmpSb = new StringBuilder(32); + return "{nonDecorInsets=" + mNonDecorInsets.toShortString(tmpSb) + + ", configInsets=" + mConfigInsets.toShortString(tmpSb) + + ", nonDecorFrame=" + mNonDecorFrame.toShortString(tmpSb) + + ", configFrame=" + mConfigFrame.toShortString(tmpSb) + '}'; } } @@ -1911,6 +1914,39 @@ public class DisplayPolicy { info.mNeedUpdate = true; } } + + void setTo(DecorInsets src) { + for (int i = mInfoForRotation.length - 1; i >= 0; i--) { + mInfoForRotation[i].set(src.mInfoForRotation[i]); + } + } + + void dump(String prefix, PrintWriter pw) { + for (int rotation = 0; rotation < mInfoForRotation.length; rotation++) { + final DecorInsets.Info info = mInfoForRotation[rotation]; + pw.println(prefix + Surface.rotationToString(rotation) + "=" + info); + } + } + + private static class Cache { + /** + * If {@link #mPreserveId} is this value, it is in the middle of updating display + * configuration before a transition is started. Then the active cache should be used. + */ + static final int ID_UPDATING_CONFIG = -1; + final DecorInsets mDecorInsets; + int mPreserveId; + boolean mActive; + + Cache(DisplayContent dc) { + mDecorInsets = new DecorInsets(dc); + } + + boolean canPreserve() { + return mPreserveId == ID_UPDATING_CONFIG || mDecorInsets.mDisplayContent + .mTransitionController.inTransition(mPreserveId); + } + } } /** @@ -1918,6 +1954,9 @@ public class DisplayPolicy { * call {@link DisplayContent#sendNewConfiguration()} if this method returns {@code true}. */ boolean updateDecorInsetsInfo() { + if (shouldKeepCurrentDecorInsets()) { + return false; + } final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames; final int rotation = displayFrames.mRotation; final int dw = displayFrames.mWidth; @@ -1928,6 +1967,10 @@ public class DisplayPolicy { if (newInfo.mConfigFrame.equals(currentInfo.mConfigFrame)) { return false; } + if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve() + && !mDisplayContent.isSleeping()) { + mCachedDecorInsets = null; + } mDecorInsets.invalidate(); mDecorInsets.mInfoForRotation[rotation].set(newInfo); return true; @@ -1937,6 +1980,71 @@ public class DisplayPolicy { return mDecorInsets.get(rotation, w, h); } + /** Returns {@code true} to trust that {@link #mDecorInsets} already has the expected state. */ + boolean shouldKeepCurrentDecorInsets() { + return mCachedDecorInsets != null && mCachedDecorInsets.mActive + && mCachedDecorInsets.canPreserve(); + } + + void physicalDisplayChanged() { + if (USE_CACHED_INSETS_FOR_DISPLAY_SWITCH) { + updateCachedDecorInsets(); + } + } + + /** + * Caches the current insets and switches current insets to previous cached insets. This is to + * reduce multiple display configuration changes if there are multiple insets provider windows + * which may trigger {@link #updateDecorInsetsInfo()} individually. + */ + @VisibleForTesting + void updateCachedDecorInsets() { + DecorInsets prevCache = null; + if (mCachedDecorInsets == null) { + mCachedDecorInsets = new DecorInsets.Cache(mDisplayContent); + } else { + prevCache = new DecorInsets(mDisplayContent); + prevCache.setTo(mCachedDecorInsets.mDecorInsets); + } + // Set a special id to preserve it before a real id is available from transition. + mCachedDecorInsets.mPreserveId = DecorInsets.Cache.ID_UPDATING_CONFIG; + // Cache the current insets. + mCachedDecorInsets.mDecorInsets.setTo(mDecorInsets); + // Switch current to previous cache. + if (prevCache != null) { + mDecorInsets.setTo(prevCache); + mCachedDecorInsets.mActive = true; + } + } + + /** + * Called after the display configuration is updated according to the physical change. Suppose + * there should be a display change transition, so associate the cached decor insets with the + * transition to limit the lifetime of using the cache. + */ + void physicalDisplayUpdated() { + if (mCachedDecorInsets == null) { + return; + } + if (!mDisplayContent.mTransitionController.isCollecting()) { + // Unable to know when the display switch is finished. + mCachedDecorInsets = null; + return; + } + mCachedDecorInsets.mPreserveId = + mDisplayContent.mTransitionController.getCollectingTransitionId(); + // The validator will run after the transition is finished. So if the insets are changed + // during the transition, it can update to the latest state. + mDisplayContent.mTransitionController.mStateValidators.add(() -> { + // The insets provider client may defer to change its window until screen is on. So + // only validate when awake to avoid the cache being always dropped. + if (!mDisplayContent.isSleeping() && updateDecorInsetsInfo()) { + Slog.d(TAG, "Insets changed after display switch transition"); + mDisplayContent.sendNewConfiguration(); + } + }); + } + @NavigationBarPosition int navigationBarPosition(int displayRotation) { if (mNavigationBar != null) { @@ -2613,9 +2721,10 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars="); pw.println(mRemoteInsetsControllerControlsSystemBars); pw.print(prefix); pw.println("mDecorInsetsInfo:"); - for (int rotation = 0; rotation < mDecorInsets.mInfoForRotation.length; rotation++) { - final DecorInsets.Info info = mDecorInsets.mInfoForRotation[rotation]; - pw.println(prefixInner + Surface.rotationToString(rotation) + "=" + info); + mDecorInsets.dump(prefixInner, pw); + if (mCachedDecorInsets != null) { + pw.print(prefix); pw.println("mCachedDecorInsets:"); + mCachedDecorInsets.mDecorInsets.dump(prefixInner, pw); } mSystemGestures.dump(pw, prefix); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c9316bf6e972..9890f135c462 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -431,6 +431,19 @@ class TransitionController { return inCollectingTransition(wc) || inPlayingTransition(wc); } + /** Returns {@code true} if the id matches a collecting or playing transition. */ + boolean inTransition(int syncId) { + if (mCollectingTransition != null && mCollectingTransition.getSyncId() == syncId) { + return true; + } + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + if (mPlayingTransitions.get(i).getSyncId() == syncId) { + return true; + } + } + return false; + } + /** @return {@code true} if wc is in a participant subtree */ boolean isTransitionOnDisplay(@NonNull DisplayContent dc) { if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 353a8ecaf13d..5d5a3a5e48f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -299,6 +299,33 @@ public class DisplayPolicyTests extends WindowTestsBase { } @Test + public void testSwitchDecorInsets() { + createNavBarWithProvidedInsets(mDisplayContent); + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + final DisplayInfo info = mDisplayContent.getDisplayInfo(); + final int w = info.logicalWidth; + final int h = info.logicalHeight; + displayPolicy.updateDecorInsetsInfo(); + final Rect prevConfigFrame = new Rect(displayPolicy.getDecorInsetsInfo(info.rotation, + info.logicalWidth, info.logicalHeight).mConfigFrame); + + displayPolicy.updateCachedDecorInsets(); + mDisplayContent.updateBaseDisplayMetrics(w / 2, h / 2, + info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi); + // There is no previous cache. But the current state will be cached. + assertFalse(displayPolicy.shouldKeepCurrentDecorInsets()); + + // Switch to original state. + displayPolicy.updateCachedDecorInsets(); + mDisplayContent.updateBaseDisplayMetrics(w, h, + info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi); + assertTrue(displayPolicy.shouldKeepCurrentDecorInsets()); + // The current insets are restored from cache directly. + assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation, + info.logicalWidth, info.logicalHeight).mConfigFrame); + } + + @Test public void testUpdateDisplayConfigurationByDecor() { doReturn(NO_CUTOUT).when(mDisplayContent).calculateDisplayCutoutForRotation(anyInt()); final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent); |