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