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