Also apply freeze-task-list when starting an activity

- We previously only did this for startActivityFromRecents(), but for 3p
  launchers, we need to start the RecentsActivity mid-gesture and don't
  want to punt the excluded task from the front of the list

Bug: 137199105
Test: Install 3p Launcher, force-enable gesture nav, swipe up from
      exclude-from-recents activity

Change-Id: I7881ea103e83d28df1ec94d1874295188bed1064
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b0ca0af..5a1eed8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -187,6 +187,7 @@
     private boolean mNoAnimation;
     private boolean mKeepCurTransition;
     private boolean mAvoidMoveToFront;
+    private boolean mFrozeTaskList;
 
     // We must track when we deliver the new intent since multiple code paths invoke
     // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -483,6 +484,7 @@
         mNoAnimation = starter.mNoAnimation;
         mKeepCurTransition = starter.mKeepCurTransition;
         mAvoidMoveToFront = starter.mAvoidMoveToFront;
+        mFrozeTaskList = starter.mFrozeTaskList;
 
         mVoiceSession = starter.mVoiceSession;
         mVoiceInteractor = starter.mVoiceInteractor;
@@ -1093,6 +1095,14 @@
 
     void postStartActivityProcessing(ActivityRecord r, int result,
             ActivityStack startedActivityStack) {
+        if (!ActivityManager.isStartResultSuccessful(result)) {
+            if (mFrozeTaskList) {
+                // If we specifically froze the task list as part of starting an activity, then
+                // reset the frozen list state if it failed to start. This is normally otherwise
+                // called when the freeze-timeout has elapsed.
+                mSupervisor.mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
+            }
+        }
         if (ActivityManager.isStartResultFatalError(result)) {
             return;
         }
@@ -1485,6 +1495,14 @@
                 mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
                         : DEFAULT_DISPLAY;
 
+        // If requested, freeze the task list
+        if (mOptions != null && mOptions.freezeRecentTasksReordering()
+                && mSupervisor.mRecentTasks.isCallerRecents(r.launchedFromUid)
+                && !mSupervisor.mRecentTasks.isFreezeTaskListReorderingSet()) {
+            mFrozeTaskList = true;
+            mSupervisor.mRecentTasks.setFreezeTaskListReordering();
+        }
+
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
         // fallback to launch on other displays.
@@ -1775,6 +1793,7 @@
         mNoAnimation = false;
         mKeepCurTransition = false;
         mAvoidMoveToFront = false;
+        mFrozeTaskList = false;
 
         mVoiceSession = null;
         mVoiceInteractor = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index a08923b..3d94467 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -842,4 +842,51 @@
         // Ensure the activity is moved to secondary display.
         assertEquals(secondaryDisplay, topActivity.getDisplay());
     }
+
+    /**
+     * This test ensures that starting an activity with the freeze-task-list activity option will
+     * actually freeze the task list
+     */
+    @Test
+    public void testFreezeTaskListActivityOption() {
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+        final ActivityStarter starter = prepareStarter(0 /* flags */);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setFreezeRecentTasksReordering();
+
+        starter.setReason("testFreezeTaskListActivityOption")
+                .setActivityOptions(new SafeActivityOptions(options))
+                .execute();
+
+        verify(recentTasks, times(1)).setFreezeTaskListReordering();
+        verify(recentTasks, times(0)).resetFreezeTaskListReorderingOnTimeout();
+    }
+
+    /**
+     * This test ensures that if we froze the task list as a part of starting an activity that fails
+     * to start, that we also reset the task list.
+     */
+    @Test
+    public void testFreezeTaskListActivityOptionFailedStart_expectResetFreezeTaskList() {
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+        final ActivityStarter starter = prepareStarter(0 /* flags */);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setFreezeRecentTasksReordering();
+
+        starter.setReason("testFreezeTaskListActivityOptionFailedStart")
+                .setActivityOptions(new SafeActivityOptions(options))
+                .execute();
+
+        // Simulate a failed start
+        starter.postStartActivityProcessing(null, START_ABORTED, null);
+
+        verify(recentTasks, times(1)).setFreezeTaskListReordering();
+        verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout();
+    }
 }