Force-update the orientation of before sending to client

The changes in this topic focus on cases when we ensure visibility
and config when making some activity visible. Sometimes in WM we
might skip updating orientation because we're waiting for something
(checks marked with !forceUpdate), which results in configuration of
the previously visible activity being applied to the newly visible
one.

Bug: 76011287
Test: ActivityManagerAppConfigurationTests
Change-Id: I16b1de5ea9e38883692a4658f305518facaf98e9
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f18f236..257d79d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1641,7 +1641,7 @@
         final Configuration config = mWindowManager.updateOrientationFromAppTokens(
                 getDisplayOverrideConfiguration(displayId),
                 r != null && r.mayFreezeScreenLocked(r.app) ? r.appToken : null,
-                displayId);
+                displayId, true /* forceUpdate */);
         if (r != null && markFrozenIfConfigChanged && config != null) {
             r.frozenBeforeDestroy = true;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f3423c6..b59e728 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -944,33 +944,50 @@
     /**
      * Update rotation of the display.
      *
-     * Returns true if the rotation has been changed.  In this case YOU MUST CALL
-     * {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
+     * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
+     *         {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
      */
     boolean updateRotationUnchecked() {
-        if (mService.mDeferredRotationPauseCount > 0) {
-            // Rotation updates have been paused temporarily.  Defer the update until
-            // updates have been resumed.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
-            return false;
-        }
+        return updateRotationUnchecked(false /* forceUpdate */);
+    }
 
-        ScreenRotationAnimation screenRotationAnimation =
-                mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
-        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
-            // Rotation updates cannot be performed while the previous rotation change
-            // animation is still in progress.  Skip this update.  We will try updating
-            // again after the animation is finished and the display is unfrozen.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
-            return false;
-        }
-        if (mService.mDisplayFrozen) {
-            // Even if the screen rotation animation has finished (e.g. isAnimating
-            // returns false), there is still some time where we haven't yet unfrozen
-            // the display. We also need to abort rotation here.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                    "Deferring rotation, still finishing previous rotation");
-            return false;
+    /**
+     * Update rotation of the display with an option to force the update.
+     * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
+     *                    orientation because we're waiting for some rotation to finish or display
+     *                    to unfreeze, which results in configuration of the previously visible
+     *                    activity being applied to a newly visible one. Forcing the rotation
+     *                    update allows to workaround this issue.
+     * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
+     *         {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
+     */
+    boolean updateRotationUnchecked(boolean forceUpdate) {
+        ScreenRotationAnimation screenRotationAnimation;
+        if (!forceUpdate) {
+            if (mService.mDeferredRotationPauseCount > 0) {
+                // Rotation updates have been paused temporarily.  Defer the update until
+                // updates have been resumed.
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
+                return false;
+            }
+
+            screenRotationAnimation =
+                    mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+                // Rotation updates cannot be performed while the previous rotation change
+                // animation is still in progress.  Skip this update.  We will try updating
+                // again after the animation is finished and the display is unfrozen.
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
+                return false;
+            }
+            if (mService.mDisplayFrozen) {
+                // Even if the screen rotation animation has finished (e.g. isAnimating
+                // returns false), there is still some time where we haven't yet unfrozen
+                // the display. We also need to abort rotation here.
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                        "Deferring rotation, still finishing previous rotation");
+                return false;
+            }
         }
 
         if (!mService.mDisplayEnabled) {
@@ -992,7 +1009,7 @@
 
         if (mayRotateSeamlessly) {
             final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
-            if (seamlessRotated != null) {
+            if (seamlessRotated != null && !forceUpdate) {
                 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation
                 // to complete (that is, waiting for windows to redraw). It's tempting to check
                 // w.mSeamlessRotationCount but that could be incorrect in the case of
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f42f855..9c253fb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2372,6 +2372,12 @@
     @Override
     public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
             IBinder freezeThisOneIfNeeded, int displayId) {
+        return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId,
+                false /* forceUpdate */);
+    }
+
+    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
@@ -2381,7 +2387,7 @@
         try {
             synchronized(mWindowMap) {
                 config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
-                        displayId);
+                        displayId, forceUpdate);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2391,13 +2397,13 @@
     }
 
     private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId) {
+            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
         if (!mDisplayReady) {
             return null;
         }
         Configuration config = null;
 
-        if (updateOrientationFromAppTokensLocked(displayId)) {
+        if (updateOrientationFromAppTokensLocked(displayId, forceUpdate)) {
             // If we changed the orientation but mOrientationChangeComplete is already true,
             // we used seamless rotation, and we don't need to freeze the screen.
             if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
@@ -2445,11 +2451,15 @@
      * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
      */
     boolean updateOrientationFromAppTokensLocked(int displayId) {
+        return updateOrientationFromAppTokensLocked(displayId, false /* forceUpdate */);
+    }
+
+    boolean updateOrientationFromAppTokensLocked(int displayId, boolean forceUpdate) {
         long ident = Binder.clearCallingIdentity();
         try {
             final DisplayContent dc = mRoot.getDisplayContent(displayId);
             final int req = dc.getOrientation();
-            if (req != dc.getLastOrientation()) {
+            if (req != dc.getLastOrientation() || forceUpdate) {
                 dc.setLastOrientation(req);
                 //send a message to Policy indicating orientation change to take
                 //action like disabling/enabling sensors etc.,
@@ -2457,12 +2467,8 @@
                 if (dc.isDefaultDisplay) {
                     mPolicy.setCurrentOrientationLw(req);
                 }
-                if (dc.updateRotationUnchecked()) {
-                    // changed
-                    return true;
-                }
+                return dc.updateRotationUnchecked(forceUpdate);
             }
-
             return false;
         } finally {
             Binder.restoreCallingIdentity(ident);