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