Allow launcher to control home app surfaces during recents animation

- Previously, the app leashes were reparented to the standard animation
  layer which was always above the home layer. Now we reparent both to the
  home animation layer for the duration of the app-controlled recents
  animation. This allows launcher to reorder the home surface above the app
  during the swipe gesture and draw above the app.

Bug: 70341013
Test: atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
Test: Swipe up, and ensure that the home app surface is also being animated
Change-Id: I2c0fa79c7bc1fffaf1374beb45386faea14ec20c
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 1eae567..e718c7b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,8 +20,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
-
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
@@ -48,16 +48,13 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.input.InputWindowHandle;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.InsetUtils;
-
 import com.google.android.collect.Sets;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -93,6 +90,7 @@
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mTargetAppToken;
+    private int mTargetActivityType;
     private Rect mMinimizedHomeBounds = new Rect();
 
     // We start the RecentsAnimationController in a pending-start state since we need to wait for
@@ -259,23 +257,37 @@
         mDisplayId = displayId;
     }
 
+    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
+        initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds);
+    }
+
     /**
      * Initializes the recents animation controller. This is a separate call from the constructor
      * because it may call cancelAnimation() which needs to properly clean up the controller
      * in the window manager.
      */
-    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
-        // Make leashes for each of the visible tasks and add it to the recents animation to be
-        // started
-        final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+    @VisibleForTesting
+    void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) {
+        mTargetActivityType = targetActivityType;
+
+        // Make leashes for each of the visible/target tasks and add it to the recents animation to
+        // be started
         final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
+        final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
+        if (targetStack != null) {
+            for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
+                final Task t = targetStack.getChildAt(i);
+                if (!visibleTasks.contains(t)) {
+                    visibleTasks.add(t);
+                }
+            }
+        }
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
             final WindowConfiguration config = task.getWindowConfiguration();
             if (config.tasksAreFloating()
-                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    || config.getActivityType() == targetActivityType) {
+                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 continue;
             }
             addAnimation(task, !recentTaskIds.get(task.mTaskId));
@@ -586,7 +598,10 @@
             final Rect insets = new Rect();
             mainWindow.getContentInsets(insets);
             InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
-            mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+            final int mode = topApp.getActivityType() == mTargetActivityType
+                    ? MODE_OPENING
+                    : MODE_CLOSING;
+            mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
                     !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
                     insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc23ab6..6aa0e01 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -561,9 +561,10 @@
 
     @Override
     public SurfaceControl getAnimationLeashParent() {
-        // Reparent to the animation layer so that we aren't clipped by the non-minimized
-        // stack bounds, currently we only animate the task for the recents animation
-        return getAppAnimationLayer(ANIMATION_LAYER_STANDARD);
+        // Currently, only the recents animation will create animation leashes for tasks. In this
+        // case, reparent the task to the home animation layer while it is being animated to allow
+        // the home activity to reorder the app windows relative to its own.
+        return getAppAnimationLayer(ANIMATION_LAYER_HOME);
     }
 
     boolean isTaskAnimating() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e860939..4883f97 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -42,10 +42,8 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
-
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wm.SurfaceAnimator.Animatable;
-
 import java.io.PrintWriter;
 import java.util.Comparator;
 import java.util.LinkedList;
@@ -71,7 +69,8 @@
 
     /**
      * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
-     * activities that happens below all {@link TaskStack}s.
+     * activities and all activities that are being controlled by the recents animation. This
+     * layer is generally below all {@link TaskStack}s.
      */
     static final int ANIMATION_LAYER_HOME = 2;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7caa7ae..b627df4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -227,6 +227,7 @@
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -2714,6 +2715,11 @@
         }
     }
 
+    @VisibleForTesting
+    void setRecentsAnimationController(RecentsAnimationController controller) {
+        mRecentsAnimationController = controller;
+    }
+
     public RecentsAnimationController getRecentsAnimationController() {
         return mRecentsAnimationController;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e7c45d5..aaa00452 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -24,6 +25,8 @@
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
 import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.verify;
@@ -33,6 +36,7 @@
 import android.os.Binder;
 import android.os.IInterface;
 import android.platform.test.annotations.Presubmit;
+import android.util.SparseBooleanArray;
 import android.view.IRecentsAnimationRunner;
 import android.view.SurfaceControl;
 
@@ -109,6 +113,24 @@
         }
     }
 
+    @Test
+    public void testIncludedApps_expectTargetAndVisible() throws Exception {
+        sWm.setRecentsAnimationController(mController);
+        final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        hiddenAppWindow.setHidden(true);
+        mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+
+        // Ensure that we are animating the target activity as well
+        assertTrue(mController.isAnimatingTask(homeAppWindow.getTask()));
+        assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+        assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask()));
+    }
+
     private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
         verify(binder, atLeast(0)).asBinder();
         verifyNoMoreInteractions(binder);