Exclude launcher from app transition animation while recents is running.

Changed approach to handle conflict between app transition and recents.

In order to fix flicker which happens when recents and app transition
start in a short time, we delayed app transition while recents was
running. However this approach brought side effects such as b/232984498.

When recents starts, we just want to wait that the launcher activitiy
finishes rendering and commit its visibility without animation. Added a
flag DisplayContent#mExcludeLauncherFromAnimation flag, so that we can
now explicity declare whether we want to apply animation on the launcher
or not.

Bug: 223499269
Bug: 231711212
Bug: 232984498
Test: atest com.android.server.wm.AppTransitionTests
Test: atest AppTransitionTest + manual tests
  Test 1
   1. Launch Gmail app
   2. Click icon on the bottom tab (e.g. Chat)
   3. Swipe up from the bottom (immediately after step 2)
   4. Verify closing animation only plays once
  Test 2
   1. Launch "Google TV"
   2. Play a trailer
   3. Full screen and PIP mode switch twice
   4. Verify PIP window is shown
  Test 3
   1. Change phone to portlait mode
   2. Launch Photo app
   3. Swipe up from the bottom
   4. Verify no rotation animation on the launcher
  Test 4
   1. Install 3P launcher and set it default.
   2. Launch Gmail app
   3. Swipe up to go back home
   4. Launch Chrome app
   5. Swipe up and hold to go to overview.
   6. Scroll to Gmail app and click.
   7. Verify Gmail app is launched without delay.
Change-Id: I0ccb99479684d17453ce57e8797024c0cd233ac3
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 51ed856..f7c2b73 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1879,12 +1879,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
-    "-240296576": {
-      "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "-237664290": {
       "message": "Pause the recording session on display %s",
       "level": "VERBOSE",
@@ -1987,6 +1981,12 @@
       "group": "WM_DEBUG_CONTENT_RECORDING",
       "at": "com\/android\/server\/wm\/ContentRecorder.java"
     },
+    "-134793542": {
+      "message": "handleAppTransitionReady: displayId=%d appTransition={%s} excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "-134091882": {
       "message": "Screenshotting Activity %s",
       "level": "VERBOSE",
@@ -2491,12 +2491,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "323235828": {
-      "message": "Delaying app transition for recents animation to finish",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "327461496": {
       "message": "Complete pause: %s",
       "level": "VERBOSE",
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index fa506ca..140ac33 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -76,6 +76,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.ArrayMap;
@@ -145,7 +146,8 @@
      * Returns the currently visible window that is associated with the wallpaper in case we are
      * transitioning from an activity with a wallpaper to one without.
      */
-    private @Nullable WindowState getOldWallpaper() {
+    @Nullable
+    private WindowState getOldWallpaper() {
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         final @TransitionType int firstTransit =
                 mDisplayContent.mAppTransition.getFirstAppTransition();
@@ -165,6 +167,16 @@
                 ? null : wallpaperTarget;
     }
 
+    @NonNull
+    private static ArraySet<ActivityRecord> getAppsForAnimation(
+            @NonNull ArraySet<ActivityRecord> apps, boolean excludeLauncherFromAnimation) {
+        final ArraySet<ActivityRecord> appsForAnimation = new ArraySet<>(apps);
+        if (excludeLauncherFromAnimation) {
+            appsForAnimation.removeIf(ConfigurationContainer::isActivityTypeHome);
+        }
+        return appsForAnimation;
+    }
+
     /**
      * Handle application transition for given display.
      */
@@ -214,18 +226,29 @@
         mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                 mDisplayContent.mOpeningApps);
 
-        final @TransitionOldType int transit = getTransitCompatType(
-                mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
-                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
+        // Remove launcher from app transition animation while recents is running. Recents animation
+        // is managed outside of app transition framework, so we just need to commit visibility.
+        final boolean excludeLauncherFromAnimation =
+                mDisplayContent.mOpeningApps.stream().anyMatch(
+                        (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS))
+                || mDisplayContent.mClosingApps.stream().anyMatch(
+                        (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS));
+        final ArraySet<ActivityRecord> openingAppsForAnimation = getAppsForAnimation(
+                mDisplayContent.mOpeningApps, excludeLauncherFromAnimation);
+        final ArraySet<ActivityRecord> closingAppsForAnimation = getAppsForAnimation(
+                mDisplayContent.mClosingApps, excludeLauncherFromAnimation);
+
+        @TransitionOldType final int transit = getTransitCompatType(
+                mDisplayContent.mAppTransition, openingAppsForAnimation, closingAppsForAnimation,
+                mDisplayContent.mChangingContainers,
                 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
                 mDisplayContent.mSkipAppTransitionAnimation);
         mDisplayContent.mSkipAppTransitionAnimation = false;
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "handleAppTransitionReady: displayId=%d appTransition={%s}"
-                + " openingApps=[%s] closingApps=[%s] transit=%s",
-                mDisplayContent.mDisplayId,
-                appTransition.toString(),
+                + " excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s",
+                mDisplayContent.mDisplayId, appTransition.toString(), excludeLauncherFromAnimation,
                 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
                 AppTransition.appTransitionOldToString(transit));
 
@@ -233,13 +256,15 @@
         // what will control the animation theme. If all closing windows are obscured, then there is
         // no need to do an animation. This is the case, for example, when this transition is being
         // done behind a dream window.
-        final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
-                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
-        final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
+        final ArraySet<Integer> activityTypes = collectActivityTypes(openingAppsForAnimation,
+                closingAppsForAnimation, mDisplayContent.mChangingContainers);
+        final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
+                openingAppsForAnimation, closingAppsForAnimation,
+                mDisplayContent.mChangingContainers);
         final ActivityRecord topOpeningApp =
-                getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
+                getTopApp(openingAppsForAnimation, false /* ignoreHidden */);
         final ActivityRecord topClosingApp =
-                getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
+                getTopApp(closingAppsForAnimation, false /* ignoreHidden */);
         final ActivityRecord topChangingApp =
                 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
         final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
@@ -251,14 +276,14 @@
             overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
         }
 
-        final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps)
-                || containsVoiceInteraction(mDisplayContent.mOpeningApps);
+        final boolean voiceInteraction = containsVoiceInteraction(closingAppsForAnimation)
+                || containsVoiceInteraction(openingAppsForAnimation);
 
         final int layoutRedo;
         mService.mSurfaceAnimationRunner.deferStartingAnimations();
         try {
-            applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
-                    animLp, voiceInteraction);
+            applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp,
+                    voiceInteraction);
             handleClosingApps();
             handleOpeningApps();
             handleChangingApps(transit);
