Add specific z-ordering between PiP, Dream and Assistant

The z-order of all activity stacks is defined as:
- Dream is on top of everything
- PiP is directly below the dream
- always-on-top stacks are directly below PiP; new always-on-top stacks
  are added above existing ones
- other non-always-on-top stacks come directly below always-on-top
  stacks; new non-always-on-top stacks are added directly below
  always-on-top stacks and above existing non-always-on-top stacks
- if mIsAssistantOnTopOfDream (a config) is enabled, then Assistant is
  on top of everything (including the Dream); otherwise it is a normal
  non-always-on-top stack

Bug: 152425699
Bug: 150749838

Test: m && flash
      && atest ZOrderingTests
      && atest ActivityDisplayTests#testAlwaysOnTopStackLocation

Change-Id: I59eaa3fa2d6204779a56f5be16487812042ce8e8
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index cb9b332..c93451e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -123,6 +123,10 @@
     private final RootWindowContainer.FindTaskResult
             mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
 
+    // Indicates whether the Assistant should show on top of the Dream (respectively, above
+    // everything else on screen). Otherwise, it will be put under always-on-top stacks.
+    private final boolean mAssistantOnTopOfDream;
+
     /**
      * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
      * stack has been resumed. If stacks are changing position this will hold the old stack until
@@ -148,6 +152,9 @@
         mDisplayContent = displayContent;
         mRootWindowContainer = service.mRoot;
         mAtmService = service.mAtmService;
+
+        mAssistantOnTopOfDream = mWmService.mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_assistantOnTopOfDream);
     }
 
     /**
@@ -329,55 +336,80 @@
     }
 
     /**
+     * Assigns a priority number to stack types. This priority defines an order between the types
+     * of stacks that are added to the task display area.
+     *
+     * Higher priority number indicates that the stack should have a higher z-order.
+     *
+     * @return the priority of the stack
+     */
+    private int getPriority(ActivityStack stack) {
+        if (mAssistantOnTopOfDream && stack.isActivityTypeAssistant()) return 4;
+        if (stack.isActivityTypeDream()) return 3;
+        if (stack.inPinnedWindowingMode()) return 2;
+        if (stack.isAlwaysOnTop()) return 1;
+        return 0;
+    }
+
+    private int findMinPositionForStack(ActivityStack stack) {
+        int minPosition = POSITION_BOTTOM;
+        for (int i = 0; i < mChildren.size(); ++i) {
+            if (getPriority(getStackAt(i)) < getPriority(stack)) {
+                minPosition = i;
+            } else {
+                break;
+            }
+        }
+
+        if (stack.isAlwaysOnTop()) {
+            // Since a stack could be repositioned while still being one of the children, we check
+            // if this always-on-top stack already exists and if so, set the minPosition to its
+            // previous position.
+            final int currentIndex = getIndexOf(stack);
+            if (currentIndex > minPosition) {
+                minPosition = currentIndex;
+            }
+        }
+        return minPosition;
+    }
+
+    private int findMaxPositionForStack(ActivityStack stack) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final ActivityStack curr = getStackAt(i);
+            // Since a stack could be repositioned while still being one of the children, we check
+            // if 'curr' is the same stack and skip it if so
+            final boolean sameStack = curr == stack;
+            if (getPriority(curr) <= getPriority(stack) && !sameStack) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    /**
      * When stack is added or repositioned, find a proper position for it.
-     * This will make sure that pinned stack always stays on top.
+     *
+     * The order is defined as:
+     *  - Dream is on top of everything
+     *  - PiP is directly below the Dream
+     *  - always-on-top stacks are directly below PiP; new always-on-top stacks are added above
+     *    existing ones
+     *  - other non-always-on-top stacks come directly below always-on-top stacks; new
+     *    non-always-on-top stacks are added directly below always-on-top stacks and above existing
+     *    non-always-on-top stacks
+     *  - if {@link #mAssistantOnTopOfDream} is enabled, then Assistant is on top of everything
+     *    (including the Dream); otherwise, it is a normal non-always-on-top stack
+     *
      * @param requestedPosition Position requested by caller.
      * @param stack Stack to be added or positioned.
      * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
      * @return The proper position for the stack.
      */
