summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java65
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java23
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java43
7 files changed, 170 insertions, 46 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9c56788ad28f..93074f1d0ad7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5125,26 +5125,48 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void startFreezingScreen() {
+ startFreezingScreen(ROTATION_UNDEFINED /* overrideOriginalDisplayRotation */);
+ }
+
+ void startFreezingScreen(int overrideOriginalDisplayRotation) {
ProtoLog.i(WM_DEBUG_ORIENTATION,
"Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
appToken, isVisible(), mFreezingScreen, mVisibleRequested,
new RuntimeException().fillInStackTrace());
- if (mVisibleRequested) {
- if (!mFreezingScreen) {
- mFreezingScreen = true;
- mWmService.registerAppFreezeListener(this);
- mWmService.mAppsFreezingScreen++;
- if (mWmService.mAppsFreezingScreen == 1) {
- mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
- mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
- mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+ if (!mVisibleRequested) {
+ return;
+ }
+
+ // If the override is given, the rotation of display doesn't change but we still want to
+ // cover the activity whose configuration is changing by freezing the display and running
+ // the rotation animation.
+ final boolean forceRotation = overrideOriginalDisplayRotation != ROTATION_UNDEFINED;
+ if (!mFreezingScreen) {
+ mFreezingScreen = true;
+ mWmService.registerAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen++;
+ if (mWmService.mAppsFreezingScreen == 1) {
+ if (forceRotation) {
+ // Make sure normal rotation animation will be applied.
+ mDisplayContent.getDisplayRotation().cancelSeamlessRotation();
}
+ mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */,
+ mDisplayContent, overrideOriginalDisplayRotation);
+ mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
}
- final int count = mChildren.size();
- for (int i = 0; i < count; i++) {
- final WindowState w = mChildren.get(i);
- w.onStartFreezingScreen();
- }
+ }
+ if (forceRotation) {
+ // The rotation of the real display won't change, so in order to unfreeze the screen
+ // via {@link #checkAppWindowsReadyToShow}, the windows have to be able to call
+ // {@link WindowState#reportResized} (it is skipped if the window is freezing) to update
+ // the drawn state.
+ return;
+ }
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ final WindowState w = mChildren.get(i);
+ w.onStartFreezingScreen();
}
}
@@ -6159,6 +6181,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
}
+ @Override
+ void onCancelFixedRotationTransform(int originalDisplayRotation) {
+ if (this != mDisplayContent.getLastOrientationSource()
+ || getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED) {
+ // Only need to handle the activity that should be rotated with display.
+ return;
+ }
+
+ // Perform rotation animation according to the rotation of this activity.
+ startFreezingScreen(originalDisplayRotation);
+ // This activity may relaunch or perform configuration change so once it has reported drawn,
+ // the screen can be unfrozen.
+ ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
+ }
+
void setRequestedOrientation(int requestedOrientation) {
setOrientation(requestedOrientation, mayFreezeScreenLocked());
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8d46a6769bc8..36d558312c2f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -495,6 +495,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* The launching activity which is using fixed rotation transformation.
*
* @see #handleTopActivityLaunchingInDifferentOrientation
+ * @see DisplayRotation#shouldRotateSeamlessly
*/
ActivityRecord mFixedRotationLaunchingApp;
@@ -1238,7 +1239,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (configChanged) {
mWaitingForConfig = true;
- mWmService.startFreezingDisplayLocked(0 /* exitAnim */, 0 /* enterAnim */, this);
+ mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
sendNewConfiguration();
}
@@ -1476,6 +1477,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
sendNewConfiguration();
return true;
}
+ // 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.
+ mFixedRotationLaunchingApp = null;
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bef80f0a230a..ebfe70c0c371 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -537,8 +537,29 @@ public class DisplayRotation {
}
void prepareNormalRotationAnimation() {
+ cancelSeamlessRotation();
final RotationAnimationPair anim = selectRotationAnimation();
- mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
+ mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
+ }
+
+ /**
+ * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
+ * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
+ * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
+ * and it doesn't choose seamless rotation.
+ */
+ void cancelSeamlessRotation() {
+ if (!mRotatingSeamlessly) {
+ return;
+ }
+ mDisplayContent.forAllWindows(w -> {
+ if (w.mSeamlesslyRotated) {
+ w.finishSeamlessRotation(false /* timeout */);
+ w.mSeamlesslyRotated = false;
+ }
+ }, true /* traverseTopToBottom */);
+ mSeamlessRotationCount = 0;
+ mRotatingSeamlessly = false;
}
private void prepareSeamlessRotation() {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index b92ead1a0531..5f33ea170923 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -41,7 +41,6 @@ import android.graphics.Rect;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
@@ -117,8 +116,9 @@ class ScreenRotationAnimation {
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
- private int mOriginalRotation;
- private int mOriginalWidth, mOriginalHeight;
+ private final int mOriginalRotation;
+ private final int mOriginalWidth;
+ private final int mOriginalHeight;
private int mCurRotation;
private Rect mOriginalDisplayRect = new Rect();
@@ -140,20 +140,18 @@ class ScreenRotationAnimation {
/** Intensity of light/whiteness of the layout after rotation occurs. */
private float mEndLuma;
- public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
- mService = service;
- mContext = context;
+ ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
+ mService = displayContent.mWmService;
+ mContext = mService.mContext;
mDisplayContent = displayContent;
displayContent.getBounds(mOriginalDisplayRect);
// Screenshot does NOT include rotation!
- final Display display = displayContent.getDisplay();
- int originalRotation = display.getRotation();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int realOriginalRotation = displayInfo.rotation;
final int originalWidth;
final int originalHeight;
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (fixedToUserRotation) {
+ if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
@@ -163,8 +161,8 @@ class ScreenRotationAnimation {
originalWidth = displayInfo.logicalWidth;
originalHeight = displayInfo.logicalHeight;
}
- if (originalRotation == Surface.ROTATION_90
- || originalRotation == Surface.ROTATION_270) {
+ if (realOriginalRotation == Surface.ROTATION_90
+ || realOriginalRotation == Surface.ROTATION_270) {
mWidth = originalHeight;
mHeight = originalWidth;
} else {
@@ -173,10 +171,18 @@ class ScreenRotationAnimation {
}
mOriginalRotation = originalRotation;
- mOriginalWidth = originalWidth;
- mOriginalHeight = originalHeight;
+ // If the delta is not zero, the rotation of display may not change, but we still want to
+ // apply rotation animation because there should be a top app shown as rotated. So the
+ // specified original rotation customizes the direction of animation to have better look
+ // when restoring the rotated app to the same rotation as current display.
+ final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
+ final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
+ mOriginalWidth = flipped ? originalHeight : originalWidth;
+ mOriginalHeight = flipped ? originalWidth : originalHeight;
mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
+ // Check whether the current screen contains any secure content.
+ final boolean isSecure = displayContent.hasSecureWindowOnScreen();
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
mBackColorSurface = displayContent.makeChildSurface(null)
@@ -202,7 +208,7 @@ class ScreenRotationAnimation {
t2.apply(true /* sync */);
// Capture a screenshot into the surface we just created.
- final int displayId = display.getDisplayId();
+ final int displayId = displayContent.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
surface.copyFrom(mScreenshotLayer);
SurfaceControl.ScreenshotGraphicBuffer gb =
@@ -242,7 +248,7 @@ class ScreenRotationAnimation {
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" FREEZE %s: CREATE", mScreenshotLayer);
- setRotation(t, originalRotation);
+ setRotation(t, realOriginalRotation);
t.apply();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d879765f8df..5c7d37baef37 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -29,6 +29,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
@@ -2941,7 +2942,7 @@ public class WindowManagerService extends IWindowManager.Stub
mClientFreezingScreen = true;
final long origId = Binder.clearCallingIdentity();
try {
- startFreezingDisplayLocked(exitAnim, enterAnim);
+ startFreezingDisplay(exitAnim, enterAnim);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
} finally {
@@ -5479,13 +5480,17 @@ public class WindowManagerService extends IWindowManager.Stub
return changed;
}
- void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
- startFreezingDisplayLocked(exitAnim, enterAnim,
- getDefaultDisplayContentLocked());
+ void startFreezingDisplay(int exitAnim, int enterAnim) {
+ startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());
}
- void startFreezingDisplayLocked(int exitAnim, int enterAnim,
- DisplayContent displayContent) {
+ void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {
+ startFreezingDisplay(exitAnim, enterAnim, displayContent,
+ ROTATION_UNDEFINED /* overrideOriginalRotation */);
+ }
+
+ void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
+ int overrideOriginalRotation) {
if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
return;
}
@@ -5529,14 +5534,12 @@ public class WindowManagerService extends IWindowManager.Stub
screenRotationAnimation.kill();
}
- // Check whether the current screen contains any secure content.
- boolean isSecure = displayContent.hasSecureWindowOnScreen();
-
displayContent.updateDisplayInfo();
- screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
- this);
- displayContent.setRotationAnimation(screenRotationAnimation);
+ final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
+ ? overrideOriginalRotation
+ : displayContent.getDisplayInfo().rotation;
+ displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
+ originalRotation));
}
void stopFreezingDisplayLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e34b81654c72..2d5c4c1c3b7c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -595,7 +595,17 @@ class WindowToken extends WindowContainer<WindowState> {
// The window may be detached or detaching.
return;
}
+ final int originalRotation = getWindowConfiguration().getRotation();
onConfigurationChanged(parent.getConfiguration());
+ onCancelFixedRotationTransform(originalRotation);
+ }
+
+ /**
+ * It is called when the window is using fixed rotation transform, and before display applies
+ * the same rotation, the rotation change for display is canceled, e.g. the orientation from
+ * sensor is updated to previous direction.
+ */
+ void onCancelFixedRotationTransform(int originalDisplayRotation) {
}
@Override
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 08f6409cb902..3c111e21f711 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -63,6 +63,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
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.never;
@@ -1273,6 +1274,48 @@ public class ActivityRecordTests extends ActivityTestsBase {
}
@Test
+ public void testActivityOnCancelFixedRotationTransform() {
+ mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
+ final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation();
+ spyOn(displayRotation);
+
+ final DisplayContent display = mActivity.mDisplayContent;
+ final int originalRotation = display.getRotation();
+
+ // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
+ doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+ doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
+ anyInt() /* orientation */, anyInt() /* lastRotation */);
+ // Set to visible so the activity can freeze the screen.
+ mActivity.setVisibility(true);
+
+ display.rotateInDifferentOrientationIfNeeded(mActivity);
+ display.mFixedRotationLaunchingApp = mActivity;
+ displayRotation.updateRotationUnchecked(false /* forceUpdate */);
+
+ assertTrue(displayRotation.isRotatingSeamlessly());
+
+ // Simulate the rotation has been updated to previous one, e.g. sensor updates before the
+ // remote rotation is completed.
+ doReturn(originalRotation).when(displayRotation).rotationForOrientation(
+ anyInt() /* orientation */, anyInt() /* lastRotation */);
+ display.updateOrientation();
+
+ final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
+ mActivity.finishFixedRotationTransform();
+ final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
+ rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
+
+ // Because the display doesn't rotate, the rotated activity needs to cancel the fixed
+ // rotation. There should be a rotation animation to cover the change of activity.
+ verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
+ assertTrue(mActivity.isFreezingScreen());
+ assertFalse(displayRotation.isRotatingSeamlessly());
+ assertNotNull(rotationAnim);
+ assertTrue(rotationAnim.isRotating());
+ }
+
+ @Test
public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
final ActivityRecord secondaryDisplayActivity =
createActivityOnDisplay(false /* defaultDisplay */, null /* process */);