Merge "Handle alwaysOnTop in DisplayAreas." into tm-qpr-dev
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index e3430a6..567b164 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -693,7 +693,39 @@
                         .build();
         mHierarchyOps.add(hierarchyOp);
         return this;
+    }
 
+    /**
+     * Sets/removes the always on top flag for this {@code windowContainer}. See
+     * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
+     * Please note that this method is only intended to be used for a
+     * {@link com.android.server.wm.DisplayArea}.
+     *
+     * <p>
+     *     Setting always on top to {@code True} will also make the {@code windowContainer} to move
+     *     to the top.
+     * </p>
+     * <p>
+     *     Setting always on top to {@code False} will make this {@code windowContainer} to move
+     *     below the other always on top sibling containers.
+     * </p>
+     *
+     * @param windowContainer the container which the flag need to be updated for.
+     * @param alwaysOnTop denotes whether or not always on top flag should be set.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setAlwaysOnTop(
+            @NonNull WindowContainerToken windowContainer,
+            boolean alwaysOnTop) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
+                        .setContainer(windowContainer.asBinder())
+                        .setAlwaysOnTop(alwaysOnTop)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
     }
 
     /**
@@ -1121,6 +1153,7 @@
         public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
         public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
         public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;
+        public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1171,6 +1204,8 @@
         @Nullable
         private ShortcutInfo mShortcutInfo;
 
+        private boolean mAlwaysOnTop;
+
         public static HierarchyOp createForReparent(
                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1268,6 +1303,7 @@
             mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
             mPendingIntent = copy.mPendingIntent;
             mShortcutInfo = copy.mShortcutInfo;
+            mAlwaysOnTop = copy.mAlwaysOnTop;
         }
 
         protected HierarchyOp(Parcel in) {
@@ -1289,6 +1325,7 @@
             mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
             mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
             mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
+            mAlwaysOnTop = in.readBoolean();
         }
 
         public int getType() {
@@ -1354,6 +1391,10 @@
             return mActivityIntent;
         }
 
+        public boolean isAlwaysOnTop() {
+            return mAlwaysOnTop;
+        }
+
         @Nullable
         public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
             return mTaskFragmentCreationOptions;
@@ -1422,6 +1463,9 @@
                             + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
                 case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
                     return "{requestFocusOnTaskFragment: container=" + mContainer + "}";
+                case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
+                    return "{setAlwaysOnTop: container=" + mContainer
+                            + " alwaysOnTop=" + mAlwaysOnTop + "}";
                 default:
                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                             + " mToTop=" + mToTop
@@ -1451,6 +1495,7 @@
             dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
             dest.writeTypedObject(mPendingIntent, flags);
             dest.writeTypedObject(mShortcutInfo, flags);
+            dest.writeBoolean(mAlwaysOnTop);
         }
 
         @Override
@@ -1509,6 +1554,8 @@
             @Nullable
             private ShortcutInfo mShortcutInfo;
 
+            private boolean mAlwaysOnTop;
+
             Builder(int type) {
                 mType = type;
             }
@@ -1568,6 +1615,11 @@
                 return this;
             }
 
+            Builder setAlwaysOnTop(boolean alwaysOnTop) {
+                mAlwaysOnTop = alwaysOnTop;
+                return this;
+            }
+
             Builder setTaskFragmentCreationOptions(
                     @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
                 mTaskFragmentCreationOptions = taskFragmentCreationOptions;
@@ -1596,6 +1648,7 @@
                 hierarchyOp.mLaunchOptions = mLaunchOptions;
                 hierarchyOp.mActivityIntent = mActivityIntent;
                 hierarchyOp.mPendingIntent = mPendingIntent;
+                hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
                 hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
                 hierarchyOp.mShortcutInfo = mShortcutInfo;
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 863782a..0422906 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -207,6 +207,23 @@
         return false;
     }
 
+    @Override
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        if (isAlwaysOnTop() == alwaysOnTop) {
+            return;
+        }
+        super.setAlwaysOnTop(alwaysOnTop);
+        // positionChildAtTop() must be called even when always on top gets turned off because
+        // we need to make sure that the display area is moved from among always on top containers
+        // to below other always on top containers. Since the position the display area should be
+        // inserted into is calculated properly in {@link DisplayContent#getTopInsertPosition()}
+        // in both cases, we can just request that the root task is put at top here.
+        if (getParent().asDisplayArea() != null) {
+            getParent().asDisplayArea().positionChildAt(POSITION_TOP, this,
+                    false /* includingParents */);
+        }
+    }
+
     boolean getIgnoreOrientationRequest() {
         // Adding an exception for when ignoreOrientationRequest is overridden at runtime for all
         // DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't
@@ -234,6 +251,18 @@
         // The min possible position we can insert the child at.
         int minPosition = findMinPositionForChildDisplayArea(child);
 
+        // Place all non-always-on-top containers below always-on-top ones.
+        int alwaysOnTopCount = 0;
+        for (int i = minPosition; i <= maxPosition; i++) {
+            if (mChildren.get(i).isAlwaysOnTop()) {
+                alwaysOnTopCount++;
+            }
+        }
+        if (child.isAlwaysOnTop()) {
+            minPosition = maxPosition - alwaysOnTopCount + 1;
+        } else {
+            maxPosition -= alwaysOnTopCount;
+        }
         return Math.max(Math.min(requestPosition, maxPosition), minPosition);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5a2100d..3b9cd36 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -37,6 +37,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
@@ -1079,6 +1080,18 @@
                 WindowContainer.fromBinder(hop.getContainer())
                         .removeLocalInsetsSourceProvider(hop.getInsetsTypes());
                 break;
+            case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
+                final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+                if (container == null || container.asDisplayArea() == null
+                        || !container.isAttached()) {
+                    Slog.e(TAG, "Attempt to operate on unknown or detached display area: "
+                            + container);
+                    break;
+                }
+                container.setAlwaysOnTop(hop.isAlwaysOnTop());
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                break;
+
         }
         return effects;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index d94e6c9..b87c5a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -399,9 +400,9 @@
                 parentBounds.right / 2, parentBounds.bottom);
         final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top,
                 parentBounds.right, parentBounds.bottom);
