Fix several issues with RecentsAnimation

- Prevent starting the recents animation while an app transition is already
  set, if the animation was canceled before the pending start animation
  could be processed, or if there are truly no visible tasks to animate
- Fix case where we were improperly cleaning up the recents animation if it
  was canceled while initializing. In particular we need to move the
  initialization out of the constructor for the cancel logic to work
  (which checks for a non-null controller before cleaning up). Similarly
  we were posting the timeout runnable after the initialization of the
  recents animation, even if that resulted in the animation being canceled.
  Instead, post it before initialization to ensure that canceling the
  animation will also remove the timeout.

Bug: 72953248
Test: Introduce artificial delay when starting recents animation such that
      it happens after starting an activity

Change-Id: Ie0ec37038822f28327e789e7e5a3c4c36f945ff9
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e869f58..6c3beaf 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -59,6 +59,7 @@
     private final IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+    private final int mDisplayId;
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mHomeAppToken;
@@ -159,8 +160,6 @@
     };
 
     /**
-     * Initializes a new RecentsAnimationController.
-     *
      * @param remoteAnimationRunner The remote runner which should be notified when the animation is
      *                              ready to start or has been canceled
      * @param callbacks Callbacks to be made when the animation finishes
@@ -171,16 +170,19 @@
         mService = service;
         mRunner = remoteAnimationRunner;
         mCallbacks = callbacks;
+        mDisplayId = displayId;
+    }
 
-        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
-        if (visibleTasks.isEmpty()) {
-            cancelAnimation();
-            return;
-        }
-
+    /**
+     * 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() {
         // 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);
+        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
@@ -193,6 +195,12 @@
             addAnimation(task);
         }
 
+        // Skip the animation if there is nothing to animate
+        if (mPendingAnimations.isEmpty()) {
+            cancelAnimation();
+            return;
+        }
+
         // Adjust the wallpaper visibility for the showing home activity
         final AppWindowToken recentsComponentAppToken =
                 dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -222,8 +230,10 @@
     }
 
     void startAnimation() {
-        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
-        if (!mPendingStart) {
+        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+                + " mCanceled=" + mCanceled);
+        if (!mPendingStart || mCanceled) {
+            // Skip starting if we've already started or canceled the animation
             return;
         }
         try {