Update visibility and config at the same time

Update visibility, orientation and config together to ensure that
only activities that are actually visible can affect the orientation.

Bug: 76011287
Test: ActivityManagerAppConfigurationTests
Change-Id: Icdafedf540391d186f5dbe250b2b69d36742a26c
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aaa5161..3ad461f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1360,7 +1360,9 @@
     }
 
     void goToSleep() {
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        // Ensure visibility without updating configuration, as activities are about to sleep.
+        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS,
+                false /* updateConfiguration */);
 
         // Make sure any paused or stopped but visible activities are now sleeping.
         // This ensures that the activity's onStop() is called.
@@ -1829,12 +1831,23 @@
     }
 
     /**
-     * Make sure that all activities that need to be visible (that is, they
-     * currently can be seen by the user) actually are.
+     * Make sure that all activities that need to be visible in the stack (that is, they
+     * currently can be seen by the user) actually are and update their configuration.
+     */
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
+        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                true /* updateConfiguration */);
+    }
+
+    /**
+     * Ensure visibility with an option to also update the configuration of visible activities.
+     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
      */
     // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
     final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
+            boolean preserveWindows, boolean updateConfiguration) {
         mTopActivityOccludesKeyguard = false;
         mTopDismissingKeyguardActivity = null;
         mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
@@ -1886,9 +1899,7 @@
                                 + " finishing=" + r.finishing + " state=" + r.getState());
                         // First: if this is not the current activity being started, make
                         // sure it matches the current configuration.
-                        if (r != starting) {
-                            // Ensure activity configuration ignoring stop state since we are
-                            // becoming visible.
+                        if (r != starting && updateConfiguration) {
                             r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
                                     true /* ignoreStopState */);
                         }
@@ -2608,25 +2619,16 @@
                 boolean notUpdated = true;
 
                 if (mStackSupervisor.isFocusedStack(this)) {
-
-                    // We have special rotation behavior when Keyguard is locked. Make sure all
-                    // activity visibilities are set correctly as well as the transition is updated
-                    // if needed to get the correct rotation behavior.
+                    // We have special rotation behavior when here is some active activity that
+                    // requests specific orientation or Keyguard is locked. Make sure all activity
+                    // visibilities are set correctly as well as the transition is updated if needed
+                    // to get the correct rotation behavior. Otherwise the following call to update
+                    // the orientation may cause incorrect configurations delivered to client as a
+                    // result of invisible window resize.
                     // TODO: Remove this once visibilities are set correctly immediately when
                     // starting an activity.
-                    if (mStackSupervisor.getKeyguardController().isKeyguardLocked()) {
-                        mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
-                                0 /* configChanges */, false /* preserveWindows */);
-                    }
-                    final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                            mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
-                            next.mayFreezeScreenLocked(next.app) ? next.appToken : null,
-                                    mDisplayId);
-                    if (config != null) {
-                        next.frozenBeforeDestroy = true;
-                    }
-                    notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
-                            false /* deferResume */, mDisplayId);
+                    notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                            true /* markFrozenIfConfigChanged */, false /* deferResume */);
                 }
 
                 if (notUpdated) {
@@ -3834,7 +3836,8 @@
             if (finishingActivityInNonFocusedStack) {
                 // Finishing activity that was in paused state and it was in not currently focused
                 // stack, need to make something visible in its place.
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mStackSupervisor.ensureVisibilityAndConfig(null, mDisplayId,
+                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
             if (activityRemoved) {
                 mStackSupervisor.resumeFocusedStackTopActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7310fab..f18f236 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1409,15 +1409,11 @@
             // manager with a new orientation.  We don't care about that, because the activity is
             // not currently running so we are just restarting it anyway.
             if (checkConfig) {
-                final int displayId = r.getDisplayId();
-                final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                        getDisplayOverrideConfiguration(displayId),
-                        r.mayFreezeScreenLocked(app) ? r.appToken : null, displayId);
                 // Deferring resume here because we're going to launch new activity shortly.
                 // We don't want to perform a redundant launch of the same record while ensuring
                 // configurations and trying to resume top activity of focused stack.
-                mService.updateDisplayOverrideConfigurationLocked(config, r, true /* deferResume */,
-                        displayId);
+                ensureVisibilityAndConfig(r, r.getDisplayId(),
+                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
 
             if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
@@ -1630,6 +1626,31 @@
         return true;
     }
 
+    /**
+     * Ensure all activities visibility, update orientation and configuration.
+     */
+    boolean ensureVisibilityAndConfig(ActivityRecord r, int displayId,
+            boolean markFrozenIfConfigChanged, boolean deferResume) {
+        // First ensure visibility without updating the config just yet. We need this to know what
+        // activities are affecting configuration now.
+        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */, false /* updateConfiguration */);
+
+        // Force-update the orientation from the WindowManager, since we need the true configuration
+        // to send to the client now.
+        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+                getDisplayOverrideConfiguration(displayId),
+                r != null && r.mayFreezeScreenLocked(r.app) ? r.appToken : null,
+                displayId);
+        if (r != null && markFrozenIfConfigChanged && config != null) {
+            r.frozenBeforeDestroy = true;
+        }
+
+        // Update the configuration of the activities on the display.
+        return mService.updateDisplayOverrideConfigurationLocked(config, r,
+                deferResume, displayId);
+    }
+
     private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
         int extrasSize = 0;
         if (intent != null) {
@@ -3647,8 +3668,21 @@
         mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
     }
 
+    /**
+     * Make sure that all activities that need to be visible in the system actually are and update
+     * their configuration.
+     */
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
+        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                true /* updateConfiguration */);
+    }
+
+    /**
+     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     */
+    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean updateConfiguration) {
         getKeyguardController().beginActivityVisibilityUpdate();
         try {
             // First the front stacks. In case any are not fullscreen and are in front of home.
@@ -3656,7 +3690,8 @@
                 final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
                 for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                     final ActivityStack stack = display.getChildAt(stackNdx);
-                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
+                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                            updateConfiguration);
                 }
             }
         } finally {