-        TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds);
-        TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1);
-        TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2);
+        TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds, "Parent");
+        TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1, "Child1");
+        TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2, "Child2");
         parentDa.addChild(childDa1, 0);
         parentDa.addChild(childDa2, 1);
 
@@ -619,9 +620,67 @@
         controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
     }
 
+    @Test
+    public void testSetAlwaysOnTop_movesDisplayAreaToTop() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+        parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> child1 = new TestDisplayArea(mWm, bounds, "Child1");
+        child1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> child2 = new TestDisplayArea(mWm, bounds, "Child2");
+        child2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        parent.addChild(child2, 0);
+        parent.addChild(child1, 1);
+
+        child2.setAlwaysOnTop(true);
+
+        assertEquals(parent.getChildAt(1), child2);
+        assertThat(child2.isAlwaysOnTop()).isTrue();
+    }
+
+    @Test
+    public void testDisplayAreaRequestsTopPosition_alwaysOnTopSiblingExists_doesNotMoveToTop() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+        parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
+                "AlwaysOnTopChild");
+        alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
+        child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        parent.addChild(alwaysOnTopChild, 0);
+        parent.addChild(child, 1);
+        alwaysOnTopChild.setAlwaysOnTop(true);
+
+        parent.positionChildAt(POSITION_TOP, child, false /* includingParents */);
+
+        assertEquals(parent.getChildAt(1), alwaysOnTopChild);
+        assertEquals(parent.getChildAt(0), child);
+    }
+
+    @Test
+    public void testAlwaysOnTopDisplayArea_requestsNonTopLocation_doesNotMove() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+        parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
+                "AlwaysOnTopChild");
+        alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
+        child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        parent.addChild(alwaysOnTopChild, 0);
+        parent.addChild(child, 1);
+        alwaysOnTopChild.setAlwaysOnTop(true);
+
+        parent.positionChildAt(POSITION_BOTTOM, alwaysOnTopChild, false /* includingParents */);
+
+        assertEquals(parent.getChildAt(1), alwaysOnTopChild);
+        assertEquals(parent.getChildAt(0), child);
+    }
+
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
-        private TestDisplayArea(WindowManagerService wms, Rect bounds) {
-            super(wms, ANY, "half display area");
+        private TestDisplayArea(WindowManagerService wms, Rect bounds, String name) {
+            super(wms, ANY, name);
             setBounds(bounds);
         }