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
This commit is contained in:
Riddle Hsu
2020-07-01 20:34:27 +08:00
parent e5e4c105c2
commit 332a9410aa
12 changed files with 78 additions and 18 deletions

View File

@@ -954,8 +954,6 @@ class ActivityStack extends Task {
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
forAllActivities((Consumer<ActivityRecord>) (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);

View File

@@ -253,6 +253,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
req = mLastKeyguardForcedOrientation;
}
}
mLastOrientationSource = win;
return req;
}
}

View File

@@ -1189,6 +1189,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
activity.onRemovedFromDisplay();
if (activity == mFixedRotationLaunchingApp) {
// Make sure the states of associated tokens are also cleared.
activity.finishFixedRotationTransform();
setFixedRotationLaunchingAppUnchecked(null);
}
}
@@ -1487,6 +1489,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// window was transferred ({@link #mSkipAppTransitionAnimation}).
return false;
}
if ((mAppTransition.getTransitFlags()
& WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
// The transition may be finished before keyguard hidden. In order to avoid the
// intermediate orientation change, it is more stable to freeze the display.
return false;
}
} else if (r != topRunningActivity()) {
// If the transition has not started yet, the activity must be the top.
return false;
@@ -2310,6 +2318,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void onAppTransitionDone() {
super.onAppTransitionDone();
mWmService.mWindowsChanged = true;
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested
&& !mFixedRotationLaunchingApp.isVisible()
&& !mDisplayRotation.isRotatingSeamlessly()) {
clearFixedRotationLaunchingApp();
}
}
@Override
@@ -3011,11 +3026,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
if (rotationAnimation != null) {
pw.print(subPrefix);
pw.println(" mScreenRotationAnimation:");
rotationAnimation.printTo(" ", pw);
rotationAnimation.printTo(subPrefix, pw);
} else if (dumpAll) {
pw.print(subPrefix);
pw.println(" no ScreenRotationAnimation ");
}

View File

@@ -2391,6 +2391,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// 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 */);
}
}
}

View File

@@ -1827,14 +1827,14 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
@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) {

View File

@@ -2554,6 +2554,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
pw.print(prefix); pw.println("ContainerAnimator:");
mSurfaceAnimator.dump(pw, prefix + " ");
}
if (mLastOrientationSource != null) {
pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
}
}
final void updateSurfacePosition() {

View File

@@ -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;

View File

@@ -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());

View File

@@ -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. */

View File

@@ -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());

View File

@@ -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.
*/

View File

@@ -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.