1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
25 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
26 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
27 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
28 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
29 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
30 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
31 import static android.os.Process.NOBODY_UID;
32 
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
36 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
37 
38 import static com.google.common.truth.Truth.assertThat;
39 import static com.google.common.truth.Truth.assertWithMessage;
40 
41 import static org.junit.Assert.assertEquals;
42 import static org.junit.Assert.assertFalse;
43 import static org.junit.Assert.assertNotNull;
44 import static org.junit.Assert.assertNull;
45 import static org.junit.Assert.assertTrue;
46 import static org.junit.Assert.fail;
47 import static org.mockito.ArgumentMatchers.anyBoolean;
48 import static org.mockito.ArgumentMatchers.anyInt;
49 import static org.mockito.ArgumentMatchers.eq;
50 import static org.mockito.Mockito.clearInvocations;
51 import static org.mockito.Mockito.mock;
52 import static org.mockito.Mockito.reset;
53 import static org.mockito.Mockito.times;
54 import static org.mockito.Mockito.verify;
55 import static org.mockito.Mockito.verifyZeroInteractions;
56 
57 import static java.lang.Integer.MAX_VALUE;
58 
59 import android.app.ActivityManager.RecentTaskInfo;
60 import android.app.ActivityManager.RunningTaskInfo;
61 import android.app.ActivityTaskManager;
62 import android.content.ComponentName;
63 import android.content.pm.ParceledListSlice;
64 import android.content.pm.UserInfo;
65 import android.graphics.ColorSpace;
66 import android.graphics.Point;
67 import android.graphics.Rect;
68 import android.hardware.HardwareBuffer;
69 import android.os.Bundle;
70 import android.os.RemoteException;
71 import android.os.SystemClock;
72 import android.os.UserManager;
73 import android.platform.test.annotations.Presubmit;
74 import android.util.ArraySet;
75 import android.util.SparseBooleanArray;
76 import android.view.Surface;
77 import android.window.TaskSnapshot;
78 
79 import androidx.test.filters.MediumTest;
80 
81 import com.android.server.wm.RecentTasks.Callbacks;
82 
83 import org.junit.Before;
84 import org.junit.Test;
85 import org.junit.runner.RunWith;
86 
87 import java.io.File;
88 import java.util.ArrayList;
89 import java.util.HashSet;
90 import java.util.List;
91 import java.util.Random;
92 import java.util.Set;
93 import java.util.function.Function;
94 
95 /**
96  * Build/Install/Run:
97  *  atest WmTests:RecentTasksTest
98  */
99 @MediumTest
100 @Presubmit
101 @RunWith(WindowTestRunner.class)
102 public class RecentTasksTest extends WindowTestsBase {
103     private static final int TEST_USER_0_ID = 0;
104     private static final int TEST_USER_1_ID = 10;
105     private static final int TEST_QUIET_USER_ID = 20;
106     private static final UserInfo DEFAULT_USER_INFO = new UserInfo(TEST_USER_0_ID,
107             "default", 0 /* flags */);
108     private static final UserInfo QUIET_PROFILE_USER_INFO = new UserInfo(TEST_QUIET_USER_ID,
109             "quiet_profile", null /* iconPath */, UserInfo.FLAG_QUIET_MODE,
110             UserManager.USER_TYPE_PROFILE_MANAGED);
111     private static final int INVALID_STACK_ID = 999;
112 
113     private TaskDisplayArea mTaskContainer;
114     private TestTaskPersister mTaskPersister;
115     private TestRecentTasks mRecentTasks;
116     private TestRunningTasks mRunningTasks;
117 
118     private ArrayList<Task> mTasks;
119     private ArrayList<Task> mSameDocumentTasks;
120 
121     private CallbacksRecorder mCallbacksRecorder;
122 
123     @Before
setUp()124     public void setUp() throws Exception {
125         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
126         spyOn(mTaskPersister);
127         mTaskContainer = mRootWindowContainer.getDefaultTaskDisplayArea();
128 
129         // Set the recent tasks we should use for testing in this class.
130         mRecentTasks = new TestRecentTasks(mAtm, mTaskPersister);
131         spyOn(mRecentTasks);
132         mAtm.setRecentTasks(mRecentTasks);
133         mRecentTasks.loadParametersFromResources(mContext.getResources());
134 
135         // Set the running tasks we should use for testing in this class.
136         mRunningTasks = new TestRunningTasks();
137         mAtm.mTaskSupervisor.setRunningTasks(mRunningTasks);
138 
139         mCallbacksRecorder = new CallbacksRecorder();
140         mRecentTasks.registerCallback(mCallbacksRecorder);
141 
142         mTasks = new ArrayList<>();
143         mTasks.add(createTaskBuilder(".Task1").build());
144         mTasks.add(createTaskBuilder(".Task2").build());
145         mTasks.add(createTaskBuilder(".Task3").build());
146         mTasks.add(createTaskBuilder(".Task4").build());
147         mTasks.add(createTaskBuilder(".Task5").build());
148 
149         mSameDocumentTasks = new ArrayList<>();
150         mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
151         mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
152     }
153 
154     @Test
testCallbacks()155     public void testCallbacks() {
156         // Add some tasks
157         mRecentTasks.add(mTasks.get(0));
158         mRecentTasks.add(mTasks.get(1));
159         assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(0));
160         assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(1));
161         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
162         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
163         mCallbacksRecorder.clear();
164 
165         // Remove some tasks
166         mRecentTasks.remove(mTasks.get(0));
167         mRecentTasks.remove(mTasks.get(1));
168         assertThat(mCallbacksRecorder.mAdded).isEmpty();
169         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
170         assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(0));
171         assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(1));
172         mCallbacksRecorder.clear();
173 
174         // Remove the callback, ensure we don't get any calls
175         mRecentTasks.unregisterCallback(mCallbacksRecorder);
176         mRecentTasks.add(mTasks.get(0));
177         mRecentTasks.remove(mTasks.get(0));
178         assertThat(mCallbacksRecorder.mAdded).isEmpty();
179         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
180         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
181     }
182 
183     @Test
testPersister()184     public void testPersister() {
185         // Add some tasks, ensure the persister is woken
186         mRecentTasks.add(mTasks.get(0));
187         mRecentTasks.add(mTasks.get(1));
188         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
189         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
190         reset(mTaskPersister);
191 
192         // Update a task, ensure the persister is woken
193         mRecentTasks.add(mTasks.get(0));
194         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
195         reset(mTaskPersister);
196 
197         // Remove some tasks, ensure the persister is woken
198         mRecentTasks.remove(mTasks.get(0));
199         mRecentTasks.remove(mTasks.get(1));
200         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
201         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
202         reset(mTaskPersister);
203     }
204 
205     @Test
testPersisterTrimmed()206     public void testPersisterTrimmed() {
207         mRecentTasks.setOnlyTestVisibleRange();
208 
209         // Limit the global maximum number of recent tasks to a fixed size
210         mRecentTasks.setGlobalMaxNumTasks(1 /* globalMaxNumTasks */);
211 
212         mRecentTasks.add(mTasks.get(0));
213         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
214         reset(mTaskPersister);
215 
216         // Add N+1 tasks to ensure the previous task is trimmed
217         mRecentTasks.add(mTasks.get(1));
218         triggerTrimAndAssertTrimmed(mTasks.get(0));
219         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
220         verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
221     }
222 
223     @Test
testAddDocumentTasksNoMultiple_expectNoTrim()224     public void testAddDocumentTasksNoMultiple_expectNoTrim() {
225         // Add same non-multiple-task document tasks will remove the task (to re-add it) but not
226         // trim it
227         Task documentTask1 = createDocumentTask(".DocumentTask1");
228         Task documentTask2 = createDocumentTask(".DocumentTask1");
229         mRecentTasks.add(documentTask1);
230         mRecentTasks.add(documentTask2);
231         assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
232         assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
233         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
234         assertThat(mCallbacksRecorder.mRemoved).contains(documentTask1);
235     }
236 
237     @Test
testAddTasksMaxTaskRecents_expectNoTrim()238     public void testAddTasksMaxTaskRecents_expectNoTrim() {
239         // Add a task hitting max-recents for that app will remove the task (to add the next one)
240         // but not trim it
241         Task documentTask1 = createDocumentTask(".DocumentTask1");
242         Task documentTask2 = createDocumentTask(".DocumentTask1");
243         documentTask1.maxRecents = 1;
244         documentTask2.maxRecents = 1;
245         mRecentTasks.add(documentTask1);
246         mRecentTasks.add(documentTask2);
247         assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
248         assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
249         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
250         assertThat(mCallbacksRecorder.mRemoved).contains(documentTask1);
251     }
252 
253     @Test
testAddTasksSameTask_expectNoTrim()254     public void testAddTasksSameTask_expectNoTrim() {
255         // Add a task that is already in the task list does not trigger any callbacks, it just
256         // moves in the list
257         Task documentTask1 = createDocumentTask(".DocumentTask1");
258         mRecentTasks.add(documentTask1);
259         mRecentTasks.add(documentTask1);
260         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
261         assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
262         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
263         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
264     }
265 
266     @Test
testAddMultipleDocumentTasks_expectNoTrim()267     public void testAddMultipleDocumentTasks_expectNoTrim() {
268         // Add same multiple-task document tasks does not trim the first tasks
269         Task documentTask1 = createDocumentTask(".DocumentTask1",
270                 FLAG_ACTIVITY_MULTIPLE_TASK);
271         Task documentTask2 = createDocumentTask(".DocumentTask1",
272                 FLAG_ACTIVITY_MULTIPLE_TASK);
273         mRecentTasks.add(documentTask1);
274         mRecentTasks.add(documentTask2);
275         assertThat(mCallbacksRecorder.mAdded).hasSize(2);
276         assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
277         assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
278         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
279         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
280     }
281 
282     @Test
testAddTasks_expectRemovedNoTrim()283     public void testAddTasks_expectRemovedNoTrim() {
284         // Add multiple same-affinity non-document tasks, ensure that it removes, but does not trim
285         // the other task
286         Task task1 = createTaskBuilder(".Task1")
287                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
288                 .build();
289         Task task2 = createTaskBuilder(".Task1")
290                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
291                 .build();
292         mRecentTasks.add(task1);
293         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
294         assertThat(mCallbacksRecorder.mAdded).contains(task1);
295         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
296         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
297         mCallbacksRecorder.clear();
298         mRecentTasks.add(task2);
299         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
300         assertThat(mCallbacksRecorder.mAdded).contains(task2);
301         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
302         assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
303         assertThat(mCallbacksRecorder.mRemoved).contains(task1);
304     }
305 
306     @Test
testAddMultipleTasks_expectNotRemoved()307     public void testAddMultipleTasks_expectNotRemoved() {
308         // Add multiple same-affinity non-document tasks with MULTIPLE_TASK, ensure that it does not
309         // remove the other task
310         Task task1 = createTaskBuilder(".Task1")
311                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
312                 .build();
313         Task task2 = createTaskBuilder(".Task1")
314                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
315                 .build();
316         mRecentTasks.add(task1);
317         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
318         assertThat(mCallbacksRecorder.mAdded).contains(task1);
319         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
320         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
321         mCallbacksRecorder.clear();
322         mRecentTasks.add(task2);
323         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
324         assertThat(mCallbacksRecorder.mAdded).contains(task2);
325         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
326         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
327     }
328 
329     @Test
testAddTasksDifferentStacks_expectNoRemove()330     public void testAddTasksDifferentStacks_expectNoRemove() {
331         // Adding the same task with different activity types should not trigger removal of the
332         // other task
333         Task task1 = createTaskBuilder(".Task1")
334                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
335                 .setParentTask(mTaskContainer.getRootHomeTask()).build();
336         Task task2 = createTaskBuilder(".Task1")
337                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
338                 .build();
339         mRecentTasks.add(task1);
340         mRecentTasks.add(task2);
341         assertThat(mCallbacksRecorder.mAdded).hasSize(2);
342         assertThat(mCallbacksRecorder.mAdded).contains(task1);
343         assertThat(mCallbacksRecorder.mAdded).contains(task2);
344         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
345         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
346     }
347 
348     @Test
testAddTaskCompatibleActivityType_expectRemove()349     public void testAddTaskCompatibleActivityType_expectRemove() {
350         // Test with undefined activity type since the type is not persisted by the task persister
351         // and we want to ensure that a new task will match a restored task
352         Task task1 = createTaskBuilder(".Task1")
353                 .setActivityType(ACTIVITY_TYPE_UNDEFINED)
354                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
355                 .build();
356         // Set the activity type again or the activity type of a root task would be remapped
357         // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()}
358         task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED);
359         mRecentTasks.add(task1);
360         mCallbacksRecorder.clear();
361 
362         Task task2 = createTaskBuilder(".Task1")
363                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
364                 .build();
365         assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
366         mRecentTasks.add(task2);
367         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
368         assertThat(mCallbacksRecorder.mAdded).contains(task2);
369         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
370         assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
371         assertThat(mCallbacksRecorder.mRemoved).contains(task1);
372     }
373 
374     @Test
testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove()375     public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() {
376         Task task1 = createTaskBuilder(".Task1")
377                 .setActivityType(ACTIVITY_TYPE_UNDEFINED)
378                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
379                 .setUserId(TEST_USER_0_ID)
380                 .build();
381         // Set the activity type again or the activity type of a root task would be remapped
382         // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()}
383         task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED);
384         mRecentTasks.add(task1);
385         mCallbacksRecorder.clear();
386 
387         Task task2 = createTaskBuilder(".Task1")
388                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
389                 .setUserId(TEST_USER_1_ID)
390                 .build();
391         assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
392         mRecentTasks.add(task2);
393         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
394         assertThat(mCallbacksRecorder.mAdded).contains(task2);
395         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
396         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
397     }
398 
399     @Test
testAddTaskCompatibleWindowingMode_expectRemove()400     public void testAddTaskCompatibleWindowingMode_expectRemove() {
401         Task task1 = createTaskBuilder(".Task1")
402                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
403                 .build();
404         doReturn(WINDOWING_MODE_UNDEFINED).when(task1).getWindowingMode();
405         mRecentTasks.add(task1);
406         mCallbacksRecorder.clear();
407 
408         Task task2 = createTaskBuilder(".Task1")
409                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
410                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
411                 .build();
412         assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode());
413         mRecentTasks.add(task2);
414 
415         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
416         assertThat(mCallbacksRecorder.mAdded).contains(task2);
417         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
418         assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
419         assertThat(mCallbacksRecorder.mRemoved).contains(task1);
420     }
421 
422     @Test
testAddTaskIncompatibleWindowingMode_expectNoRemove()423     public void testAddTaskIncompatibleWindowingMode_expectNoRemove() {
424         Task task1 = createTaskBuilder(".Task1")
425                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
426                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
427                 .build();
428         assertEquals(WINDOWING_MODE_FULLSCREEN, task1.getWindowingMode());
429         mRecentTasks.add(task1);
430 
431         Task task2 = createTaskBuilder(".Task1")
432                 .setWindowingMode(WINDOWING_MODE_PINNED)
433                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
434                 .build();
435         assertEquals(WINDOWING_MODE_PINNED, task2.getWindowingMode());
436         mRecentTasks.add(task2);
437 
438         assertThat(mCallbacksRecorder.mAdded).hasSize(2);
439         assertThat(mCallbacksRecorder.mAdded).contains(task1);
440         assertThat(mCallbacksRecorder.mAdded).contains(task2);
441         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
442         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
443     }
444 
445     @Test
testRemoveAffinityTask()446     public void testRemoveAffinityTask() {
447         // Add task to recents
448         final String taskAffinity = "affinity";
449         final int uid = 10123;
450         final Task task1 = createTaskBuilder(".Task1").build();
451         final ComponentName componentName = getUniqueComponentName();
452         task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid);
453         mRecentTasks.add(task1);
454 
455         // Add another task to recents, and make sure the previous task was removed.
456         final Task task2 = createTaskBuilder(".Task2").build();
457         task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid);
458         mRecentTasks.add(task2);
459         assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
460                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
461     }
462 
463     @Test
testAppendOrganizedChildTaskInfo()464     public void testAppendOrganizedChildTaskInfo() {
465         final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build();
466         root.mCreatedByOrganizer = true;
467         // Add organized and non-organized child.
468         final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build();
469         final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build();
470         doReturn(true).when(child1).isOrganized();
471         doReturn(false).when(child2).isOrganized();
472         mRecentTasks.add(root);
473 
474         // Make sure only organized child will be appended.
475         final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
476         final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos;
477         assertEquals(childrenTaskInfos.size(), 1);
478         assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId);
479     }
480 
481     @Test
testAddTasksHomeClearUntrackedTasks_expectFinish()482     public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
483         // There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
484         // FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
485         // because user may not be able to return to the task.
486         final String className = ".PermissionsReview";
487         final Function<Boolean, Task> taskBuilder = visible -> {
488             final Task task = createTaskBuilder(className).build();
489             // Make the task non-empty.
490             final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
491             r.setVisibility(visible);
492             return task;
493         };
494 
495         final Task task1 = taskBuilder.apply(false /* visible */);
496         mRecentTasks.add(task1);
497         final Task task2 = taskBuilder.apply(true /* visible */);
498         mRecentTasks.add(task2);
499         final Task task3 = createTaskBuilder(className).build();
500         mRecentTasks.add(task3);
501         // Only the last added task is kept in recents and the previous 2 tasks will become hidden
502         // tasks because their intents are identical.
503         mRecentTasks.add(task1);
504         // Go home to trigger the removal of untracked tasks.
505         mRecentTasks.add(createTaskBuilder(".Home")
506                 .setParentTask(mTaskContainer.getRootHomeTask())
507                 .build());
508         triggerIdleToTrim();
509 
510         // The task was added into recents again so it is not hidden and shouldn't be removed.
511         assertNotNull(task1.getTopNonFinishingActivity());
512         // All activities in the invisible task should be finishing or removed.
513         assertNull(task3.getTopNonFinishingActivity());
514         // The visible task should not be affected.
515         assertNotNull(task2.getTopNonFinishingActivity());
516     }
517 
518     @Test
testUsersTasks()519     public void testUsersTasks() {
520         mRecentTasks.setOnlyTestVisibleRange();
521         mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
522 
523         // Setup some tasks for the users
524         mTaskPersister.mUserTaskIdsOverride = new SparseBooleanArray();
525         mTaskPersister.mUserTaskIdsOverride.put(1, true);
526         mTaskPersister.mUserTaskIdsOverride.put(2, true);
527         mTaskPersister.mUserTasksOverride = new ArrayList<>();
528         mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
529         mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
530 
531         // Assert no user tasks are initially loaded
532         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
533 
534         // Load user 0 tasks
535         mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
536         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
537         assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
538         assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
539 
540         // Load user 1 tasks
541         mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID);
542         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
543         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_1_ID);
544         assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
545         assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
546         assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID));
547         assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID));
548 
549         // Unload user 1 tasks
550         mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID);
551         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
552         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
553                 .doesNotContain(TEST_USER_1_ID);
554         assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
555         assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
556 
557         // Unload user 0 tasks
558         mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
559         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
560                 .doesNotContain(TEST_USER_0_ID);
561         assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
562                 .doesNotContain(TEST_USER_1_ID);
563     }
564 
565     @Test
testOrderedIteration()566     public void testOrderedIteration() {
567         mRecentTasks.setOnlyTestVisibleRange();
568         Task task1 = createTaskBuilder(".Task1").build();
569         task1.lastActiveTime = new Random().nextInt();
570         Task task2 = createTaskBuilder(".Task1").build();
571         task2.lastActiveTime = new Random().nextInt();
572         Task task3 = createTaskBuilder(".Task1").build();
573         task3.lastActiveTime = new Random().nextInt();
574         Task task4 = createTaskBuilder(".Task1").build();
575         task4.lastActiveTime = new Random().nextInt();
576         mRecentTasks.add(task1);
577         mRecentTasks.add(task2);
578         mRecentTasks.add(task3);
579         mRecentTasks.add(task4);
580 
581         long prevLastActiveTime = 0;
582         final ArrayList<Task> tasks = mRecentTasks.getRawTasks();
583         for (int i = 0; i < tasks.size(); i++) {
584             final Task task = tasks.get(i);
585             assertThat(prevLastActiveTime).isLessThan(task.lastActiveTime);
586             prevLastActiveTime = task.lastActiveTime;
587         }
588     }
589 
590     @Test
testTrimToGlobalMaxNumRecents()591     public void testTrimToGlobalMaxNumRecents() {
592         mRecentTasks.setOnlyTestVisibleRange();
593 
594         // Limit the global maximum number of recent tasks to a fixed size
595         mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */);
596 
597         // Add N+1 tasks
598         mRecentTasks.add(mTasks.get(0));
599         mRecentTasks.add(mTasks.get(1));
600         mRecentTasks.add(mTasks.get(2));
601 
602         // Ensure that the last task was trimmed as an inactive task
603         triggerTrimAndAssertTrimmed(mTasks.get(0));
604     }
605 
606     @Test
testTrimQuietProfileTasks()607     public void testTrimQuietProfileTasks() {
608         mRecentTasks.setOnlyTestVisibleRange();
609         Task qt1 = createTaskBuilder(".QuietTask1").setUserId(TEST_QUIET_USER_ID).build();
610         Task qt2 = createTaskBuilder(".QuietTask2").setUserId(TEST_QUIET_USER_ID).build();
611         mRecentTasks.add(qt1);
612         mRecentTasks.add(qt2);
613 
614         mRecentTasks.add(mTasks.get(0));
615         mRecentTasks.add(mTasks.get(1));
616 
617         // Ensure that the quiet user's tasks was trimmed once the new tasks were added
618         triggerTrimAndAssertTrimmed(qt1, qt2);
619     }
620 
621     @Test
testSessionDuration()622     public void testSessionDuration() {
623         mRecentTasks.setOnlyTestVisibleRange();
624         mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */);
625 
626         Task t1 = createTaskBuilder(".Task1").build();
627         t1.touchActiveTime();
628         mRecentTasks.add(t1);
629 
630         // Force a small sleep just beyond the session duration
631         SystemClock.sleep(75);
632 
633         // Assert that the old task has been removed due to being out of the active session
634         triggerTrimAndAssertTrimmed(t1);
635     }
636 
637     @Test
testVisibleTasks_excludedFromRecents()638     public void testVisibleTasks_excludedFromRecents() {
639         mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
640 
641         Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
642                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
643                 .build();
644         Task excludedTask2 = createTaskBuilder(".ExcludedTask2")
645                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
646                 .build();
647         Task detachedExcludedTask = createTaskBuilder(".DetachedExcludedTask")
648                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
649                 .build();
650 
651         // Move home to front so other task can satisfy the condition in RecentTasks#isTrimmable.
652         mRootWindowContainer.getDefaultTaskDisplayArea().getRootHomeTask().moveToFront("test");
653         // Avoid Task#autoRemoveFromRecents when removing from parent.
654         detachedExcludedTask.setHasBeenVisible(true);
655         detachedExcludedTask.removeImmediately();
656         assertFalse(detachedExcludedTask.isAttached());
657 
658         mRecentTasks.add(detachedExcludedTask);
659         mRecentTasks.add(excludedTask1);
660         mRecentTasks.add(mTasks.get(0));
661         mRecentTasks.add(mTasks.get(1));
662         mRecentTasks.add(mTasks.get(2));
663         mRecentTasks.add(excludedTask2);
664 
665         // Except the first-most excluded task, other excluded tasks should be trimmed.
666         triggerTrimAndAssertTrimmed(excludedTask1, detachedExcludedTask);
667     }
668 
669     @Test
testVisibleTasks_excludedFromRecents_firstTaskNotVisible()670     public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
671         // Create some set of tasks, some of which are visible and some are not
672         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
673                 .setParentTask(mTaskContainer.getRootHomeTask())
674                 .build();
675         homeTask.mUserSetupComplete = true;
676         mRecentTasks.add(homeTask);
677         Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
678                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
679                 .build();
680         excludedTask1.mUserSetupComplete = true;
681         mRecentTasks.add(excludedTask1);
682 
683         // Expect that the first visible excluded-from-recents task is visible
684         assertGetRecentTasksOrder(0 /* flags */, excludedTask1);
685     }
686 
687     @Test
testVisibleTasks_excludedFromRecents_nonDefaultDisplayTaskNotVisible()688     public void testVisibleTasks_excludedFromRecents_nonDefaultDisplayTaskNotVisible() {
689         Task excludedTaskOnVirtualDisplay = createTaskBuilder(".excludedTaskOnVirtualDisplay")
690                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
691                 .build();
692         excludedTaskOnVirtualDisplay.mUserSetupComplete = true;
693         doReturn(false).when(excludedTaskOnVirtualDisplay).isOnHomeDisplay();
694         mRecentTasks.add(mTasks.get(0));
695         mRecentTasks.add(excludedTaskOnVirtualDisplay);
696 
697         // Expect that the first visible excluded-from-recents task is visible
698         assertGetRecentTasksOrder(0 /* flags */, mTasks.get(0));
699     }
700 
701     @Test
testVisibleTasks_excludedFromRecents_withExcluded()702     public void testVisibleTasks_excludedFromRecents_withExcluded() {
703         // Create some set of tasks, some of which are visible and some are not
704         Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
705         t1.mUserSetupComplete = true;
706         mRecentTasks.add(t1);
707         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
708                 .setParentTask(mTaskContainer.getRootHomeTask())
709                 .build();
710         homeTask.mUserSetupComplete = true;
711         mRecentTasks.add(homeTask);
712         Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
713                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
714                 .build();
715         excludedTask1.mUserSetupComplete = true;
716         mRecentTasks.add(excludedTask1);
717         Task excludedTask2 = createTaskBuilder(".ExcludedTask2")
718                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
719                 .build();
720         excludedTask2.mUserSetupComplete = true;
721         mRecentTasks.add(excludedTask2);
722         Task t2 = createTaskBuilder("com.android.pkg2", ".Task1").build();
723         t2.mUserSetupComplete = true;
724         mRecentTasks.add(t2);
725 
726         assertGetRecentTasksOrder(RECENT_WITH_EXCLUDED, t2, excludedTask2, excludedTask1, t1);
727     }
728 
729     @Test
testVisibleTasks_minNum()730     public void testVisibleTasks_minNum() {
731         mRecentTasks.setOnlyTestVisibleRange();
732         mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */);
733 
734         for (int i = 0; i < 4; i++) {
735             final Task task = mTasks.get(i);
736             task.touchActiveTime();
737             mRecentTasks.add(task);
738         }
739 
740         // Force a small sleep just beyond the session duration
741         SystemClock.sleep(50);
742 
743         // Add a new task to trigger tasks to be trimmed
744         mRecentTasks.add(mTasks.get(4));
745 
746         // Ensure that there are a minimum number of tasks regardless of session length
747         triggerTrimAndAssertNoTasksTrimmed();
748     }
749 
750     @Test
testVisibleTasks_maxNum()751     public void testVisibleTasks_maxNum() {
752         mRecentTasks.setOnlyTestVisibleRange();
753         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
754 
755         for (int i = 0; i < 5; i++) {
756             final Task task = mTasks.get(i);
757             task.touchActiveTime();
758             mRecentTasks.add(task);
759         }
760 
761         // Ensure that only the last number of max tasks are kept
762         triggerTrimAndAssertTrimmed(mTasks.get(0), mTasks.get(1));
763     }
764 
765     /**
766      * Tests that tasks on always on top multi-window tasks are not visible and not trimmed/removed.
767      */
768     @Test
testVisibleTasks_alwaysOnTop()769     public void testVisibleTasks_alwaysOnTop() {
770         mRecentTasks.setOnlyTestVisibleRange();
771         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
772 
773         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
774         final Task alwaysOnTopTask = taskDisplayArea.createRootTask(WINDOWING_MODE_MULTI_WINDOW,
775                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
776         alwaysOnTopTask.setAlwaysOnTop(true);
777         alwaysOnTopTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true);
778 
779         assertFalse("Always on top tasks should not be visible recents",
780                 mRecentTasks.isVisibleRecentTask(alwaysOnTopTask));
781 
782         mRecentTasks.add(alwaysOnTopTask);
783 
784         // Add N+1 visible tasks.
785         mRecentTasks.add(mTasks.get(0));
786         mRecentTasks.add(mTasks.get(1));
787         mRecentTasks.add(mTasks.get(2));
788         mRecentTasks.add(mTasks.get(3));
789 
790         // excludedTask is not trimmed.
791         triggerTrimAndAssertTrimmed(mTasks.get(0));
792 
793         mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
794 
795         // Only visible tasks removed.
796         triggerTrimAndAssertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3));
797     }
798 
799     @Test
testVisibleTask_displayCanNotShowTaskFromRecents_expectNotVisible()800     public void testVisibleTask_displayCanNotShowTaskFromRecents_expectNotVisible() {
801         final DisplayContent displayContent = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
802         doReturn(false).when(displayContent).canShowTasksInHostDeviceRecents();
803         final Task task = displayContent.getDefaultTaskDisplayArea().createRootTask(
804                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
805         mRecentTasks.add(task);
806 
807         assertFalse(mRecentTasks.isVisibleRecentTask(task));
808     }
809 
810     @Test
testFreezeTaskListOrder_reorderExistingTask()811     public void testFreezeTaskListOrder_reorderExistingTask() {
812         // Add some tasks
813         mRecentTasks.add(mTasks.get(0));
814         mRecentTasks.add(mTasks.get(1));
815         mRecentTasks.add(mTasks.get(2));
816         mRecentTasks.add(mTasks.get(3));
817         mRecentTasks.add(mTasks.get(4));
818         mCallbacksRecorder.clear();
819 
820         // Freeze the list
821         mRecentTasks.setFreezeTaskListReordering();
822         assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
823 
824         // Relaunch a few tasks
825         mRecentTasks.add(mTasks.get(3));
826         mRecentTasks.add(mTasks.get(2));
827 
828         // Commit the task ordering with a specific task focused
829         mRecentTasks.resetFreezeTaskListReordering(mTasks.get(2));
830         assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
831 
832         // Ensure that the order of the task list is the same as before, but with the focused task
833         // at the front
834         assertRecentTasksOrder(mTasks.get(2),
835                 mTasks.get(4),
836                 mTasks.get(3),
837                 mTasks.get(1),
838                 mTasks.get(0));
839 
840         assertThat(mCallbacksRecorder.mAdded).isEmpty();
841         assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
842         assertThat(mCallbacksRecorder.mRemoved).isEmpty();
843     }
844 
845     @Test
testFreezeTaskListOrder_addRemoveTasks()846     public void testFreezeTaskListOrder_addRemoveTasks() {
847         // Add some tasks
848         mRecentTasks.add(mTasks.get(0));
849         mRecentTasks.add(mTasks.get(1));
850         mRecentTasks.add(mTasks.get(2));
851         mCallbacksRecorder.clear();
852 
853         // Freeze the list
854         mRecentTasks.setFreezeTaskListReordering();
855         assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
856 
857         // Add and remove some tasks
858         mRecentTasks.add(mTasks.get(3));
859         mRecentTasks.add(mTasks.get(4));
860         mRecentTasks.remove(mTasks.get(0));
861         mRecentTasks.remove(mTasks.get(1));
862 
863         // Unfreeze the list
864         mRecentTasks.resetFreezeTaskListReordering(null);
865         assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
866 
867         // Ensure that the order of the task list accounts for the added and removed tasks (added
868         // at the end)
869         assertRecentTasksOrder(mTasks.get(4),
870                 mTasks.get(3),
871                 mTasks.get(2));
872 
873         assertThat(mCallbacksRecorder.mAdded).hasSize(2);
874         assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(3));
875         assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(4));
876         assertThat(mCallbacksRecorder.mRemoved).hasSize(2);
877         assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(0));
878         assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(1));
879     }
880 
881     @Test
testFreezeTaskListOrder_replaceTask()882     public void testFreezeTaskListOrder_replaceTask() {
883         // Create two tasks with the same affinity
884         Task affinityTask1 = createTaskBuilder(".AffinityTask1")
885                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
886                 .build();
887         Task affinityTask2 = createTaskBuilder(".AffinityTask2")
888                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
889                 .build();
890         affinityTask2.affinity = affinityTask1.affinity = "affinity";
891 
892         // Add some tasks
893         mRecentTasks.add(mTasks.get(0));
894         mRecentTasks.add(affinityTask1);
895         mRecentTasks.add(mTasks.get(1));
896         mCallbacksRecorder.clear();
897 
898         // Freeze the list
899         mRecentTasks.setFreezeTaskListReordering();
900         assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
901 
902         // Add the affinity task
903         mRecentTasks.add(affinityTask2);
904 
905         assertRecentTasksOrder(mTasks.get(1),
906                 affinityTask2,
907                 mTasks.get(0));
908 
909         assertThat(mCallbacksRecorder.mAdded).hasSize(1);
910         assertThat(mCallbacksRecorder.mAdded).contains(affinityTask2);
911         assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
912         assertThat(mCallbacksRecorder.mRemoved).contains(affinityTask1);
913     }
914 
915     @Test
testFreezeTaskListOrder_timeout()916     public void testFreezeTaskListOrder_timeout() {
917         for (Task t : mTasks) {
918             // Make all the tasks non-empty
919             new ActivityBuilder(mAtm).setTask(t).build();
920         }
921 
922         // Add some tasks
923         mRecentTasks.add(mTasks.get(0));
924         mRecentTasks.add(mTasks.get(1));
925         mRecentTasks.add(mTasks.get(2));
926         mRecentTasks.add(mTasks.get(3));
927         mRecentTasks.add(mTasks.get(4));
928 
929         // Freeze the list
930         mRecentTasks.setFreezeTaskListReordering();
931         assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
932 
933         // Relaunch a few tasks
934         mRecentTasks.add(mTasks.get(2));
935         mRecentTasks.add(mTasks.get(1));
936 
937         Task stack = mTasks.get(2).getRootTask();
938         stack.moveToFront("", mTasks.get(2));
939         doReturn(stack).when(mAtm.mRootWindowContainer).getTopDisplayFocusedRootTask();
940 
941         // Simulate the reset from the timeout
942         mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
943         assertFalse(mRecentTasks.isFreezeTaskListReorderingSet());
944 
945         // Ensure that the order of the task list is the same as before, but with the focused task
946         // at the front
947         assertRecentTasksOrder(mTasks.get(2),
948                 mTasks.get(4),
949                 mTasks.get(3),
950                 mTasks.get(1),
951                 mTasks.get(0));
952     }
953 
954     @Test
testBackStackTasks_expectNoTrim()955     public void testBackStackTasks_expectNoTrim() {
956         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
957 
958         final Task homeStack = mTaskContainer.getRootHomeTask();
959         final Task aboveHomeStack = mTaskContainer.createRootTask(
960                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
961 
962         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
963         // the tasks belong in stacks above the home stack
964         mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
965         mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build());
966         mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
967         mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build());
968 
969         triggerTrimAndAssertNoTasksTrimmed();
970     }
971 
972     @Test
testBehindHomeStackTasks_expectTaskTrimmed()973     public void testBehindHomeStackTasks_expectTaskTrimmed() {
974         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
975 
976         final Task behindHomeStack = mTaskContainer.createRootTask(
977                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
978         final Task homeStack = mTaskContainer.getRootHomeTask();
979         final Task aboveHomeStack = mTaskContainer.createRootTask(
980                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
981 
982         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
983         // the home stack is trimmed once a new task is added
984         final Task behindHomeTask = createTaskBuilder(".Task1")
985                 .setParentTask(behindHomeStack)
986                 .build();
987         mRecentTasks.add(behindHomeTask);
988         mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
989         mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
990 
991         triggerTrimAndAssertTrimmed(behindHomeTask);
992     }
993 
994     @Test
testOtherDisplayTasks_expectNoTrim()995     public void testOtherDisplayTasks_expectNoTrim() {
996         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
997 
998         final Task homeTask = mTaskContainer.getRootHomeTask();
999         final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
1000         final Task otherDisplayRootTask = otherDisplay.getDefaultTaskDisplayArea().createRootTask(
1001                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
1002 
1003         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
1004         // removed
1005         mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build());
1006         mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask)
1007                 .build());
1008         mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask)
1009                 .build());
1010         mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build());
1011 
1012         triggerTrimAndAssertNoTasksTrimmed();
1013     }
1014 
1015     @Test
testRemovePackageByName()1016     public void testRemovePackageByName() {
1017         // Add a number of tasks with the same package name
1018         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task1").build());
1019         mRecentTasks.add(createTaskBuilder("com.android.pkg2", ".Task2").build());
1020         mRecentTasks.add(createTaskBuilder("com.android.pkg3", ".Task3").build());
1021         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task4").build());
1022         mRecentTasks.removeTasksByPackageName("com.android.pkg1", TEST_USER_0_ID);
1023 
1024         final ArrayList<Task> tasks = mRecentTasks.getRawTasks();
1025         for (int i = 0; i < tasks.size(); i++) {
1026             if (tasks.get(i).intent.getComponent().getPackageName().equals("com.android.pkg1")) {
1027                 fail("Expected com.android.pkg1 tasks to be removed");
1028             }
1029         }
1030 
1031         // If the task has a non-stopped activity, the removal will wait for its onDestroy.
1032         final Task task = tasks.get(0);
1033         final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build();
1034         top.lastVisibleTime = 123;
1035         top.setState(ActivityRecord.State.RESUMED, "test");
1036         mRecentTasks.removeTasksByPackageName(task.getBasePackageName(), TEST_USER_0_ID);
1037         assertTrue(task.mKillProcessesOnDestroyed);
1038         top.setState(ActivityRecord.State.DESTROYING, "test");
1039         top.destroyed("test");
1040         assertFalse(task.mKillProcessesOnDestroyed);
1041     }
1042 
1043     @Test
testRemoveAllVisibleTasks()1044     public void testRemoveAllVisibleTasks() {
1045         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
1046 
1047         // Create some set of tasks, some of which are visible and some are not
1048         Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
1049         mRecentTasks.add(t1);
1050         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".HomeTask")
1051                 .setParentTask(mTaskContainer.getRootHomeTask()).build());
1052         Task t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
1053         mRecentTasks.add(t2);
1054         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".PipTask")
1055                 .setWindowingMode(WINDOWING_MODE_PINNED).build());
1056         Task t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
1057         mRecentTasks.add(t3);
1058 
1059         // Create some more tasks that are out of visible range, but are still visible
1060         Task t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
1061         mRecentTasks.add(t4);
1062         Task t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
1063         mRecentTasks.add(t5);
1064 
1065         // Create some more tasks that are out of the active session range, but are still visible
1066         Task t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
1067         t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
1068         mRecentTasks.add(t6);
1069         Task t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
1070         t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
1071         mRecentTasks.add(t7);
1072 
1073         // Remove all the visible tasks and ensure that they are removed
1074         mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
1075         triggerTrimAndAssertTrimmed(t1, t2, t3, t4, t5, t6, t7);
1076     }
1077 
1078     @Test
testRemoveAllVisibleTasksPerUser()1079     public void testRemoveAllVisibleTasksPerUser() {
1080         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
1081 
1082         // Create a visible task per user
1083         Task t1 = createTaskBuilder(".Task1")
1084                 .setUserId(TEST_USER_0_ID)
1085                 .build();
1086         mRecentTasks.add(t1);
1087 
1088         Task t2 = createTaskBuilder(".Task2")
1089                 .setUserId(TEST_QUIET_USER_ID)
1090                 .build();
1091         mRecentTasks.add(t2);
1092 
1093         Task t3 = createTaskBuilder(".Task3")
1094                 .setUserId(TEST_USER_1_ID)
1095                 .build();
1096         mRecentTasks.add(t3);
1097 
1098         // Remove all the visible tasks and ensure that they are removed
1099         mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
1100         triggerTrimAndAssertTrimmed(t1, t2);
1101     }
1102 
1103     @Test
testNotRestoreRecentTaskApis()1104     public void testNotRestoreRecentTaskApis() {
1105         final Task task = createTaskBuilder(".Task").build();
1106         final int taskId = task.mTaskId;
1107         mRecentTasks.add(task);
1108         // Only keep the task in RecentTasks.
1109         task.removeIfPossible();
1110 
1111         // The following APIs should not restore task from recents to the active list.
1112         assertNotRestoreTask(() -> mAtm.setFocusedTask(taskId));
1113         assertNotRestoreTask(() -> mAtm.startSystemLockTaskMode(taskId));
1114         assertNotRestoreTask(() -> mAtm.cancelTaskWindowTransition(taskId));
1115         assertNotRestoreTask(
1116                 () -> mAtm.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
1117     }
1118 
1119     @Test
addTask_callsTaskNotificationController()1120     public void addTask_callsTaskNotificationController() {
1121         final Task task = createTaskBuilder(".Task").build();
1122 
1123         mRecentTasks.add(task);
1124         mRecentTasks.remove(task);
1125 
1126         TaskChangeNotificationController controller =
1127                 mAtm.getTaskChangeNotificationController();
1128         verify(controller, times(2)).notifyTaskListUpdated();
1129     }
1130 
1131     @Test
addTask_taskAlreadyInRecentsMovedToTop_callsTaskNotificationController()1132     public void addTask_taskAlreadyInRecentsMovedToTop_callsTaskNotificationController() {
1133         final Task firstTask = createTaskBuilder(".Task").build();
1134         final Task secondTask = createTaskBuilder(".Task2").build();
1135 
1136         mRecentTasks.add(firstTask);
1137         mRecentTasks.add(secondTask);
1138 
1139         TaskChangeNotificationController controller =
1140                 mAtm.getTaskChangeNotificationController();
1141         clearInvocations(controller);
1142 
1143         // Add firstTask back to top
1144         mRecentTasks.add(firstTask);
1145         verify(controller).notifyTaskListUpdated();
1146     }
1147 
1148     @Test
addTask_taskAlreadyInRecentsOnTop_doesNotNotify()1149     public void addTask_taskAlreadyInRecentsOnTop_doesNotNotify() {
1150         final Task firstTask = createTaskBuilder(".Task").build();
1151         final Task secondTask = createTaskBuilder(".Task2").build();
1152 
1153         mRecentTasks.add(firstTask);
1154         mRecentTasks.add(secondTask);
1155 
1156         TaskChangeNotificationController controller =
1157                 mAtm.getTaskChangeNotificationController();
1158         clearInvocations(controller);
1159 
1160         // Add secondTask to top again
1161         mRecentTasks.add(secondTask);
1162         verifyZeroInteractions(controller);
1163     }
1164 
1165     @Test
removeTask_callsTaskNotificationController()1166     public void removeTask_callsTaskNotificationController() {
1167         final Task task = createTaskBuilder(".Task").build();
1168 
1169         mRecentTasks.add(task);
1170         mRecentTasks.remove(task);
1171 
1172         // 2 calls - Once for add and once for remove
1173         TaskChangeNotificationController controller =
1174                 mAtm.getTaskChangeNotificationController();
1175         verify(controller, times(2)).notifyTaskListUpdated();
1176     }
1177 
1178     @Test
removeALlVisibleTask_callsTaskNotificationController_twice()1179     public void removeALlVisibleTask_callsTaskNotificationController_twice() {
1180         final Task task1 = createTaskBuilder(".Task").build();
1181         final Task task2 = createTaskBuilder(".Task2").build();
1182 
1183         mRecentTasks.add(task1);
1184         mRecentTasks.add(task2);
1185         mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
1186 
1187         // 4 calls - Twice for add and twice for remove
1188         TaskChangeNotificationController controller =
1189                 mAtm.getTaskChangeNotificationController();
1190         verify(controller, times(4)).notifyTaskListUpdated();
1191     }
1192 
1193     @Test
testTaskInfo_expectNoExtras()1194     public void testTaskInfo_expectNoExtras() {
1195         final Bundle data = new Bundle();
1196         data.putInt("key", 100);
1197         final Task task1 = createTaskBuilder(".Task").build();
1198         final ActivityRecord r1 = new ActivityBuilder(mAtm)
1199                 .setTask(task1)
1200                 .setIntentExtras(data)
1201                 .build();
1202         mRecentTasks.add(r1.getTask());
1203 
1204         final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
1205         assertTrue(infos.size() == 1);
1206         for (int i = 0; i < infos.size(); i++)  {
1207             final Bundle extras = infos.get(i).baseIntent.getExtras();
1208             assertTrue(extras == null || extras.isEmpty());
1209         }
1210     }
1211 
1212     @Test
testLastSnapshotData_snapshotSaved()1213     public void testLastSnapshotData_snapshotSaved() {
1214         final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80));
1215         final Task task1 = createTaskBuilder(".Task").build();
1216         task1.onSnapshotChanged(snapshot);
1217 
1218         mRecentTasks.add(task1);
1219         final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
1220         final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
1221                 infos.get(0).lastSnapshotData;
1222         assertTrue(lastSnapshotData.taskSize.equals(100, 100));
1223         assertTrue(lastSnapshotData.bufferSize.equals(80, 80));
1224     }
1225 
1226     @Test
testLastSnapshotData_noBuffer()1227     public void testLastSnapshotData_noBuffer() {
1228         final Task task1 = createTaskBuilder(".Task").build();
1229         final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null);
1230         task1.onSnapshotChanged(snapshot);
1231 
1232         mRecentTasks.add(task1);
1233         final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
1234         final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
1235                 infos.get(0).lastSnapshotData;
1236         assertTrue(lastSnapshotData.taskSize.equals(100, 100));
1237         assertNull(lastSnapshotData.bufferSize);
1238     }
1239 
1240     @Test
testLastSnapshotData_notSet()1241     public void testLastSnapshotData_notSet() {
1242         final Task task1 = createTaskBuilder(".Task").build();
1243 
1244         mRecentTasks.add(task1);
1245         final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
1246         final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
1247                 infos.get(0).lastSnapshotData;
1248         assertNull(lastSnapshotData.taskSize);
1249         assertNull(lastSnapshotData.bufferSize);
1250     }
1251 
1252     @Test
testCreateRecentTaskInfo_detachedTask()1253     public void testCreateRecentTaskInfo_detachedTask() {
1254         final Task task = createTaskBuilder(".Task").build();
1255         final ComponentName componentName = getUniqueComponentName();
1256         new ActivityBuilder(mSupervisor.mService)
1257                 .setTask(task)
1258                 .setUid(NOBODY_UID)
1259                 .setComponent(componentName)
1260                 .build();
1261         final TaskDisplayArea tda = task.getDisplayArea();
1262 
1263         assertTrue(task.isAttached());
1264         assertTrue(task.supportsMultiWindow());
1265 
1266         RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
1267                 true /* getTasksAllowed */);
1268 
1269         assertTrue(info.supportsMultiWindow);
1270 
1271         info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
1272                 false /* getTasksAllowed */);
1273 
1274         assertFalse(info.topActivity.equals(componentName));
1275         assertFalse(info.topActivityInfo.packageName.equals(componentName.getPackageName()));
1276         assertFalse(info.baseActivity.equals(componentName));
1277 
1278         // The task can be put in split screen even if it is not attached now.
1279         task.removeImmediately();
1280 
1281         info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
1282                 true /* getTasksAllowed */);
1283 
1284         assertTrue(info.supportsMultiWindow);
1285 
1286         // Test non-resizable.
1287         // The non-resizable task cannot be put in split screen because of the config.
1288         doReturn(false).when(tda).supportsNonResizableMultiWindow();
1289         doReturn(false).when(task).isResizeable();
1290 
1291         info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
1292                 true /* getTasksAllowed */);
1293 
1294         assertFalse(info.supportsMultiWindow);
1295 
1296         // Even if it is not attached, the non-resizable task can be put in split screen as long as
1297         // the device supports it.
1298         doReturn(true).when(tda).supportsNonResizableMultiWindow();
1299 
1300         info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
1301                 true /* getTasksAllowed */);
1302 
1303         assertTrue(info.supportsMultiWindow);
1304     }
1305 
1306     @Test
testRemoveCompatibleRecentTask()1307     public void testRemoveCompatibleRecentTask() {
1308         final Task task1 = createTaskBuilder(".Task").setWindowingMode(
1309                 WINDOWING_MODE_FULLSCREEN).build();
1310         mRecentTasks.add(task1);
1311         final Task task2 = createTaskBuilder(".Task").setWindowingMode(
1312                 WINDOWING_MODE_MULTI_WINDOW).build();
1313         mRecentTasks.add(task2);
1314         assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
1315                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
1316 
1317         // Set windowing mode and ensure the same fullscreen task that created earlier is removed.
1318         task2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1319         mRecentTasks.removeCompatibleRecentTask(task2);
1320         assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
1321                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
1322         assertEquals(task2.mTaskId, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
1323                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().get(0).taskId);
1324     }
1325 
createSnapshot(Point taskSize, Point bufferSize)1326     private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
1327         HardwareBuffer buffer = null;
1328         if (bufferSize != null) {
1329             buffer = mock(HardwareBuffer.class);
1330             doReturn(bufferSize.x).when(buffer).getWidth();
1331             doReturn(bufferSize.y).when(buffer).getHeight();
1332         }
1333         return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer,
1334                 ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
1335                 Surface.ROTATION_0, taskSize, new Rect() /* contentInsets */,
1336                 new Rect() /* letterboxInsets*/, false /* isLowResolution */,
1337                 true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
1338                 false /* isTranslucent */, false /* hasImeSurface */);
1339     }
1340 
1341     /**
1342      * Ensures that the raw recent tasks list is in the provided order. Note that the expected tasks
1343      * should be ordered from least to most recent.
1344      */
assertRecentTasksOrder(Task... expectedTasks)1345     private void assertRecentTasksOrder(Task... expectedTasks) {
1346         ArrayList<Task> tasks = mRecentTasks.getRawTasks();
1347         assertTrue(expectedTasks.length == tasks.size());
1348         for (int i = 0; i < tasks.size(); i++)  {
1349             assertTrue(expectedTasks[i] == tasks.get(i));
1350         }
1351     }
1352 
getRecentTasks(int flags)1353     private List<RecentTaskInfo> getRecentTasks(int flags) {
1354         doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
1355         doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
1356         return mRecentTasks.getRecentTasks(MAX_VALUE, flags, true /* getTasksAllowed */,
1357                 TEST_USER_0_ID, 0 /* callingUid */).getList();
1358     }
1359 
1360     /**
1361      * Ensures that the recent tasks list is in the provided order. Note that the expected tasks
1362      * should be ordered from least to most recent.
1363      */
assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks)1364     private void assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks) {
1365         List<RecentTaskInfo> infos = getRecentTasks(getRecentTaskFlags);
1366         assertTrue(expectedTasks.length == infos.size());
1367         for (int i = 0; i < infos.size(); i++)  {
1368             assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId);
1369         }
1370     }
1371 
assertNotRestoreTask(Runnable action)1372     private void assertNotRestoreTask(Runnable action) {
1373         // Verify stack count doesn't change because task with fullscreen mode and standard type
1374         // would have its own stack.
1375         final int originalStackCount = mTaskContainer.getRootTaskCount();
1376         action.run();
1377         assertEquals(originalStackCount, mTaskContainer.getRootTaskCount());
1378     }
1379 
doTestRecentTasksApis(boolean expectCallable)1380     private void doTestRecentTasksApis(boolean expectCallable) {
1381         assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID));
1382         assertSecurityException(expectCallable,
1383                 () -> mAtm.removeRootTasksInWindowingModes(
1384                         new int[]{WINDOWING_MODE_UNDEFINED}));
1385         assertSecurityException(expectCallable,
1386                 () -> mAtm.removeRootTasksWithActivityTypes(
1387                         new int[]{ACTIVITY_TYPE_UNDEFINED}));
1388         assertSecurityException(expectCallable, () -> mAtm.removeTask(0));
1389         assertSecurityException(expectCallable,
1390                 () -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true));
1391         assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
1392         assertSecurityException(expectCallable,
1393                 () -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
1394         assertSecurityException(expectCallable, () -> {
1395             try {
1396                 mAtm.getFocusedRootTaskInfo();
1397             } catch (RemoteException e) {
1398                 // Ignore
1399             }
1400         });
1401         assertSecurityException(expectCallable,
1402                 () -> mAtm.startActivityFromRecents(0, new Bundle()));
1403         assertSecurityException(expectCallable, () -> mAtm.getTaskSnapshot(0, true, false));
1404         assertSecurityException(expectCallable, () -> mAtm.registerTaskStackListener(null));
1405         assertSecurityException(expectCallable,
1406                 () -> mAtm.unregisterTaskStackListener(null));
1407         assertSecurityException(expectCallable, () -> mAtm.getTaskDescription(0));
1408         assertSecurityException(expectCallable, () -> mAtm.cancelTaskWindowTransition(0));
1409         assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, 0,
1410                 null));
1411         assertSecurityException(expectCallable, () -> mAtm.cancelRecentsAnimation(true));
1412         assertSecurityException(expectCallable, () -> mAtm.stopAppSwitches());
1413         assertSecurityException(expectCallable, () -> mAtm.resumeAppSwitches());
1414     }
1415 
testGetTasksApis(boolean expectCallable)1416     private void testGetTasksApis(boolean expectCallable) {
1417         mAtm.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
1418         mAtm.getTasks(MAX_VALUE);
1419         if (expectCallable) {
1420             assertTrue(mRecentTasks.mLastAllowed);
1421             assertTrue(mRunningTasks.mLastAllowed);
1422         } else {
1423             assertFalse(mRecentTasks.mLastAllowed);
1424             assertFalse(mRunningTasks.mLastAllowed);
1425         }
1426     }
1427 
createTaskBuilder(String className)1428     private TaskBuilder createTaskBuilder(String className) {
1429         return createTaskBuilder(mContext.getPackageName(), className);
1430     }
1431 
createTaskBuilder(String packageName, String className)1432     private TaskBuilder createTaskBuilder(String packageName, String className) {
1433         return new TaskBuilder(mAtm.mTaskSupervisor)
1434                 .setComponent(new ComponentName(packageName, className))
1435                 .setUserId(TEST_USER_0_ID);
1436     }
1437 
createDocumentTask(String className)1438     private Task createDocumentTask(String className) {
1439         return createDocumentTask(className, 0);
1440     }
1441 
createDocumentTask(String className, int flags)1442     private Task createDocumentTask(String className, int flags) {
1443         Task task = createTaskBuilder(className)
1444                 .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags)
1445                 .build();
1446         task.affinity = null;
1447         task.maxRecents = ActivityTaskManager.getMaxAppRecentsLimitStatic();
1448         return task;
1449     }
1450 
triggerIdleToTrim()1451     private void triggerIdleToTrim() {
1452         doNothing().when(mAtm).scheduleAppGcsLocked();
1453         final ActivityRecord r = mRootWindowContainer.topRunningActivity();
1454         mSupervisor.activityIdleInternal(r != null ? r : mock(ActivityRecord.class),
1455                 false /* fromTimeout */, false /* processPausingActivities */, null /* config */);
1456     }
1457 
triggerTrimAndAssertNoTasksTrimmed()1458     private void triggerTrimAndAssertNoTasksTrimmed() {
1459         triggerTrimAndAssertTrimmed();
1460     }
1461 
triggerTrimAndAssertTrimmed(Task... tasks)1462     private void triggerTrimAndAssertTrimmed(Task... tasks) {
1463         triggerIdleToTrim();
1464         final ArrayList<Task> trimmed = mCallbacksRecorder.mTrimmed;
1465         final ArrayList<Task> removed = mCallbacksRecorder.mRemoved;
1466         assertWithMessage("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size())
1467                 .that(trimmed).hasSize(tasks.length);
1468         assertWithMessage("Expected " + tasks.length + " removed tasks, got " + removed.size())
1469                 .that(removed).hasSize(tasks.length);
1470         for (Task task : tasks) {
1471             assertWithMessage("Expected trimmed task: " + task).that(trimmed).contains(task);
1472             assertWithMessage("Expected removed task: " + task).that(removed).contains(task);
1473         }
1474     }
1475 
assertSecurityException(boolean expectCallable, Runnable runnable)1476     private void assertSecurityException(boolean expectCallable, Runnable runnable) {
1477         boolean noSecurityException = true;
1478         try {
1479             runnable.run();
1480         } catch (SecurityException se) {
1481             noSecurityException = false;
1482         } catch (Exception e) {
1483             // We only care about SecurityExceptions, fall through here.
1484         }
1485         if (noSecurityException != expectCallable) {
1486             fail("Expected callable: " + expectCallable + " but got no security exception: "
1487                     + noSecurityException);
1488         }
1489     }
1490 
1491     private static class CallbacksRecorder implements Callbacks {
1492         public final ArrayList<Task> mAdded = new ArrayList<>();
1493         public final ArrayList<Task> mTrimmed = new ArrayList<>();
1494         public final ArrayList<Task> mRemoved = new ArrayList<>();
1495 
clear()1496         void clear() {
1497             mAdded.clear();
1498             mTrimmed.clear();
1499             mRemoved.clear();
1500         }
1501 
1502         @Override
onRecentTaskAdded(Task task)1503         public void onRecentTaskAdded(Task task) {
1504             mAdded.add(task);
1505         }
1506 
1507         @Override
onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess)1508         public void onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) {
1509             if (wasTrimmed) {
1510                 mTrimmed.add(task);
1511             }
1512             mRemoved.add(task);
1513         }
1514     }
1515 
1516     private static class TestTaskPersister extends TaskPersister {
1517         public SparseBooleanArray mUserTaskIdsOverride;
1518         public ArrayList<Task> mUserTasksOverride;
1519 
TestTaskPersister(File workingDir)1520         TestTaskPersister(File workingDir) {
1521             super(workingDir);
1522         }
1523 
1524         @Override
loadPersistedTaskIdsForUser(int userId)1525         SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
1526             if (mUserTaskIdsOverride != null) {
1527                 return mUserTaskIdsOverride;
1528             }
1529             return super.loadPersistedTaskIdsForUser(userId);
1530         }
1531 
1532         @Override
restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks)1533         List<Task> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) {
1534             if (mUserTasksOverride != null) {
1535                 return mUserTasksOverride;
1536             }
1537             return super.restoreTasksForUserLocked(userId, preaddedTasks);
1538         }
1539     }
1540 
1541     private static class TestRecentTasks extends RecentTasks {
1542         private boolean mIsTrimmableOverride;
1543 
1544         public boolean mLastAllowed;
1545 
TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister)1546         TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
1547             super(service, taskPersister);
1548         }
1549 
1550         @Override
getProfileIds(int userId)1551         Set<Integer> getProfileIds(int userId) {
1552             Set<Integer> profileIds = new HashSet<>();
1553             profileIds.add(TEST_USER_0_ID);
1554             profileIds.add(TEST_QUIET_USER_ID);
1555             return profileIds;
1556         }
1557 
1558         @Override
getUserInfo(int userId)1559         UserInfo getUserInfo(int userId) {
1560             switch (userId) {
1561                 case TEST_USER_0_ID:
1562                 case TEST_USER_1_ID:
1563                     return DEFAULT_USER_INFO;
1564                 case TEST_QUIET_USER_ID:
1565                     return QUIET_PROFILE_USER_INFO;
1566             }
1567             return null;
1568         }
1569 
1570         @Override
getCurrentProfileIds()1571         int[] getCurrentProfileIds() {
1572             return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
1573         }
1574 
1575         /**
1576          * To simplify the setup for some tests, the caller can request that we only rely on the
1577          * visible range test to determine what is trimmable. In this case, we don't try to
1578          * use the stack order to determine additionally if the task is trimmable when it is not
1579          * in the visible range.
1580          */
setOnlyTestVisibleRange()1581         void setOnlyTestVisibleRange() {
1582             mIsTrimmableOverride = true;
1583         }
1584 
1585         @Override
getRecentTasks(int maxNum, int flags, boolean getTasksAllowed, int userId, int callingUid)1586         ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags,
1587                 boolean getTasksAllowed, int userId, int callingUid) {
1588             mLastAllowed = getTasksAllowed;
1589             return super.getRecentTasks(maxNum, flags, getTasksAllowed, userId, callingUid);
1590         }
1591 
1592         @Override
isTrimmable(Task task)1593         protected boolean isTrimmable(Task task) {
1594             return mIsTrimmableOverride || super.isTrimmable(task);
1595         }
1596     }
1597 
1598     private static class TestRunningTasks extends RunningTasks {
1599         public boolean mLastAllowed;
1600 
1601         @Override
getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks, WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds)1602         void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
1603                 WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
1604             mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
1605             super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
1606         }
1607     }
1608 }
1609