Merge changes Ic966923a,I92c6dfac,Id370d798 into rvc-dev

* changes:
  Clear fixed rotation if display config does not change
  Polish fixed rotated recents animation
  Extensive handling of fixed rotation launching app
This commit is contained in:
Riddle Hsu
2020-04-29 08:32:47 +00:00
committed by Android (Google) Code Review
7 changed files with 211 additions and 79 deletions

View File

@@ -494,10 +494,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* The launching activity which is using fixed rotation transformation.
*
* @see #handleTopActivityLaunchingInDifferentOrientation
* @see #setFixedRotationLaunchingApp
* @see DisplayRotation#shouldRotateSeamlessly
*/
ActivityRecord mFixedRotationLaunchingApp;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
/** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
@@ -928,6 +932,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
@@ -1266,6 +1271,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (configUpdated) {
return;
}
// The display configuration doesn't change. If there is a launching transformed app, that
// means its request to change display configuration has been discarded, then it should
// respect to the current configuration of display.
clearFixedRotationLaunchingApp();
// Something changed (E.g. device rotation), but no configuration update is needed.
// E.g. changing device rotation by 180 degrees. Go ahead and perform surface placement to
// unfreeze the display since we froze it when the rotation was updated in
@@ -1383,7 +1394,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
orientationSource != null ? orientationSource.asActivityRecord() : null;
// Currently there is no use case from non-activity.
if (r != null && handleTopActivityLaunchingInDifferentOrientation(r)) {
mFixedRotationLaunchingApp = r;
// Display orientation should be deferred until the top fixed rotation is finished.
return false;
}
@@ -1448,47 +1458,66 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return false;
}
startFixedRotationTransform(r, rotation);
mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() {
void done() {
r.finishFixedRotationTransform();
mAppTransition.unregisterListener(this);
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
if (token == r.token) {
done();
}
}
@Override
public void onAppTransitionCancelledLocked(int transit) {
done();
}
@Override
public void onAppTransitionTimeoutLocked() {
done();
}
});
setFixedRotationLaunchingApp(r, rotation);
return true;
}
/** @return {@code true} if the display orientation will be changed. */
boolean continueUpdateOrientationForDiffOrienLaunchingApp(WindowToken token) {
if (token != mFixedRotationLaunchingApp) {
return false;
/**
* Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed
* rotation transform to it and indicate that the display may be rotated after it is launched.
*/
void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) {
final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
if (prevRotatedLaunchingApp != null
&& prevRotatedLaunchingApp.getWindowConfiguration().getRotation() == rotation
// It is animating so we can expect there will have a transition callback.
&& prevRotatedLaunchingApp.isAnimating(TRANSITION | PARENTS)) {
// It may be the case that multiple activities launch consecutively. Because their
// rotation are the same, the transformed state can be shared to avoid duplicating
// the heavy operations. This also benefits that the states of multiple activities
// are handled together.
r.linkFixedRotationTransform(prevRotatedLaunchingApp);
return;
}
if (!r.hasFixedRotationTransform()) {
startFixedRotationTransform(r, rotation);
}
mFixedRotationLaunchingApp = r;
if (prevRotatedLaunchingApp != null) {
prevRotatedLaunchingApp.finishFixedRotationTransform();
}
}
/**
* Continue updating the orientation change of display if it was deferred by a top activity
* launched in a different orientation.
*/
void continueUpdateOrientationForDiffOrienLaunchingApp() {
if (mFixedRotationLaunchingApp == null) {
return;
}
// Update directly because the app which will change the orientation of display is ready.
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
sendNewConfiguration();
return true;
return;
}
// The display won't rotate (e.g. the orientation from sensor has updated again before
// applying rotation to display), so clear it to stop using seamless rotation.
// The orientation of display is not changed.
clearFixedRotationLaunchingApp();
}
/**
* Clears the {@link mFixedRotationLaunchingApp} without applying rotation to display. It is
* used when the display won't rotate (e.g. the orientation from sensor has updated again before
* applying rotation to display) but the launching app has been transformed. So the record need
* to be cleared and restored to stop using seamless rotation and rotated configuration.
*/
private void clearFixedRotationLaunchingApp() {
if (mFixedRotationLaunchingApp == null) {
return;
}
mFixedRotationLaunchingApp.finishFixedRotationTransform();
mFixedRotationLaunchingApp = null;
return false;
}
private void startFixedRotationTransform(WindowToken token, int rotation) {
@@ -2858,6 +2887,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.print(" mLastStatusBarVisibility=0x");
pw.println(Integer.toHexString(mLastStatusBarVisibility));
}
if (mFixedRotationLaunchingApp != null) {
pw.println(" mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp);
}
pw.println();
mWallpaperController.dump(pw, " ");
@@ -5184,7 +5216,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) {
applyRotationAndClearFixedRotation(currRotation, overrideRotation);
applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
}
mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
@@ -5200,7 +5232,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* fixed rotation transform also needs to be cleared to make sure the rotated activity fits
* the display naturally.
*/
private void applyRotationAndClearFixedRotation(int oldRotation, int newRotation) {
private void applyRotationAndFinishFixedRotation(int oldRotation, int newRotation) {
if (mFixedRotationLaunchingApp == null) {
applyRotation(oldRotation, newRotation);
return;
@@ -5219,7 +5251,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
});
mFixedRotationLaunchingApp.clearFixedRotationTransform(
mFixedRotationLaunchingApp.finishFixedRotationTransform(
() -> applyRotation(oldRotation, newRotation));
mFixedRotationLaunchingApp = null;
}
@@ -5494,6 +5526,75 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
});
}
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
/**
* The animating activity which shows the recents task list. It is set between
* {@link RecentsAnimationController#initialize} and
* {@link RecentsAnimationController#cancelAnimation}.
*/
private ActivityRecord mAnimatingRecents;
/**
* If the recents activity has a fixed orientation which is different from the current top
* activity, it will be rotated before being shown so we avoid a screen rotation animation
* when showing the Recents view.
*/
void onStartRecentsAnimation(@NonNull ActivityRecord r) {
mAnimatingRecents = r;
rotateInDifferentOrientationIfNeeded(r);
if (r.hasFixedRotationTransform()) {
// Set the record so we can recognize it to continue to update display orientation
// if the recents activity becomes the top later.
setFixedRotationLaunchingApp(r, r.getWindowConfiguration().getRotation());
}
}
/**
* If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we
* don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition.
*/
void onFinishRecentsAnimation() {
final ActivityRecord animatingRecents = mAnimatingRecents;
mAnimatingRecents = null;
if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp
&& !animatingRecents.hasFixedRotationTransform()) {
// The recents activity won't be the top, such as giving up the swipe up gesture
// and return to the original top.
mFixedRotationLaunchingApp = null;
}
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
final ActivityRecord r = getActivityRecord(token);
// Ignore the animating recents so the fixed rotation transform won't be switched twice
// by finishing the recents animation and moving it to top. That also avoids flickering
// due to wait for previous activity to be paused if it supports PiP that ignores the
// effect of resume-while-pausing.
if (r == null || r == mAnimatingRecents) {
return;
}
if (mFixedRotationLaunchingApp != null
&& mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
continueUpdateOrientationForDiffOrienLaunchingApp();
} else {
r.finishFixedRotationTransform();
}
}
@Override
public void onAppTransitionCancelledLocked(int transit) {
continueUpdateOrientationForDiffOrienLaunchingApp();
}
@Override
public void onAppTransitionTimeoutLocked() {
continueUpdateOrientationForDiffOrienLaunchingApp();
}
}
class RemoteInsetsControlTarget implements InsetsControlTarget {
private final IDisplayWindowInsetsController mRemoteInsetsController;

View File

@@ -411,10 +411,7 @@ public class RecentsAnimationController implements DeathRecipient {
mService.mWindowPlacerLocked.performSurfacePlacement();
// If the target activity has a fixed orientation which is different from the current top
// activity, it will be rotated before being shown so we avoid a screen rotation
// animation when showing the Recents view.
mDisplayContent.rotateInDifferentOrientationIfNeeded(mTargetActivityRecord);
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);
// Notify that the animation has started
if (mStatusBar != null) {
@@ -736,11 +733,13 @@ public class RecentsAnimationController implements DeathRecipient {
if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
mTargetActivityRecord.token);
}
if (mTargetActivityRecord.hasFixedRotationTransform()) {
} else {
// The target activity will be moved to original position (non-top). Since it won't
// affect display orientation, just finish the transform.
mTargetActivityRecord.finishFixedRotationTransform();
}
}
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
// Notify that the animation has ended
if (mStatusBar != null) {

View File

@@ -119,7 +119,6 @@ class WindowToken extends WindowContainer<WindowState> {
* rotated by the given rotated display info, frames and insets.
*/
private static class FixedRotationTransformState {
final WindowToken mOwner;
final DisplayInfo mDisplayInfo;
final DisplayFrames mDisplayFrames;
final InsetsState mInsetsState;
@@ -133,10 +132,9 @@ class WindowToken extends WindowContainer<WindowState> {
final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
boolean mIsTransforming = true;
FixedRotationTransformState(WindowToken owner, DisplayInfo rotatedDisplayInfo,
FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState,
Configuration rotatedConfig, int currentRotation) {
mOwner = owner;
mDisplayInfo = rotatedDisplayInfo;
mDisplayFrames = rotatedDisplayFrames;
mInsetsState = rotatedInsetsState;
@@ -482,6 +480,14 @@ class WindowToken extends WindowContainer<WindowState> {
return mFixedRotationTransformState != null;
}
/** Returns {@code true} if the given token shares the same transform. */
boolean hasFixedRotationTransform(WindowToken token) {
if (mFixedRotationTransformState == null || token == null) {
return false;
}
return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState;
}
boolean isFinishingFixedRotationTransform() {
return mFixedRotationTransformState != null
&& !mFixedRotationTransformState.mIsTransforming;
@@ -520,15 +526,14 @@ class WindowToken extends WindowContainer<WindowState> {
final InsetsState insetsState = new InsetsState();
mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState,
mDisplayContent.getConfiguration().uiMode);
mFixedRotationTransformState = new FixedRotationTransformState(this, info, displayFrames,
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
insetsState, new Configuration(config), mDisplayContent.getRotation());
onConfigurationChanged(getParent().getConfiguration());
}
/**
* Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
* one. This takes the same effect as {@link #applyFixedRotationTransform}, but the linked state
* can only be cleared by the state owner.
* one. This takes the same effect as {@link #applyFixedRotationTransform}.
*/
void linkFixedRotationTransform(WindowToken other) {
if (mFixedRotationTransformState != null) {
@@ -543,28 +548,15 @@ class WindowToken extends WindowContainer<WindowState> {
onConfigurationChanged(getParent().getConfiguration());
}
/**
* Finishes the transform and continue updating the orientation change of display. Only the
* state owner can finish the transform state.
*/
void finishFixedRotationTransform() {
if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) {
return;
}
final boolean changed =
mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this);
// If it is not the launching app or the display is not rotated, make sure the transform is
// cleared and the configuration is restored from parent.
if (!changed) {
clearFixedRotationTransform(null /* applyDisplayRotation */);
}
finishFixedRotationTransform(null /* applyDisplayRotation */);
}
/**
* Clears the transform and apply display rotation if the action is given. If the display will
* Finishes the transform and apply display rotation if the action is given. If the display will
* not rotate, the transformed containers are restored to their original states.
*/
void clearFixedRotationTransform(Runnable applyDisplayRotation) {
void finishFixedRotationTransform(Runnable applyDisplayRotation) {
final FixedRotationTransformState state = mFixedRotationTransformState;
if (state == null) {
return;

View File

@@ -66,6 +66,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import android.app.ActivityOptions;
@@ -1402,6 +1403,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
mActivity.finishFixedRotationTransform();
final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
assertNotNull(rotationAnim);
rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
// Because the display doesn't rotate, the rotated activity needs to cancel the fixed
@@ -1409,8 +1411,18 @@ public class ActivityRecordTests extends ActivityTestsBase {
verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
assertTrue(mActivity.isFreezingScreen());
assertFalse(displayRotation.isRotatingSeamlessly());
assertNotNull(rotationAnim);
assertTrue(rotationAnim.isRotating());
// Simulate the remote rotation has completed and the configuration doesn't change, then
// the rotated activity should also be restored by clearing the transform.
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
doReturn(false).when(displayRotation).isWaitingForRemoteRotation();
clearInvocations(mActivity);
display.mFixedRotationLaunchingApp = mActivity;
display.sendNewConfiguration();
assertNull(display.mFixedRotationLaunchingApp);
assertFalse(mActivity.hasFixedRotationTransform());
}
@Test

View File

@@ -790,9 +790,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
final int newOrientation = getRotatedOrientation(dc);
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
@@ -812,9 +810,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
final int newOrientation = getRotatedOrientation(dc);
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
@@ -1083,7 +1079,8 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mDisplayContent.mOpeningApps.add(app);
app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final int newOrientation = getRotatedOrientation(mDisplayContent);
app.setRequestedOrientation(newOrientation);
assertTrue(app.isFixedRotationTransforming());
assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly(
@@ -1124,12 +1121,25 @@ public class DisplayContentTests extends WindowTestsBase {
mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */);
verify(t, never()).setPosition(any(), eq(0), eq(0));
// Launch another activity before the transition is finished.
final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setDisplay(mDisplayContent).build().getTopMostActivity();
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mDisplayContent.mOpeningApps.add(app2);
app2.setRequestedOrientation(newOrientation);
// The activity should share the same transform state as the existing one.
assertTrue(app.hasFixedRotationTransform(app2));
// The display should be rotated after the launch is finished.
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
// The animation in old rotation should be cancelled.
assertFalse(closingApp.isAnimating());
// The display should be rotated after the launch is finished.
// The fixed rotation should be cleared and the new rotation is applied to display.
assertFalse(app.hasFixedRotationTransform());
assertFalse(app2.hasFixedRotationTransform());
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
}
@@ -1292,6 +1302,12 @@ public class DisplayContentTests extends WindowTestsBase {
assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
}
private static int getRotatedOrientation(DisplayContent dc) {
return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
}
private static List<WindowState> reverseList(List<WindowState> list) {
final ArrayList<WindowState> result = new ArrayList<>(list);
Collections.reverse(result);

View File

@@ -344,9 +344,17 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
assertEquals(homeActivity, mDefaultDisplay.mFixedRotationLaunchingApp);
// Check that the home app is in portrait
assertEquals(Configuration.ORIENTATION_PORTRAIT,
homeActivity.getConfiguration().orientation);
// Home activity won't become top (return to landActivity), so its fixed rotation and the
// top rotated record should be cleared.
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
assertFalse(homeActivity.hasFixedRotationTransform());
assertNull(mDefaultDisplay.mFixedRotationLaunchingApp);
}
@Test
@@ -401,12 +409,16 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertEquals(Configuration.ORIENTATION_PORTRAIT,
wallpapers.get(0).getConfiguration().orientation);
// Wallpaper's transform state is controlled by home, so the invocation should be no-op.
wallpaperWindowToken.finishFixedRotationTransform();
assertTrue(wallpaperWindowToken.hasFixedRotationTransform());
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
// The transform state should keep because we expect to listen the signal from the
// transition executed by moving the task to front.
assertTrue(homeActivity.hasFixedRotationTransform());
assertEquals(homeActivity, mDefaultDisplay.mFixedRotationLaunchingApp);
mDefaultDisplay.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(
homeActivity.token);
// Wallpaper's transform state should be cleared with home.
homeActivity.finishFixedRotationTransform();
assertFalse(homeActivity.hasFixedRotationTransform());
assertFalse(wallpaperWindowToken.hasFixedRotationTransform());
}

View File

@@ -136,7 +136,7 @@ public class WindowTokenTests extends WindowTestsBase {
}
@Test
public void testClearFixedRotationTransform() {
public void testFinishFixedRotationTransform() {
final WindowToken appToken = mAppWindow.mToken;
final WindowToken wallpaperToken = mWallpaperWindow.mToken;
final Configuration config = new Configuration(mDisplayContent.getConfiguration());
@@ -152,7 +152,7 @@ public class WindowTokenTests extends WindowTestsBase {
assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation());
// The display doesn't rotate, the transformation will be canceled.
mAppWindow.mToken.clearFixedRotationTransform(null /* applyDisplayRotation */);
mAppWindow.mToken.finishFixedRotationTransform();
// The window tokens should restore to the original rotation.
assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation());