Ignore rotation update during recent animation.

During the recent animation, the closing app is still considered on top
and its required orientation is used for the next rotation computation.

When going to launcher and rotating the device at the same time, a
rotation update is triggered by the sensor and if the closing app allows
a change of orientation, the display ends up rotating at the end of the
animation, which make the launcher rotate even if it is not supposed to
do so.

Test: atest
WmTests: com.android.server.wm.DisplayContentTests#testRecentsNotRotatingWithFixedRotation
Fixes: 157447718
Change-Id: I3f2a46ddfe5e76ad3200ea27ad272ae36b6bcc8d
This commit is contained in:
Vadim Caen
2020-06-05 12:28:07 +02:00
parent 26dfdc587f
commit a8849f27bc
3 changed files with 43 additions and 3 deletions

View File

@@ -32,6 +32,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
@@ -1526,12 +1527,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
* Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed
* 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 == r
if (prevRotatedLaunchingApp == r
&& r.getWindowConfiguration().getRotation() == rotation) {
// The given launching app and target rotation are the same as the existing ones.
return;
@@ -5659,6 +5660,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
/**
* Return {@code true} if there is an ongoing animation to the "Recents" activity and this
* activity as a fixed orientation so shouldn't be rotated.
*/
boolean isFixedOrientationRecentsAnimating() {
return mAnimatingRecents != null
&& mAnimatingRecents.getRequestedConfigurationOrientation()
!= ORIENTATION_UNDEFINED;
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
final ActivityRecord r = getActivityRecord(token);

View File

@@ -430,6 +430,15 @@ public class DisplayRotation {
"Deferring rotation, still finishing previous rotation");
return false;
}
if (mDisplayContent.mFixedRotationTransitionListener
.isFixedOrientationRecentsAnimating()) {
// During the recents animation, the closing app might still be considered on top.
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
// rotation update while the animation is running.
return false;
}
}
if (!mService.mDisplayEnabled) {

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -79,6 +80,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import android.annotation.SuppressLint;
import android.app.ActivityTaskManager;
@@ -1230,12 +1232,30 @@ public class DisplayContentTests extends WindowTestsBase {
assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
}
@Test
public void testRecentsNotRotatingWithFixedRotation() {
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean());
doCallRealMethod().when(displayRotation).updateOrientation(anyInt(), anyBoolean());
final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS);
recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
assertFalse(displayRotation.updateRotationUnchecked(false));
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(false);
assertTrue(displayRotation.updateRotationUnchecked(false));
}
@Test
public void testRemoteRotation() {
DisplayContent dc = createNewDisplay();
final DisplayRotation dr = dc.getDisplayRotation();
Mockito.doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
final boolean[] continued = new boolean[1];
// TODO(display-merge): Remove cast