From 9a9dbf9d260167b27d351a24f8a93c011a26d253 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 28 Oct 2020 18:10:10 -0700 Subject: [PATCH] Avoid clearing size compat when app is shown from paused Before, we always clear the size compat when the orientation request has been changed, but that also happens when app is shown from paused state. Now, instead of clearing the inaccurate size compat, we only update it after Task has computed its bounds based on the current app. Fix: 171882701 Test: atest WmTests:SizeCompatTests Test: atest WmTests:TaskTests Test: manual: go recents with ignore orientation request Change-Id: I22ca6a8e7e06337c7799e10e8218e39b212c624c --- .../com/android/server/wm/ActivityRecord.java | 27 ++++++++------ .../core/java/com/android/server/wm/Task.java | 28 ++++++++------ .../android/server/wm/SizeCompatTests.java | 37 ++++++++++++++++++- .../src/com/android/server/wm/TaskTests.java | 20 ++++++++++ 4 files changed, 87 insertions(+), 25 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f65a5024bdea9..c5cfb13ca3766 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6358,7 +6358,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null; - onDescendantOrientationChanged(freezeToken, this); + if (onDescendantOrientationChanged(freezeToken, this)) { + // The app is just becoming visible, and the parent Task has updated with the + // orientation request. Update the size compat mode. + updateSizeCompatMode(); + } } /** @@ -6524,6 +6528,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } + if (task == null || (!handlesOrientationChangeFromDescendant() + && task.getLastTaskBoundsComputeActivity() != this)) { + // Don't compute when Task hasn't computed its bounds for this app, because the Task can + // be letterboxed, and its bounds may not be accurate until then. + return; + } + Configuration overrideConfig = getRequestedOverrideConfiguration(); final Configuration fullConfig = getConfiguration(); @@ -6548,20 +6559,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this); } - void clearSizeCompatMode(boolean recomputeTask) { + @VisibleForTesting + void clearSizeCompatMode() { mSizeCompatScale = 1f; mSizeCompatBounds = null; mCompatDisplayInsets = null; - if (recomputeTask) { - // Recompute from Task because letterbox can also happen on Task level. - task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration()); - } - } - - @VisibleForTesting - void clearSizeCompatMode() { - clearSizeCompatMode(true /* recomputeTask */); + // Recompute from Task because letterbox can also happen on Task level. + task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration()); } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 54a66cce9b255..c7df5a78d8649 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -572,6 +572,10 @@ class Task extends WindowContainer { /** Current activity that is resumed, or null if there is none. */ ActivityRecord mResumedActivity = null; + /** Last activity that is used to compute the Task bounds. */ + @Nullable + private ActivityRecord mLastTaskBoundsComputeActivity; + private boolean mForceShowForAllUsers; /** When set, will force the task to report as invisible. */ @@ -1496,6 +1500,11 @@ class Task extends WindowContainer { } void cleanUpActivityReferences(ActivityRecord r) { + // mLastTaskBoundsComputeActivity is set at leaf Task + if (mLastTaskBoundsComputeActivity == r) { + mLastTaskBoundsComputeActivity = null; + } + final WindowContainer parent = getParent(); if (parent != null && parent.asTask() != null) { parent.asTask().cleanUpActivityReferences(r); @@ -2833,6 +2842,8 @@ class Task extends WindowContainer { private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) { + mLastTaskBoundsComputeActivity = getTopNonFinishingActivity(false /* includeOverlays */); + int windowingMode = getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { @@ -2963,6 +2974,11 @@ class Task extends WindowContainer { return bounds; } + @Nullable + ActivityRecord getLastTaskBoundsComputeActivity() { + return mLastTaskBoundsComputeActivity; + } + /** Updates the task's bounds and override configuration to match what is expected for the * input stack. */ void updateOverrideConfigurationForStack(Task inStack) { @@ -3307,18 +3323,6 @@ class Task extends WindowContainer { // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill // it if possible. if (getParent() != null) { - final ActivityRecord activity = requestingContainer.asActivityRecord(); - if (activity != null) { - // Clear the size compat cache to recompute the bounds for requested orientation; - // otherwise when Task#computeFullscreenBounds(), it will not try to do Task level - // letterboxing because app may prefer to keep its original size (size compat). - // - // Normally, ActivityRecord#clearSizeCompatMode() recomputes from its parent Task, - // which is the leaf Task. However, because this orientation request is new to all - // Tasks, pass false to clearSizeCompatMode, and trigger onConfigurationChanged from - // here (root Task) to make sure all Tasks are up-to-date. - activity.clearSizeCompatMode(false /* recomputeTask */); - } onConfigurationChanged(getParent().getConfiguration()); return true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 4300971be6897..ddc29f5dd516a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doCallRealMethod; import android.app.ActivityManager; @@ -702,7 +703,6 @@ public class SizeCompatTests extends WindowTestsBase { // size compat cache. verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); - verify(newActivity).clearSizeCompatMode(false /* recomputeTask */); final Rect displayBounds = display.getBounds(); final Rect taskBounds = mTask.getBounds(); @@ -744,7 +744,6 @@ public class SizeCompatTests extends WindowTestsBase { // size compat cache. verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); - verify(newActivity).clearSizeCompatMode(false /* recomputeTask */); final Rect displayBounds = display.getBounds(); final Rect taskBounds = mTask.getBounds(); @@ -761,6 +760,40 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(taskBounds, newActivityBounds); } + @Test + public void testDisplayIgnoreOrientationRequest_pausedAppNotLostSizeCompat() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + final DisplayContent display = mActivity.mDisplayContent; + display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app. + prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT); + clearInvocations(mActivity); + + assertTrue(mTask.isTaskLetterboxed()); + assertFalse(mActivity.inSizeCompatMode()); + assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); + + // Rotate display to portrait. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + // App should be in size compat. + assertFalse(mTask.isTaskLetterboxed()); + assertScaled(); + assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); + + // App still in size compat, and the bounds don't change. + verify(mActivity, never()).clearSizeCompatMode(); + assertFalse(mTask.isTaskLetterboxed()); + assertScaled(); + assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); + assertEquals(activityBounds, mActivity.getBounds()); + } + private static WindowState addWindowToActivity(ActivityRecord activity) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 0b257ee876200..238f31448ba1d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -257,4 +257,24 @@ public class TaskTests extends WindowTestsBase { task.resolveOverrideConfiguration(parentConfig); assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); } + + @Test + public void testCleanUpActivityReferences_clearLastTaskBoundsComputeActivity() { + final Task rootTask = createTaskStackOnDisplay(mDisplayContent); + final Task leafTask = createTaskInStack(rootTask, 0 /* userId */); + final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask); + final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask); + activity1.finishing = false; + leafTask.resolveOverrideConfiguration(rootTask.getConfiguration()); + + assertEquals(activity1, leafTask.getLastTaskBoundsComputeActivity()); + + leafTask.cleanUpActivityReferences(activity2); + + assertNotNull(leafTask.getLastTaskBoundsComputeActivity()); + + leafTask.cleanUpActivityReferences(activity1); + + assertNull(leafTask.getLastTaskBoundsComputeActivity()); + } }