@@ -310,7 +335,7 @@
      *                     case we are transitioning from an activity with a wallpaper to one
      *                     without. Otherwise null.
      */
-    static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
+    @TransitionOldType static int getTransitCompatType(AppTransition appTransition,
             ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
             ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
             @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {
@@ -341,8 +366,8 @@
         if (skipAppTransitionAnimation) {
             return WindowManager.TRANSIT_OLD_UNSET;
         }
-        final @TransitionFlags int flags = appTransition.getTransitFlags();
-        final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
+        @TransitionFlags final int flags = appTransition.getTransitFlags();
+        @TransitionType final int firstTransit = appTransition.getFirstAppTransition();
 
         // Special transitions
         // TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
@@ -720,11 +745,9 @@
      */
     @Nullable
     private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
-            ArraySet<Integer> activityTypes) {
+            ArraySet<Integer> activityTypes, ArraySet<ActivityRecord> openingApps,
+            ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingApps) {
         ActivityRecord result;
-        final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
-        final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
-        final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
 
         // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
@@ -1152,11 +1175,6 @@
             if (activity == null) {
                 continue;
             }
-            if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Delaying app transition for recents animation to finish");
-                return false;
-            }
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                     "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
                             + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 3592158..436cf36 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -25,6 +27,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -409,38 +412,50 @@
     }
 
     @Test
-    public void testDelayWhileRecents() {
+    public void testExcludeLauncher() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
         doReturn(false).when(dc).onDescendantOrientationChanged(any());
         final Task task = createTask(dc);
 
-        // Simulate activity1 launches activity2.
+        // Simulate activity1 launches activity2
         final ActivityRecord activity1 = createActivityRecord(task);
         activity1.setVisible(true);
         activity1.mVisibleRequested = false;
         activity1.allDrawn = true;
+        dc.mClosingApps.add(activity1);
         final ActivityRecord activity2 = createActivityRecord(task);
         activity2.setVisible(false);
         activity2.mVisibleRequested = true;
         activity2.allDrawn = true;
-
-        dc.mClosingApps.add(activity1);
         dc.mOpeningApps.add(activity2);
         dc.prepareAppTransition(TRANSIT_OPEN);
-        assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
+
+        // Simulate start recents
+        final ActivityRecord homeActivity = createActivityRecord(dc, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_HOME);
+        homeActivity.setVisible(false);
+        homeActivity.mVisibleRequested = true;
+        homeActivity.allDrawn = true;
+        dc.mOpeningApps.add(homeActivity);
+        dc.prepareAppTransition(TRANSIT_NONE);
+        doReturn(true).when(task)
+                .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
 
         // Wait until everything in animation handler get executed to prevent the exiting window
         // from being removed during WindowSurfacePlacer Traversal.
         waitUntilHandlersIdle();
 
-        // Start recents
-        doReturn(true).when(task)
-                .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
-
         dc.mAppTransitionController.handleAppTransitionReady();
 
-        verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
-        verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
+        verify(activity1).commitVisibility(eq(false), anyBoolean(), anyBoolean());
+        verify(activity1).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(false),
+                anyBoolean(), any());
+        verify(activity2).commitVisibility(eq(true), anyBoolean(), anyBoolean());
+        verify(activity2).applyAnimation(any(), eq(TRANSIT_OLD_ACTIVITY_OPEN), eq(true),
+                anyBoolean(), any());
+        verify(homeActivity).commitVisibility(eq(true), anyBoolean(), anyBoolean());
+        verify(homeActivity, never()).applyAnimation(any(), anyInt(), anyBoolean(), anyBoolean(),
+                any());
     }
 
     @Test