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