-    private int findPositionForStack(int requestedPosition, ActivityStack stack,
-            boolean adding) {
-        if (stack.isActivityTypeDream()) {
-            return POSITION_TOP;
-        }
-
-        if (stack.inPinnedWindowingMode()) {
-            return POSITION_TOP;
-        }
-
-        final int topChildPosition = mChildren.size() - 1;
-        int belowAlwaysOnTopPosition = POSITION_BOTTOM;
-        for (int i = topChildPosition; i >= 0; --i) {
-            // Since a stack could be repositioned while being one of the child, return
-            // current index if that's the same stack we are positioning and it is always on
-            // top.
-            final boolean sameStack = mChildren.get(i) == stack;
-            if ((sameStack && stack.isAlwaysOnTop())
-                    || (!sameStack && !mChildren.get(i).isAlwaysOnTop())) {
-                belowAlwaysOnTopPosition = i;
-                break;
-            }
-        }
-
+    private int findPositionForStack(int requestedPosition, ActivityStack stack, boolean adding) {
         // The max possible position we can insert the stack at.
-        int maxPosition = POSITION_TOP;
+        int maxPosition = findMaxPositionForStack(stack);
         // The min possible position we can insert the stack at.
-        int minPosition = POSITION_BOTTOM;
-
-        if (stack.isAlwaysOnTop()) {
-            if (hasPinnedTask()) {
-                // Always-on-top stacks go below the pinned stack.
-                maxPosition = mChildren.indexOf(mRootPinnedTask) - 1;
-            }
-            // Always-on-top stacks need to be above all other stacks.
-            minPosition = belowAlwaysOnTopPosition
-                    != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
-        } else {
-            // Other stacks need to be below the always-on-top stacks.
-            maxPosition = belowAlwaysOnTopPosition
-                    != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
-        }
+        int minPosition = findMinPositionForStack(stack);
 
         // Cap the requested position to something reasonable for the previous position check
         // below.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 1debd8c..e3bb1b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -270,6 +272,28 @@
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
         assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
+
+        final ActivityStack dreamStack = taskDisplayArea.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
+        assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
+        assertTrue(dreamStack.isAlwaysOnTop());
+        topPosition = taskDisplayArea.getStackCount() - 1;
+        // Ensure dream shows above all activities, including PiP
+        assertEquals(dreamStack, taskDisplayArea.getTopStack());
+        assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1));
+
+        final ActivityStack assistStack = taskDisplayArea.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+        assertEquals(taskDisplayArea, assistStack.getDisplayArea());
+        assertFalse(assistStack.isAlwaysOnTop());
+        topPosition = taskDisplayArea.getStackCount() - 1;
+
+        // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream
+        // is false and on top of everything when true.
+        final boolean isAssistantOnTop = mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream);
+        assertEquals(assistStack, taskDisplayArea.getStackAt(
+                    isAssistantOnTop ? topPosition : topPosition - 4));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 0a6d3f3..20fab90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -471,23 +471,39 @@
         assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        // Assistant stack shouldn't be visible behind translucent split-screen stack
+        // Assistant stack shouldn't be visible behind translucent split-screen stack,
+        // unless it is configured to show on top of everything.
         doReturn(false).when(assistantStack).isTranslucent(any());
         doReturn(true).when(splitScreenPrimary).isTranslucent(any());
         doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
         splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
         splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
-        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-        assertEquals(STACK_VISIBILITY_INVISIBLE,
-                assistantStack.getVisibility(null /* starting */));
-        assertEquals(STACK_VISIBILITY_VISIBLE,
-                splitScreenPrimary.getVisibility(null /* starting */));
-        assertEquals(STACK_VISIBILITY_INVISIBLE,
-                splitScreenSecondary.getVisibility(null /* starting */));
-        assertEquals(STACK_VISIBILITY_VISIBLE,
-                splitScreenSecondary2.getVisibility(null /* starting */));
+
+        if (isAssistantOnTop()) {
+            assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+            assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+            assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+            assertEquals(STACK_VISIBILITY_VISIBLE,
+                    assistantStack.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_INVISIBLE,
+                    splitScreenPrimary.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_INVISIBLE,
+                    splitScreenSecondary.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_INVISIBLE,
+                    splitScreenSecondary2.getVisibility(null /* starting */));
+        } else {
+            assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+            assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+            assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+            assertEquals(STACK_VISIBILITY_INVISIBLE,
+                    assistantStack.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_VISIBLE,
+                    splitScreenPrimary.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_INVISIBLE,
+                    splitScreenSecondary.getVisibility(null /* starting */));
+            assertEquals(STACK_VISIBILITY_VISIBLE,
+                    splitScreenSecondary2.getVisibility(null /* starting */));
+        }
     }
 
     @Test
@@ -927,9 +943,15 @@
 
         splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
 
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+        if (isAssistantOnTop()) {
+            assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+            assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+            assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        } else {
+            assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+            assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+            assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+        }
     }
 
     private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
@@ -1344,6 +1366,11 @@
                 anyBoolean());
     }
 
+    private boolean isAssistantOnTop() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_assistantOnTopOfDream);
+    }
+
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
         final DisplayContent display = mock(DisplayContent.class);