1 /*
2  * Copyright (C) 2014 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_IGNORE_UNAVAILABLE;
20 import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
21 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
25 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
26 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
28 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
29 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
30 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
31 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
32 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
33 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
34 import static android.os.Process.SYSTEM_UID;
35 import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
36 import static android.view.WindowInsets.Type.mandatorySystemGestures;
37 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
38 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
39 
40 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
41 import static com.android.server.wm.ActivityRecord.State.RESUMED;
42 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
43 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
44 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
45 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
46 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
47 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
48 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
49 
50 import android.annotation.Nullable;
51 import android.app.ActivityManager;
52 import android.app.ActivityTaskManager;
53 import android.app.AppGlobals;
54 import android.content.ComponentName;
55 import android.content.Intent;
56 import android.content.pm.ActivityInfo;
57 import android.content.pm.ApplicationInfo;
58 import android.content.pm.IPackageManager;
59 import android.content.pm.PackageManager;
60 import android.content.pm.ParceledListSlice;
61 import android.content.pm.UserInfo;
62 import android.content.res.Resources;
63 import android.graphics.Bitmap;
64 import android.graphics.Insets;
65 import android.graphics.Rect;
66 import android.os.Environment;
67 import android.os.IBinder;
68 import android.os.RemoteException;
69 import android.os.SystemClock;
70 import android.os.UserHandle;
71 import android.text.TextUtils;
72 import android.util.ArraySet;
73 import android.util.IntArray;
74 import android.util.Slog;
75 import android.util.SparseArray;
76 import android.util.SparseBooleanArray;
77 import android.view.InsetsState;
78 import android.view.MotionEvent;
79 import android.view.WindowInsets;
80 import android.view.WindowManagerPolicyConstants.PointerEventListener;
81 
82 import com.android.internal.annotations.VisibleForTesting;
83 import com.android.internal.protolog.common.ProtoLog;
84 import com.android.internal.util.function.pooled.PooledLambda;
85 import com.android.server.am.ActivityManagerService;
86 
87 import com.google.android.collect.Sets;
88 
89 import java.io.File;
90 import java.io.PrintWriter;
91 import java.util.ArrayList;
92 import java.util.Arrays;
93 import java.util.Collections;
94 import java.util.Comparator;
95 import java.util.HashMap;
96 import java.util.Set;
97 import java.util.concurrent.TimeUnit;
98 import java.util.concurrent.atomic.AtomicBoolean;
99 
100 /**
101  * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
102  * least recent.
103  *
104  * The trimming logic can be boiled down to the following.  For recent task list with a number of
105  * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
106  * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
107  * sub-range are presented to the user, based on the device type, last task active time, or other
108  * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
109  * (considering the back stack) are considered trimmable. If the device does not support recent
110  * tasks, then trimming is completely disabled.
111  *
112  * eg.
113  * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
114  *     [VVV  VV   VVVV  V V V     ] // Visible tasks
115  *     [RRR  RR   XXXX  X X X     ] // Visible range tasks, eg. if the device only shows 5 tasks,
116  *                                  // 'X' tasks are trimmed.
117  */
118 class RecentTasks {
119     private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
120     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
121     private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
122 
123     private static final int DEFAULT_INITIAL_CAPACITY = 5;
124 
125     // The duration of time after freezing the recent tasks list where getRecentTasks() will return
126     // a stable ordering of the tasks. Upon the next call to getRecentTasks() beyond this duration,
127     // the task list will be unfrozen and committed (the current top task will be moved to the
128     // front of the list)
129     private static final long FREEZE_TASK_LIST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
130 
131     // Comparator to sort by taskId
132     private static final Comparator<Task> TASK_ID_COMPARATOR =
133             (lhs, rhs) -> rhs.mTaskId - lhs.mTaskId;
134 
135     // Placeholder variables to keep track of activities/apps that are no longer avialble while
136     // iterating through the recents list
137     private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
138     private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
139     private TaskChangeNotificationController mTaskNotificationController;
140 
141     /**
142      * Callbacks made when manipulating the list.
143      */
144     interface Callbacks {
145         /**
146          * Called when a task is added to the recent tasks list.
147          */
onRecentTaskAdded(Task task)148         void onRecentTaskAdded(Task task);
149 
150         /**
151          * Called when a task is removed from the recent tasks list.
152          */
onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess)153         void onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess);
154     }
155 
156     /**
157      * Save recent tasks information across reboots.
158      */
159     private final TaskPersister mTaskPersister;
160     private final ActivityTaskManagerService mService;
161     private final ActivityTaskSupervisor mSupervisor;
162 
163     /**
164      * Keeps track of the static recents package/component which is granted additional permissions
165      * to call recents-related APIs.
166      */
167     private int mRecentsUid = -1;
168     private ComponentName mRecentsComponent = null;
169     @Nullable
170     private String mFeatureId;
171 
172     /**
173      * Mapping of user id -> whether recent tasks have been loaded for that user.
174      * The AtomicBoolean per user will be locked when reading persisted task from storage.
175      */
176     private final SparseArray<AtomicBoolean> mUsersWithRecentsLoaded = new SparseArray<>(
177             DEFAULT_INITIAL_CAPACITY);
178 
179     /**
180      * Stores for each user task ids that are taken by tasks residing in persistent storage. These
181      * tasks may or may not currently be in memory.
182      */
183     private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
184             DEFAULT_INITIAL_CAPACITY);
185 
186     // List of all active recent tasks
187     private final ArrayList<Task> mTasks = new ArrayList<>();
188     private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
189 
190     /** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */
191     private final ArrayList<Task> mHiddenTasks = new ArrayList<>();
192     /** The maximum size that the hidden tasks are cached. */
193     private static final int MAX_HIDDEN_TASK_SIZE = 10;
194 
195     /** Whether to trim inactive tasks when activities are idle. */
196     private boolean mCheckTrimmableTasksOnIdle;
197 
198     // These values are generally loaded from resources, but can be set dynamically in the tests
199     private boolean mHasVisibleRecentTasks;
200     private int mGlobalMaxNumTasks;
201     private int mMinNumVisibleTasks;
202     private int mMaxNumVisibleTasks;
203     private long mActiveTasksSessionDurationMs;
204 
205     // When set, the task list will not be reordered as tasks within the list are moved to the
206     // front. Newly created tasks, or tasks that are removed from the list will continue to change
207     // the list.  This does not affect affiliated tasks.
208     private boolean mFreezeTaskListReordering;
209     private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS;
210 
211     // Mainly to avoid object recreation on multiple calls.
212     private final ArrayList<Task> mTmpRecents = new ArrayList<>();
213     private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
214     private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
215     private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
216     private final Rect mTmpRect = new Rect();
217 
218     // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app
219     private final PointerEventListener mListener = new PointerEventListener() {
220         @Override
221         public void onPointerEvent(MotionEvent ev) {
222             if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN
223                     || ev.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE) {
224                 // Skip if we aren't freezing or starting a gesture
225                 return;
226             }
227             int displayId = ev.getDisplayId();
228             int x = (int) ev.getX();
229             int y = (int) ev.getY();
230             mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> {
231                 synchronized (mService.mGlobalLock) {
232                     final RootWindowContainer rac = mService.mRootWindowContainer;
233                     final DisplayContent dc = rac.getDisplayContent(displayId).mDisplayContent;
234                     final WindowState win = dc.getTouchableWinAtPointLocked((float) x, (float) y);
235                     if (win == null) {
236                         return;
237                     }
238 
239                     // Verify the touch is within the mandatory system gesture inset bounds of the
240                     // window, use the raw insets state to ignore window z-order
241                     final InsetsState insetsState = dc.getInsetsStateController()
242                             .getRawInsetsState();
243                     mTmpRect.set(win.getFrame());
244                     mTmpRect.inset(insetsState.calculateInsets(win.getFrame(),
245                             mandatorySystemGestures(), false /* ignoreVisibility */));
246                     if (!mTmpRect.contains(x, y)) {
247                         return;
248                     }
249 
250                     // Unfreeze the task list once we touch down in a task
251                     final boolean isAppWindowTouch = FIRST_APPLICATION_WINDOW <= win.mAttrs.type
252                             && win.mAttrs.type <= LAST_APPLICATION_WINDOW;
253                     if (isAppWindowTouch) {
254                         final Task stack = mService.getTopDisplayFocusedRootTask();
255                         final Task topTask = stack != null ? stack.getTopMostTask() : null;
256                         ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list"
257                                 + " reason=app touch win=%s x=%d y=%d insetFrame=%s", win, x, y,
258                                 mTmpRect);
259                         resetFreezeTaskListReordering(topTask);
260                     }
261                 }
262             }, null).recycleOnUse());
263         }
264     };
265 
266     private final Runnable mResetFreezeTaskListOnTimeoutRunnable =
267             this::resetFreezeTaskListReorderingOnTimeout;
268 
269     @VisibleForTesting
RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister)270     RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
271         mService = service;
272         mSupervisor = mService.mTaskSupervisor;
273         mTaskPersister = taskPersister;
274         mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
275         mHasVisibleRecentTasks = true;
276         mTaskNotificationController = service.getTaskChangeNotificationController();
277     }
278 
RecentTasks(ActivityTaskManagerService service, ActivityTaskSupervisor taskSupervisor)279     RecentTasks(ActivityTaskManagerService service, ActivityTaskSupervisor taskSupervisor) {
280         final File systemDir = Environment.getDataSystemDirectory();
281         final Resources res = service.mContext.getResources();
282         mService = service;
283         mSupervisor = mService.mTaskSupervisor;
284         mTaskPersister = new TaskPersister(systemDir, taskSupervisor, service, this,
285                 taskSupervisor.mPersisterQueue);
286         mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
287         mTaskNotificationController = service.getTaskChangeNotificationController();
288         mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
289         loadParametersFromResources(res);
290     }
291 
292     @VisibleForTesting
setParameters(int minNumVisibleTasks, int maxNumVisibleTasks, long activeSessionDurationMs)293     void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
294             long activeSessionDurationMs) {
295         mMinNumVisibleTasks = minNumVisibleTasks;
296         mMaxNumVisibleTasks = maxNumVisibleTasks;
297         mActiveTasksSessionDurationMs = activeSessionDurationMs;
298     }
299 
300     @VisibleForTesting
setGlobalMaxNumTasks(int globalMaxNumTasks)301     void setGlobalMaxNumTasks(int globalMaxNumTasks) {
302         mGlobalMaxNumTasks = globalMaxNumTasks;
303     }
304 
305     @VisibleForTesting
setFreezeTaskListTimeout(long timeoutMs)306     void setFreezeTaskListTimeout(long timeoutMs) {
307         mFreezeTaskListTimeoutMs = timeoutMs;
308     }
309 
getInputListener()310     PointerEventListener getInputListener() {
311         return mListener;
312     }
313 
314     /**
315      * Freezes the current recent task list order until either a user interaction with the current
316      * app, or a timeout occurs.
317      */
setFreezeTaskListReordering()318     void setFreezeTaskListReordering() {
319         // Only fire the callback once per quickswitch session, not on every individual switch
320         if (!mFreezeTaskListReordering) {
321             mTaskNotificationController.notifyTaskListFrozen(true);
322             mFreezeTaskListReordering = true;
323         }
324 
325         ProtoLog.i(WM_DEBUG_TASKS, "Setting frozen recents task list");
326 
327         // Always update the reordering time when this is called to ensure that the timeout
328         // is reset
329         mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
330         mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
331     }
332 
333     /**
334      * Commits the frozen recent task list order, moving the provided {@param topTask} to the
335      * front of the list.
336      */
resetFreezeTaskListReordering(Task topTask)337     void resetFreezeTaskListReordering(Task topTask) {
338         if (!mFreezeTaskListReordering) {
339             return;
340         }
341 
342         // Once we end freezing the task list, reset the existing task order to the stable state
343         mFreezeTaskListReordering = false;
344         mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
345 
346         // If the top task is provided, then restore the top task to the front of the list
347         if (topTask != null) {
348             mTasks.remove(topTask);
349             mTasks.add(0, topTask);
350         }
351 
352         // Resume trimming tasks
353         trimInactiveRecentTasks();
354 
355         mTaskNotificationController.notifyTaskStackChanged();
356         mTaskNotificationController.notifyTaskListFrozen(false);
357     }
358 
359     /**
360      * Resets the frozen recent task list order if the timeout has passed. This should be called
361      * before we need to iterate the task list in order (either for purposes of returning the list
362      * to SystemUI or if we need to trim tasks in order)
363      */
364     @VisibleForTesting
resetFreezeTaskListReorderingOnTimeout()365     void resetFreezeTaskListReorderingOnTimeout() {
366         synchronized (mService.mGlobalLock) {
367             final Task focusedStack = mService.getTopDisplayFocusedRootTask();
368             final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null;
369             final Task reorderToEndTask = topTask != null && topTask.hasChild() ? topTask : null;
370             ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list reason=timeout");
371             resetFreezeTaskListReordering(reorderToEndTask);
372         }
373     }
374 
375     @VisibleForTesting
isFreezeTaskListReorderingSet()376     boolean isFreezeTaskListReorderingSet() {
377         return mFreezeTaskListReordering;
378     }
379 
380     /**
381      * Loads the parameters from the system resources.
382      */
383     @VisibleForTesting
loadParametersFromResources(Resources res)384     void loadParametersFromResources(Resources res) {
385         if (ActivityManager.isLowRamDeviceStatic()) {
386             mMinNumVisibleTasks = res.getInteger(
387                     com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
388             mMaxNumVisibleTasks = res.getInteger(
389                     com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
390         } else {
391             mMinNumVisibleTasks = res.getInteger(
392                     com.android.internal.R.integer.config_minNumVisibleRecentTasks);
393             mMaxNumVisibleTasks = res.getInteger(
394                     com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
395         }
396         final int sessionDurationHrs = res.getInteger(
397                 com.android.internal.R.integer.config_activeTaskDurationHours);
398         mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
399                 ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
400                 : -1;
401     }
402 
403     /**
404      * Loads the static recents component.  This is called after the system is ready, but before
405      * any dependent services (like SystemUI) is started.
406      */
loadRecentsComponent(Resources res)407     void loadRecentsComponent(Resources res) {
408         final String rawRecentsComponent = res.getString(
409                 com.android.internal.R.string.config_recentsComponentName);
410         if (TextUtils.isEmpty(rawRecentsComponent)) {
411             return;
412         }
413 
414         final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
415         if (cn != null) {
416             try {
417                 final ApplicationInfo appInfo = AppGlobals.getPackageManager().getApplicationInfo(
418                         cn.getPackageName(),
419                         PackageManager.MATCH_UNINSTALLED_PACKAGES
420                                 | PackageManager.MATCH_DISABLED_COMPONENTS,
421                         mService.mContext.getUserId());
422                 if (appInfo != null) {
423                     mRecentsUid = appInfo.uid;
424                     mRecentsComponent = cn;
425                 }
426             } catch (RemoteException e) {
427                 Slog.w(TAG, "Could not load application info for recents component: " + cn);
428             }
429         }
430     }
431 
432     /**
433      * @return whether the current caller has the same uid as the recents component.
434      */
isCallerRecents(int callingUid)435     boolean isCallerRecents(int callingUid) {
436         return UserHandle.isSameApp(callingUid, mRecentsUid);
437     }
438 
439     /**
440      * @return whether the given component is the recents component and shares the same uid as the
441      * recents component.
442      */
isRecentsComponent(ComponentName cn, int uid)443     boolean isRecentsComponent(ComponentName cn, int uid) {
444         return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
445     }
446 
447     /**
448      * @return whether the home app is also the active handler of recent tasks.
449      */
isRecentsComponentHomeActivity(int userId)450     boolean isRecentsComponentHomeActivity(int userId) {
451         final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
452                 .getDefaultHomeActivity(userId);
453         return defaultHomeActivity != null && mRecentsComponent != null &&
454                 defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
455     }
456 
457     /**
458      * @return the recents component.
459      */
getRecentsComponent()460     ComponentName getRecentsComponent() {
461         return mRecentsComponent;
462     }
463 
464     /**
465      * @return the featureId for the recents component.
466      */
467     @Nullable
getRecentsComponentFeatureId()468     String getRecentsComponentFeatureId() {
469         return mFeatureId;
470     }
471 
472     /**
473      * @return the uid for the recents component.
474      */
getRecentsComponentUid()475     int getRecentsComponentUid() {
476         return mRecentsUid;
477     }
478 
registerCallback(Callbacks callback)479     void registerCallback(Callbacks callback) {
480         mCallbacks.add(callback);
481     }
482 
unregisterCallback(Callbacks callback)483     void unregisterCallback(Callbacks callback) {
484         mCallbacks.remove(callback);
485     }
486 
notifyTaskAdded(Task task)487     private void notifyTaskAdded(Task task) {
488         for (int i = 0; i < mCallbacks.size(); i++) {
489             mCallbacks.get(i).onRecentTaskAdded(task);
490         }
491         mTaskNotificationController.notifyTaskListUpdated();
492     }
493 
notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess)494     private void notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) {
495         for (int i = 0; i < mCallbacks.size(); i++) {
496             mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
497         }
498         mTaskNotificationController.notifyTaskListUpdated();
499     }
500 
501     /**
502      * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
503      * Does nothing if they are already loaded. This may perform IO operation, so the caller should
504      * not hold a lock.
505      */
loadRecentTasksIfNeeded(int userId)506     void loadRecentTasksIfNeeded(int userId) {
507         AtomicBoolean userLoaded;
508         synchronized (mService.mGlobalLock) {
509             userLoaded = mUsersWithRecentsLoaded.get(userId);
510             if (userLoaded == null) {
511                 mUsersWithRecentsLoaded.append(userId, userLoaded = new AtomicBoolean());
512             }
513         }
514         synchronized (userLoaded) {
515             if (userLoaded.get()) {
516                 // The recent tasks of the user are already loaded.
517                 return;
518             }
519             // Read task files from storage.
520             final SparseBooleanArray persistedTaskIds =
521                     mTaskPersister.readPersistedTaskIdsFromFileForUser(userId);
522             final TaskPersister.RecentTaskFiles taskFiles = TaskPersister.loadTasksForUser(userId);
523             synchronized (mService.mGlobalLock) {
524                 restoreRecentTasksLocked(userId, persistedTaskIds, taskFiles);
525             }
526             userLoaded.set(true);
527         }
528     }
529 
530     /** Restores recent tasks from raw data (the files are already read into memory). */
restoreRecentTasksLocked(int userId, SparseBooleanArray persistedTaskIds, TaskPersister.RecentTaskFiles taskFiles)531     private void restoreRecentTasksLocked(int userId, SparseBooleanArray persistedTaskIds,
532             TaskPersister.RecentTaskFiles taskFiles) {
533         mTaskPersister.setPersistedTaskIds(userId, persistedTaskIds);
534         mPersistedTaskIds.put(userId, persistedTaskIds.clone());
535         // Check if any tasks are added before recents is loaded.
536         final IntArray existedTaskIds = new IntArray();
537         for (int i = mTasks.size() - 1; i >= 0; i--) {
538             final Task task = mTasks.get(i);
539             if (task.mUserId == userId && shouldPersistTaskLocked(task)) {
540                 existedTaskIds.add(task.mTaskId);
541             }
542         }
543         Slog.i(TAG, "Restoring recents for user " + userId);
544         final ArrayList<Task> tasks = mTaskPersister.restoreTasksForUserLocked(userId, taskFiles,
545                 existedTaskIds);
546 
547         // Tasks are ordered from most recent to least recent. Update the last active time to be
548         // in sync with task recency when device reboots, so the most recent task has the
549         // highest last active time
550         long currentElapsedTime = SystemClock.elapsedRealtime();
551         for (int i = 0; i < tasks.size(); i++) {
552             Task task = tasks.get(i);
553             task.lastActiveTime = currentElapsedTime - i;
554         }
555 
556         mTasks.addAll(tasks);
557         cleanupLocked(userId);
558 
559         // If we have tasks added before loading recents, we need to update persistent task IDs.
560         if (existedTaskIds.size() > 0) {
561             syncPersistentTaskIdsLocked();
562         }
563     }
564 
isRecentTasksLoaded(int userId)565     private boolean isRecentTasksLoaded(int userId) {
566         final AtomicBoolean userLoaded = mUsersWithRecentsLoaded.get(userId);
567         return userLoaded != null && userLoaded.get();
568     }
569 
570     /**
571      * @return whether the {@param taskId} is currently in use for the given user.
572      */
containsTaskId(int taskId, int userId)573     boolean containsTaskId(int taskId, int userId) {
574         final SparseBooleanArray taskIds = mPersistedTaskIds.get(userId);
575         return taskIds != null && taskIds.get(taskId);
576     }
577 
578     /** Returns all the task ids for the user from {@link #usersWithRecentsLoadedLocked}. */
getTaskIdsForLoadedUser(int loadedUserId)579     SparseBooleanArray getTaskIdsForLoadedUser(int loadedUserId) {
580         final SparseBooleanArray taskIds = mPersistedTaskIds.get(loadedUserId);
581         if (taskIds == null) {
582             Slog.wtf(TAG, "Loaded user without loaded tasks, userId=" + loadedUserId);
583             return new SparseBooleanArray();
584         }
585         return taskIds;
586     }
587 
588     /**
589      * Kicks off the task persister to write any pending tasks to disk.
590      */
notifyTaskPersisterLocked(Task task, boolean flush)591     void notifyTaskPersisterLocked(Task task, boolean flush) {
592         final Task rootTask = task != null ? task.getRootTask() : null;
593         if (rootTask != null && rootTask.isActivityTypeHomeOrRecents()) {
594             // Never persist the home or recents task.
595             return;
596         }
597         syncPersistentTaskIdsLocked();
598         mTaskPersister.wakeup(task, flush);
599     }
600 
syncPersistentTaskIdsLocked()601     private void syncPersistentTaskIdsLocked() {
602         for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
603             int userId = mPersistedTaskIds.keyAt(i);
604             if (isRecentTasksLoaded(userId)) {
605                 // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
606                 // referenced here should not be null.
607                 mPersistedTaskIds.valueAt(i).clear();
608             }
609         }
610         for (int i = mTasks.size() - 1; i >= 0; i--) {
611             final Task task = mTasks.get(i);
612             if (shouldPersistTaskLocked(task)) {
613                 // Set of persisted taskIds for task.userId should not be null here
614                 // TODO Investigate why it can happen. For now initialize with an empty set
615                 if (mPersistedTaskIds.get(task.mUserId) == null) {
616                     Slog.wtf(TAG, "No task ids found for userId " + task.mUserId + ". task=" + task
617                             + " mPersistedTaskIds=" + mPersistedTaskIds);
618                     mPersistedTaskIds.put(task.mUserId, new SparseBooleanArray());
619                 }
620                 mPersistedTaskIds.get(task.mUserId).put(task.mTaskId, true);
621             }
622         }
623     }
624 
shouldPersistTaskLocked(Task task)625     private static boolean shouldPersistTaskLocked(Task task) {
626         final Task rootTask = task.getRootTask();
627         return task.isPersistable && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents());
628     }
629 
onSystemReadyLocked()630     void onSystemReadyLocked() {
631         loadRecentsComponent(mService.mContext.getResources());
632         mTasks.clear();
633     }
634 
getTaskDescriptionIcon(String path)635     Bitmap getTaskDescriptionIcon(String path) {
636         return mTaskPersister.getTaskDescriptionIcon(path);
637     }
638 
saveImage(Bitmap image, String path)639     void saveImage(Bitmap image, String path) {
640         mTaskPersister.saveImage(image, path);
641     }
642 
flush()643     void flush() {
644         synchronized (mService.mGlobalLock) {
645             syncPersistentTaskIdsLocked();
646         }
647         mTaskPersister.flush();
648     }
649 
650     /**
651      * Returns all userIds for which recents from persistent storage are loaded into this list.
652      *
653      * @return an array of userIds.
654      */
usersWithRecentsLoadedLocked()655     int[] usersWithRecentsLoadedLocked() {
656         int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
657         int len = 0;
658         for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
659             int userId = mUsersWithRecentsLoaded.keyAt(i);
660             if (mUsersWithRecentsLoaded.valueAt(i).get()) {
661                 usersWithRecentsLoaded[len++] = userId;
662             }
663         }
664         if (len < usersWithRecentsLoaded.length) {
665             // should never happen.
666             return Arrays.copyOf(usersWithRecentsLoaded, len);
667         }
668         return usersWithRecentsLoaded;
669     }
670 
671     /**
672      * Removes recent tasks and any other state kept in memory for the passed in user. Does not
673      * touch the information present on persistent storage.
674      *
675      * @param userId the id of the user
676      */
unloadUserDataFromMemoryLocked(int userId)677     void unloadUserDataFromMemoryLocked(int userId) {
678         if (isRecentTasksLoaded(userId)) {
679             Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
680             mUsersWithRecentsLoaded.delete(userId);
681             removeTasksForUserLocked(userId);
682         }
683         mPersistedTaskIds.delete(userId);
684         mTaskPersister.unloadUserDataFromMemory(userId);
685     }
686 
687     /** Remove recent tasks for a user. */
removeTasksForUserLocked(int userId)688     private void removeTasksForUserLocked(int userId) {
689         if (userId <= 0) {
690             Slog.i(TAG, "Can't remove recent task on user " + userId);
691             return;
692         }
693 
694         for (int i = mTasks.size() - 1; i >= 0; --i) {
695             Task task = mTasks.get(i);
696             if (task.mUserId == userId) {
697                 ProtoLog.i(WM_DEBUG_TASKS, "remove RecentTask %s when finishing user "
698                         + "%d", task, userId);
699                 remove(task);
700             }
701         }
702     }
703 
onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId)704     void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
705         final Set<String> packageNames = Sets.newHashSet(packages);
706         for (int i = mTasks.size() - 1; i >= 0; --i) {
707             final Task task = mTasks.get(i);
708             if (task.realActivity != null
709                     && packageNames.contains(task.realActivity.getPackageName())
710                     && task.mUserId == userId
711                     && task.realActivitySuspended != suspended) {
712                 task.realActivitySuspended = suspended;
713                 if (suspended) {
714                     mSupervisor.removeTask(task, false, REMOVE_FROM_RECENTS, "suspended-package");
715                 }
716                 notifyTaskPersisterLocked(task, false);
717             }
718         }
719     }
720 
onLockTaskModeStateChanged(int lockTaskModeState, int userId)721     void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
722         if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
723             return;
724         }
725         for (int i = mTasks.size() - 1; i >= 0; --i) {
726             final Task task = mTasks.get(i);
727             if (task.mUserId == userId && !mService.getLockTaskController().isTaskAuthAllowlisted(
728                     task.mLockTaskAuth)) {
729                 remove(task);
730             }
731         }
732     }
733 
removeTasksByPackageName(String packageName, int userId)734     void removeTasksByPackageName(String packageName, int userId) {
735         for (int i = mTasks.size() - 1; i >= 0; --i) {
736             final Task task = mTasks.get(i);
737             if (task.mUserId != userId) continue;
738             if (!task.getBasePackageName().equals(packageName)) continue;
739 
740             mSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-package-task");
741         }
742     }
743 
removeAllVisibleTasks(int userId)744     void removeAllVisibleTasks(int userId) {
745         Set<Integer> profileIds = getProfileIds(userId);
746         for (int i = mTasks.size() - 1; i >= 0; --i) {
747             final Task task = mTasks.get(i);
748             if (!profileIds.contains(task.mUserId)) continue;
749             if (isVisibleRecentTask(task)) {
750                 mTasks.remove(i);
751                 notifyTaskRemoved(task, true /* wasTrimmed */, true /* killProcess */);
752             }
753         }
754     }
755 
cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, int userId)756     void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
757             int userId) {
758         for (int i = mTasks.size() - 1; i >= 0; --i) {
759             final Task task = mTasks.get(i);
760             if (userId != UserHandle.USER_ALL && task.mUserId != userId) {
761                 continue;
762             }
763 
764             ComponentName cn = task.intent != null ? task.intent.getComponent() : null;
765             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
766                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
767             if (sameComponent) {
768                 mSupervisor.removeTask(task, false, REMOVE_FROM_RECENTS, "disabled-package");
769             }
770         }
771     }
772 
773     /**
774      * Update the recent tasks lists: make sure tasks should still be here (their
775      * applications / activities still exist), update their availability, fix-up ordering
776      * of affiliations.
777      */
cleanupLocked(int userId)778     void cleanupLocked(int userId) {
779         int recentsCount = mTasks.size();
780         if (recentsCount == 0) {
781             // Happens when called from the packagemanager broadcast before boot,
782             // or just any empty list.
783             return;
784         }
785 
786         // Clear the temp lists
787         mTmpAvailActCache.clear();
788         mTmpAvailAppCache.clear();
789 
790         final IPackageManager pm = AppGlobals.getPackageManager();
791         for (int i = recentsCount - 1; i >= 0; i--) {
792             final Task task = mTasks.get(i);
793             if (userId != UserHandle.USER_ALL && task.mUserId != userId) {
794                 // Only look at tasks for the user ID of interest.
795                 continue;
796             }
797             if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) {
798                 // This situation is broken, and we should just get rid of it now.
799                 remove(task);
800                 Slog.w(TAG, "Removing auto-remove without activity: " + task);
801                 continue;
802             }
803             // Check whether this activity is currently available.
804             if (task.realActivity != null) {
805                 ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
806                 if (ai == null) {
807                     try {
808                         // At this first cut, we're only interested in
809                         // activities that are fully runnable based on
810                         // current system state.
811                         ai = pm.getActivityInfo(task.realActivity,
812                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING
813                                         | ActivityManagerService.STOCK_PM_FLAGS, userId);
814                     } catch (RemoteException e) {
815                         // Will never happen.
816                         continue;
817                     }
818                     if (ai == null) {
819                         ai = NO_ACTIVITY_INFO_TOKEN;
820                     }
821                     mTmpAvailActCache.put(task.realActivity, ai);
822                 }
823                 if (ai == NO_ACTIVITY_INFO_TOKEN) {
824                     // This could be either because the activity no longer exists, or the
825                     // app is temporarily gone. For the former we want to remove the recents
826                     // entry; for the latter we want to mark it as unavailable.
827                     ApplicationInfo app = mTmpAvailAppCache
828                             .get(task.realActivity.getPackageName());
829                     if (app == null) {
830                         try {
831                             app = pm.getApplicationInfo(task.realActivity.getPackageName(),
832                                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
833                         } catch (RemoteException e) {
834                             // Will never happen.
835                             continue;
836                         }
837                         if (app == null) {
838                             app = NO_APPLICATION_INFO_TOKEN;
839                         }
840                         mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
841                     }
842                     if (app == NO_APPLICATION_INFO_TOKEN
843                             || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
844                         // Doesn't exist any more! Good-bye.
845                         remove(task);
846                         Slog.w(TAG, "Removing no longer valid recent: " + task);
847                         continue;
848                     } else {
849                         // Otherwise just not available for now.
850                         if (DEBUG_RECENTS && task.isAvailable) {
851                             Slog.d(TAG_RECENTS,
852                                     "Making recent unavailable: " + task);
853                         }
854                         task.isAvailable = false;
855                     }
856                 } else {
857                     if (!ai.enabled || !ai.applicationInfo.enabled
858                             || (ai.applicationInfo.flags
859                             & ApplicationInfo.FLAG_INSTALLED) == 0) {
860                         if (DEBUG_RECENTS && task.isAvailable) {
861                             Slog.d(TAG_RECENTS,
862                                     "Making recent unavailable: " + task
863                                             + " (enabled=" + ai.enabled + "/"
864                                             + ai.applicationInfo.enabled
865                                             + " flags="
866                                             + Integer.toHexString(ai.applicationInfo.flags)
867                                             + ")");
868                         }
869                         task.isAvailable = false;
870                     } else {
871                         if (DEBUG_RECENTS && !task.isAvailable) {
872                             Slog.d(TAG_RECENTS,
873                                     "Making recent available: " + task);
874                         }
875                         task.isAvailable = true;
876                     }
877                 }
878             }
879         }
880 
881         // Verify the affiliate chain for each task.
882         int i = 0;
883         recentsCount = mTasks.size();
884         while (i < recentsCount) {
885             i = processNextAffiliateChainLocked(i);
886         }
887         // recent tasks are now in sorted, affiliated order.
888     }
889 
890     /**
891      * @return whether the given {@param task} can be added to the list without causing another
892      * task to be trimmed as a result of that add.
893      */
canAddTaskWithoutTrim(Task task)894     private boolean canAddTaskWithoutTrim(Task task) {
895         return findRemoveIndexForAddTask(task) == -1;
896     }
897 
898     /**
899      * Returns the list of {@link ActivityManager.AppTask}s.
900      */
getAppTasksList(int callingUid, String callingPackage)901     ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
902         final ArrayList<IBinder> list = new ArrayList<>();
903         final int size = mTasks.size();
904         for (int i = 0; i < size; i++) {
905             final Task task = mTasks.get(i);
906             // Skip tasks that do not match the caller.  We don't need to verify
907             // callingPackage, because we are also limiting to callingUid and know
908             // that will limit to the correct security sandbox.
909             if (task.effectiveUid != callingUid) {
910                 continue;
911             }
912             if (!callingPackage.equals(task.getBasePackageName())) {
913                 continue;
914             }
915             AppTaskImpl taskImpl = new AppTaskImpl(mService, task.mTaskId, callingUid);
916             list.add(taskImpl.asBinder());
917         }
918         return list;
919     }
920 
921     @VisibleForTesting
getProfileIds(int userId)922     Set<Integer> getProfileIds(int userId) {
923         Set<Integer> userIds = new ArraySet<>();
924         int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */);
925         for (int i = 0; i < profileIds.length; i++) {
926             userIds.add(Integer.valueOf(profileIds[i]));
927         }
928         return userIds;
929     }
930 
931     @VisibleForTesting
getUserInfo(int userId)932     UserInfo getUserInfo(int userId) {
933         return mService.getUserManager().getUserInfo(userId);
934     }
935 
936     @VisibleForTesting
getCurrentProfileIds()937     int[] getCurrentProfileIds() {
938         return mService.mAmInternal.getCurrentProfileIds();
939     }
940 
941     /**
942      * @return the list of recent tasks for presentation.
943      */
getRecentTasks(int maxNum, int flags, boolean getTasksAllowed, int userId, int callingUid)944     ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
945             boolean getTasksAllowed, int userId, int callingUid) {
946         return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
947                 userId, callingUid));
948     }
949 
950     /**
951      * @return the list of recent tasks for presentation.
952      */
getRecentTasksImpl(int maxNum, int flags, boolean getTasksAllowed, int userId, int callingUid)953     private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
954             boolean getTasksAllowed, int userId, int callingUid) {
955         final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
956         final Set<Integer> includedUsers = getProfileIds(userId);
957         includedUsers.add(Integer.valueOf(userId));
958 
959         final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
960         final int size = mTasks.size();
961         int numVisibleTasks = 0;
962         for (int i = 0; i < size; i++) {
963             final Task task = mTasks.get(i);
964 
965             if (isVisibleRecentTask(task)) {
966                 numVisibleTasks++;
967                 if (isInVisibleRange(task, i, numVisibleTasks, withExcluded)) {
968                     // Fall through
969                 } else {
970                     // Not in visible range
971                     continue;
972                 }
973             } else {
974                 // Not visible
975                 continue;
976             }
977 
978             // Skip remaining tasks once we reach the requested size
979             if (res.size() >= maxNum) {
980                 continue;
981             }
982 
983             // Only add calling user or related users recent tasks
984             if (!includedUsers.contains(Integer.valueOf(task.mUserId))) {
985                 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + task);
986                 continue;
987             }
988 
989             if (task.realActivitySuspended) {
990                 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + task);
991                 continue;
992             }
993 
994             if (!getTasksAllowed) {
995                 // If the caller doesn't have the GET_TASKS permission, then only
996                 // allow them to see a small subset of tasks -- their own and home.
997                 if (!task.isActivityTypeHome() && task.effectiveUid != callingUid) {
998                     if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + task);
999                     continue;
1000                 }
1001             }
1002 
1003             if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) {
1004                 // Don't include auto remove tasks that are finished or finishing.
1005                 if (DEBUG_RECENTS) {
1006                     Slog.d(TAG_RECENTS, "Skipping, auto-remove without activity: " + task);
1007                 }
1008                 continue;
1009             }
1010             if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !task.isAvailable) {
1011                 if (DEBUG_RECENTS) {
1012                     Slog.d(TAG_RECENTS, "Skipping, unavail real act: " + task);
1013                 }
1014                 continue;
1015             }
1016 
1017             if (!task.mUserSetupComplete) {
1018                 // Don't include task launched while user is not done setting-up.
1019 
1020                 // NOTE: not guarding with DEBUG_RECENTS as it's not frequent enough to spam logcat,
1021                 // but is useful when running CTS.
1022                 Slog.d(TAG_RECENTS, "Skipping, user setup not complete: " + task);
1023                 continue;
1024             }
1025 
1026             res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
1027         }
1028         return res;
1029     }
1030 
1031     /**
1032      * @return the list of persistable task ids.
1033      */
getPersistableTaskIds(ArraySet<Integer> persistentTaskIds)1034     void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
1035         final int size = mTasks.size();
1036         for (int i = 0; i < size; i++) {
1037             final Task task = mTasks.get(i);
1038             if (TaskPersister.DEBUG) {
1039                 Slog.d(TAG, "LazyTaskWriter: task=" + task
1040                         + " persistable=" + task.isPersistable);
1041             }
1042             final Task rootTask = task.getRootTask();
1043             if ((task.isPersistable || task.inRecents)
1044                     && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents())) {
1045                 if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
1046                 persistentTaskIds.add(task.mTaskId);
1047             } else {
1048                 if (TaskPersister.DEBUG) {
1049                     Slog.d(TAG, "omitting from persistentTaskIds task="
1050                             + task);
1051                 }
1052             }
1053         }
1054     }
1055 
1056     @VisibleForTesting
getRawTasks()1057     ArrayList<Task> getRawTasks() {
1058         return mTasks;
1059     }
1060 
1061     /**
1062      * @return ids of tasks that are presented in Recents UI.
1063      */
getRecentTaskIds()1064     SparseBooleanArray getRecentTaskIds() {
1065         final SparseBooleanArray res = new SparseBooleanArray();
1066         final int size = mTasks.size();
1067         int numVisibleTasks = 0;
1068         for (int i = 0; i < size; i++) {
1069             final Task task = mTasks.get(i);
1070             if (isVisibleRecentTask(task)) {
1071                 numVisibleTasks++;
1072                 if (isInVisibleRange(task, i, numVisibleTasks, false /* skipExcludedCheck */)) {
1073                     res.put(task.mTaskId, true);
1074                 }
1075             }
1076         }
1077         return res;
1078     }
1079 
1080     /**
1081      * @return the task in the task list with the given {@param id} if one exists.
1082      */
getTask(int id)1083     Task getTask(int id) {
1084         final int recentsCount = mTasks.size();
1085         for (int i = 0; i < recentsCount; i++) {
1086             Task task = mTasks.get(i);
1087             if (task.mTaskId == id) {
1088                 return task;
1089             }
1090         }
1091         return null;
1092     }
1093 
1094     /**
1095      * Add a new task to the recent tasks list.
1096      */
add(Task task)1097     void add(Task task) {
1098         if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
1099 
1100         final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId
1101                 || task.mNextAffiliateTaskId != INVALID_TASK_ID
1102                 || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
1103 
1104         int recentsCount = mTasks.size();
1105         // Quick case: never add voice sessions.
1106         // TODO: VI what about if it's just an activity?
1107         // Probably nothing to do here
1108         if (task.voiceSession != null) {
1109             if (DEBUG_RECENTS) {
1110                 Slog.d(TAG_RECENTS,
1111                         "addRecent: not adding voice interaction " + task);
1112             }
1113             return;
1114         }
1115         // Another quick case: check if the top-most recent task is the same.
1116         if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
1117             if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
1118             return;
1119         }
1120         // Another quick case: check if this is part of a set of affiliated
1121         // tasks that are at the top.
1122         if (isAffiliated && recentsCount > 0 && task.inRecents
1123                 && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
1124             if (DEBUG_RECENTS) {
1125                 Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
1126                         + " at top when adding " + task);
1127             }
1128             return;
1129         }
1130 
1131         boolean needAffiliationFix = false;
1132 
1133         // Slightly less quick case: the task is already in recents, so all we need
1134         // to do is move it.
1135         if (task.inRecents) {
1136             int taskIndex = mTasks.indexOf(task);
1137             if (taskIndex >= 0) {
1138                 if (!isAffiliated) {
1139                     if (!mFreezeTaskListReordering) {
1140                         // Simple case: this is not an affiliated task, so we just move it to the
1141                         // front unless overridden by the provided activity options
1142                         int indexToAdd = findIndexToAdd(task);
1143                         mTasks.remove(taskIndex);
1144                         mTasks.add(indexToAdd, task);
1145                         if (taskIndex != 0) {
1146                             // Only notify when position changes
1147                             mTaskNotificationController.notifyTaskListUpdated();
1148                         }
1149 
1150                         if (DEBUG_RECENTS) {
1151                             Slog.d(TAG_RECENTS, "addRecent: moving " + task + " to index "
1152                                     + indexToAdd + " from " + taskIndex);
1153                         }
1154                     }
1155                     notifyTaskPersisterLocked(task, false);
1156                     return;
1157                 }
1158             } else {
1159                 Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
1160                 needAffiliationFix = true;
1161             }
1162         }
1163 
1164         if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
1165         final int removedIndex = removeForAddTask(task);
1166 
1167         task.inRecents = true;
1168         if (!isAffiliated || needAffiliationFix) {
1169             // If this is a simple non-affiliated task, or we had some failure trying to
1170             // handle it as part of an affilated task, then just place it at the top.
1171             // But if the list is frozen, adding the task to the removed index to keep the order.
1172             int indexToAdd = mFreezeTaskListReordering && removedIndex != -1 ? removedIndex : 0;
1173             mTasks.add(indexToAdd, task);
1174             notifyTaskAdded(task);
1175             if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
1176         } else if (isAffiliated) {
1177             // If this is a new affiliated task, then move all of the affiliated tasks
1178             // to the front and insert this new one.
1179             Task other = task.mNextAffiliate;
1180             if (other == null) {
1181                 other = task.mPrevAffiliate;
1182             }
1183             if (other != null) {
1184                 int otherIndex = mTasks.indexOf(other);
1185                 if (otherIndex >= 0) {
1186                     // Insert new task at appropriate location.
1187                     int taskIndex;
1188                     if (other == task.mNextAffiliate) {
1189                         // We found the index of our next affiliation, which is who is
1190                         // before us in the list, so add after that point.
1191                         taskIndex = otherIndex + 1;
1192                     } else {
1193                         // We found the index of our previous affiliation, which is who is
1194                         // after us in the list, so add at their position.
1195                         taskIndex = otherIndex;
1196                     }
1197                     if (DEBUG_RECENTS) {
1198                         Slog.d(TAG_RECENTS,
1199                                 "addRecent: new affiliated task added at " + taskIndex + ": "
1200                                         + task);
1201                     }
1202                     mTasks.add(taskIndex, task);
1203                     notifyTaskAdded(task);
1204 
1205                     // Now move everything to the front.
1206                     if (moveAffiliatedTasksToFront(task, taskIndex)) {
1207                         // All went well.
1208                         return;
1209                     }
1210 
1211                     // Uh oh...  something bad in the affiliation chain, try to rebuild
1212                     // everything and then go through our general path of adding a new task.
1213                     needAffiliationFix = true;
1214                 } else {
1215                     if (DEBUG_RECENTS) {
1216                         Slog.d(TAG_RECENTS,
1217                                 "addRecent: couldn't find other affiliation " + other);
1218                     }
1219                     needAffiliationFix = true;
1220                 }
1221             } else {
1222                 if (DEBUG_RECENTS) {
1223                     Slog.d(TAG_RECENTS,
1224                             "addRecent: adding affiliated task without next/prev:" + task);
1225                 }
1226                 needAffiliationFix = true;
1227             }
1228         }
1229 
1230         if (needAffiliationFix) {
1231             if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
1232             cleanupLocked(task.mUserId);
1233         }
1234 
1235         mCheckTrimmableTasksOnIdle = true;
1236         notifyTaskPersisterLocked(task, false /* flush */);
1237     }
1238 
1239     // Looks for a new index to move the recent Task. Note that the recent Task should not be
1240     // placed higher than another recent Task that has higher hierarchical z-ordering.
findIndexToAdd(Task task)1241     private int findIndexToAdd(Task task) {
1242         int indexToAdd = 0;
1243         for (int i = 0; i < mTasks.size(); i++) {
1244             final Task otherTask = mTasks.get(i);
1245             if (task == otherTask) {
1246                 break;
1247             }
1248 
1249             if (!otherTask.isAttached()) {
1250                 // Stop searching if not attached.
1251                 break;
1252             }
1253 
1254             if (otherTask.inPinnedWindowingMode()) {
1255                 // Skip pip task without increasing index since pip is always on screen.
1256                 continue;
1257             }
1258 
1259             if (otherTask.topRunningActivity() == null) {
1260                 // Skip if there's no running activity in the Task.
1261                 continue;
1262             }
1263 
1264             // Stop searching if the task has higher z-ordering, or increase the index and
1265             // continue the search.
1266             if (task.compareTo(otherTask) > 0) {
1267                 break;
1268             }
1269 
1270             indexToAdd = i + 1;
1271         }
1272         return indexToAdd;
1273     }
1274 
1275     /**
1276      * Add the task to the bottom if possible.
1277      */
addToBottom(Task task)1278     boolean addToBottom(Task task) {
1279         if (!canAddTaskWithoutTrim(task)) {
1280             // Adding this task would cause the task to be removed (since it's appended at
1281             // the bottom and would be trimmed) so just return now
1282             return false;
1283         }
1284 
1285         add(task);
1286         return true;
1287     }
1288 
1289     /**
1290      * Remove a task from the recent tasks list.
1291      */
remove(Task task)1292     void remove(Task task) {
1293         mTasks.remove(task);
1294         notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
1295     }
1296 
1297     /**
1298      * Called when an activity reports idle. The caller should not be in any loop that iterates
1299      * window hierarchy. so it is safe (e.g. index out of bound) to remove inactive tasks.
1300      */
onActivityIdle(ActivityRecord r)1301     void onActivityIdle(ActivityRecord r) {
1302         // Clean up the hidden tasks when going to home because the user may not be unable to return
1303         // to the task from recents.
1304         if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome() && r.isState(RESUMED)) {
1305             removeUnreachableHiddenTasks(r.getWindowingMode());
1306         }
1307         if (mCheckTrimmableTasksOnIdle) {
1308             mCheckTrimmableTasksOnIdle = false;
1309             trimInactiveRecentTasks();
1310         }
1311     }
1312 
1313     /**
1314      * Trims the recents task list to the global max number of recents.
1315      */
trimInactiveRecentTasks()1316     private void trimInactiveRecentTasks() {
1317         if (mFreezeTaskListReordering) {
1318             // Defer trimming inactive recent tasks until we are unfrozen
1319             return;
1320         }
1321 
1322         int recentsCount = mTasks.size();
1323 
1324         // Remove from the end of the list until we reach the max number of recents
1325         while (recentsCount > mGlobalMaxNumTasks) {
1326             final Task task = mTasks.remove(recentsCount - 1);
1327             notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
1328             recentsCount--;
1329             if (DEBUG_RECENTS_TRIM_TASKS) {
1330                 Slog.d(TAG, "Trimming over max-recents task=" + task
1331                         + " max=" + mGlobalMaxNumTasks);
1332             }
1333         }
1334 
1335         // Remove any tasks that belong to currently quiet profiles
1336         final int[] profileUserIds = getCurrentProfileIds();
1337         mTmpQuietProfileUserIds.clear();
1338         for (int userId : profileUserIds) {
1339             final UserInfo userInfo = getUserInfo(userId);
1340             if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
1341                 mTmpQuietProfileUserIds.put(userId, true);
1342             }
1343             if (DEBUG_RECENTS_TRIM_TASKS) {
1344                 Slog.d(TAG, "User: " + userInfo
1345                         + " quiet=" + mTmpQuietProfileUserIds.get(userId));
1346             }
1347         }
1348 
1349         // Remove any inactive tasks, calculate the latest set of visible tasks.
1350         int numVisibleTasks = 0;
1351         for (int i = 0; i < mTasks.size(); ) {
1352             final Task task = mTasks.get(i);
1353 
1354             if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
1355                 if (!mHasVisibleRecentTasks) {
1356                     // Keep all active tasks if visible recent tasks is not supported
1357                     i++;
1358                     continue;
1359                 }
1360 
1361                 if (!isVisibleRecentTask(task)) {
1362                     // Keep all active-but-invisible tasks
1363                     i++;
1364                     continue;
1365                 } else {
1366                     numVisibleTasks++;
1367                     if (isInVisibleRange(task, i, numVisibleTasks, false /* skipExcludedCheck */)
1368                             || !isTrimmable(task)) {
1369                         // Keep visible tasks in range
1370                         i++;
1371                         continue;
1372                     } else {
1373                         // Fall through to trim visible tasks that are no longer in range and
1374                         // trimmable
1375                         if (DEBUG_RECENTS_TRIM_TASKS) {
1376                             Slog.d(TAG,
1377                                     "Trimming out-of-range visible task=" + task);
1378                         }
1379                     }
1380                 }
1381             } else {
1382                 // Fall through to trim inactive tasks
1383                 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
1384             }
1385 
1386             // Task is no longer active, trim it from the list
1387             mTasks.remove(task);
1388             notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
1389             notifyTaskPersisterLocked(task, false /* flush */);
1390         }
1391     }
1392 
1393     /**
1394      * @return whether the given task should be considered active.
1395      */
isActiveRecentTask(Task task, SparseBooleanArray quietProfileUserIds)1396     private boolean isActiveRecentTask(Task task, SparseBooleanArray quietProfileUserIds) {
1397         if (DEBUG_RECENTS_TRIM_TASKS) {
1398             Slog.d(TAG, "isActiveRecentTask: task=" + task
1399                     + " globalMax=" + mGlobalMaxNumTasks);
1400         }
1401 
1402         if (quietProfileUserIds.get(task.mUserId)) {
1403             // Quiet profile user's tasks are never active
1404             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
1405             return false;
1406         }
1407 
1408         if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.mTaskId) {
1409             // Keep the task active if its affiliated task is also active
1410             final Task affiliatedTask = getTask(task.mAffiliatedTaskId);
1411             if (affiliatedTask != null) {
1412                 if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
1413                     if (DEBUG_RECENTS_TRIM_TASKS) {
1414                         Slog.d(TAG,
1415                                 "\taffiliatedWithTask=" + affiliatedTask + " is not active");
1416                     }
1417                     return false;
1418                 }
1419             }
1420         }
1421 
1422         // All other tasks are considered active
1423         return true;
1424     }
1425 
1426     /**
1427      * @return whether the given active task should be presented to the user through SystemUI.
1428      */
1429     @VisibleForTesting
isVisibleRecentTask(Task task)1430     boolean isVisibleRecentTask(Task task) {
1431         if (DEBUG_RECENTS_TRIM_TASKS) {
1432             Slog.d(TAG, "isVisibleRecentTask: task=" + task
1433                     + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
1434                     + " sessionDuration=" + mActiveTasksSessionDurationMs
1435                     + " inactiveDuration=" + task.getInactiveDuration()
1436                     + " activityType=" + task.getActivityType()
1437                     + " windowingMode=" + task.getWindowingMode()
1438                     + " isAlwaysOnTopWhenVisible=" + task.isAlwaysOnTopWhenVisible()
1439                     + " intentFlags=" + task.getBaseIntent().getFlags());
1440         }
1441 
1442         switch (task.getActivityType()) {
1443             case ACTIVITY_TYPE_HOME:
1444             case ACTIVITY_TYPE_RECENTS:
1445             case ACTIVITY_TYPE_DREAM:
1446                 // Ignore certain activity types completely
1447                 return false;
1448             case ACTIVITY_TYPE_ASSISTANT:
1449                 // Ignore assistant that chose to be excluded from Recents, even if it's a top
1450                 // task.
1451                 if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1452                         == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
1453                     return false;
1454                 }
1455                 break;
1456         }
1457 
1458         // Ignore certain windowing modes
1459         switch (task.getWindowingMode()) {
1460             case WINDOWING_MODE_PINNED:
1461                 return false;
1462             case WINDOWING_MODE_MULTI_WINDOW:
1463                 // Ignore tasks that are always on top
1464                 if (task.isAlwaysOnTopWhenVisible()) {
1465                     return false;
1466                 }
1467                 break;
1468         }
1469 
1470         // If we're in lock task mode, ignore the root task
1471         if (task == mService.getLockTaskController().getRootTask()) {
1472             return false;
1473         }
1474 
1475         // Ignore the task if it is started on a display which is not allow to show its tasks on
1476         // Recents.
1477         if (task.getDisplayContent() != null
1478                 && !task.getDisplayContent().canShowTasksInHostDeviceRecents()) {
1479             return false;
1480         }
1481 
1482         return true;
1483     }
1484 
1485     /**
1486      * @return whether the given visible task is within the policy range.
1487      */
isInVisibleRange(Task task, int taskIndex, int numVisibleTasks, boolean skipExcludedCheck)1488     private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks,
1489             boolean skipExcludedCheck) {
1490         if (!skipExcludedCheck) {
1491             // Keep the most recent task of home display even if it is excluded from recents.
1492             final boolean isExcludeFromRecents =
1493                     (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1494                             == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
1495             if (isExcludeFromRecents) {
1496                 if (DEBUG_RECENTS_TRIM_TASKS) {
1497                     Slog.d(TAG,
1498                             "\texcludeFromRecents=true, taskIndex = " + taskIndex
1499                                     + ", isOnHomeDisplay: " + task.isOnHomeDisplay());
1500                 }
1501                 // The Recents is only supported on default display now, we should only keep the
1502                 // most recent task of home display.
1503                 return (task.isOnHomeDisplay() && taskIndex == 0);
1504             }
1505         }
1506 
1507         if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
1508             // Always keep up to the min number of recent tasks, after that fall through to the
1509             // checks below
1510             return true;
1511         }
1512 
1513         // The given task if always treated as in visible range if it is the origin of pinned task.
1514         if (task.mChildPipActivity != null) return true;
1515 
1516         if (mMaxNumVisibleTasks >= 0) {
1517             // Always keep up to the max number of recent tasks, but return false afterwards
1518             return numVisibleTasks <= mMaxNumVisibleTasks;
1519         }
1520 
1521         if (mActiveTasksSessionDurationMs > 0) {
1522             // Keep the task if the inactive time is within the session window, this check must come
1523             // after the checks for the min/max visible task range
1524             if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
1525                 return true;
1526             }
1527         }
1528 
1529         return false;
1530     }
1531 
1532     /** @return whether the given task can be trimmed even if it is outside the visible range. */
isTrimmable(Task task)1533     protected boolean isTrimmable(Task task) {
1534         // The task was detached, just trim it.
1535         if (!task.isAttached()) {
1536             return true;
1537         }
1538 
1539         // Ignore tasks from different displays
1540         // TODO (b/115289124): No Recents on non-default displays.
1541         if (!task.isOnHomeDisplay()) {
1542             return false;
1543         }
1544 
1545         final Task rootHomeTask = task.getDisplayArea().getRootHomeTask();
1546         // Home task does not exist. Don't trim the task.
1547         if (rootHomeTask == null) {
1548             return false;
1549         }
1550         // Trim tasks that are behind the home task.
1551         return task.compareTo(rootHomeTask) < 0;
1552     }
1553 
1554     /** Remove the tasks that user may not be able to return when exceeds the cache limit. */
removeUnreachableHiddenTasks(int windowingMode)1555     private void removeUnreachableHiddenTasks(int windowingMode) {
1556         final int size = mHiddenTasks.size();
1557         if (size <= MAX_HIDDEN_TASK_SIZE) {
1558             return;
1559         }
1560         for (int i = size - 1; i >= MAX_HIDDEN_TASK_SIZE; i--) {
1561             final Task hiddenTask = mHiddenTasks.get(i);
1562             if (!hiddenTask.hasChild() || hiddenTask.inRecents) {
1563                 // The task was removed by other path or it became reachable (added to recents).
1564                 mHiddenTasks.remove(i);
1565                 continue;
1566             }
1567             if (hiddenTask.getWindowingMode() != windowingMode
1568                     || hiddenTask.getTopVisibleActivity() != null) {
1569                 // The task may be reachable from the back stack of other windowing mode or it is
1570                 // currently in use. Keep the task in the hidden list to avoid losing track, e.g.
1571                 // after dismissing primary split screen.
1572                 continue;
1573             }
1574             mHiddenTasks.remove(i);
1575             mSupervisor.removeTask(hiddenTask, false /* killProcess */,
1576                     !REMOVE_FROM_RECENTS, "remove-hidden-task");
1577         }
1578     }
1579 
1580     /**
1581      * If needed, remove oldest existing entries in recents that are for the same kind
1582      * of task as the given one.
1583      */
removeForAddTask(Task task)1584     private int removeForAddTask(Task task) {
1585         // The adding task will be in recents so it is not hidden.
1586         mHiddenTasks.remove(task);
1587 
1588         final int removeIndex = findRemoveIndexForAddTask(task);
1589         if (removeIndex == -1) {
1590             // Nothing to trim
1591             return removeIndex;
1592         }
1593 
1594         // There is a similar task that will be removed for the addition of {@param task}, but it
1595         // can be the same task, and if so, the task will be re-added in add(), so skip the
1596         // callbacks here.
1597         final Task removedTask = mTasks.remove(removeIndex);
1598         if (removedTask != task) {
1599             if (removedTask.hasChild() && !removedTask.isActivityTypeHome()) {
1600                 Slog.i(TAG, "Add " + removedTask + " to hidden list because adding " + task);
1601                 // A non-empty task is replaced by a new task. Because the removed task is no longer
1602                 // managed by the recent tasks list, add it to the hidden list to prevent the task
1603                 // from becoming dangling.
1604                 mHiddenTasks.add(0, removedTask);
1605             }
1606             notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
1607             if (DEBUG_RECENTS_TRIM_TASKS) {
1608                 Slog.d(TAG, "Trimming task=" + removedTask
1609                         + " for addition of task=" + task);
1610             }
1611         }
1612         notifyTaskPersisterLocked(removedTask, false /* flush */);
1613         return removeIndex;
1614     }
1615 
1616     /**
1617      * Find the task that would be removed if the given {@param task} is added to the recent tasks
1618      * list (if any).
1619      */
findRemoveIndexForAddTask(Task task)1620     private int findRemoveIndexForAddTask(Task task) {
1621         final int recentsCount = mTasks.size();
1622         final Intent intent = task.intent;
1623         final boolean document = intent != null && intent.isDocument();
1624         int maxRecents = task.maxRecents - 1;
1625         for (int i = 0; i < recentsCount; i++) {
1626             final Task t = mTasks.get(i);
1627             if (task != t) {
1628                 if (!hasCompatibleActivityTypeAndWindowingMode(task, t)
1629                         || task.mUserId != t.mUserId) {
1630                     continue;
1631                 }
1632                 final Intent trIntent = t.intent;
1633                 final boolean sameAffinity =
1634                         task.affinity != null && task.affinity.equals(t.affinity);
1635                 final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
1636                 boolean multiTasksAllowed = false;
1637                 final int flags = intent != null ? intent.getFlags() : 0;
1638                 if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
1639                         && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
1640                     multiTasksAllowed = true;
1641                 }
1642                 final boolean trIsDocument = trIntent != null && trIntent.isDocument();
1643                 final boolean bothDocuments = document && trIsDocument;
1644                 if (!sameAffinity && !sameIntent && !bothDocuments) {
1645                     continue;
1646                 }
1647 
1648                 if (bothDocuments) {
1649                     // Do these documents belong to the same activity?
1650                     final boolean sameActivity = task.realActivity != null
1651                             && t.realActivity != null
1652                             && task.realActivity.equals(t.realActivity);
1653                     if (!sameActivity) {
1654                         // If the document is open in another app or is not the same document, we
1655                         // don't need to trim it.
1656                         continue;
1657                     } else if (maxRecents > 0) {
1658                         --maxRecents;
1659                         if (!sameIntent || multiTasksAllowed) {
1660                             // We don't want to trim if we are not over the max allowed entries and
1661                             // the tasks are not of the same intent filter, or multiple entries for
1662                             // the task is allowed.
1663                             continue;
1664                         }
1665                     }
1666                     // Hit the maximum number of documents for this task. Fall through
1667                     // and remove this document from recents.
1668                 } else if (document || trIsDocument) {
1669                     // Only one of these is a document. Not the droid we're looking for.
1670                     continue;
1671                 } else if (multiTasksAllowed) {
1672                     // Neither is a document, but the new task supports multiple tasks so keep the
1673                     // existing task
1674                     continue;
1675                 }
1676             }
1677             return i;
1678         }
1679         return -1;
1680     }
1681 
1682     // Extract the affiliates of the chain containing recent at index start.
processNextAffiliateChainLocked(int start)1683     private int processNextAffiliateChainLocked(int start) {
1684         final Task startTask = mTasks.get(start);
1685         final int affiliateId = startTask.mAffiliatedTaskId;
1686 
1687         // Quick identification of isolated tasks. I.e. those not launched behind.
1688         if (startTask.mTaskId == affiliateId && startTask.mPrevAffiliate == null &&
1689                 startTask.mNextAffiliate == null) {
1690             // There is still a slim chance that there are other tasks that point to this task
1691             // and that the chain is so messed up that this task no longer points to them but
1692             // the gain of this optimization outweighs the risk.
1693             startTask.inRecents = true;
1694             return start + 1;
1695         }
1696 
1697         // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
1698         mTmpRecents.clear();
1699         for (int i = mTasks.size() - 1; i >= start; --i) {
1700             final Task task = mTasks.get(i);
1701             if (task.mAffiliatedTaskId == affiliateId) {
1702                 mTasks.remove(i);
1703                 mTmpRecents.add(task);
1704             }
1705         }
1706 
1707         // Sort them all by taskId. That is the order they were create in and that order will
1708         // always be correct.
1709         Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
1710 
1711         // Go through and fix up the linked list.
1712         // The first one is the end of the chain and has no next.
1713         final Task first = mTmpRecents.get(0);
1714         first.inRecents = true;
1715         if (first.mNextAffiliate != null) {
1716             Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
1717             first.setNextAffiliate(null);
1718             notifyTaskPersisterLocked(first, false);
1719         }
1720         // Everything in the middle is doubly linked from next to prev.
1721         final int tmpSize = mTmpRecents.size();
1722         for (int i = 0; i < tmpSize - 1; ++i) {
1723             final Task next = mTmpRecents.get(i);
1724             final Task prev = mTmpRecents.get(i + 1);
1725             if (next.mPrevAffiliate != prev) {
1726                 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
1727                         " setting prev=" + prev);
1728                 next.setPrevAffiliate(prev);
1729                 notifyTaskPersisterLocked(next, false);
1730             }
1731             if (prev.mNextAffiliate != next) {
1732                 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
1733                         " setting next=" + next);
1734                 prev.setNextAffiliate(next);
1735                 notifyTaskPersisterLocked(prev, false);
1736             }
1737             prev.inRecents = true;
1738         }
1739         // The last one is the beginning of the list and has no prev.
1740         final Task last = mTmpRecents.get(tmpSize - 1);
1741         if (last.mPrevAffiliate != null) {
1742             Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
1743             last.setPrevAffiliate(null);
1744             notifyTaskPersisterLocked(last, false);
1745         }
1746 
1747         // Insert the group back into mTmpTasks at start.
1748         mTasks.addAll(start, mTmpRecents);
1749         mTmpRecents.clear();
1750 
1751         // Let the caller know where we left off.
1752         return start + tmpSize;
1753     }
1754 
moveAffiliatedTasksToFront(Task task, int taskIndex)1755     private boolean moveAffiliatedTasksToFront(Task task, int taskIndex) {
1756         int recentsCount = mTasks.size();
1757         Task top = task;
1758         int topIndex = taskIndex;
1759         while (top.mNextAffiliate != null && topIndex > 0) {
1760             top = top.mNextAffiliate;
1761             topIndex--;
1762         }
1763         if (DEBUG_RECENTS) {
1764             Slog.d(TAG_RECENTS, "addRecent: adding affiliates starting at "
1765                     + topIndex + " from initial " + taskIndex);
1766         }
1767         // Find the end of the chain, doing a validity check along the way.
1768         boolean isValid = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
1769         int endIndex = topIndex;
1770         Task prev = top;
1771         while (endIndex < recentsCount) {
1772             Task cur = mTasks.get(endIndex);
1773             if (DEBUG_RECENTS) {
1774                 Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
1775                         + endIndex + " " + cur);
1776             }
1777             if (cur == top) {
1778                 // Verify start of the chain.
1779                 if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
1780                     Slog.wtf(TAG, "Bad chain @" + endIndex
1781                             + ": first task has next affiliate: " + prev);
1782                     isValid = false;
1783                     break;
1784                 }
1785             } else {
1786                 // Verify middle of the chain's next points back to the one before.
1787                 if (cur.mNextAffiliate != prev
1788                         || cur.mNextAffiliateTaskId != prev.mTaskId) {
1789                     Slog.wtf(TAG, "Bad chain @" + endIndex
1790                             + ": middle task " + cur + " @" + endIndex
1791                             + " has bad next affiliate "
1792                             + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
1793                             + ", expected " + prev);
1794                     isValid = false;
1795                     break;
1796                 }
1797             }
1798             if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
1799                 // Chain ends here.
1800                 if (cur.mPrevAffiliate != null) {
1801                     Slog.wtf(TAG, "Bad chain @" + endIndex
1802                             + ": last task " + cur + " has previous affiliate "
1803                             + cur.mPrevAffiliate);
1804                     isValid = false;
1805                 }
1806                 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
1807                 break;
1808             } else {
1809                 // Verify middle of the chain's prev points to a valid item.
1810                 if (cur.mPrevAffiliate == null) {
1811                     Slog.wtf(TAG, "Bad chain @" + endIndex
1812                             + ": task " + cur + " has previous affiliate "
1813                             + cur.mPrevAffiliate + " but should be id "
1814                             + cur.mPrevAffiliate);
1815                     isValid = false;
1816                     break;
1817                 }
1818             }
1819             if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
1820                 Slog.wtf(TAG, "Bad chain @" + endIndex
1821                         + ": task " + cur + " has affiliated id "
1822                         + cur.mAffiliatedTaskId + " but should be "
1823                         + task.mAffiliatedTaskId);
1824                 isValid = false;
1825                 break;
1826             }
1827             prev = cur;
1828             endIndex++;
1829             if (endIndex >= recentsCount) {
1830                 Slog.wtf(TAG, "Bad chain ran off index " + endIndex
1831                         + ": last task " + prev);
1832                 isValid = false;
1833                 break;
1834             }
1835         }
1836         if (isValid) {
1837             if (endIndex < taskIndex) {
1838                 Slog.wtf(TAG, "Bad chain @" + endIndex
1839                         + ": did not extend to task " + task + " @" + taskIndex);
1840                 isValid = false;
1841             }
1842         }
1843         if (isValid) {
1844             // All looks good, we can just move all of the affiliated tasks
1845             // to the top.
1846             for (int i = topIndex; i <= endIndex; i++) {
1847                 if (DEBUG_RECENTS) {
1848                     Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
1849                             + " from " + i + " to " + (i - topIndex));
1850                 }
1851                 Task cur = mTasks.remove(i);
1852                 mTasks.add(i - topIndex, cur);
1853             }
1854             if (DEBUG_RECENTS) {
1855                 Slog.d(TAG_RECENTS, "addRecent: done moving tasks  " + topIndex
1856                         + " to " + endIndex);
1857             }
1858             return true;
1859         }
1860 
1861         // Whoops, couldn't do it.
1862         return false;
1863     }
1864 
dump(PrintWriter pw, boolean dumpAll, String dumpPackage)1865     void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
1866         pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
1867         pw.println("mRecentsUid=" + mRecentsUid);
1868         pw.println("mRecentsComponent=" + mRecentsComponent);
1869         pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering);
1870         pw.println("mFreezeTaskListReorderingPendingTimeout="
1871                 + mService.mH.hasCallbacks(mResetFreezeTaskListOnTimeoutRunnable));
1872         if (!mHiddenTasks.isEmpty()) {
1873             pw.println("mHiddenTasks=" + mHiddenTasks);
1874         }
1875         if (mTasks.isEmpty()) {
1876             return;
1877         }
1878 
1879         // Dump raw recent task list
1880         boolean printedAnything = false;
1881         boolean printedHeader = false;
1882         final int size = mTasks.size();
1883         for (int i = 0; i < size; i++) {
1884             final Task task = mTasks.get(i);
1885             if (dumpPackage != null) {
1886                 boolean match = task.intent != null
1887                         && task.intent.getComponent() != null
1888                         && dumpPackage.equals(
1889                         task.intent.getComponent().getPackageName());
1890                 if (!match) {
1891                     match |= task.affinityIntent != null
1892                             && task.affinityIntent.getComponent() != null
1893                             && dumpPackage.equals(
1894                             task.affinityIntent.getComponent().getPackageName());
1895                 }
1896                 if (!match) {
1897                     match |= task.origActivity != null
1898                             && dumpPackage.equals(task.origActivity.getPackageName());
1899                 }
1900                 if (!match) {
1901                     match |= task.realActivity != null
1902                             && dumpPackage.equals(task.realActivity.getPackageName());
1903                 }
1904                 if (!match) {
1905                     match |= dumpPackage.equals(task.mCallingPackage);
1906                 }
1907                 if (!match) {
1908                     continue;
1909                 }
1910             }
1911 
1912             if (!printedHeader) {
1913                 pw.println("  Recent tasks:");
1914                 printedHeader = true;
1915                 printedAnything = true;
1916             }
1917             pw.print("  * Recent #");
1918             pw.print(i);
1919             pw.print(": ");
1920             pw.println(task);
1921             if (dumpAll) {
1922                 task.dump(pw, "    ");
1923             }
1924         }
1925 
1926         // Dump visible recent task list
1927         if (mHasVisibleRecentTasks) {
1928             // Reset the header flag for the next block
1929             printedHeader = false;
1930             ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE,
1931                     0, true /* getTasksAllowed */, mService.getCurrentUserId(), SYSTEM_UID);
1932             for (int i = 0; i < tasks.size(); i++) {
1933                 final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i);
1934                 if (dumpPackage != null) {
1935                     boolean match = taskInfo.baseIntent != null
1936                             && taskInfo.baseIntent.getComponent() != null
1937                             && dumpPackage.equals(
1938                             taskInfo.baseIntent.getComponent().getPackageName());
1939                     if (!match) {
1940                         match |= taskInfo.baseActivity != null
1941                                 && dumpPackage.equals(taskInfo.baseActivity.getPackageName());
1942                     }
1943                     if (!match) {
1944                         match |= taskInfo.topActivity != null
1945                                 && dumpPackage.equals(taskInfo.topActivity.getPackageName());
1946                     }
1947                     if (!match) {
1948                         match |= taskInfo.origActivity != null
1949                                 && dumpPackage.equals(taskInfo.origActivity.getPackageName());
1950                     }
1951                     if (!match) {
1952                         match |= taskInfo.realActivity != null
1953                                 && dumpPackage.equals(taskInfo.realActivity.getPackageName());
1954                     }
1955                     if (!match) {
1956                         continue;
1957                     }
1958                 }
1959                 if (!printedHeader) {
1960                     if (printedAnything) {
1961                         // Separate from the last block if it printed
1962                         pw.println();
1963                     }
1964                     pw.println("  Visible recent tasks (most recent first):");
1965                     printedHeader = true;
1966                     printedAnything = true;
1967                 }
1968 
1969                 pw.print("  * RecentTaskInfo #");
1970                 pw.print(i);
1971                 pw.print(": ");
1972                 taskInfo.dump(pw, "    ");
1973             }
1974         }
1975 
1976         if (!printedAnything) {
1977             pw.println("  (nothing)");
1978         }
1979     }
1980 
1981     /**
1982      * Creates a new RecentTaskInfo from a Task.
1983      */
createRecentTaskInfo(Task tr, boolean stripExtras, boolean getTasksAllowed)1984     ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
1985             boolean getTasksAllowed) {
1986         final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
1987         // If the recent Task is detached, we consider it will be re-attached to the default
1988         // TaskDisplayArea because we currently only support recent overview in the default TDA.
1989         final TaskDisplayArea tda = tr.isAttached()
1990                 ? tr.getDisplayArea()
1991                 : mService.mRootWindowContainer.getDefaultTaskDisplayArea();
1992         tr.fillTaskInfo(rti, stripExtras, tda);
1993         // Fill in some deprecated values.
1994         rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
1995         rti.persistentId = rti.taskId;
1996         rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
1997         if (!getTasksAllowed) {
1998             Task.trimIneffectiveInfo(tr, rti);
1999         }
2000 
2001         // Fill in organized child task info for the task created by organizer.
2002         if (tr.mCreatedByOrganizer) {
2003             for (int i = tr.getChildCount() - 1; i >= 0; i--) {
2004                 final Task childTask = tr.getChildAt(i).asTask();
2005                 if (childTask != null && childTask.isOrganized()) {
2006                     final ActivityManager.RecentTaskInfo cti = new ActivityManager.RecentTaskInfo();
2007                     childTask.fillTaskInfo(cti, true /* stripExtras */, tda);
2008                     rti.childrenTaskInfos.add(cti);
2009                 }
2010             }
2011         }
2012         return rti;
2013     }
2014 
2015     /**
2016      * @return Whether the activity types and windowing modes of the two tasks are considered
2017      * compatible. This is necessary because we currently don't persist the activity type
2018      * or the windowing mode with the task, so they can be undefined when restored.
2019      */
hasCompatibleActivityTypeAndWindowingMode(Task t1, Task t2)2020     private boolean hasCompatibleActivityTypeAndWindowingMode(Task t1, Task t2) {
2021         final int activityType = t1.getActivityType();
2022         final int windowingMode = t1.getWindowingMode();
2023         final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
2024         final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
2025         final int otherActivityType = t2.getActivityType();
2026         final int otherWindowingMode = t2.getWindowingMode();
2027         final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
2028         final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
2029 
2030         // An activity type and windowing mode is compatible if they are the exact same type/mode,
2031         // or if one of the type/modes is undefined
2032         final boolean isCompatibleType = activityType == otherActivityType
2033                 || isUndefinedType || isOtherUndefinedType;
2034         final boolean isCompatibleMode = windowingMode == otherWindowingMode
2035                 || isUndefinedMode || isOtherUndefinedMode;
2036 
2037         return isCompatibleType && isCompatibleMode;
2038     }
2039 }
2040