Add always on top feature support

Add basic functionalities for always on top feature.

- Add a new flag to WindowConfiguration to represent a task wanting to
be on top.
- Update the logic on changing the z-order of windows to make sure
always on top windows are placed above other windows.

Bug: 69370884
Test: go/wm-smoke
Test: atest DisplayContentTests
Test: Used ArcCompanionLibDemo app to verify that when always-on-top is
      set, the app is above the other Android apps and Chrome windows.

Change-Id: Ie8edeb8ceeed0b9ec154b6031ed6cbe7ecc65b12
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b0e6208..578a1489 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1569,6 +1569,11 @@
     }
 
     @VisibleForTesting
+    WindowList<TaskStack> getStacks() {
+        return mTaskStackContainers.mChildren;
+    }
+
+    @VisibleForTesting
     TaskStack getTopStack() {
         return mTaskStackContainers.getTopStack();
     }
@@ -3429,21 +3434,44 @@
             boolean toTop = requestedPosition == POSITION_TOP;
             toTop |= adding ? requestedPosition >= topChildPosition + 1
                     : requestedPosition >= topChildPosition;
-            int targetPosition = requestedPosition;
 
-            if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED && hasPinnedStack()) {
-                // The pinned stack is always the top most stack (always-on-top) when it is present.
-                TaskStack topStack = mChildren.get(topChildPosition);
-                if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
+            if (stack.inPinnedWindowingMode()) {
+                // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
+                // just return the candidate position.
+                return requestedPosition;
+            }
+
+            // We might call mChildren.get() with targetPosition below, but targetPosition might be
+            // POSITION_TOP (INTEGER_MAX). We need to adjust the value to the actual index in the
+            // array.
+            int targetPosition = toTop ? topChildPosition : requestedPosition;
+            // Note that the index we should return varies depending on the value of adding.
+            // When we're adding a new stack the index is the current target position.
+            // When we're positioning an existing stack the index is the position below the target
+            // stack, because WindowContainer#positionAt() first removes element and then adds
+            // it to specified place.
+            if (toTop && adding) {
+                targetPosition++;
+            }
+
+            // Note we might have multiple always on top windows.
+            while (targetPosition >= 0) {
+                int adjustedTargetStackId = adding ? targetPosition - 1 : targetPosition;
+                if (adjustedTargetStackId < 0 || adjustedTargetStackId > topChildPosition) {
+                    break;
                 }
-
-                // So, stack is moved just below the pinned stack.
-                // When we're adding a new stack the target is the current pinned stack position.
-                // When we're positioning an existing stack the target is the position below pinned
-                // stack, because WindowContainer#positionAt() first removes element and then adds
-                // it to specified place.
-                targetPosition = adding ? topChildPosition : topChildPosition - 1;
+                TaskStack targetStack = mChildren.get(adjustedTargetStackId);
+                if (!targetStack.isAlwaysOnTop()) {
+                    // We reached a stack that isn't always-on-top.
+                    break;
+                }
+                if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
+                    // Always on-top non-pinned windowing mode stacks can go anywhere below pinned
+                    // stack.
+                    break;
+                }
+                // We go one level down, looking for the place on which the new stack can be put.
+                targetPosition--;
             }
 
             return targetPosition;