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