blob: d69ae3d5c3c031c2594b73d405d31e523e43c6e0 [file] [log] [blame]
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001/*
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
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080018
Winson Chung1dbc8112017-09-28 18:05:31 -070019import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
Winson Chung1dbc8112017-09-28 18:05:31 -070020import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
Winson Chung1dbc8112017-09-28 18:05:31 -070021import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
Garfield Tan891146c2018-10-09 12:14:00 -070022import static android.app.ActivityTaskManager.INVALID_TASK_ID;
Vadim Trysheveff42d32018-03-05 18:33:48 -080023import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
Winson Chungd6aa3db2017-10-05 17:18:43 -070024import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
25import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
Winson Chung27f81882018-04-19 14:45:03 -070026import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
Winson Chungd6aa3db2017-10-05 17:18:43 -070027import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
28import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
Winson Chung27f81882018-04-19 14:45:03 -070029import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
Winson Chungb4132992018-07-03 15:52:38 -070030import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
Wale Ogunwalee8d5f652016-04-22 16:27:39 -070031import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
32import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
33import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Winson Chungb4132992018-07-03 15:52:38 -070034import static android.os.Process.SYSTEM_UID;
Louis Changbd48dca2018-08-29 17:44:34 +080035import static android.view.Display.DEFAULT_DISPLAY;
36
Garfield Tan891146c2018-10-09 12:14:00 -070037import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
Wale Ogunwale59507092018-10-29 09:00:30 -070038import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
39import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
40import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
41import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
42import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
43import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
44import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
Wale Ogunwalee23149f2015-03-06 15:39:44 -080045
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080046import android.app.ActivityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070047import android.app.ActivityTaskManager;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080048import android.app.AppGlobals;
49import android.content.ComponentName;
50import android.content.Intent;
51import android.content.pm.ActivityInfo;
52import android.content.pm.ApplicationInfo;
53import android.content.pm.IPackageManager;
54import android.content.pm.PackageManager;
Winson Chung1dbc8112017-09-28 18:05:31 -070055import android.content.pm.ParceledListSlice;
Winson Chungd6aa3db2017-10-05 17:18:43 -070056import android.content.pm.UserInfo;
57import android.content.res.Resources;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080058import android.graphics.Bitmap;
Winson Chung1dbc8112017-09-28 18:05:31 -070059import android.os.Bundle;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080060import android.os.Environment;
Winson Chung1dbc8112017-09-28 18:05:31 -070061import android.os.IBinder;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080062import android.os.RemoteException;
Winson Chungc5fe7ff2019-02-19 14:49:25 -080063import android.os.SystemClock;
Winson Chungd6aa3db2017-10-05 17:18:43 -070064import android.os.SystemProperties;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080065import android.os.UserHandle;
Winson Chung3f0e59a2017-10-25 10:19:05 -070066import android.text.TextUtils;
Daichi Hirono4cb941e2017-03-31 14:30:41 +090067import android.util.ArraySet;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080068import android.util.Slog;
Suprabh Shukla4bccb462016-02-10 18:45:12 -080069import android.util.SparseArray;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080070import android.util.SparseBooleanArray;
Winson Chungc5fe7ff2019-02-19 14:49:25 -080071import android.view.MotionEvent;
72import android.view.WindowManagerPolicyConstants.PointerEventListener;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080073
Winson Chung1dbc8112017-09-28 18:05:31 -070074import com.android.internal.annotations.VisibleForTesting;
Winson Chungc5fe7ff2019-02-19 14:49:25 -080075import com.android.internal.util.function.pooled.PooledLambda;
Wale Ogunwale59507092018-10-29 09:00:30 -070076import com.android.server.am.ActivityManagerService;
Winson Chung1dbc8112017-09-28 18:05:31 -070077
Winson Chung3f0e59a2017-10-25 10:19:05 -070078import com.google.android.collect.Sets;
79
Suprabh Shukla09a88f52015-12-02 14:36:31 -080080import java.io.File;
Winson Chung1dbc8112017-09-28 18:05:31 -070081import java.io.PrintWriter;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080082import java.util.ArrayList;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080083import java.util.Arrays;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080084import java.util.Collections;
85import java.util.Comparator;
86import java.util.HashMap;
Wale Ogunwale86b74462018-07-02 08:42:43 -070087import java.util.List;
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +000088import java.util.Set;
Winson Chungd6aa3db2017-10-05 17:18:43 -070089import java.util.concurrent.TimeUnit;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080090
91/**
Winson Chung1dbc8112017-09-28 18:05:31 -070092 * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
93 * least recent.
Winson Chung0ec2a352017-10-26 11:38:30 -070094 *
95 * The trimming logic can be boiled down to the following. For recent task list with a number of
96 * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
97 * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
98 * sub-range are presented to the user, based on the device type, last task active time, or other
99 * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
100 * (considering the back stack) are considered trimmable. If the device does not support recent
101 * tasks, then trimming is completely disabled.
102 *
103 * eg.
104 * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
105 * [VVV VV VVVV V V V ] // Visible tasks
106 * [RRR RR XXXX X X X ] // Visible range tasks, eg. if the device only shows 5 tasks,
107 * // 'X' tasks are trimmed.
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800108 */
Winson Chung1dbc8112017-09-28 18:05:31 -0700109class RecentTasks {
Wale Ogunwale98875612018-10-12 07:53:02 -0700110 private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
Wale Ogunwaleee006da2015-03-30 14:49:25 -0700111 private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
112 private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800113
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800114 private static final int DEFAULT_INITIAL_CAPACITY = 5;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800115
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800116 // The duration of time after freezing the recent tasks list where getRecentTasks() will return
117 // a stable ordering of the tasks. Upon the next call to getRecentTasks() beyond this duration,
118 // the task list will be unfrozen and committed (the current top task will be moved to the
119 // front of the list)
120 private static final long FREEZE_TASK_LIST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
Winson6976f7b2016-05-03 14:58:12 -0700121
Winson Chung1dbc8112017-09-28 18:05:31 -0700122 // Comparator to sort by taskId
123 private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
124 (lhs, rhs) -> rhs.taskId - lhs.taskId;
125
126 // Placeholder variables to keep track of activities/apps that are no longer avialble while
127 // iterating through the recents list
128 private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
129 private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
130
131 /**
132 * Callbacks made when manipulating the list.
133 */
134 interface Callbacks {
135 /**
136 * Called when a task is added to the recent tasks list.
137 */
138 void onRecentTaskAdded(TaskRecord task);
139
140 /**
141 * Called when a task is removed from the recent tasks list.
142 */
Winson Chunge6439102018-07-30 15:48:01 -0700143 void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
Winson Chung1dbc8112017-09-28 18:05:31 -0700144 }
145
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800146 /**
147 * Save recent tasks information across reboots.
148 */
149 private final TaskPersister mTaskPersister;
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700150 private final ActivityTaskManagerService mService;
151 private final ActivityStackSupervisor mSupervisor;
Winson Chung1dbc8112017-09-28 18:05:31 -0700152
153 /**
Winson Chung3f0e59a2017-10-25 10:19:05 -0700154 * Keeps track of the static recents package/component which is granted additional permissions
155 * to call recents-related APIs.
156 */
157 private int mRecentsUid = -1;
158 private ComponentName mRecentsComponent = null;
159
160 /**
Winson Chung1dbc8112017-09-28 18:05:31 -0700161 * Mapping of user id -> whether recent tasks have been loaded for that user.
162 */
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800163 private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
164 DEFAULT_INITIAL_CAPACITY);
165
166 /**
167 * Stores for each user task ids that are taken by tasks residing in persistent storage. These
168 * tasks may or may not currently be in memory.
169 */
Winson Chung1dbc8112017-09-28 18:05:31 -0700170 private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800171 DEFAULT_INITIAL_CAPACITY);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800172
Winson Chungd6aa3db2017-10-05 17:18:43 -0700173 // List of all active recent tasks
Winson Chung1dbc8112017-09-28 18:05:31 -0700174 private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
175 private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
176
Winson Chungd6aa3db2017-10-05 17:18:43 -0700177 // These values are generally loaded from resources, but can be set dynamically in the tests
178 private boolean mHasVisibleRecentTasks;
179 private int mGlobalMaxNumTasks;
180 private int mMinNumVisibleTasks;
181 private int mMaxNumVisibleTasks;
182 private long mActiveTasksSessionDurationMs;
183
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800184 // When set, the task list will not be reordered as tasks within the list are moved to the
185 // front. Newly created tasks, or tasks that are removed from the list will continue to change
186 // the list. This does not affect affiliated tasks.
187 private boolean mFreezeTaskListReordering;
188 private long mFreezeTaskListReorderingTime;
189 private long mFreezeTaskListTimeoutMs = FREEZE_TASK_LIST_TIMEOUT_MS;
190
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800191 // Mainly to avoid object recreation on multiple calls.
Winson Chung1dbc8112017-09-28 18:05:31 -0700192 private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800193 private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
194 private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
Winson Chungd6aa3db2017-10-05 17:18:43 -0700195 private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800196
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800197 // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app
198 private final PointerEventListener mListener = new PointerEventListener() {
199 @Override
200 public void onPointerEvent(MotionEvent ev) {
201 if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) {
202 // Skip if we aren't freezing or starting a gesture
203 return;
204 }
205 int displayId = ev.getDisplayId();
206 int x = (int) ev.getX();
207 int y = (int) ev.getY();
208 mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> {
209 synchronized (mService.mGlobalLock) {
210 // Unfreeze the task list once we touch down in a task
211 final RootActivityContainer rac = mService.mRootActivityContainer;
212 final DisplayContent dc = rac.getActivityDisplay(displayId).mDisplayContent;
213 if (dc.pointWithinAppWindow(x, y)) {
214 final ActivityStack stack = mService.getTopDisplayFocusedStack();
215 final TaskRecord topTask = stack != null ? stack.topTask() : null;
216 resetFreezeTaskListReordering(topTask);
217 }
218 }
219 }, null).recycleOnUse());
220 }
221 };
222
Winson Chung1dbc8112017-09-28 18:05:31 -0700223 @VisibleForTesting
Wale Ogunwale86b74462018-07-02 08:42:43 -0700224 RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700225 mService = service;
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700226 mSupervisor = mService.mStackSupervisor;
Winson Chung1dbc8112017-09-28 18:05:31 -0700227 mTaskPersister = taskPersister;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700228 mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
Winson Chungd6aa3db2017-10-05 17:18:43 -0700229 mHasVisibleRecentTasks = true;
Winson Chung1dbc8112017-09-28 18:05:31 -0700230 }
231
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700232 RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) {
Winson Chungd6aa3db2017-10-05 17:18:43 -0700233 final File systemDir = Environment.getDataSystemDirectory();
234 final Resources res = service.mContext.getResources();
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800235 mService = service;
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700236 mSupervisor = mService.mStackSupervisor;
Garfield Tan891146c2018-10-09 12:14:00 -0700237 mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this,
238 stackSupervisor.mPersisterQueue);
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700239 mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
Winson Chungd6aa3db2017-10-05 17:18:43 -0700240 mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
Winson Chung3f0e59a2017-10-25 10:19:05 -0700241 loadParametersFromResources(res);
Winson Chungd6aa3db2017-10-05 17:18:43 -0700242 }
243
244 @VisibleForTesting
245 void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
246 long activeSessionDurationMs) {
247 mMinNumVisibleTasks = minNumVisibleTasks;
248 mMaxNumVisibleTasks = maxNumVisibleTasks;
249 mActiveTasksSessionDurationMs = activeSessionDurationMs;
250 }
251
252 @VisibleForTesting
253 void setGlobalMaxNumTasks(int globalMaxNumTasks) {
254 mGlobalMaxNumTasks = globalMaxNumTasks;
255 }
256
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800257 @VisibleForTesting
258 void setFreezeTaskListTimeoutParams(long reorderingTime, long timeoutMs) {
259 mFreezeTaskListReorderingTime = reorderingTime;
260 mFreezeTaskListTimeoutMs = timeoutMs;
261 }
262
263 PointerEventListener getInputListener() {
264 return mListener;
265 }
266
267 /**
268 * Freezes the current recent task list order until either a user interaction with the current
269 * app, or a timeout occurs.
270 */
271 void setFreezeTaskListReordering() {
272 // Always update the reordering time when this is called to ensure that the timeout
273 // is reset
274 mFreezeTaskListReordering = true;
275 mFreezeTaskListReorderingTime = SystemClock.elapsedRealtime();
276 }
277
278 /**
279 * Commits the frozen recent task list order, moving the provided {@param topTask} to the
280 * front of the list.
281 */
282 void resetFreezeTaskListReordering(TaskRecord topTask) {
283 if (!mFreezeTaskListReordering) {
284 return;
285 }
286
287 // Once we end freezing the task list, reset the existing task order to the stable state
288 mFreezeTaskListReordering = false;
289
290 // If the top task is provided, then restore the top task to the front of the list
291 if (topTask != null) {
292 mTasks.remove(topTask);
293 mTasks.add(0, topTask);
294 }
295
296 // Resume trimming tasks
297 trimInactiveRecentTasks();
298 }
299
300 /**
301 * Resets the frozen recent task list order if the timeout has passed. This should be called
302 * before we need to iterate the task list in order (either for purposes of returning the list
303 * to SystemUI or if we need to trim tasks in order)
304 */
305 void resetFreezeTaskListReorderingOnTimeout() {
306 // Unfreeze the recent task list if the time heuristic has passed
307 if (mFreezeTaskListReorderingTime
308 > (SystemClock.elapsedRealtime() - mFreezeTaskListTimeoutMs)) {
309 return;
310 }
311
312 final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
313 final TaskRecord topTask = focusedStack != null
314 ? focusedStack.topTask()
315 : null;
316 resetFreezeTaskListReordering(topTask);
317 }
318
319 @VisibleForTesting
320 boolean isFreezeTaskListReorderingSet() {
321 return mFreezeTaskListReordering;
322 }
323
Winson Chungd6aa3db2017-10-05 17:18:43 -0700324 /**
325 * Loads the parameters from the system resources.
326 */
327 @VisibleForTesting
328 void loadParametersFromResources(Resources res) {
329 if (ActivityManager.isLowRamDeviceStatic()) {
330 mMinNumVisibleTasks = res.getInteger(
331 com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
332 mMaxNumVisibleTasks = res.getInteger(
333 com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
334 } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
335 mMinNumVisibleTasks = res.getInteger(
336 com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
337 mMaxNumVisibleTasks = res.getInteger(
338 com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
339 } else {
340 mMinNumVisibleTasks = res.getInteger(
341 com.android.internal.R.integer.config_minNumVisibleRecentTasks);
342 mMaxNumVisibleTasks = res.getInteger(
343 com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
344 }
345 final int sessionDurationHrs = res.getInteger(
346 com.android.internal.R.integer.config_activeTaskDurationHours);
347 mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
348 ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
349 : -1;
Winson Chung1dbc8112017-09-28 18:05:31 -0700350 }
351
Winson Chung3f0e59a2017-10-25 10:19:05 -0700352 /**
353 * Loads the static recents component. This is called after the system is ready, but before
354 * any dependent services (like SystemUI) is started.
355 */
356 void loadRecentsComponent(Resources res) {
357 final String rawRecentsComponent = res.getString(
358 com.android.internal.R.string.config_recentsComponentName);
359 if (TextUtils.isEmpty(rawRecentsComponent)) {
360 return;
361 }
362
363 final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
364 if (cn != null) {
365 try {
366 final ApplicationInfo appInfo = AppGlobals.getPackageManager()
367 .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
368 if (appInfo != null) {
369 mRecentsUid = appInfo.uid;
370 mRecentsComponent = cn;
371 }
372 } catch (RemoteException e) {
373 Slog.w(TAG, "Could not load application info for recents component: " + cn);
374 }
375 }
376 }
377
378 /**
379 * @return whether the current caller has the same uid as the recents component.
380 */
381 boolean isCallerRecents(int callingUid) {
382 return UserHandle.isSameApp(callingUid, mRecentsUid);
383 }
384
385 /**
386 * @return whether the given component is the recents component and shares the same uid as the
387 * recents component.
388 */
389 boolean isRecentsComponent(ComponentName cn, int uid) {
390 return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
391 }
392
Winson Chungec1ef092017-10-25 16:22:34 -0700393 /**
Winson Chungc1674272018-02-21 10:15:17 -0800394 * @return whether the home app is also the active handler of recent tasks.
395 */
396 boolean isRecentsComponentHomeActivity(int userId) {
Wale Ogunwale906f9c62018-07-23 11:23:44 -0700397 final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
Winson Chungc1674272018-02-21 10:15:17 -0800398 .getDefaultHomeActivity(userId);
Alice Sheng1de98fc2018-03-16 09:45:58 -0700399 return defaultHomeActivity != null && mRecentsComponent != null &&
Winson Chungc1674272018-02-21 10:15:17 -0800400 defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
401 }
402
403 /**
Winson Chungec1ef092017-10-25 16:22:34 -0700404 * @return the recents component.
405 */
406 ComponentName getRecentsComponent() {
407 return mRecentsComponent;
408 }
409
410 /**
411 * @return the uid for the recents component.
412 */
413 int getRecentsComponentUid() {
414 return mRecentsUid;
415 }
416
Winson Chung1dbc8112017-09-28 18:05:31 -0700417 void registerCallback(Callbacks callback) {
418 mCallbacks.add(callback);
419 }
420
421 void unregisterCallback(Callbacks callback) {
422 mCallbacks.remove(callback);
423 }
424
425 private void notifyTaskAdded(TaskRecord task) {
426 for (int i = 0; i < mCallbacks.size(); i++) {
427 mCallbacks.get(i).onRecentTaskAdded(task);
428 }
429 }
430
Winson Chunge6439102018-07-30 15:48:01 -0700431 private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700432 for (int i = 0; i < mCallbacks.size(); i++) {
Winson Chunge6439102018-07-30 15:48:01 -0700433 mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
Winson Chung1dbc8112017-09-28 18:05:31 -0700434 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800435 }
436
437 /**
Suprabh Shuklaf5bf0cb2016-01-19 17:42:03 -0800438 * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
439 * Does nothing if they are already loaded.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800440 *
441 * @param userId the user Id
442 */
443 void loadUserRecentsLocked(int userId) {
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900444 if (mUsersWithRecentsLoaded.get(userId)) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700445 // User already loaded, return early
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900446 return;
447 }
448
449 // Load the task ids if not loaded.
450 loadPersistedTaskIdsForUserLocked(userId);
451
452 // Check if any tasks are added before recents is loaded
453 final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
Winson Chung1dbc8112017-09-28 18:05:31 -0700454 for (final TaskRecord task : mTasks) {
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900455 if (task.userId == userId && shouldPersistTaskLocked(task)) {
456 preaddedTasks.put(task.taskId, true);
457 }
458 }
459
460 Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800461 List<TaskRecord> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks);
462 mTasks.addAll(tasks);
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900463 cleanupLocked(userId);
464 mUsersWithRecentsLoaded.put(userId, true);
465
466 // If we have tasks added before loading recents, we need to update persistent task IDs.
467 if (preaddedTasks.size() > 0) {
468 syncPersistentTaskIdsLocked();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800469 }
470 }
471
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800472 private void loadPersistedTaskIdsForUserLocked(int userId) {
473 // An empty instead of a null set here means that no persistent taskIds were present
474 // on file when we loaded them.
475 if (mPersistedTaskIds.get(userId) == null) {
476 mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
Fyodor Kupolovc4b46dd2016-03-17 12:57:36 -0700477 Slog.i(TAG, "Loaded persisted task ids for user " + userId);
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800478 }
479 }
480
Winson Chung1dbc8112017-09-28 18:05:31 -0700481 /**
482 * @return whether the {@param taskId} is currently in use for the given user.
483 */
484 boolean containsTaskId(int taskId, int userId) {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800485 loadPersistedTaskIdsForUserLocked(userId);
486 return mPersistedTaskIds.get(userId).get(taskId);
487 }
488
Winson Chung1dbc8112017-09-28 18:05:31 -0700489 /**
490 * @return all the task ids for the user with the given {@param userId}.
491 */
492 SparseBooleanArray getTaskIdsForUser(int userId) {
493 loadPersistedTaskIdsForUserLocked(userId);
494 return mPersistedTaskIds.get(userId);
495 }
496
497 /**
498 * Kicks off the task persister to write any pending tasks to disk.
499 */
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800500 void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
Andrii Kulian02b7a832016-10-06 23:11:56 -0700501 final ActivityStack stack = task != null ? task.getStack() : null;
Matthew Ngae1ff4f2016-11-10 15:49:14 -0800502 if (stack != null && stack.isHomeOrRecentsStack()) {
503 // Never persist the home or recents stack.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800504 return;
505 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800506 syncPersistentTaskIdsLocked();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800507 mTaskPersister.wakeup(task, flush);
508 }
509
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800510 private void syncPersistentTaskIdsLocked() {
511 for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
512 int userId = mPersistedTaskIds.keyAt(i);
513 if (mUsersWithRecentsLoaded.get(userId)) {
514 // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
515 // referenced here should not be null.
516 mPersistedTaskIds.valueAt(i).clear();
517 }
518 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700519 for (int i = mTasks.size() - 1; i >= 0; i--) {
520 final TaskRecord task = mTasks.get(i);
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900521 if (shouldPersistTaskLocked(task)) {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800522 // Set of persisted taskIds for task.userId should not be null here
Fyodor Kupolovc4b46dd2016-03-17 12:57:36 -0700523 // TODO Investigate why it can happen. For now initialize with an empty set
524 if (mPersistedTaskIds.get(task.userId) == null) {
525 Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
526 + " mPersistedTaskIds=" + mPersistedTaskIds);
527 mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
528 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800529 mPersistedTaskIds.get(task.userId).put(task.taskId, true);
530 }
531 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800532 }
533
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900534 private static boolean shouldPersistTaskLocked(TaskRecord task) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700535 final ActivityStack stack = task.getStack();
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900536 return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
537 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800538
539 void onSystemReadyLocked() {
Winson Chung3f0e59a2017-10-25 10:19:05 -0700540 loadRecentsComponent(mService.mContext.getResources());
Winson Chung1dbc8112017-09-28 18:05:31 -0700541 mTasks.clear();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800542 }
543
544 Bitmap getTaskDescriptionIcon(String path) {
545 return mTaskPersister.getTaskDescriptionIcon(path);
546 }
547
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800548 void saveImage(Bitmap image, String path) {
549 mTaskPersister.saveImage(image, path);
550 }
551
552 void flush() {
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700553 synchronized (mService.mGlobalLock) {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800554 syncPersistentTaskIdsLocked();
555 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800556 mTaskPersister.flush();
557 }
558
559 /**
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800560 * Returns all userIds for which recents from persistent storage are loaded into this list.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800561 *
562 * @return an array of userIds.
563 */
564 int[] usersWithRecentsLoadedLocked() {
565 int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
566 int len = 0;
567 for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
568 int userId = mUsersWithRecentsLoaded.keyAt(i);
569 if (mUsersWithRecentsLoaded.valueAt(i)) {
570 usersWithRecentsLoaded[len++] = userId;
571 }
572 }
573 if (len < usersWithRecentsLoaded.length) {
574 // should never happen.
575 return Arrays.copyOf(usersWithRecentsLoaded, len);
576 }
577 return usersWithRecentsLoaded;
578 }
579
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800580 /**
581 * Removes recent tasks and any other state kept in memory for the passed in user. Does not
582 * touch the information present on persistent storage.
583 *
584 * @param userId the id of the user
585 */
586 void unloadUserDataFromMemoryLocked(int userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700587 if (mUsersWithRecentsLoaded.get(userId)) {
588 Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
589 mUsersWithRecentsLoaded.delete(userId);
590 removeTasksForUserLocked(userId);
591 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800592 mPersistedTaskIds.delete(userId);
593 mTaskPersister.unloadUserDataFromMemory(userId);
594 }
595
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800596 /** Remove recent tasks for a user. */
Winson Chung1dbc8112017-09-28 18:05:31 -0700597 private void removeTasksForUserLocked(int userId) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800598 if(userId <= 0) {
599 Slog.i(TAG, "Can't remove recent task on user " + userId);
600 return;
601 }
602
Winson Chung1dbc8112017-09-28 18:05:31 -0700603 for (int i = mTasks.size() - 1; i >= 0; --i) {
604 TaskRecord tr = mTasks.get(i);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800605 if (tr.userId == userId) {
Wale Ogunwaleee006da2015-03-30 14:49:25 -0700606 if(DEBUG_TASKS) Slog.i(TAG_TASKS,
607 "remove RecentTask " + tr + " when finishing user" + userId);
Winson Chungb4132992018-07-03 15:52:38 -0700608 remove(tr);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800609 }
610 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800611 }
612
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000613 void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
614 final Set<String> packageNames = Sets.newHashSet(packages);
Winson Chung1dbc8112017-09-28 18:05:31 -0700615 for (int i = mTasks.size() - 1; i >= 0; --i) {
616 final TaskRecord tr = mTasks.get(i);
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000617 if (tr.realActivity != null
618 && packageNames.contains(tr.realActivity.getPackageName())
619 && tr.userId == userId
620 && tr.realActivitySuspended != suspended) {
621 tr.realActivitySuspended = suspended;
Suprabh Shukla021b57a2018-03-08 18:21:50 -0800622 if (suspended) {
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700623 mSupervisor.removeTaskByIdLocked(tr.taskId, false,
Suprabh Shukla021b57a2018-03-08 18:21:50 -0800624 REMOVE_FROM_RECENTS, "suspended-package");
625 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000626 notifyTaskPersisterLocked(tr, false);
627 }
628 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700629 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000630
Charles Hed62f9652017-11-01 10:05:51 +0000631 void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
632 if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
633 return;
634 }
635 for (int i = mTasks.size() - 1; i >= 0; --i) {
636 final TaskRecord tr = mTasks.get(i);
Wale Ogunwaled95c06b2018-05-08 10:35:38 -0700637 if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
Charles Hed62f9652017-11-01 10:05:51 +0000638 remove(tr);
639 }
640 }
641 }
642
Winson Chung1dbc8112017-09-28 18:05:31 -0700643 void removeTasksByPackageName(String packageName, int userId) {
Winson Chung63085842017-10-13 17:18:58 -0700644 for (int i = mTasks.size() - 1; i >= 0; --i) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700645 final TaskRecord tr = mTasks.get(i);
646 final String taskPackageName =
647 tr.getBaseIntent().getComponent().getPackageName();
Winson Chung99720d42018-04-09 11:28:46 -0700648 if (tr.userId != userId) continue;
649 if (!taskPackageName.equals(packageName)) continue;
Winson Chung1dbc8112017-09-28 18:05:31 -0700650
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700651 mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
Winson Chung0ec2a352017-10-26 11:38:30 -0700652 "remove-package-task");
Winson Chung1dbc8112017-09-28 18:05:31 -0700653 }
654 }
655
Winson Chunge6439102018-07-30 15:48:01 -0700656 void removeAllVisibleTasks() {
657 for (int i = mTasks.size() - 1; i >= 0; --i) {
658 final TaskRecord tr = mTasks.get(i);
659 if (isVisibleRecentTask(tr)) {
660 mTasks.remove(i);
661 notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
662 }
663 }
664 }
665
Winson Chung1dbc8112017-09-28 18:05:31 -0700666 void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
667 int userId) {
Winson Chung63085842017-10-13 17:18:58 -0700668 for (int i = mTasks.size() - 1; i >= 0; --i) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700669 final TaskRecord tr = mTasks.get(i);
670 if (userId != UserHandle.USER_ALL && tr.userId != userId) {
671 continue;
672 }
673
Bryce Lee1a990e52018-04-23 10:54:11 -0700674 ComponentName cn = tr.intent != null ? tr.intent.getComponent() : null;
Winson Chung1dbc8112017-09-28 18:05:31 -0700675 final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
676 && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
677 if (sameComponent) {
Wale Ogunwale16e505a2018-05-07 15:00:49 -0700678 mSupervisor.removeTaskByIdLocked(tr.taskId, false,
Winson Chung0ec2a352017-10-26 11:38:30 -0700679 REMOVE_FROM_RECENTS, "disabled-package");
Winson Chung1dbc8112017-09-28 18:05:31 -0700680 }
681 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000682 }
683
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800684 /**
685 * Update the recent tasks lists: make sure tasks should still be here (their
686 * applications / activities still exist), update their availability, fix-up ordering
687 * of affiliations.
688 */
689 void cleanupLocked(int userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700690 int recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800691 if (recentsCount == 0) {
692 // Happens when called from the packagemanager broadcast before boot,
693 // or just any empty list.
694 return;
695 }
696
Winson Chung1dbc8112017-09-28 18:05:31 -0700697 // Clear the temp lists
698 mTmpAvailActCache.clear();
699 mTmpAvailAppCache.clear();
700
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800701 final IPackageManager pm = AppGlobals.getPackageManager();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800702 for (int i = recentsCount - 1; i >= 0; i--) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700703 final TaskRecord task = mTasks.get(i);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800704 if (userId != UserHandle.USER_ALL && task.userId != userId) {
705 // Only look at tasks for the user ID of interest.
706 continue;
707 }
708 if (task.autoRemoveRecents && task.getTopActivity() == null) {
709 // This situation is broken, and we should just get rid of it now.
Winson Chungb4132992018-07-03 15:52:38 -0700710 remove(task);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800711 Slog.w(TAG, "Removing auto-remove without activity: " + task);
712 continue;
713 }
714 // Check whether this activity is currently available.
715 if (task.realActivity != null) {
716 ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
717 if (ai == null) {
718 try {
719 // At this first cut, we're only interested in
720 // activities that are fully runnable based on
721 // current system state.
722 ai = pm.getActivityInfo(task.realActivity,
Philip P. Moltmanncff8f0f2018-03-27 12:51:51 -0700723 PackageManager.MATCH_DEBUG_TRIAGED_MISSING
724 | ActivityManagerService.STOCK_PM_FLAGS, userId);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800725 } catch (RemoteException e) {
726 // Will never happen.
727 continue;
728 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800729 if (ai == null) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700730 ai = NO_ACTIVITY_INFO_TOKEN;
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800731 }
732 mTmpAvailActCache.put(task.realActivity, ai);
733 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700734 if (ai == NO_ACTIVITY_INFO_TOKEN) {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800735 // This could be either because the activity no longer exists, or the
736 // app is temporarily gone. For the former we want to remove the recents
737 // entry; for the latter we want to mark it as unavailable.
738 ApplicationInfo app = mTmpAvailAppCache
739 .get(task.realActivity.getPackageName());
740 if (app == null) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800741 try {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800742 app = pm.getApplicationInfo(task.realActivity.getPackageName(),
743 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800744 } catch (RemoteException e) {
745 // Will never happen.
746 continue;
747 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800748 if (app == null) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700749 app = NO_APPLICATION_INFO_TOKEN;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800750 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800751 mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
752 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700753 if (app == NO_APPLICATION_INFO_TOKEN
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800754 || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
755 // Doesn't exist any more! Good-bye.
Winson Chungb4132992018-07-03 15:52:38 -0700756 remove(task);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800757 Slog.w(TAG, "Removing no longer valid recent: " + task);
758 continue;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800759 } else {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800760 // Otherwise just not available for now.
761 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
762 "Making recent unavailable: " + task);
763 task.isAvailable = false;
764 }
765 } else {
766 if (!ai.enabled || !ai.applicationInfo.enabled
767 || (ai.applicationInfo.flags
768 & ApplicationInfo.FLAG_INSTALLED) == 0) {
769 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
770 "Making recent unavailable: " + task
771 + " (enabled=" + ai.enabled + "/"
772 + ai.applicationInfo.enabled
773 + " flags="
774 + Integer.toHexString(ai.applicationInfo.flags)
775 + ")");
776 task.isAvailable = false;
777 } else {
778 if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
779 "Making recent available: " + task);
780 task.isAvailable = true;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800781 }
782 }
783 }
784 }
785
786 // Verify the affiliate chain for each task.
787 int i = 0;
Winson Chung1dbc8112017-09-28 18:05:31 -0700788 recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800789 while (i < recentsCount) {
790 i = processNextAffiliateChainLocked(i);
791 }
792 // recent tasks are now in sorted, affiliated order.
793 }
794
Winson Chung1dbc8112017-09-28 18:05:31 -0700795 /**
796 * @return whether the given {@param task} can be added to the list without causing another
797 * task to be trimmed as a result of that add.
798 */
799 private boolean canAddTaskWithoutTrim(TaskRecord task) {
Winson Chung079221f2017-12-13 17:43:34 -0800800 return findRemoveIndexForAddTask(task) == -1;
Winson Chung1dbc8112017-09-28 18:05:31 -0700801 }
802
803 /**
804 * Returns the list of {@link ActivityManager.AppTask}s.
805 */
806 ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
807 final ArrayList<IBinder> list = new ArrayList<>();
808 final int size = mTasks.size();
809 for (int i = 0; i < size; i++) {
810 final TaskRecord tr = mTasks.get(i);
811 // Skip tasks that do not match the caller. We don't need to verify
812 // callingPackage, because we are also limiting to callingUid and know
813 // that will limit to the correct security sandbox.
814 if (tr.effectiveUid != callingUid) {
815 continue;
816 }
817 Intent intent = tr.getBaseIntent();
818 if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
819 continue;
820 }
Winson Chungabfdcce2018-07-02 17:23:33 -0700821 AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.taskId, callingUid);
Winson Chung1dbc8112017-09-28 18:05:31 -0700822 list.add(taskImpl.asBinder());
823 }
824 return list;
825 }
826
Wale Ogunwale86b74462018-07-02 08:42:43 -0700827 @VisibleForTesting
828 Set<Integer> getProfileIds(int userId) {
829 Set<Integer> userIds = new ArraySet<>();
830 final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
831 false /* enabledOnly */);
832 for (int i = profiles.size() - 1; i >= 0; --i) {
833 userIds.add(profiles.get(i).id);
834 }
835 return userIds;
836 }
837
838 @VisibleForTesting
839 UserInfo getUserInfo(int userId) {
840 return mService.getUserManager().getUserInfo(userId);
841 }
842
843 @VisibleForTesting
844 int[] getCurrentProfileIds() {
845 return mService.mAmInternal.getCurrentProfileIds();
846 }
847
Winson Chung1dbc8112017-09-28 18:05:31 -0700848 /**
849 * @return the list of recent tasks for presentation.
850 */
851 ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
852 boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
Winson Chungb4132992018-07-03 15:52:38 -0700853 return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
854 getDetailedTasks, userId, callingUid));
855 }
856
Winson Chungb4132992018-07-03 15:52:38 -0700857 /**
858 * @return the list of recent tasks for presentation.
859 */
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800860 private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
Winson Chungb4132992018-07-03 15:52:38 -0700861 boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700862 final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
863
Wale Ogunwale214f3482018-10-04 11:00:47 -0700864 if (!mService.mAmInternal.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700865 Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
Winson Chungb4132992018-07-03 15:52:38 -0700866 return new ArrayList<>();
Winson Chung1dbc8112017-09-28 18:05:31 -0700867 }
868 loadUserRecentsLocked(userId);
869
Wale Ogunwale86b74462018-07-02 08:42:43 -0700870 final Set<Integer> includedUsers = getProfileIds(userId);
Winson Chung1dbc8112017-09-28 18:05:31 -0700871 includedUsers.add(Integer.valueOf(userId));
872
Winson Chungc5fe7ff2019-02-19 14:49:25 -0800873 // Check if the frozen task list has timed out
874 resetFreezeTaskListReorderingOnTimeout();
875
Winson Chung1dbc8112017-09-28 18:05:31 -0700876 final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
877 final int size = mTasks.size();
Winson Chungfb44d212017-10-04 11:39:10 -0700878 int numVisibleTasks = 0;
Winson Chung1dbc8112017-09-28 18:05:31 -0700879 for (int i = 0; i < size; i++) {
880 final TaskRecord tr = mTasks.get(i);
Winson Chungfb44d212017-10-04 11:39:10 -0700881
882 if (isVisibleRecentTask(tr)) {
883 numVisibleTasks++;
884 if (isInVisibleRange(tr, numVisibleTasks)) {
885 // Fall through
886 } else {
887 // Not in visible range
888 continue;
889 }
890 } else {
891 // Not visible
892 continue;
893 }
894
Winson Chung1dbc8112017-09-28 18:05:31 -0700895 // Skip remaining tasks once we reach the requested size
896 if (res.size() >= maxNum) {
897 continue;
898 }
899
900 // Only add calling user or related users recent tasks
901 if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
902 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
903 continue;
904 }
905
906 if (tr.realActivitySuspended) {
907 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
908 continue;
909 }
910
911 // Return the entry if desired by the caller. We always return
912 // the first entry, because callers always expect this to be the
913 // foreground app. We may filter others if the caller has
914 // not supplied RECENT_WITH_EXCLUDED and there is some reason
915 // we should exclude the entry.
916
917 if (i == 0
918 || withExcluded
919 || (tr.intent == null)
Winson Chungb4132992018-07-03 15:52:38 -0700920 || ((tr.intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
Winson Chung1dbc8112017-09-28 18:05:31 -0700921 == 0)) {
922 if (!getTasksAllowed) {
923 // If the caller doesn't have the GET_TASKS permission, then only
924 // allow them to see a small subset of tasks -- their own and home.
925 if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
926 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
927 continue;
928 }
929 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700930 if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
931 // Don't include auto remove tasks that are finished or finishing.
932 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
933 "Skipping, auto-remove without activity: " + tr);
934 continue;
935 }
936 if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
937 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
938 "Skipping, unavail real act: " + tr);
939 continue;
940 }
941
942 if (!tr.mUserSetupComplete) {
943 // Don't include task launched while user is not done setting-up.
944 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
945 "Skipping, user setup not complete: " + tr);
946 continue;
947 }
948
Winson Chung61c9e5a2017-10-11 10:39:32 -0700949 final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
Winson Chung1dbc8112017-09-28 18:05:31 -0700950 if (!getDetailedTasks) {
951 rti.baseIntent.replaceExtras((Bundle)null);
952 }
953
954 res.add(rti);
955 }
956 }
Winson Chungb4132992018-07-03 15:52:38 -0700957 return res;
Winson Chung1dbc8112017-09-28 18:05:31 -0700958 }
959
960 /**
961 * @return the list of persistable task ids.
962 */
963 void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
964 final int size = mTasks.size();
965 for (int i = 0; i < size; i++) {
966 final TaskRecord task = mTasks.get(i);
967 if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
968 + " persistable=" + task.isPersistable);
969 final ActivityStack stack = task.getStack();
970 if ((task.isPersistable || task.inRecents)
971 && (stack == null || !stack.isHomeOrRecentsStack())) {
972 if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
973 persistentTaskIds.add(task.taskId);
974 } else {
975 if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
976 + task);
977 }
978 }
979 }
980
Winson Chungd6aa3db2017-10-05 17:18:43 -0700981 @VisibleForTesting
982 ArrayList<TaskRecord> getRawTasks() {
983 return mTasks;
984 }
985
Winson Chung1dbc8112017-09-28 18:05:31 -0700986 /**
Vadim Tryshev593e9562018-03-08 17:15:45 -0800987 * @return ids of tasks that are presented in Recents UI.
988 */
989 SparseBooleanArray getRecentTaskIds() {
990 final SparseBooleanArray res = new SparseBooleanArray();
991 final int size = mTasks.size();
992 int numVisibleTasks = 0;
993 for (int i = 0; i < size; i++) {
994 final TaskRecord tr = mTasks.get(i);
995 if (isVisibleRecentTask(tr)) {
996 numVisibleTasks++;
997 if (isInVisibleRange(tr, numVisibleTasks)) {
998 res.put(tr.taskId, true);
999 }
1000 }
1001 }
1002 return res;
1003 }
1004
1005 /**
Winson Chung1dbc8112017-09-28 18:05:31 -07001006 * @return the task in the task list with the given {@param id} if one exists.
1007 */
1008 TaskRecord getTask(int id) {
1009 final int recentsCount = mTasks.size();
1010 for (int i = 0; i < recentsCount; i++) {
1011 TaskRecord tr = mTasks.get(i);
1012 if (tr.taskId == id) {
1013 return tr;
1014 }
1015 }
1016 return null;
1017 }
1018
1019 /**
1020 * Add a new task to the recent tasks list.
1021 */
1022 void add(TaskRecord task) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001023 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
1024
Winson Chung1dbc8112017-09-28 18:05:31 -07001025 final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
1026 || task.mNextAffiliateTaskId != INVALID_TASK_ID
1027 || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
1028
1029 int recentsCount = mTasks.size();
1030 // Quick case: never add voice sessions.
1031 // TODO: VI what about if it's just an activity?
1032 // Probably nothing to do here
1033 if (task.voiceSession != null) {
1034 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
1035 "addRecent: not adding voice interaction " + task);
1036 return;
1037 }
1038 // Another quick case: check if the top-most recent task is the same.
1039 if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
1040 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
1041 return;
1042 }
1043 // Another quick case: check if this is part of a set of affiliated
1044 // tasks that are at the top.
1045 if (isAffiliated && recentsCount > 0 && task.inRecents
1046 && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
1047 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
1048 + " at top when adding " + task);
1049 return;
1050 }
1051
1052 boolean needAffiliationFix = false;
1053
1054 // Slightly less quick case: the task is already in recents, so all we need
1055 // to do is move it.
1056 if (task.inRecents) {
1057 int taskIndex = mTasks.indexOf(task);
1058 if (taskIndex >= 0) {
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001059 if (!isAffiliated) {
1060 if (!mFreezeTaskListReordering) {
1061 // Simple case: this is not an affiliated task, so we just move it to the
1062 // front unless overridden by the provided activity options
1063 mTasks.remove(taskIndex);
1064 mTasks.add(0, task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001065
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001066 if (DEBUG_RECENTS) {
1067 Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
1068 + " from " + taskIndex);
1069 }
1070 }
1071 notifyTaskPersisterLocked(task, false);
1072 return;
Winson Chung1dbc8112017-09-28 18:05:31 -07001073 }
1074 } else {
1075 Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
1076 needAffiliationFix = true;
1077 }
1078 }
1079
1080 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
Winson Chung079221f2017-12-13 17:43:34 -08001081 removeForAddTask(task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001082
1083 task.inRecents = true;
1084 if (!isAffiliated || needAffiliationFix) {
1085 // If this is a simple non-affiliated task, or we had some failure trying to
1086 // handle it as part of an affilated task, then just place it at the top.
1087 mTasks.add(0, task);
1088 notifyTaskAdded(task);
1089 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
1090 } else if (isAffiliated) {
1091 // If this is a new affiliated task, then move all of the affiliated tasks
1092 // to the front and insert this new one.
1093 TaskRecord other = task.mNextAffiliate;
1094 if (other == null) {
1095 other = task.mPrevAffiliate;
1096 }
1097 if (other != null) {
1098 int otherIndex = mTasks.indexOf(other);
1099 if (otherIndex >= 0) {
1100 // Insert new task at appropriate location.
1101 int taskIndex;
1102 if (other == task.mNextAffiliate) {
1103 // We found the index of our next affiliation, which is who is
1104 // before us in the list, so add after that point.
1105 taskIndex = otherIndex+1;
1106 } else {
1107 // We found the index of our previous affiliation, which is who is
1108 // after us in the list, so add at their position.
1109 taskIndex = otherIndex;
1110 }
1111 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
1112 "addRecent: new affiliated task added at " + taskIndex + ": " + task);
1113 mTasks.add(taskIndex, task);
1114 notifyTaskAdded(task);
1115
1116 // Now move everything to the front.
1117 if (moveAffiliatedTasksToFront(task, taskIndex)) {
1118 // All went well.
1119 return;
1120 }
1121
1122 // Uh oh... something bad in the affiliation chain, try to rebuild
1123 // everything and then go through our general path of adding a new task.
1124 needAffiliationFix = true;
1125 } else {
1126 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
1127 "addRecent: couldn't find other affiliation " + other);
1128 needAffiliationFix = true;
1129 }
1130 } else {
1131 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
1132 "addRecent: adding affiliated task without next/prev:" + task);
1133 needAffiliationFix = true;
1134 }
1135 }
1136
1137 if (needAffiliationFix) {
1138 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
1139 cleanupLocked(task.userId);
1140 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001141
1142 // Trim the set of tasks to the active set
1143 trimInactiveRecentTasks();
Winson Chung1dbc8112017-09-28 18:05:31 -07001144 }
1145
1146 /**
1147 * Add the task to the bottom if possible.
1148 */
1149 boolean addToBottom(TaskRecord task) {
1150 if (!canAddTaskWithoutTrim(task)) {
1151 // Adding this task would cause the task to be removed (since it's appended at
1152 // the bottom and would be trimmed) so just return now
1153 return false;
1154 }
1155
1156 add(task);
1157 return true;
1158 }
1159
1160 /**
1161 * Remove a task from the recent tasks list.
1162 */
1163 void remove(TaskRecord task) {
1164 mTasks.remove(task);
Winson Chunge6439102018-07-30 15:48:01 -07001165 notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
Winson Chung1dbc8112017-09-28 18:05:31 -07001166 }
1167
1168 /**
1169 * Trims the recents task list to the global max number of recents.
1170 */
Winson Chungd6aa3db2017-10-05 17:18:43 -07001171 private void trimInactiveRecentTasks() {
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001172 if (mFreezeTaskListReordering) {
1173 // Defer trimming inactive recent tasks until we are unfrozen
1174 return;
1175 }
1176
Winson Chung1dbc8112017-09-28 18:05:31 -07001177 int recentsCount = mTasks.size();
Winson Chungd6aa3db2017-10-05 17:18:43 -07001178
1179 // Remove from the end of the list until we reach the max number of recents
1180 while (recentsCount > mGlobalMaxNumTasks) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001181 final TaskRecord tr = mTasks.remove(recentsCount - 1);
Winson Chunge6439102018-07-30 15:48:01 -07001182 notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
Winson Chung1dbc8112017-09-28 18:05:31 -07001183 recentsCount--;
Winson Chungd6aa3db2017-10-05 17:18:43 -07001184 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
1185 + " max=" + mGlobalMaxNumTasks);
Winson Chung1dbc8112017-09-28 18:05:31 -07001186 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001187
1188 // Remove any tasks that belong to currently quiet profiles
Wale Ogunwale86b74462018-07-02 08:42:43 -07001189 final int[] profileUserIds = getCurrentProfileIds();
Winson Chungd6aa3db2017-10-05 17:18:43 -07001190 mTmpQuietProfileUserIds.clear();
1191 for (int userId : profileUserIds) {
Wale Ogunwale86b74462018-07-02 08:42:43 -07001192 final UserInfo userInfo = getUserInfo(userId);
Winson Chung129771a02017-10-18 13:49:42 -07001193 if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001194 mTmpQuietProfileUserIds.put(userId, true);
1195 }
1196 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
1197 + " quiet=" + mTmpQuietProfileUserIds.get(userId));
1198 }
1199
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001200 // Remove any inactive tasks, calculate the latest set of visible tasks.
Winson Chungd6aa3db2017-10-05 17:18:43 -07001201 int numVisibleTasks = 0;
1202 for (int i = 0; i < mTasks.size();) {
1203 final TaskRecord task = mTasks.get(i);
1204
1205 if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
1206 if (!mHasVisibleRecentTasks) {
1207 // Keep all active tasks if visible recent tasks is not supported
1208 i++;
1209 continue;
1210 }
1211
1212 if (!isVisibleRecentTask(task)) {
1213 // Keep all active-but-invisible tasks
1214 i++;
1215 continue;
1216 } else {
1217 numVisibleTasks++;
Winson Chung0ec2a352017-10-26 11:38:30 -07001218 if (isInVisibleRange(task, numVisibleTasks) || !isTrimmable(task)) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001219 // Keep visible tasks in range
1220 i++;
1221 continue;
1222 } else {
Winson Chung0ec2a352017-10-26 11:38:30 -07001223 // Fall through to trim visible tasks that are no longer in range and
1224 // trimmable
Winson Chungd6aa3db2017-10-05 17:18:43 -07001225 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
1226 "Trimming out-of-range visible task=" + task);
1227 }
1228 }
1229 } else {
1230 // Fall through to trim inactive tasks
1231 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
1232 }
1233
1234 // Task is no longer active, trim it from the list
1235 mTasks.remove(task);
Winson Chunge6439102018-07-30 15:48:01 -07001236 notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
Winson Chungd6aa3db2017-10-05 17:18:43 -07001237 notifyTaskPersisterLocked(task, false /* flush */);
1238 }
1239 }
1240
1241 /**
1242 * @return whether the given task should be considered active.
1243 */
1244 private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
1245 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
1246 + " globalMax=" + mGlobalMaxNumTasks);
1247
1248 if (quietProfileUserIds.get(task.userId)) {
1249 // Quiet profile user's tasks are never active
1250 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
1251 return false;
1252 }
1253
1254 if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
1255 // Keep the task active if its affiliated task is also active
1256 final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
1257 if (affiliatedTask != null) {
1258 if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
1259 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
1260 "\taffiliatedWithTask=" + affiliatedTask + " is not active");
1261 return false;
1262 }
1263 }
1264 }
1265
1266 // All other tasks are considered active
1267 return true;
1268 }
1269
1270 /**
1271 * @return whether the given active task should be presented to the user through SystemUI.
1272 */
Mark Renouf2aaf4cd2019-02-19 17:24:43 -05001273 @VisibleForTesting
1274 boolean isVisibleRecentTask(TaskRecord task) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001275 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
1276 + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
1277 + " sessionDuration=" + mActiveTasksSessionDurationMs
1278 + " inactiveDuration=" + task.getInactiveDuration()
1279 + " activityType=" + task.getActivityType()
Vadim Trysheveff42d32018-03-05 18:33:48 -08001280 + " windowingMode=" + task.getWindowingMode()
1281 + " intentFlags=" + task.getBaseIntent().getFlags());
Winson Chungd6aa3db2017-10-05 17:18:43 -07001282
Winson Chungd6aa3db2017-10-05 17:18:43 -07001283 switch (task.getActivityType()) {
1284 case ACTIVITY_TYPE_HOME:
1285 case ACTIVITY_TYPE_RECENTS:
Vadim Trysheveff42d32018-03-05 18:33:48 -08001286 // Ignore certain activity types completely
Winson Chungd6aa3db2017-10-05 17:18:43 -07001287 return false;
Vadim Trysheveff42d32018-03-05 18:33:48 -08001288 case ACTIVITY_TYPE_ASSISTANT:
1289 // Ignore assistant that chose to be excluded from Recents, even if it's a top
1290 // task.
Winson Chungb4132992018-07-03 15:52:38 -07001291 if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1292 == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
Vadim Trysheveff42d32018-03-05 18:33:48 -08001293 return false;
1294 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001295 }
1296
1297 // Ignore certain windowing modes
1298 switch (task.getWindowingMode()) {
1299 case WINDOWING_MODE_PINNED:
1300 return false;
1301 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
1302 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
1303 final ActivityStack stack = task.getStack();
1304 if (stack != null && stack.topTask() == task) {
1305 // Only the non-top task of the primary split screen mode is visible
1306 return false;
1307 }
1308 }
1309
Mark Renouf2aaf4cd2019-02-19 17:24:43 -05001310 // Tasks managed by/associated with an ActivityView should be excluded from recents.
1311 // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
1312 // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
1313 final ActivityStack stack = task.getStack();
1314 if (stack != null) {
1315 ActivityDisplay display = stack.getDisplay();
1316 if (display != null && display.isSingleTaskInstance()) {
1317 return false;
1318 }
1319 }
1320
Benjamin Franz7dcbfb02018-01-16 15:16:16 +00001321 // If we're in lock task mode, ignore the root task
Wale Ogunwaled95c06b2018-05-08 10:35:38 -07001322 if (task == mService.getLockTaskController().getRootTask()) {
Benjamin Franz7dcbfb02018-01-16 15:16:16 +00001323 return false;
1324 }
1325
Winson Chungd6aa3db2017-10-05 17:18:43 -07001326 return true;
1327 }
1328
1329 /**
1330 * @return whether the given visible task is within the policy range.
1331 */
1332 private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
1333 // Keep the last most task even if it is excluded from recents
1334 final boolean isExcludeFromRecents =
Winson Chungb4132992018-07-03 15:52:38 -07001335 (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1336 == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
Winson Chungd6aa3db2017-10-05 17:18:43 -07001337 if (isExcludeFromRecents) {
1338 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
1339 return numVisibleTasks == 1;
1340 }
1341
1342 if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
1343 // Always keep up to the min number of recent tasks, after that fall through to the
1344 // checks below
1345 return true;
1346 }
1347
1348 if (mMaxNumVisibleTasks >= 0) {
1349 // Always keep up to the max number of recent tasks, but return false afterwards
1350 return numVisibleTasks <= mMaxNumVisibleTasks;
1351 }
1352
1353 if (mActiveTasksSessionDurationMs > 0) {
1354 // Keep the task if the inactive time is within the session window, this check must come
1355 // after the checks for the min/max visible task range
1356 if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
1357 return true;
1358 }
1359 }
1360
1361 return false;
Winson Chung1dbc8112017-09-28 18:05:31 -07001362 }
1363
1364 /**
Winson Chung0ec2a352017-10-26 11:38:30 -07001365 * @return whether the given task can be trimmed even if it is outside the visible range.
1366 */
1367 protected boolean isTrimmable(TaskRecord task) {
1368 final ActivityStack stack = task.getStack();
Winson Chung0ec2a352017-10-26 11:38:30 -07001369
1370 // No stack for task, just trim it
1371 if (stack == null) {
1372 return true;
1373 }
1374
1375 // Ignore tasks from different displays
Louis Changbd48dca2018-08-29 17:44:34 +08001376 // TODO (b/115289124): No Recents on non-default displays.
1377 if (stack.mDisplayId != DEFAULT_DISPLAY) {
Winson Chung0ec2a352017-10-26 11:38:30 -07001378 return false;
1379 }
1380
1381 // Trim tasks that are in stacks that are behind the home stack
1382 final ActivityDisplay display = stack.getDisplay();
Louis Changbd48dca2018-08-29 17:44:34 +08001383 return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
Winson Chung0ec2a352017-10-26 11:38:30 -07001384 }
1385
1386 /**
Winson Chung1dbc8112017-09-28 18:05:31 -07001387 * If needed, remove oldest existing entries in recents that are for the same kind
1388 * of task as the given one.
1389 */
Winson Chung079221f2017-12-13 17:43:34 -08001390 private void removeForAddTask(TaskRecord task) {
1391 final int removeIndex = findRemoveIndexForAddTask(task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001392 if (removeIndex == -1) {
1393 // Nothing to trim
1394 return;
1395 }
1396
Winson Chungd6aa3db2017-10-05 17:18:43 -07001397 // There is a similar task that will be removed for the addition of {@param task}, but it
1398 // can be the same task, and if so, the task will be re-added in add(), so skip the
1399 // callbacks here.
Winson Chung1dbc8112017-09-28 18:05:31 -07001400 final TaskRecord removedTask = mTasks.remove(removeIndex);
1401 if (removedTask != task) {
Winson Chunge6439102018-07-30 15:48:01 -07001402 notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
Winson Chungd6aa3db2017-10-05 17:18:43 -07001403 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
1404 + " for addition of task=" + task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001405 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001406 notifyTaskPersisterLocked(removedTask, false /* flush */);
Winson Chung1dbc8112017-09-28 18:05:31 -07001407 }
1408
1409 /**
1410 * Find the task that would be removed if the given {@param task} is added to the recent tasks
1411 * list (if any).
1412 */
Winson Chung079221f2017-12-13 17:43:34 -08001413 private int findRemoveIndexForAddTask(TaskRecord task) {
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001414 if (mFreezeTaskListReordering) {
1415 // Defer removing tasks due to the addition of new tasks until the task list is unfrozen
1416 return -1;
1417 }
1418
Winson Chung6c9dcad2018-03-13 16:57:55 -07001419 final int recentsCount = mTasks.size();
Winson Chung1dbc8112017-09-28 18:05:31 -07001420 final Intent intent = task.intent;
1421 final boolean document = intent != null && intent.isDocument();
1422 int maxRecents = task.maxRecents - 1;
Winson Chung1dbc8112017-09-28 18:05:31 -07001423 for (int i = 0; i < recentsCount; i++) {
1424 final TaskRecord tr = mTasks.get(i);
Winson Chung1dbc8112017-09-28 18:05:31 -07001425 if (task != tr) {
Winson Chung27f81882018-04-19 14:45:03 -07001426 if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
1427 || task.userId != tr.userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001428 continue;
1429 }
1430 final Intent trIntent = tr.intent;
1431 final boolean sameAffinity =
1432 task.affinity != null && task.affinity.equals(tr.affinity);
1433 final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
1434 boolean multiTasksAllowed = false;
1435 final int flags = intent.getFlags();
1436 if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
1437 && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
1438 multiTasksAllowed = true;
1439 }
1440 final boolean trIsDocument = trIntent != null && trIntent.isDocument();
1441 final boolean bothDocuments = document && trIsDocument;
1442 if (!sameAffinity && !sameIntent && !bothDocuments) {
1443 continue;
1444 }
1445
1446 if (bothDocuments) {
1447 // Do these documents belong to the same activity?
1448 final boolean sameActivity = task.realActivity != null
1449 && tr.realActivity != null
1450 && task.realActivity.equals(tr.realActivity);
1451 if (!sameActivity) {
1452 // If the document is open in another app or is not the same document, we
1453 // don't need to trim it.
1454 continue;
1455 } else if (maxRecents > 0) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001456 --maxRecents;
1457 if (!sameIntent || multiTasksAllowed) {
1458 // We don't want to trim if we are not over the max allowed entries and
1459 // the tasks are not of the same intent filter, or multiple entries for
1460 // the task is allowed.
1461 continue;
1462 }
1463 }
1464 // Hit the maximum number of documents for this task. Fall through
1465 // and remove this document from recents.
1466 } else if (document || trIsDocument) {
1467 // Only one of these is a document. Not the droid we're looking for.
1468 continue;
1469 }
1470 }
1471 return i;
1472 }
1473 return -1;
1474 }
1475
1476 // Extract the affiliates of the chain containing recent at index start.
1477 private int processNextAffiliateChainLocked(int start) {
1478 final TaskRecord startTask = mTasks.get(start);
1479 final int affiliateId = startTask.mAffiliatedTaskId;
1480
1481 // Quick identification of isolated tasks. I.e. those not launched behind.
1482 if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
1483 startTask.mNextAffiliate == null) {
1484 // There is still a slim chance that there are other tasks that point to this task
1485 // and that the chain is so messed up that this task no longer points to them but
1486 // the gain of this optimization outweighs the risk.
1487 startTask.inRecents = true;
1488 return start + 1;
1489 }
1490
1491 // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
1492 mTmpRecents.clear();
1493 for (int i = mTasks.size() - 1; i >= start; --i) {
1494 final TaskRecord task = mTasks.get(i);
1495 if (task.mAffiliatedTaskId == affiliateId) {
1496 mTasks.remove(i);
1497 mTmpRecents.add(task);
1498 }
1499 }
1500
1501 // Sort them all by taskId. That is the order they were create in and that order will
1502 // always be correct.
1503 Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
1504
1505 // Go through and fix up the linked list.
1506 // The first one is the end of the chain and has no next.
1507 final TaskRecord first = mTmpRecents.get(0);
1508 first.inRecents = true;
1509 if (first.mNextAffiliate != null) {
1510 Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
1511 first.setNextAffiliate(null);
1512 notifyTaskPersisterLocked(first, false);
1513 }
1514 // Everything in the middle is doubly linked from next to prev.
1515 final int tmpSize = mTmpRecents.size();
1516 for (int i = 0; i < tmpSize - 1; ++i) {
1517 final TaskRecord next = mTmpRecents.get(i);
1518 final TaskRecord prev = mTmpRecents.get(i + 1);
1519 if (next.mPrevAffiliate != prev) {
1520 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
1521 " setting prev=" + prev);
1522 next.setPrevAffiliate(prev);
1523 notifyTaskPersisterLocked(next, false);
1524 }
1525 if (prev.mNextAffiliate != next) {
1526 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
1527 " setting next=" + next);
1528 prev.setNextAffiliate(next);
1529 notifyTaskPersisterLocked(prev, false);
1530 }
1531 prev.inRecents = true;
1532 }
1533 // The last one is the beginning of the list and has no prev.
1534 final TaskRecord last = mTmpRecents.get(tmpSize - 1);
1535 if (last.mPrevAffiliate != null) {
1536 Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
1537 last.setPrevAffiliate(null);
1538 notifyTaskPersisterLocked(last, false);
1539 }
1540
Winson Chungd6aa3db2017-10-05 17:18:43 -07001541 // Insert the group back into mTmpTasks at start.
Winson Chung1dbc8112017-09-28 18:05:31 -07001542 mTasks.addAll(start, mTmpRecents);
1543 mTmpRecents.clear();
1544
1545 // Let the caller know where we left off.
1546 return start + tmpSize;
1547 }
1548
1549 private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
1550 int recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001551 TaskRecord top = task;
1552 int topIndex = taskIndex;
1553 while (top.mNextAffiliate != null && topIndex > 0) {
1554 top = top.mNextAffiliate;
1555 topIndex--;
1556 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001557 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001558 + topIndex + " from intial " + taskIndex);
1559 // Find the end of the chain, doing a sanity check along the way.
1560 boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
1561 int endIndex = topIndex;
1562 TaskRecord prev = top;
1563 while (endIndex < recentsCount) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001564 TaskRecord cur = mTasks.get(endIndex);
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001565 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001566 + endIndex + " " + cur);
1567 if (cur == top) {
1568 // Verify start of the chain.
1569 if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
1570 Slog.wtf(TAG, "Bad chain @" + endIndex
1571 + ": first task has next affiliate: " + prev);
1572 sane = false;
1573 break;
1574 }
1575 } else {
1576 // Verify middle of the chain's next points back to the one before.
1577 if (cur.mNextAffiliate != prev
1578 || cur.mNextAffiliateTaskId != prev.taskId) {
1579 Slog.wtf(TAG, "Bad chain @" + endIndex
1580 + ": middle task " + cur + " @" + endIndex
1581 + " has bad next affiliate "
1582 + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
1583 + ", expected " + prev);
1584 sane = false;
1585 break;
1586 }
1587 }
1588 if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
1589 // Chain ends here.
1590 if (cur.mPrevAffiliate != null) {
1591 Slog.wtf(TAG, "Bad chain @" + endIndex
1592 + ": last task " + cur + " has previous affiliate "
1593 + cur.mPrevAffiliate);
1594 sane = false;
1595 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001596 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001597 break;
1598 } else {
1599 // Verify middle of the chain's prev points to a valid item.
1600 if (cur.mPrevAffiliate == null) {
1601 Slog.wtf(TAG, "Bad chain @" + endIndex
1602 + ": task " + cur + " has previous affiliate "
1603 + cur.mPrevAffiliate + " but should be id "
1604 + cur.mPrevAffiliate);
1605 sane = false;
1606 break;
1607 }
1608 }
1609 if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
1610 Slog.wtf(TAG, "Bad chain @" + endIndex
1611 + ": task " + cur + " has affiliated id "
1612 + cur.mAffiliatedTaskId + " but should be "
1613 + task.mAffiliatedTaskId);
1614 sane = false;
1615 break;
1616 }
1617 prev = cur;
1618 endIndex++;
1619 if (endIndex >= recentsCount) {
1620 Slog.wtf(TAG, "Bad chain ran off index " + endIndex
1621 + ": last task " + prev);
1622 sane = false;
1623 break;
1624 }
1625 }
1626 if (sane) {
1627 if (endIndex < taskIndex) {
1628 Slog.wtf(TAG, "Bad chain @" + endIndex
1629 + ": did not extend to task " + task + " @" + taskIndex);
1630 sane = false;
1631 }
1632 }
1633 if (sane) {
1634 // All looks good, we can just move all of the affiliated tasks
1635 // to the top.
1636 for (int i=topIndex; i<=endIndex; i++) {
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001637 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001638 + " from " + i + " to " + (i-topIndex));
Winson Chung1dbc8112017-09-28 18:05:31 -07001639 TaskRecord cur = mTasks.remove(i);
1640 mTasks.add(i - topIndex, cur);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001641 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001642 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001643 + " to " + endIndex);
1644 return true;
1645 }
1646
1647 // Whoops, couldn't do it.
1648 return false;
1649 }
1650
Winson Chung1dbc8112017-09-28 18:05:31 -07001651 void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
1652 pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
Winson Chung3f0e59a2017-10-25 10:19:05 -07001653 pw.println("mRecentsUid=" + mRecentsUid);
1654 pw.println("mRecentsComponent=" + mRecentsComponent);
Winson Chungc5fe7ff2019-02-19 14:49:25 -08001655 pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering);
1656 pw.println("mFreezeTaskListReorderingTime (time since)="
1657 + (SystemClock.elapsedRealtime() - mFreezeTaskListReorderingTime) + "ms");
Winson Chung1dbc8112017-09-28 18:05:31 -07001658 if (mTasks.isEmpty()) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001659 return;
1660 }
1661
Winson Chungb4132992018-07-03 15:52:38 -07001662 // Dump raw recent task list
Winson Chung3f0e59a2017-10-25 10:19:05 -07001663 boolean printedAnything = false;
1664 boolean printedHeader = false;
Winson Chung1dbc8112017-09-28 18:05:31 -07001665 final int size = mTasks.size();
1666 for (int i = 0; i < size; i++) {
1667 final TaskRecord tr = mTasks.get(i);
1668 if (dumpPackage != null && (tr.realActivity == null ||
1669 !dumpPackage.equals(tr.realActivity.getPackageName()))) {
1670 continue;
1671 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001672
Winson Chung3f0e59a2017-10-25 10:19:05 -07001673 if (!printedHeader) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001674 pw.println(" Recent tasks:");
Winson Chung3f0e59a2017-10-25 10:19:05 -07001675 printedHeader = true;
1676 printedAnything = true;
Winson Chung1dbc8112017-09-28 18:05:31 -07001677 }
1678 pw.print(" * Recent #"); pw.print(i); pw.print(": ");
1679 pw.println(tr);
1680 if (dumpAll) {
1681 tr.dump(pw, " ");
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001682 }
1683 }
1684
Winson Chungb4132992018-07-03 15:52:38 -07001685 // Dump visible recent task list
1686 if (mHasVisibleRecentTasks) {
1687 // Reset the header flag for the next block
1688 printedHeader = false;
1689 ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE,
1690 0, true /* getTasksAllowed */, false /* getDetailedTasks */,
1691 mService.getCurrentUserId(), SYSTEM_UID);
1692 for (int i = 0; i < tasks.size(); i++) {
1693 final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i);
1694 if (!printedHeader) {
1695 if (printedAnything) {
1696 // Separate from the last block if it printed
1697 pw.println();
1698 }
1699 pw.println(" Visible recent tasks (most recent first):");
1700 printedHeader = true;
1701 printedAnything = true;
1702 }
1703
1704 pw.print(" * RecentTaskInfo #"); pw.print(i); pw.print(": ");
1705 taskInfo.dump(pw, " ");
1706 }
1707 }
1708
Winson Chung3f0e59a2017-10-25 10:19:05 -07001709 if (!printedAnything) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001710 pw.println(" (nothing)");
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001711 }
1712 }
1713
1714 /**
Winson Chung1dbc8112017-09-28 18:05:31 -07001715 * Creates a new RecentTaskInfo from a TaskRecord.
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001716 */
Winson Chung61c9e5a2017-10-11 10:39:32 -07001717 ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001718 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
Mark Renoufc808f062019-02-07 15:20:37 -05001719 tr.fillTaskInfo(rti);
Winson Chungabfdcce2018-07-02 17:23:33 -07001720 // Fill in some deprecated values
1721 rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
1722 rti.persistentId = rti.taskId;
Winson Chung1dbc8112017-09-28 18:05:31 -07001723 return rti;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001724 }
Winson Chung27f81882018-04-19 14:45:03 -07001725
1726 /**
1727 * @return Whether the activity types and windowing modes of the two tasks are considered
1728 * compatible. This is necessary because we currently don't persist the activity type
1729 * or the windowing mode with the task, so they can be undefined when restored.
1730 */
1731 private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) {
1732 final int activityType = t1.getActivityType();
1733 final int windowingMode = t1.getWindowingMode();
1734 final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
1735 final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
1736 final int otherActivityType = t2.getActivityType();
1737 final int otherWindowingMode = t2.getWindowingMode();
1738 final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
1739 final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
1740
1741 // An activity type and windowing mode is compatible if they are the exact same type/mode,
1742 // or if one of the type/modes is undefined
1743 final boolean isCompatibleType = activityType == otherActivityType
1744 || isUndefinedType || isOtherUndefinedType;
1745 final boolean isCompatibleMode = windowingMode == otherWindowingMode
1746 || isUndefinedMode || isOtherUndefinedMode;
1747
1748 return isCompatibleType && isCompatibleMode;
1749 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001750}