From 332a9410aaa8e68e61a8185cee18a23ef02eaf6b Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 1 Jul 2020 20:34:27 +0800 Subject: [PATCH] Fix extra configuration change when unlocking directly When unlocking the device directly, if the top activity has different orientation than keyguard, the orientation of display be updated first. Otherwise the activity may be updated to the orientation requested by keyguard, and then update again according to the activity. If fixed rotation is active in this case, it only increase useless cost because the display is not frozen, the orientation changes will have some intermediate states, such as keyguard-going-away is just cleared but the specified orientation of keyguard still exists a while. Also make the cleanup of fixed rotation more robust to avoid other unexpected paths retain the stale state. Bug: 160131683 Bug: 160239395 Test: atest RootActivityContainerTests# \ testAwakeFromSleepingWithAppConfiguration Change-Id: I3552b81ffaaff062aa2ee90c51928c92dfb83c48 --- .../com/android/server/wm/ActivityStack.java | 2 -- .../com/android/server/wm/DisplayArea.java | 1 + .../com/android/server/wm/DisplayContent.java | 19 +++++++++-- .../server/wm/RootWindowContainer.java | 6 ++++ .../android/server/wm/TaskDisplayArea.java | 8 ++--- .../android/server/wm/WindowContainer.java | 3 ++ .../server/wm/ActivityRecordTests.java | 5 ++- .../android/server/wm/ActivityStackTests.java | 3 +- .../wm/ActivityTaskManagerServiceTests.java | 3 +- .../server/wm/DisplayContentTests.java | 3 +- .../server/wm/RootActivityContainerTests.java | 32 +++++++++++++++++++ .../server/wm/SystemServiceTestsBase.java | 11 +++++++ 12 files changed, 78 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 8f0de7312ee5a..7abff527007f2 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -954,8 +954,6 @@ class ActivityStack extends Task { void awakeFromSleepingLocked() { // Ensure activities are no longer sleeping. forAllActivities((Consumer) (r) -> r.setSleeping(false)); - ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */); if (mPausingActivity != null) { Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause"); mPausingActivity.activityPaused(true); diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 8260cb31acda0..2be3acc520580 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -253,6 +253,7 @@ public class DisplayArea extends WindowContainer { req = mLastKeyguardForcedOrientation; } } + mLastOrientationSource = win; return req; } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c56440785bba5..2da2c29802315 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1189,6 +1189,8 @@ class DisplayContent extends WindowContainer // activity here. resumeFocusedStacksTopActivities(); } + // The visibility update must not be called before resuming the top, so the + // display orientation can be updated first if needed. Otherwise there may + // have redundant configuration changes due to apply outdated display + // orientation (from keyguard) to activity. + stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */); } } } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 205a8d2aeeac0..17e8f4d9a046c 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1827,14 +1827,14 @@ final class TaskDisplayArea extends DisplayArea { @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { pw.println(prefix + "TaskDisplayArea " + getName()); - super.dump(pw, prefix, dumpAll); + final String doublePrefix = prefix + " "; + super.dump(pw, doublePrefix, dumpAll); if (mPreferredTopFocusableStack != null) { - pw.println(prefix + " mPreferredTopFocusableStack=" + mPreferredTopFocusableStack); + pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack); } if (mLastFocusedStack != null) { - pw.println(prefix + " mLastFocusedStack=" + mLastFocusedStack); + pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack); } - final String doublePrefix = prefix + " "; final String triplePrefix = doublePrefix + " "; pw.println(doublePrefix + "Application tokens in top down Z order:"); for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5a73fabaf9d06..d5705a246e928 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2554,6 +2554,9 @@ class WindowContainer extends ConfigurationContainer< pw.print(prefix); pw.println("ContainerAnimator:"); mSurfaceAnimator.dump(pw, prefix + " "); } + if (mLastOrientationSource != null) { + pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource); + } } final void updateSurfacePosition() { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 668f04785bbc5..c7b45efb2de18 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -127,8 +127,7 @@ public class ActivityRecordTests extends ActivityTestsBase { mTask = mStack.getBottomMostTask(); mActivity = mTask.getTopNonFinishingActivity(); - doReturn(false).when(mService).isBooting(); - doReturn(true).when(mService).isBooted(); + setBooted(mService); } @Test @@ -1535,7 +1534,7 @@ public class ActivityRecordTests extends ActivityTestsBase { * Sets orientation without notifying the parent to simulate that the display has not applied * the requested orientation yet. */ - private static void setRotatedScreenOrientationSilently(ActivityRecord r) { + static void setRotatedScreenOrientationSilently(ActivityRecord r) { final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT ? SCREEN_ORIENTATION_LANDSCAPE : SCREEN_ORIENTATION_PORTRAIT; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 37882bb2ba76b..1b42a0466cf73 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1392,8 +1392,7 @@ public class ActivityStackTests extends ActivityTestsBase { } mSupervisor.endDeferResume(); - doReturn(false).when(mService).isBooting(); - doReturn(true).when(mService).isBooted(); + setBooted(mService); // 2 activities are started while keyguard is locked, so they are waiting to be resolved. assertFalse(unknownAppVisibilityController.allResolved()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 64b5eca1beb82..f65d6e0c82af5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -67,8 +67,7 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - doReturn(false).when(mService).isBooting(); - doReturn(true).when(mService).isBooted(); + setBooted(mService); } /** Verify that activity is finished correctly upon request. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index ddb186a1d2da5..23029017bac95 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1239,8 +1239,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testNoFixedRotationWithPip() { // Make resume-top really update the activity state. - doReturn(false).when(mWm.mAtmService).isBooting(); - doReturn(true).when(mWm.mAtmService).isBooted(); + setBooted(mWm.mAtmService); // Speed up the test by a few seconds. mWm.mAtmService.deferWindowLayout(); doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 5dba004559132..51db099676b03 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -42,6 +42,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -57,8 +58,10 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.platform.test.annotations.Presubmit; +import android.util.MergedConfiguration; import android.util.Pair; import androidx.test.filters.MediumTest; @@ -221,6 +224,35 @@ public class RootActivityContainerTests extends ActivityTestsBase { null /* target */, null /* targetOptions */); } + @Test + public void testAwakeFromSleepingWithAppConfiguration() { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true).build(); + activity.moveFocusableActivityToTop("test"); + assertTrue(activity.getStack().isFocusedStackOnDisplay()); + ActivityRecordTests.setRotatedScreenOrientationSilently(activity); + + final Configuration rotatedConfig = new Configuration(); + display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation() + .rotationForOrientation(activity.getOrientation(), display.getRotation())); + assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation); + // Assume the activity was shown in different orientation. For example, the top activity is + // landscape and the portrait lockscreen is shown. + activity.setLastReportedConfiguration( + new MergedConfiguration(mService.getGlobalConfiguration(), rotatedConfig)); + activity.setState(ActivityState.STOPPED, "sleep"); + + display.setIsSleeping(true); + doReturn(false).when(display).shouldSleep(); + // Allow to resume when awaking. + setBooted(mService); + mRootWindowContainer.applySleepTokens(true); + + // The display orientation should be changed by the activity so there is no relaunch. + verify(activity, never()).relaunchActivityLocked(anyBoolean()); + assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation); + } + /** * Verifies that removal of activity with task and stack is done correctly. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java index d7462f810bb7c..53c2a5b8967d1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + import android.os.Handler; import android.testing.DexmakerShareClassLoaderRule; @@ -66,6 +68,15 @@ class SystemServiceTestsBase { return mLockRule.waitForLocked(callable); } + /** + * Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really + * be executed to update activity state and configuration when resuming the current top. + */ + static void setBooted(ActivityTaskManagerService atmService) { + doReturn(false).when(atmService).isBooting(); + doReturn(true).when(atmService).isBooted(); + } + /** * Utility class to compare the output of T#toString. It is convenient to have readable output * of assertion if the string content can represent the expected states.