Reparent the activity in the task with associated window containers.

- Currently moveActivityToStack() adds the activity to the top of the
  new task in the new stack, but the code path to update the window
  container controllers assumes that they are in the same task and
  attempts to position it by index.  Instead, we should properly
  reparent the activity in the new task (just like we reparent tasks
  into new stacks), and also reparent the associated app window tokens
  into their new tasks on the WM side.
- Also should fix an issue when trying to reparent an activity from one
  task to another task by affinity.

Bug: 34394702
Test: AppWidgetContainerControllerTests#testReparent
Test: android.server.cts.ActivityManagerPinnedStackTests#testEnterPipFromTaskWithMultipleActivities
Change-Id: Ib767f85eb4f469cfd1c108c55242996325bdb866
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 27e0f29..3a86874 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -41,6 +41,7 @@
 import android.view.IApplicationToken;
 import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.AttributeCache;
 /**
  * Controller for the app window token container. This is created by activity manager to link
@@ -202,7 +203,7 @@
                         + " controller=" + taskController);
             }
 
-            atoken = new AppWindowToken(mService, token, voiceInteraction, task.getDisplayContent(),
+            atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
                     requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
                     alwaysFocusable, this);
@@ -212,6 +213,18 @@
         }
     }
 
+    @VisibleForTesting
+    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+            boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+            boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+            int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+            boolean alwaysFocusable, AppWindowContainerController controller) {
+        return  new AppWindowToken(service, token, voiceInteraction, dc,
+                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+                controller);
+    }
+
     public void removeContainer(int displayId) {
         synchronized(mWindowMap) {
             final DisplayContent dc = mRoot.getDisplayContent(displayId);
@@ -230,6 +243,25 @@
         throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
     }
 
+    public void reparent(TaskWindowContainerController taskController, int position) {
+        synchronized (mWindowMap) {
+            if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
+                    + " to task=" + taskController + " at " + position);
+            if (mContainer == null) {
+                if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
+                        "reparent: could not find app token=" + mToken);
+                return;
+            }
+            final Task task = taskController.mContainer;
+            if (task == null) {
+                throw new IllegalArgumentException("reparent: could not find task="
+                        + taskController);
+            }
+            mContainer.reparent(task, position);
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
     public Configuration setOrientation(int requestedOrientation, int displayId,
             Configuration displayConfig, boolean freezeScreenIfNeeded) {
         synchronized(mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 079dc8f..bcc720d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -850,6 +850,27 @@
         }
     }
 
+    void reparent(Task task, int position) {
+        if (task == mTask) {
+            throw new IllegalArgumentException(
+                    "window token=" + this + " already child of task=" + mTask);
+        }
+        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "reParentWindowToken: removing window token=" + this
+                + " from task=" + mTask);
+        final DisplayContent prevDisplayContent = getDisplayContent();
+
+        getParent().removeChild(this);
+        task.addChild(this, position);
+
+        // Relayout display(s).
+        final DisplayContent displayContent = task.getDisplayContent();
+        displayContent.setLayoutNeeded();
+        if (prevDisplayContent != displayContent) {
+            onDisplayChanged(displayContent);
+            prevDisplayContent.setLayoutNeeded();
+        }
+    }
+
     private boolean canFreezeBounds() {
         // For freeform windows, we can't freeze the bounds at the moment because this would make
         // the resizing unresponsive.