SystemUI Split via TaskOrganizer

Use the early TaskOrganizer concepts to implement Split-screen
in system-ui.

This includes changes to both FW and SystemUI. The changes to
FW involve removing the use of split-screen specific behavior (like
minimize dock and direct ordering) and also reducing things that
care about primary vs secondary. It also changed ActivityStack
to inherit bounds from parent** when in split-mode so that sysui
only needs to manipulate the tile and/or reparent stacks to
effect their geometry.

This means a lot of layout logic moves to SystemUI. The bulk of
the work done in ActivityStack which is split-screen related is
moved into SplitDisplayLayout. This basically takes a snapshot of
display configuration and manages the sizes of splits and their
snap targets.

Intermediate dragging of divider bar now only moves root task leashes
around rather than talking to WM. This includes position as well
as crop (which used to be stack crop). Once the user releases
the divider bar, it will calculate (based on snaps) the new
root task sizes and update their configurations via
WindowContainerTransaction. Because the interim updates are only
on the leashes, no configuration updates occur until the end.

Entering/Exiting split-mode is now handled by SplitScreentaskOrganizer#
onTaskInfoChanged. This is effectively a state-machine that
looks at the current split task membership vs. previous and then decides
when to move things into/out-of split tasks and how to coordinate with the
DividerView.

Minimized dock is relegated to a purely system-ui concept. To
accomplish this, **the home *stack* is set to the minimizedhomebounds
by systemui. This means that it's relative position to its parent is
negative! This allows us to leave the split sizes constant, have
their children inherit the "actual" split sizes, but keep the
home stack unchanging in its minimized size. We just adjust the crop
negative to reveal it.

IME handling is done through the same mechanism as app-driven IME
animation... only Divider receives the control instead of the app.
This allows synchronized animation of split tasks with IME. To
account for insets, though, when IME is opened, the bottom stack
will be repositioned in WM.

Bug: 133381284
Test: Manual, use split-screen, rotate device, launch unresizable
      apps in split, use divider snap to close/maximize apps, etc.
Change-Id: I7133e151a1037c42b275b97857936437a7a6725f
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 132e486..882d5c7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,7 +37,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -229,6 +228,7 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
 import android.view.WindowManager;
 
 import com.android.internal.R;
@@ -2269,6 +2269,9 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
+                if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+                    return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
+                }
                 final Task task = mRootWindowContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
@@ -2286,10 +2289,16 @@
                 }
 
                 final ActivityStack stack = task.getStack();
+                // Convert some windowing-mode changes into root-task reparents for split-screen.
+                if (stack.getTile() != null) {
+                    stack.getDisplay().onSplitScreenModeDismissed();
+                }
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingMode", task);
                 }
                 stack.setWindowingMode(windowingMode);
+                stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                        true /* notifyClients */);
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2719,36 +2728,8 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                if (isInLockTaskMode()) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
-                            + getLockTaskModeState());
-                    return false;
-                }
-
-                final Task task = mRootWindowContainer.anyTaskForId(taskId,
-                        MATCH_TASK_IN_STACKS_ONLY);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
-                    return false;
-                }
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to split-screen windowing mode");
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK,
-                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
-                                + " to createMode=" + createMode + " toTop=" + toTop);
-                mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
-                final int windowingMode = task.getWindowingMode();
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
-                }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                        false /* creating */);
-                return windowingMode != task.getWindowingMode();
+                return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                        toTop);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2756,6 +2737,49 @@
     }
 
     /**
+     * Moves the specified task into a split-screen tile.
+     */
+    private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
+        if (!WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+            throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+                    + "split-screen mode: " + windowingMode);
+        }
+        if (isInLockTaskMode()) {
+            Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
+                    + getLockTaskModeState());
+            return false;
+        }
+
+        final Task task = mRootWindowContainer.anyTaskForId(taskId,
+                MATCH_TASK_IN_STACKS_ONLY);
+        if (task == null) {
+            Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+            return false;
+        }
+        if (!task.isActivityTypeStandardOrUndefined()) {
+            throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                    + " non-standard task " + taskId + " to split-screen windowing mode");
+        }
+
+        final int prevMode = task.getWindowingMode();
+        final ActivityStack stack = task.getStack();
+        TaskTile tile = null;
+        for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
+            tile = stack.getDisplay().getStackAt(i).asTile();
+            if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                break;
+            }
+        }
+        if (tile == null) {
+            throw new IllegalStateException("Can't enter split without associated tile");
+        }
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+        mTaskOrganizerController.applyContainerTransaction(wct, null);
+        return prevMode != task.getWindowingMode();
+    }
+
+    /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
@@ -3963,46 +3987,6 @@
     }
 
     /**
-     * Dismisses split-screen multi-window mode.
-     * @param toTop If true the current primary split-screen stack will be placed or left on top.
-     */
-    @Override
-    public void dismissSplitScreenMode(boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(
-                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack =
-                        mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
-                    return;
-                }
-
-                if (toTop) {
-                    // Caller wants the current split-screen primary stack to be the top stack after
-                    // it goes fullscreen, so move it to the front.
-                    stack.moveToFront("dismissSplitScreenMode");
-                } else {
-                    // In this case the current split-screen primary stack shouldn't be the top
-                    // stack after it goes fullscreen, so we move the focus to the top-most
-                    // split-screen secondary stack next to it.
-                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
-                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                    if (otherStack != null) {
-                        otherStack.moveToFront("dismissSplitScreenMode_other");
-                    }
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
      * Dismisses Pip
      * @param animate True if the dismissal should be animated.
      * @param animationDuration The duration of the resize animation in milliseconds or -1 if the