WM: Track waitForConfig per display

If a virtual display is present while the screen is rotated, reconfiguring the virtual display
will set the global mWaitingForConfig flag and can prevent window relayouts and
surface placements for windows on the primary display. This will cause windows to be
drawn incorrectly in the old rotation and get stuck in this state until there is another rotation.

Bug: b/117770934
Test: go/wm-smoke
Test: Test rotation with huddle presenting
Change-Id: I14034a8e8aac857b46702c0108e07adcb4eb8ac5
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..3acacbc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -352,6 +352,14 @@
     int pendingLayoutChanges;
     int mDeferredRotationPauseCount;
 
+    /**
+     * Used to gate application window layout until we have sent the complete configuration.
+     * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+     *       we want to replace this flag with a mechanism that will confirm the configuration
+     *       applied by the client is the one expected by the system server.
+     */
+    boolean mWaitingForConfig;
+
     // TODO(multi-display): remove some of the usages.
     @VisibleForTesting
     boolean isDefaultDisplay;
@@ -1284,7 +1292,7 @@
                 + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
 
         if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
-            mService.mWaitingForConfig = true;
+            mWaitingForConfig = true;
         }
 
         mRotation = rotation;
@@ -3675,6 +3683,7 @@
             }
             mTmpWindow = w;
             w.setDisplayLayoutNeeded();
+            w.finishSeamlessRotation(true /* timeout */);
             mService.markForSeamlessRotation(w, false);
         }, true /* traverseTopToBottom */);
 
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
+     *
+     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+     * it.
      */
-    public void finish(WindowState win) {
+    public void finish(WindowState win, boolean timeout) {
         mTransform.reset();
         final Transaction t = win.getPendingTransaction();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
-        if (win.mWinAnimator.mSurfaceController != null) {
+        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
             t.deferTransactionUntil(win.mSurfaceControl,
                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39a8465..25f3128 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -599,7 +599,6 @@
     long mDisplayFreezeTime = 0;
     int mLastDisplayFreezeDuration = 0;
     Object mLastFinishedFreezeSource = null;
-    boolean mWaitingForConfig = false;
     boolean mSwitchingUser = false;
 
     final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -1870,7 +1869,7 @@
         long origId = Binder.clearCallingIdentity();
         final int displayId;
         synchronized (mGlobalLock) {
-            WindowState win = windowForClientLocked(session, client, false);
+            final WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return 0;
             }
@@ -1885,8 +1884,9 @@
 
             win.setFrameNumber(frameNumber);
 
-            if (!mWaitingForConfig) {
-                win.finishSeamlessRotation();
+            final DisplayContent dc = win.getDisplayContent();
+            if (!dc.mWaitingForConfig) {
+                win.finishSeamlessRotation(false /* timeout */);
             }
 
             int attrChanges = 0;
@@ -2438,7 +2438,7 @@
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             displayContent.computeScreenConfiguration(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
-                mWaitingForConfig = true;
+                displayContent.mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2451,10 @@
         return config;
     }
 
-    void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
-        if (mWaitingForConfig) {
-            mWaitingForConfig = false;
+    void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+            @NonNull DisplayContent dc) {
+        if (dc.mWaitingForConfig) {
+            dc.mWaitingForConfig = false;
             mLastFinishedFreezeSource = "new-config";
         }
 
@@ -4181,13 +4182,11 @@
                 // placement to unfreeze the display since we froze it when the rotation was updated
                 // in DisplayContent#updateRotationUnchecked.
                 synchronized (mGlobalLock) {
-                    if (mWaitingForConfig) {
-                        mWaitingForConfig = false;
+                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                    if (dc != null && dc.mWaitingForConfig) {
+                        dc.mWaitingForConfig = false;
                         mLastFinishedFreezeSource = "config-unchanged";
-                        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                        if (dc != null) {
-                            dc.setLayoutNeeded();
-                        }
+                        dc.setLayoutNeeded();
                         mWindowPlacerLocked.performSurfacePlacement();
                     }
                 }
@@ -5136,7 +5135,7 @@
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
-            mWaitingForConfig = true;
+            displayContent.mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
             displayContent.sendNewConfiguration();
@@ -5396,23 +5395,24 @@
             return;
         }
 
-        final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
-        if (mWaitingForConfig || mAppsFreezingScreen > 0
+        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+        final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+        final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+        if (waitingForConfig || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
-                || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+                || mClientFreezingScreen || numOpeningApps > 0) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
-                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
                 + ", mAppsFreezingScreen=" + mAppsFreezingScreen
                 + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
                 + ", mClientFreezingScreen=" + mClientFreezingScreen
-                + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+                + ", mOpeningApps.size()=" + numOpeningApps);
             return;
         }
 
         if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
                 "stopFreezingDisplayLocked: Unfreezing now");
 
-        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
 
         // We must make a local copy of the displayId as it can be potentially overwritten later on
         // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -6033,7 +6033,6 @@
                     pw.print(" windows="); pw.print(mWindowsFreezingScreen);
                     pw.print(" client="); pw.print(mClientFreezingScreen);
                     pw.print(" apps="); pw.print(mAppsFreezingScreen);
-                    pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             pw.print("  mRotation="); pw.print(defaultDisplayContent.getRotation());
                     pw.print(" mAltOrientation=");
@@ -6042,6 +6041,9 @@
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
                             pw.println(defaultDisplayContent.getLastOrientation());
+                    pw.print(" waitingForConfig=");
+                            pw.println(defaultDisplayContent.mWaitingForConfig);
+
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 567b583..6f044f3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -180,6 +180,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
@@ -579,8 +580,8 @@
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
-            boolean requested) {
+    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+            @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
         if (!isVisibleNow() || mIsWallpaper) {
             return;
@@ -597,9 +598,9 @@
         }
     }
 
-    void finishSeamlessRotation() {
+    void finishSeamlessRotation(boolean timeout) {
         if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this);
+            mPendingSeamlessRotate.finish(this, timeout);
             mFinishSeamlessRotateFrameNumber = getFrameNumber();
             mPendingSeamlessRotate = null;
             mService.markForSeamlessRotation(this, false);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
             return;
         }
 
-        if (mService.mWaitingForConfig) {
+        // TODO(multi-display):
+        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+        if (defaultDisplay.mWaitingForConfig) {
             // Our configuration has changed (most likely rotation), but we
             // don't yet have the complete configuration to report to
             // applications.  Don't do any window layout until we have it.