Add hierarchy operations to container transaction

Also add a way to query for child containers of a container.

Combined, these provide enough flexibility to re-arrange
containers to system-ui. For now, it is locked-down a bit so
it only provides children of TaskTiles and can only reparent
ActivityStacks. Eventually, this can be expanded for nested
tasks or display areas.

Bug: 133381284
Test: Added TaskTileTest#testHierarchyTransaction
Change-Id: Id0e89a4e4b9352d9392349c4e1fe7d69b4f9f212
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 096541f..0a0530c9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -47,6 +49,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -375,6 +378,45 @@
         }
     }
 
+    @Override
+    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
+        enforceStackPermission("getChildTasks()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (parent == null) {
+                    throw new IllegalArgumentException("Can't get children of null parent");
+                }
+                final WindowContainer container = WindowContainer.fromBinder(parent.asBinder());
+                if (container == null) {
+                    Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
+                    return null;
+                }
+                // For now, only support returning children of persistent root tasks (of which the
+                // only current implementation is TaskTile).
+                if (!(container instanceof TaskTile)) {
+                    Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
+                    return null;
+                }
+                ArrayList<RunningTaskInfo> out = new ArrayList<>();
+                // Tiles aren't real parents, so we need to go through stacks on the display to
+                // ensure correct ordering.
+                final DisplayContent dc = container.getDisplayContent();
+                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack as = dc.getStackAt(i);
+                    if (as.getTile() == container) {
+                        final RunningTaskInfo info = new RunningTaskInfo();
+                        as.fillTaskInfo(info);
+                        out.add(info);
+                    }
+                }
+                return out;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private int sanitizeAndApplyChange(WindowContainer container,
             WindowContainerTransaction.Change change) {
         if (!(container instanceof Task)) {
@@ -405,6 +447,54 @@
         return effects;
     }
 
+    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
+            WindowContainerTransaction.HierarchyOp hop) {
+        if (!(container instanceof Task)) {
+            throw new IllegalArgumentException("Invalid container in hierarchy op");
+        }
+        if (hop.isReparent()) {
+            // special case for tiles since they are "virtual" parents
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                TaskTile newParent = hop.getNewParent() == null ? null
+                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
+                if (as.getTile() != newParent) {
+                    if (as.getTile() != null) {
+                        as.getTile().removeChild(as);
+                    }
+                    if (newParent != null) {
+                        if (!as.affectedBySplitScreenResize()) {
+                            return 0;
+                        }
+                        newParent.addChild(as, POSITION_TOP);
+                    }
+                }
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else if (container instanceof Task) {
+                throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+            }
+        } else {
+            // Ugh, of course ActivityStack has its own special reorder logic...
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else {
+                container.getParent().positionChildAt(
+                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                        container, false /* includingParents */);
+            }
+        }
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
     private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
             int windowMask, Configuration config) {
         if ((container instanceof ActivityStack)
@@ -470,8 +560,7 @@
                     while (entries.hasNext()) {
                         final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                 entries.next();
-                        final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
-                                entry.getKey()).getContainer();
+                        final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
                         int containerEffect = applyWindowContainerChange(wc, entry.getValue());
                         effects |= containerEffect;
 
@@ -484,6 +573,13 @@
                             mBLASTSyncEngine.addToSyncSet(syncId, wc);
                         }
                     }
+                    // Hierarchy changes
+                    final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+                    for (int i = 0, n = hops.size(); i < n; ++i) {
+                        final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+                        final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+                        effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+                    }
                     if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
                         // Already calls ensureActivityConfig
                         mService.mRootWindowContainer.ensureActivitiesVisible(