Support for setting stack windowing mode

We currently create a in stack in the windowing mode we want anytime we
want to change the windowing mode of a task which isn't optimal.
We now change the current stack windowing mode for some cases vs.
creating a new stack.

Test: go/wm-smoke
Bug: 64146578
Fixes: 68948655
Fixes: 65739235
Fixes: 29068935
Change-Id: I9fceb7d608e86610c1fb5ba59b15087a96e7401e
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c4b810f..4d839d0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -1488,7 +1489,7 @@
      * Returns the topmost stack on the display that is compatible with the input windowing mode.
      * Null is no compatible stack on the display.
      */
-    TaskStack getStack(int windowingMode) {
+    TaskStack getTopStackInWindowingMode(int windowingMode) {
         return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
     }
 
@@ -1764,10 +1765,6 @@
 
         final TaskStack stack = new TaskStack(mService, stackId, controller);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
-
-        if (stack.inSplitScreenPrimaryWindowingMode()) {
-            mDividerControllerLocked.notifyDockedStackExistsChanged(true);
-        }
         return stack;
     }
 
@@ -2274,7 +2271,7 @@
 
     /** Returns true if the stack in the windowing mode is visible. */
     boolean isStackVisible(int windowingMode) {
-        final TaskStack stack = getStack(windowingMode);
+        final TaskStack stack = getTopStackInWindowingMode(windowingMode);
         return stack != null && stack.isVisible();
     }
 
@@ -3391,6 +3388,12 @@
             }
             for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
                 final TaskStack stack = mTaskStackContainers.getChildAt(i);
+                if (activityType == ACTIVITY_TYPE_UNDEFINED
+                        && windowingMode == stack.getWindowingMode()) {
+                    // Passing in undefined type means we want to match the topmost stack with the
+                    // windowing mode.
+                    return stack;
+                }
                 if (stack.isCompatible(windowingMode, activityType)) {
                     return stack;
                 }
@@ -3461,6 +3464,7 @@
                             + " already exist on display=" + this + " stack=" + stack);
                 }
                 mSplitScreenPrimaryStack = stack;
+                mDividerControllerLocked.notifyDockedStackExistsChanged(true);
             }
         }
 
@@ -3471,6 +3475,10 @@
                 mPinnedStack = null;
             } else if (stack == mSplitScreenPrimaryStack) {
                 mSplitScreenPrimaryStack = null;
+                // Re-set the split-screen create mode whenever the split-screen stack is removed.
+                mService.setDockedStackCreateStateLocked(
+                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+                mDividerControllerLocked.notifyDockedStackExistsChanged(false);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index e693e5a..d79ba89 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -559,7 +559,7 @@
     void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
         // TODO: Maybe only allow split-screen windowing modes?
         final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
-                ? mDisplayContent.getStack(targetWindowingMode)
+                ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
                 : null;
         final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
@@ -663,11 +663,15 @@
         if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
             return;
         }
-        final TaskStack fullscreenStack = mDisplayContent.getStack(
+        final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
-        final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
-        setMinimizedDockedStack(homeVisible && !homeBehind, animate);
+        boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
+        if (homeVisible && topSecondaryStack != null) {
+            // Home should only be considered visible if it is greater or equal to the top secondary
+            // stack in terms of z-order.
+            homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
+        }
+        setMinimizedDockedStack(homeVisible, animate);
     }
 
     private boolean isWithinDisplay(Task task) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index aff1bc6..95c1d53 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -225,11 +225,13 @@
         }
     }
 
-    private void getRawBounds(Rect outBounds) {
-        if (mContainer.getRawFullscreen()) {
-            outBounds.setEmpty();
-        } else {
-            mContainer.getRawBounds(outBounds);
+    public void getRawBounds(Rect outBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer.getRawFullscreen()) {
+                outBounds.setEmpty();
+            } else {
+                mContainer.getRawBounds(outBounds);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index dde7946..053fb47 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -685,9 +685,12 @@
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int prevWindowingMode = getWindowingMode();
         super.onConfigurationChanged(newParentConfig);
-        if (mDisplayContent != null && prevWindowingMode != getWindowingMode()) {
-            mDisplayContent.onStackWindowingModeChanged(this);
+        final int windowingMode = getWindowingMode();
+        if (mDisplayContent == null || prevWindowingMode == windowingMode) {
+            return;
         }
+        mDisplayContent.onStackWindowingModeChanged(this);
+        updateBoundsForWindowModeChange();
     }
 
     @Override
@@ -699,39 +702,41 @@
         mDisplayContent = dc;
         mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
                 "animation background stackId=" + mStackId);
+        updateBoundsForWindowModeChange();
+        super.onDisplayChanged(dc);
+    }
 
+    private void updateBoundsForWindowModeChange() {
         Rect bounds = null;
-        final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
-        if (inSplitScreenPrimaryWindowingMode()
-                || (dockedStack != null && inSplitScreenSecondaryWindowingMode()
-                        && !dockedStack.fillsParent())) {
+        final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
+        final TaskStack splitScreenStack =
+                mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        if (inSplitScreenPrimary || (splitScreenStack != null
+                && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) {
             // The existence of a docked stack affects the size of other static stack created since
             // the docked stack occupies a dedicated region on screen, but only if the dock stack is
             // not fullscreen. If it's fullscreen, it means that we are in the transition of
             // dismissing it, so we must not resize this stack.
             bounds = new Rect();
-            dc.getLogicalDisplayRect(mTmpRect);
+            mDisplayContent.getLogicalDisplayRect(mTmpRect);
             mTmpRect2.setEmpty();
-            if (dockedStack != null) {
-                dockedStack.getRawBounds(mTmpRect2);
+            if (splitScreenStack != null) {
+                splitScreenStack.getRawBounds(mTmpRect2);
             }
             final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
                     == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
             getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
-                    mDisplayContent.mDividerControllerLocked.getContentWidth(),
-                    dockedOnTopOrLeft);
+                    mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
         } else if (inPinnedWindowingMode()) {
             // Update the bounds based on any changes to the display info
             getAnimationOrCurrentBounds(mTmpRect2);
-            boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
-                    mTmpRect2, mTmpRect3);
-            if (updated) {
+            if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+                    mTmpRect2, mTmpRect3)) {
                 bounds = new Rect(mTmpRect3);
             }
         }
 
         updateDisplayInfo(bounds);
-        super.onDisplayChanged(dc);
     }
 
     /**
@@ -922,10 +927,6 @@
             mAnimationBackgroundSurface = null;
         }
 
-        if (inSplitScreenPrimaryWindowingMode()) {
-            mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
-        }
-
         mDisplayContent = null;
         mService.mWindowPlacerLocked.requestTraversal();
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index df51be1..723b888 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6860,11 +6860,16 @@
     public void setWillReplaceWindow(IBinder token, boolean animate) {
         synchronized (mWindowMap) {
             final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
-            if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+            if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
                 return;
             }
+            if (!appWindowToken.hasContentToDisplay()) {
+                Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+                        + token);
+                return;
+            }
             appWindowToken.setWillReplaceWindows(animate);
         }
     }
@@ -6884,11 +6889,16 @@
     void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
         synchronized (mWindowMap) {
             final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
-            if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+            if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
                 return;
             }
+            if (!appWindowToken.hasContentToDisplay()) {
+                Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+                        + token);
+                return;
+            }
 
             if (childrenOnly) {
                 appWindowToken.setWillReplaceChildWindows();