From c0e76e7cd8998354347b838822ecbe4c7b9ddf1f Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 22 Apr 2022 16:56:10 +0000 Subject: [PATCH] Update cache key calculation for statusbar insets We use Display#uniqueId as a cache key for status bar insets when using multiple displays (e.g. foldable device). But uniqueId and display size could be inconsistent (see b/198965093#comment13): we could receive uniqueId for a different display than the current display size in the configuration. So if it is calculated once with wrong display size it is saved in the cache and status bar is rendered with wrong insets every time. I updated the cache key calculation with the actual inputs of the calculation algorithm (display size and cutouts) which are consistent when onConfigurationChange received. Bug: 212924195 Test: folding and unlocking a foldable phone >1000 times, rotating when folded/unfolded => checking that the statusbar has correct size Change-Id: Ibd8575529b031747a802b56db3eb12069b9ba6b0 --- .../statusbar/phone/PhoneStatusBarView.java | 17 +++-- .../phone/StatusBarContentInsetsProvider.kt | 64 +++++++++++-------- .../window/StatusBarWindowController.java | 13 ++-- 3 files changed, 60 insertions(+), 34 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index fb8397ba8f4fa..7aeb08dd5ddb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -53,6 +53,8 @@ public class PhoneStatusBarView extends FrameLayout { private View mCutoutSpace; @Nullable private DisplayCutout mDisplayCutout; + @Nullable + private Rect mDisplaySize; private int mStatusBarHeight; @Nullable private TouchEventHandler mTouchEventHandler; @@ -87,7 +89,7 @@ public class PhoneStatusBarView extends FrameLayout { // Always have Battery meters in the status bar observe the dark/light modes. Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery); Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock); - if (updateOrientationAndCutout()) { + if (updateDisplayParameters()) { updateLayoutForCutout(); } } @@ -106,7 +108,7 @@ public class PhoneStatusBarView extends FrameLayout { updateResources(); // May trigger cutout space layout-ing - if (updateOrientationAndCutout()) { + if (updateDisplayParameters()) { updateLayoutForCutout(); requestLayout(); } @@ -114,7 +116,7 @@ public class PhoneStatusBarView extends FrameLayout { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (updateOrientationAndCutout()) { + if (updateDisplayParameters()) { updateLayoutForCutout(); requestLayout(); } @@ -124,7 +126,7 @@ public class PhoneStatusBarView extends FrameLayout { /** * @return boolean indicating if we need to update the cutout location / margins */ - private boolean updateOrientationAndCutout() { + private boolean updateDisplayParameters() { boolean changed = false; int newRotation = RotationUtils.getExactRotation(mContext); if (newRotation != mRotationOrientation) { @@ -137,6 +139,13 @@ public class PhoneStatusBarView extends FrameLayout { mDisplayCutout = getRootWindowInsets().getDisplayCutout(); } + final Rect newSize = mContext.getResources().getConfiguration().windowConfiguration + .getMaxBounds(); + if (!Objects.equals(newSize, mDisplaySize)) { + changed = true; + mDisplaySize = newSize; + } + return changed; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index 67de4e3b3c39b..f5462bc0fba54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -38,6 +38,7 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation import com.android.systemui.util.leak.RotationUtils.getExactRotation import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation +import com.android.systemui.util.traceSection import java.io.PrintWriter import java.lang.Math.max @@ -133,8 +134,10 @@ class StatusBarContentInsetsProvider @Inject constructor( * (i.e., ROTATION_NONE will always return the same bounds regardless of the context * from which this method is called) */ - fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect { - var insets = insetsCache[getCacheKey(rotation = rotation)] + fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int, + displayCutout: DisplayCutout?): Rect { + val key = getCacheKey(rotation, displayCutout) + var insets = insetsCache[key] if (insets == null) { insets = getStatusBarContentAreaForRotation(rotation) } @@ -156,20 +159,23 @@ class StatusBarContentInsetsProvider @Inject constructor( * * @param rotation the target rotation for which to calculate insets */ - fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Pair { - val key = getCacheKey(rotation) + fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Pair = + traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") { + val displayCutout = context.display.cutout + val key = getCacheKey(rotation, displayCutout) - val point = Point() - context.display.getRealSize(point) - // Target rotation can be a different orientation than the current device rotation - point.orientToRotZero(getExactRotation(context)) - val width = point.logicalWidth(rotation) + val screenBounds = context.resources.configuration.windowConfiguration.maxBounds + val point = Point(screenBounds.width(), screenBounds.height()) - val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( - rotation, getResourcesForRotation(rotation, context), key) + // Target rotation can be a different orientation than the current device rotation + point.orientToRotZero(getExactRotation(context)) + val width = point.logicalWidth(rotation) - return Pair(area.left, width - area.right) - } + val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( + rotation, displayCutout, getResourcesForRotation(rotation, context), key) + + Pair(area.left, width - area.right) + } /** * Calculate the left and right insets for the status bar content in the device's current @@ -192,9 +198,10 @@ class StatusBarContentInsetsProvider @Inject constructor( fun getStatusBarContentAreaForRotation( @Rotation rotation: Int ): Rect { - val key = getCacheKey(rotation) + val displayCutout = context.display.cutout + val key = getCacheKey(rotation, displayCutout) return insetsCache[key] ?: getAndSetCalculatedAreaForRotation( - rotation, getResourcesForRotation(rotation, context), key) + rotation, displayCutout, getResourcesForRotation(rotation, context), key) } /** @@ -207,20 +214,21 @@ class StatusBarContentInsetsProvider @Inject constructor( private fun getAndSetCalculatedAreaForRotation( @Rotation targetRotation: Int, + displayCutout: DisplayCutout?, rotatedResources: Resources, key: CacheKey ): Rect { - return getCalculatedAreaForRotation(targetRotation, rotatedResources) + return getCalculatedAreaForRotation(displayCutout, targetRotation, rotatedResources) .also { insetsCache.put(key, it) } } private fun getCalculatedAreaForRotation( + displayCutout: DisplayCutout?, @Rotation targetRotation: Int, rotatedResources: Resources ): Rect { - val dc = context.display.cutout val currentRotation = getExactRotation(context) val roundedCornerPadding = rotatedResources @@ -245,7 +253,7 @@ class StatusBarContentInsetsProvider @Inject constructor( return calculateInsetsForRotationWithRotatedResources( currentRotation, targetRotation, - dc, + displayCutout, context.resources.configuration.windowConfiguration.maxBounds, SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation), minLeft, @@ -266,15 +274,19 @@ class StatusBarContentInsetsProvider @Inject constructor( pw.println(insetsCache) } - private fun getCacheKey(@Rotation rotation: Int): CacheKey = + private fun getCacheKey( + @Rotation rotation: Int, + displayCutout: DisplayCutout?): CacheKey = CacheKey( - uniqueDisplayId = context.display.uniqueId, - rotation = rotation + rotation = rotation, + displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds), + displayCutout = displayCutout ) private data class CacheKey( - val uniqueDisplayId: String, - @Rotation val rotation: Int + @Rotation val rotation: Int, + val displaySize: Rect, + val displayCutout: DisplayCutout? ) } @@ -369,7 +381,7 @@ fun calculateInsetsForRotationWithRotatedResources( /** * Calculate the insets needed from the left and right edges for the given rotation. * - * @param dc Device display cutout + * @param displayCutout Device display cutout * @param sbHeight appropriate status bar height for this rotation * @param width display width calculated for ROTATION_NONE * @param height display height calculated for ROTATION_NONE @@ -386,7 +398,7 @@ fun calculateInsetsForRotationWithRotatedResources( * rotation */ private fun getStatusBarLeftRight( - dc: DisplayCutout?, + displayCutout: DisplayCutout?, sbHeight: Int, width: Int, height: Int, @@ -402,7 +414,7 @@ private fun getStatusBarLeftRight( val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width - val cutoutRects = dc?.boundingRects + val cutoutRects = displayCutout?.boundingRects if (cutoutRects == null || cutoutRects.isEmpty()) { return Rect(minLeft, 0, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java index c53d5107afcc2..e0d780a5fcd50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java @@ -31,7 +31,9 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.RemoteException; +import android.os.Trace; import android.util.Log; +import android.view.DisplayCutout; import android.view.Gravity; import android.view.IWindowManager; import android.view.Surface; @@ -130,7 +132,9 @@ public class StatusBarWindowController { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. + Trace.beginSection("StatusBarWindowController.getBarLayoutParams"); mLp = getBarLayoutParams(mContext.getDisplay().getRotation()); + Trace.endSection(); mWindowManager.addView(mStatusBarWindowView, mLp); mLpChanged.copyFrom(mLp); @@ -223,14 +227,15 @@ public class StatusBarWindowController { private void calculateStatusBarLocationsForAllRotations() { Rect[] bounds = new Rect[4]; + final DisplayCutout displayCutout = mContext.getDisplay().getCutout(); bounds[0] = mContentInsetsProvider - .getBoundingRectForPrivacyChipForRotation(ROTATION_NONE); + .getBoundingRectForPrivacyChipForRotation(ROTATION_NONE, displayCutout); bounds[1] = mContentInsetsProvider - .getBoundingRectForPrivacyChipForRotation(ROTATION_LANDSCAPE); + .getBoundingRectForPrivacyChipForRotation(ROTATION_LANDSCAPE, displayCutout); bounds[2] = mContentInsetsProvider - .getBoundingRectForPrivacyChipForRotation(ROTATION_UPSIDE_DOWN); + .getBoundingRectForPrivacyChipForRotation(ROTATION_UPSIDE_DOWN, displayCutout); bounds[3] = mContentInsetsProvider - .getBoundingRectForPrivacyChipForRotation(ROTATION_SEASCAPE); + .getBoundingRectForPrivacyChipForRotation(ROTATION_SEASCAPE, displayCutout); try { mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), bounds);