blob: a20452bb507467cdacabfcb02ef6db4b6cadf6e3 [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
17package com.android.server.am;
18
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;
Vadim Trysheveff42d32018-03-05 18:33:48 -080022import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
Winson Chungd6aa3db2017-10-05 17:18:43 -070023import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
24import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
25import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
26import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
Wale Ogunwalee8d5f652016-04-22 16:27:39 -070027import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
28import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
29import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Winson Chung1dbc8112017-09-28 18:05:31 -070030
Suprabh Shukla09a88f52015-12-02 14:36:31 -080031import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
Winson Chungd6aa3db2017-10-05 17:18:43 -070032import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080033import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
34import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
35import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
36import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
37import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
Winson Chung1dbc8112017-09-28 18:05:31 -070038import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
Wale Ogunwalee23149f2015-03-06 15:39:44 -080039import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
40
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080041import android.app.ActivityManager;
42import android.app.AppGlobals;
43import android.content.ComponentName;
44import android.content.Intent;
45import android.content.pm.ActivityInfo;
46import android.content.pm.ApplicationInfo;
47import android.content.pm.IPackageManager;
48import android.content.pm.PackageManager;
Winson Chung1dbc8112017-09-28 18:05:31 -070049import android.content.pm.ParceledListSlice;
Winson Chungd6aa3db2017-10-05 17:18:43 -070050import android.content.pm.UserInfo;
51import android.content.res.Resources;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080052import android.graphics.Bitmap;
Winson Chung1dbc8112017-09-28 18:05:31 -070053import android.graphics.Rect;
Winson Chung1dbc8112017-09-28 18:05:31 -070054import android.os.Bundle;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080055import android.os.Environment;
Winson Chung1dbc8112017-09-28 18:05:31 -070056import android.os.IBinder;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080057import android.os.RemoteException;
Winson Chungd6aa3db2017-10-05 17:18:43 -070058import android.os.SystemProperties;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080059import android.os.UserHandle;
Winson Chung3f0e59a2017-10-25 10:19:05 -070060import android.text.TextUtils;
Daichi Hirono4cb941e2017-03-31 14:30:41 +090061import android.util.ArraySet;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080062import android.util.Slog;
Suprabh Shukla4bccb462016-02-10 18:45:12 -080063import android.util.SparseArray;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080064import android.util.SparseBooleanArray;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080065
Winson Chung1dbc8112017-09-28 18:05:31 -070066import com.android.internal.annotations.VisibleForTesting;
Winson Chung61c9e5a2017-10-11 10:39:32 -070067import com.android.server.am.TaskRecord.TaskActivitiesReport;
Winson Chung1dbc8112017-09-28 18:05:31 -070068
Winson Chung3f0e59a2017-10-25 10:19:05 -070069import com.google.android.collect.Sets;
70
Suprabh Shukla09a88f52015-12-02 14:36:31 -080071import java.io.File;
Winson Chung1dbc8112017-09-28 18:05:31 -070072import java.io.PrintWriter;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080073import java.util.ArrayList;
Suprabh Shukla09a88f52015-12-02 14:36:31 -080074import java.util.Arrays;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080075import java.util.Collections;
76import java.util.Comparator;
77import java.util.HashMap;
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +000078import java.util.Set;
Winson Chungd6aa3db2017-10-05 17:18:43 -070079import java.util.concurrent.TimeUnit;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080080
81/**
Winson Chung1dbc8112017-09-28 18:05:31 -070082 * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
83 * least recent.
Winson Chung0ec2a352017-10-26 11:38:30 -070084 *
85 * The trimming logic can be boiled down to the following. For recent task list with a number of
86 * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
87 * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
88 * sub-range are presented to the user, based on the device type, last task active time, or other
89 * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
90 * (considering the back stack) are considered trimmable. If the device does not support recent
91 * tasks, then trimming is completely disabled.
92 *
93 * eg.
94 * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
95 * [VVV VV VVVV V V V ] // Visible tasks
96 * [RRR RR XXXX X X X ] // Visible range tasks, eg. if the device only shows 5 tasks,
97 * // 'X' tasks are trimmed.
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080098 */
Winson Chung1dbc8112017-09-28 18:05:31 -070099class RecentTasks {
Wale Ogunwalee23149f2015-03-06 15:39:44 -0800100 private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
Wale Ogunwaleee006da2015-03-30 14:49:25 -0700101 private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
102 private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
Winson Chung1dbc8112017-09-28 18:05:31 -0700103 private static final boolean TRIMMED = true;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800104
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800105 private static final int DEFAULT_INITIAL_CAPACITY = 5;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800106
Winson6976f7b2016-05-03 14:58:12 -0700107 // Whether or not to move all affiliated tasks to the front when one of the tasks is launched
108 private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
109
Winson Chung1dbc8112017-09-28 18:05:31 -0700110 // Comparator to sort by taskId
111 private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
112 (lhs, rhs) -> rhs.taskId - lhs.taskId;
113
114 // Placeholder variables to keep track of activities/apps that are no longer avialble while
115 // iterating through the recents list
116 private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
117 private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
118
119 /**
120 * Callbacks made when manipulating the list.
121 */
122 interface Callbacks {
123 /**
124 * Called when a task is added to the recent tasks list.
125 */
126 void onRecentTaskAdded(TaskRecord task);
127
128 /**
129 * Called when a task is removed from the recent tasks list.
130 */
Winson Chungd6aa3db2017-10-05 17:18:43 -0700131 void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
Winson Chung1dbc8112017-09-28 18:05:31 -0700132 }
133
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800134 /**
135 * Save recent tasks information across reboots.
136 */
137 private final TaskPersister mTaskPersister;
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800138 private final ActivityManagerService mService;
Winson Chungd6aa3db2017-10-05 17:18:43 -0700139 private final UserController mUserController;
Winson Chung1dbc8112017-09-28 18:05:31 -0700140
141 /**
Winson Chung3f0e59a2017-10-25 10:19:05 -0700142 * Keeps track of the static recents package/component which is granted additional permissions
143 * to call recents-related APIs.
144 */
145 private int mRecentsUid = -1;
146 private ComponentName mRecentsComponent = null;
147
148 /**
Winson Chung1dbc8112017-09-28 18:05:31 -0700149 * Mapping of user id -> whether recent tasks have been loaded for that user.
150 */
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800151 private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
152 DEFAULT_INITIAL_CAPACITY);
153
154 /**
155 * Stores for each user task ids that are taken by tasks residing in persistent storage. These
156 * tasks may or may not currently be in memory.
157 */
Winson Chung1dbc8112017-09-28 18:05:31 -0700158 private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800159 DEFAULT_INITIAL_CAPACITY);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800160
Winson Chungd6aa3db2017-10-05 17:18:43 -0700161 // List of all active recent tasks
Winson Chung1dbc8112017-09-28 18:05:31 -0700162 private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
163 private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
164
Winson Chungd6aa3db2017-10-05 17:18:43 -0700165 // These values are generally loaded from resources, but can be set dynamically in the tests
166 private boolean mHasVisibleRecentTasks;
167 private int mGlobalMaxNumTasks;
168 private int mMinNumVisibleTasks;
169 private int mMaxNumVisibleTasks;
170 private long mActiveTasksSessionDurationMs;
171
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800172 // Mainly to avoid object recreation on multiple calls.
Winson Chung1dbc8112017-09-28 18:05:31 -0700173 private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800174 private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
175 private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
Winson Chungd6aa3db2017-10-05 17:18:43 -0700176 private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
Winson Chung3f0e59a2017-10-25 10:19:05 -0700177 private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800178
Winson Chung1dbc8112017-09-28 18:05:31 -0700179 @VisibleForTesting
Winson Chungd6aa3db2017-10-05 17:18:43 -0700180 RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
181 UserController userController) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700182 mService = service;
Winson Chungd6aa3db2017-10-05 17:18:43 -0700183 mUserController = userController;
Winson Chung1dbc8112017-09-28 18:05:31 -0700184 mTaskPersister = taskPersister;
Winson Chungd6aa3db2017-10-05 17:18:43 -0700185 mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
186 mHasVisibleRecentTasks = true;
Winson Chung1dbc8112017-09-28 18:05:31 -0700187 }
188
189 RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) {
Winson Chungd6aa3db2017-10-05 17:18:43 -0700190 final File systemDir = Environment.getDataSystemDirectory();
191 final Resources res = service.mContext.getResources();
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800192 mService = service;
Winson Chungd6aa3db2017-10-05 17:18:43 -0700193 mUserController = service.mUserController;
Winson Chung1dbc8112017-09-28 18:05:31 -0700194 mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
Winson Chungd6aa3db2017-10-05 17:18:43 -0700195 mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
196 mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
Winson Chung3f0e59a2017-10-25 10:19:05 -0700197 loadParametersFromResources(res);
Winson Chungd6aa3db2017-10-05 17:18:43 -0700198 }
199
200 @VisibleForTesting
201 void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
202 long activeSessionDurationMs) {
203 mMinNumVisibleTasks = minNumVisibleTasks;
204 mMaxNumVisibleTasks = maxNumVisibleTasks;
205 mActiveTasksSessionDurationMs = activeSessionDurationMs;
206 }
207
208 @VisibleForTesting
209 void setGlobalMaxNumTasks(int globalMaxNumTasks) {
210 mGlobalMaxNumTasks = globalMaxNumTasks;
211 }
212
213 /**
214 * Loads the parameters from the system resources.
215 */
216 @VisibleForTesting
217 void loadParametersFromResources(Resources res) {
218 if (ActivityManager.isLowRamDeviceStatic()) {
219 mMinNumVisibleTasks = res.getInteger(
220 com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
221 mMaxNumVisibleTasks = res.getInteger(
222 com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
223 } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
224 mMinNumVisibleTasks = res.getInteger(
225 com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
226 mMaxNumVisibleTasks = res.getInteger(
227 com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
228 } else {
229 mMinNumVisibleTasks = res.getInteger(
230 com.android.internal.R.integer.config_minNumVisibleRecentTasks);
231 mMaxNumVisibleTasks = res.getInteger(
232 com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
233 }
234 final int sessionDurationHrs = res.getInteger(
235 com.android.internal.R.integer.config_activeTaskDurationHours);
236 mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
237 ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
238 : -1;
Winson Chung1dbc8112017-09-28 18:05:31 -0700239 }
240
Winson Chung3f0e59a2017-10-25 10:19:05 -0700241 /**
242 * Loads the static recents component. This is called after the system is ready, but before
243 * any dependent services (like SystemUI) is started.
244 */
245 void loadRecentsComponent(Resources res) {
246 final String rawRecentsComponent = res.getString(
247 com.android.internal.R.string.config_recentsComponentName);
248 if (TextUtils.isEmpty(rawRecentsComponent)) {
249 return;
250 }
251
252 final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
253 if (cn != null) {
254 try {
255 final ApplicationInfo appInfo = AppGlobals.getPackageManager()
256 .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
257 if (appInfo != null) {
258 mRecentsUid = appInfo.uid;
259 mRecentsComponent = cn;
260 }
261 } catch (RemoteException e) {
262 Slog.w(TAG, "Could not load application info for recents component: " + cn);
263 }
264 }
265 }
266
267 /**
268 * @return whether the current caller has the same uid as the recents component.
269 */
270 boolean isCallerRecents(int callingUid) {
271 return UserHandle.isSameApp(callingUid, mRecentsUid);
272 }
273
274 /**
275 * @return whether the given component is the recents component and shares the same uid as the
276 * recents component.
277 */
278 boolean isRecentsComponent(ComponentName cn, int uid) {
279 return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
280 }
281
Winson Chungec1ef092017-10-25 16:22:34 -0700282 /**
Winson Chungc1674272018-02-21 10:15:17 -0800283 * @return whether the home app is also the active handler of recent tasks.
284 */
285 boolean isRecentsComponentHomeActivity(int userId) {
286 final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
287 .getDefaultHomeActivity(userId);
Alice Sheng1de98fc2018-03-16 09:45:58 -0700288 return defaultHomeActivity != null && mRecentsComponent != null &&
Winson Chungc1674272018-02-21 10:15:17 -0800289 defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
290 }
291
292 /**
Winson Chungec1ef092017-10-25 16:22:34 -0700293 * @return the recents component.
294 */
295 ComponentName getRecentsComponent() {
296 return mRecentsComponent;
297 }
298
299 /**
300 * @return the uid for the recents component.
301 */
302 int getRecentsComponentUid() {
303 return mRecentsUid;
304 }
305
Winson Chung1dbc8112017-09-28 18:05:31 -0700306 void registerCallback(Callbacks callback) {
307 mCallbacks.add(callback);
308 }
309
310 void unregisterCallback(Callbacks callback) {
311 mCallbacks.remove(callback);
312 }
313
314 private void notifyTaskAdded(TaskRecord task) {
315 for (int i = 0; i < mCallbacks.size(); i++) {
316 mCallbacks.get(i).onRecentTaskAdded(task);
317 }
318 }
319
320 private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
321 for (int i = 0; i < mCallbacks.size(); i++) {
Winson Chungd6aa3db2017-10-05 17:18:43 -0700322 mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
Winson Chung1dbc8112017-09-28 18:05:31 -0700323 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800324 }
325
326 /**
Suprabh Shuklaf5bf0cb2016-01-19 17:42:03 -0800327 * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
328 * Does nothing if they are already loaded.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800329 *
330 * @param userId the user Id
331 */
332 void loadUserRecentsLocked(int userId) {
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900333 if (mUsersWithRecentsLoaded.get(userId)) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700334 // User already loaded, return early
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900335 return;
336 }
337
338 // Load the task ids if not loaded.
339 loadPersistedTaskIdsForUserLocked(userId);
340
341 // Check if any tasks are added before recents is loaded
342 final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
Winson Chung1dbc8112017-09-28 18:05:31 -0700343 for (final TaskRecord task : mTasks) {
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900344 if (task.userId == userId && shouldPersistTaskLocked(task)) {
345 preaddedTasks.put(task.taskId, true);
346 }
347 }
348
349 Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
Winson Chung1dbc8112017-09-28 18:05:31 -0700350 mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900351 cleanupLocked(userId);
352 mUsersWithRecentsLoaded.put(userId, true);
353
354 // If we have tasks added before loading recents, we need to update persistent task IDs.
355 if (preaddedTasks.size() > 0) {
356 syncPersistentTaskIdsLocked();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800357 }
358 }
359
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800360 private void loadPersistedTaskIdsForUserLocked(int userId) {
361 // An empty instead of a null set here means that no persistent taskIds were present
362 // on file when we loaded them.
363 if (mPersistedTaskIds.get(userId) == null) {
364 mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
Fyodor Kupolovc4b46dd2016-03-17 12:57:36 -0700365 Slog.i(TAG, "Loaded persisted task ids for user " + userId);
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800366 }
367 }
368
Winson Chung1dbc8112017-09-28 18:05:31 -0700369 /**
370 * @return whether the {@param taskId} is currently in use for the given user.
371 */
372 boolean containsTaskId(int taskId, int userId) {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800373 loadPersistedTaskIdsForUserLocked(userId);
374 return mPersistedTaskIds.get(userId).get(taskId);
375 }
376
Winson Chung1dbc8112017-09-28 18:05:31 -0700377 /**
378 * @return all the task ids for the user with the given {@param userId}.
379 */
380 SparseBooleanArray getTaskIdsForUser(int userId) {
381 loadPersistedTaskIdsForUserLocked(userId);
382 return mPersistedTaskIds.get(userId);
383 }
384
385 /**
386 * Kicks off the task persister to write any pending tasks to disk.
387 */
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800388 void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
Andrii Kulian02b7a832016-10-06 23:11:56 -0700389 final ActivityStack stack = task != null ? task.getStack() : null;
Matthew Ngae1ff4f2016-11-10 15:49:14 -0800390 if (stack != null && stack.isHomeOrRecentsStack()) {
391 // Never persist the home or recents stack.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800392 return;
393 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800394 syncPersistentTaskIdsLocked();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800395 mTaskPersister.wakeup(task, flush);
396 }
397
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800398 private void syncPersistentTaskIdsLocked() {
399 for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
400 int userId = mPersistedTaskIds.keyAt(i);
401 if (mUsersWithRecentsLoaded.get(userId)) {
402 // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
403 // referenced here should not be null.
404 mPersistedTaskIds.valueAt(i).clear();
405 }
406 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700407 for (int i = mTasks.size() - 1; i >= 0; i--) {
408 final TaskRecord task = mTasks.get(i);
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900409 if (shouldPersistTaskLocked(task)) {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800410 // Set of persisted taskIds for task.userId should not be null here
Fyodor Kupolovc4b46dd2016-03-17 12:57:36 -0700411 // TODO Investigate why it can happen. For now initialize with an empty set
412 if (mPersistedTaskIds.get(task.userId) == null) {
413 Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
414 + " mPersistedTaskIds=" + mPersistedTaskIds);
415 mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
416 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800417 mPersistedTaskIds.get(task.userId).put(task.taskId, true);
418 }
419 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800420 }
421
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900422 private static boolean shouldPersistTaskLocked(TaskRecord task) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700423 final ActivityStack stack = task.getStack();
Daichi Hirono4cb941e2017-03-31 14:30:41 +0900424 return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
425 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800426
427 void onSystemReadyLocked() {
Winson Chung3f0e59a2017-10-25 10:19:05 -0700428 loadRecentsComponent(mService.mContext.getResources());
Winson Chung1dbc8112017-09-28 18:05:31 -0700429 mTasks.clear();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800430 mTaskPersister.startPersisting();
431 }
432
433 Bitmap getTaskDescriptionIcon(String path) {
434 return mTaskPersister.getTaskDescriptionIcon(path);
435 }
436
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800437 void saveImage(Bitmap image, String path) {
438 mTaskPersister.saveImage(image, path);
439 }
440
441 void flush() {
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800442 synchronized (mService) {
443 syncPersistentTaskIdsLocked();
444 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800445 mTaskPersister.flush();
446 }
447
448 /**
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800449 * Returns all userIds for which recents from persistent storage are loaded into this list.
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800450 *
451 * @return an array of userIds.
452 */
453 int[] usersWithRecentsLoadedLocked() {
454 int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
455 int len = 0;
456 for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
457 int userId = mUsersWithRecentsLoaded.keyAt(i);
458 if (mUsersWithRecentsLoaded.valueAt(i)) {
459 usersWithRecentsLoaded[len++] = userId;
460 }
461 }
462 if (len < usersWithRecentsLoaded.length) {
463 // should never happen.
464 return Arrays.copyOf(usersWithRecentsLoaded, len);
465 }
466 return usersWithRecentsLoaded;
467 }
468
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800469 /**
470 * Removes recent tasks and any other state kept in memory for the passed in user. Does not
471 * touch the information present on persistent storage.
472 *
473 * @param userId the id of the user
474 */
475 void unloadUserDataFromMemoryLocked(int userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700476 if (mUsersWithRecentsLoaded.get(userId)) {
477 Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
478 mUsersWithRecentsLoaded.delete(userId);
479 removeTasksForUserLocked(userId);
480 }
Suprabh Shukla4bccb462016-02-10 18:45:12 -0800481 mPersistedTaskIds.delete(userId);
482 mTaskPersister.unloadUserDataFromMemory(userId);
483 }
484
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800485 /** Remove recent tasks for a user. */
Winson Chung1dbc8112017-09-28 18:05:31 -0700486 private void removeTasksForUserLocked(int userId) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800487 if(userId <= 0) {
488 Slog.i(TAG, "Can't remove recent task on user " + userId);
489 return;
490 }
491
Winson Chung1dbc8112017-09-28 18:05:31 -0700492 for (int i = mTasks.size() - 1; i >= 0; --i) {
493 TaskRecord tr = mTasks.get(i);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800494 if (tr.userId == userId) {
Wale Ogunwaleee006da2015-03-30 14:49:25 -0700495 if(DEBUG_TASKS) Slog.i(TAG_TASKS,
496 "remove RecentTask " + tr + " when finishing user" + userId);
Winson Chung1dbc8112017-09-28 18:05:31 -0700497 remove(mTasks.get(i));
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800498 }
499 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800500 }
501
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000502 void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
503 final Set<String> packageNames = Sets.newHashSet(packages);
Winson Chung1dbc8112017-09-28 18:05:31 -0700504 for (int i = mTasks.size() - 1; i >= 0; --i) {
505 final TaskRecord tr = mTasks.get(i);
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000506 if (tr.realActivity != null
507 && packageNames.contains(tr.realActivity.getPackageName())
508 && tr.userId == userId
509 && tr.realActivitySuspended != suspended) {
510 tr.realActivitySuspended = suspended;
Suprabh Shukla021b57a2018-03-08 18:21:50 -0800511 if (suspended) {
512 mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
513 REMOVE_FROM_RECENTS, "suspended-package");
514 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000515 notifyTaskPersisterLocked(tr, false);
516 }
517 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700518 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000519
Charles Hed62f9652017-11-01 10:05:51 +0000520 void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
521 if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
522 return;
523 }
524 for (int i = mTasks.size() - 1; i >= 0; --i) {
525 final TaskRecord tr = mTasks.get(i);
Bryce Lee2b8e0372018-04-05 17:01:37 -0700526 if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
Charles Hed62f9652017-11-01 10:05:51 +0000527 remove(tr);
528 }
529 }
530 }
531
Winson Chung1dbc8112017-09-28 18:05:31 -0700532 void removeTasksByPackageName(String packageName, int userId) {
Winson Chung63085842017-10-13 17:18:58 -0700533 for (int i = mTasks.size() - 1; i >= 0; --i) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700534 final TaskRecord tr = mTasks.get(i);
535 final String taskPackageName =
536 tr.getBaseIntent().getComponent().getPackageName();
Winson Chung99720d42018-04-09 11:28:46 -0700537 if (tr.userId != userId) continue;
538 if (!taskPackageName.equals(packageName)) continue;
Winson Chung1dbc8112017-09-28 18:05:31 -0700539
Winson Chung0ec2a352017-10-26 11:38:30 -0700540 mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
541 "remove-package-task");
Winson Chung1dbc8112017-09-28 18:05:31 -0700542 }
543 }
544
545 void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
546 int userId) {
Winson Chung63085842017-10-13 17:18:58 -0700547 for (int i = mTasks.size() - 1; i >= 0; --i) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700548 final TaskRecord tr = mTasks.get(i);
549 if (userId != UserHandle.USER_ALL && tr.userId != userId) {
550 continue;
551 }
552
553 ComponentName cn = tr.intent.getComponent();
554 final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
555 && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
556 if (sameComponent) {
557 mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
Winson Chung0ec2a352017-10-26 11:38:30 -0700558 REMOVE_FROM_RECENTS, "disabled-package");
Winson Chung1dbc8112017-09-28 18:05:31 -0700559 }
560 }
Andrei Stingaceanu4ccec532016-01-13 12:10:21 +0000561 }
562
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800563 /**
564 * Update the recent tasks lists: make sure tasks should still be here (their
565 * applications / activities still exist), update their availability, fix-up ordering
566 * of affiliations.
567 */
568 void cleanupLocked(int userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700569 int recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800570 if (recentsCount == 0) {
571 // Happens when called from the packagemanager broadcast before boot,
572 // or just any empty list.
573 return;
574 }
575
Winson Chung1dbc8112017-09-28 18:05:31 -0700576 // Clear the temp lists
577 mTmpAvailActCache.clear();
578 mTmpAvailAppCache.clear();
579
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800580 final IPackageManager pm = AppGlobals.getPackageManager();
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800581 for (int i = recentsCount - 1; i >= 0; i--) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700582 final TaskRecord task = mTasks.get(i);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800583 if (userId != UserHandle.USER_ALL && task.userId != userId) {
584 // Only look at tasks for the user ID of interest.
585 continue;
586 }
587 if (task.autoRemoveRecents && task.getTopActivity() == null) {
588 // This situation is broken, and we should just get rid of it now.
Winson Chung1dbc8112017-09-28 18:05:31 -0700589 mTasks.remove(i);
590 notifyTaskRemoved(task, !TRIMMED);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800591 Slog.w(TAG, "Removing auto-remove without activity: " + task);
592 continue;
593 }
594 // Check whether this activity is currently available.
595 if (task.realActivity != null) {
596 ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
597 if (ai == null) {
598 try {
599 // At this first cut, we're only interested in
600 // activities that are fully runnable based on
601 // current system state.
602 ai = pm.getActivityInfo(task.realActivity,
Philip P. Moltmanncff8f0f2018-03-27 12:51:51 -0700603 PackageManager.MATCH_DEBUG_TRIAGED_MISSING
604 | ActivityManagerService.STOCK_PM_FLAGS, userId);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800605 } catch (RemoteException e) {
606 // Will never happen.
607 continue;
608 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800609 if (ai == null) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700610 ai = NO_ACTIVITY_INFO_TOKEN;
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800611 }
612 mTmpAvailActCache.put(task.realActivity, ai);
613 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700614 if (ai == NO_ACTIVITY_INFO_TOKEN) {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800615 // This could be either because the activity no longer exists, or the
616 // app is temporarily gone. For the former we want to remove the recents
617 // entry; for the latter we want to mark it as unavailable.
618 ApplicationInfo app = mTmpAvailAppCache
619 .get(task.realActivity.getPackageName());
620 if (app == null) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800621 try {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800622 app = pm.getApplicationInfo(task.realActivity.getPackageName(),
623 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800624 } catch (RemoteException e) {
625 // Will never happen.
626 continue;
627 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800628 if (app == null) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700629 app = NO_APPLICATION_INFO_TOKEN;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800630 }
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800631 mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
632 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700633 if (app == NO_APPLICATION_INFO_TOKEN
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800634 || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
635 // Doesn't exist any more! Good-bye.
Winson Chung1dbc8112017-09-28 18:05:31 -0700636 mTasks.remove(i);
637 notifyTaskRemoved(task, !TRIMMED);
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800638 Slog.w(TAG, "Removing no longer valid recent: " + task);
639 continue;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800640 } else {
Suprabh Shukla09a88f52015-12-02 14:36:31 -0800641 // Otherwise just not available for now.
642 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
643 "Making recent unavailable: " + task);
644 task.isAvailable = false;
645 }
646 } else {
647 if (!ai.enabled || !ai.applicationInfo.enabled
648 || (ai.applicationInfo.flags
649 & ApplicationInfo.FLAG_INSTALLED) == 0) {
650 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
651 "Making recent unavailable: " + task
652 + " (enabled=" + ai.enabled + "/"
653 + ai.applicationInfo.enabled
654 + " flags="
655 + Integer.toHexString(ai.applicationInfo.flags)
656 + ")");
657 task.isAvailable = false;
658 } else {
659 if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
660 "Making recent available: " + task);
661 task.isAvailable = true;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800662 }
663 }
664 }
665 }
666
667 // Verify the affiliate chain for each task.
668 int i = 0;
Winson Chung1dbc8112017-09-28 18:05:31 -0700669 recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800670 while (i < recentsCount) {
671 i = processNextAffiliateChainLocked(i);
672 }
673 // recent tasks are now in sorted, affiliated order.
674 }
675
Winson Chung1dbc8112017-09-28 18:05:31 -0700676 /**
677 * @return whether the given {@param task} can be added to the list without causing another
678 * task to be trimmed as a result of that add.
679 */
680 private boolean canAddTaskWithoutTrim(TaskRecord task) {
Winson Chung079221f2017-12-13 17:43:34 -0800681 return findRemoveIndexForAddTask(task) == -1;
Winson Chung1dbc8112017-09-28 18:05:31 -0700682 }
683
684 /**
685 * Returns the list of {@link ActivityManager.AppTask}s.
686 */
687 ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
688 final ArrayList<IBinder> list = new ArrayList<>();
689 final int size = mTasks.size();
690 for (int i = 0; i < size; i++) {
691 final TaskRecord tr = mTasks.get(i);
692 // Skip tasks that do not match the caller. We don't need to verify
693 // callingPackage, because we are also limiting to callingUid and know
694 // that will limit to the correct security sandbox.
695 if (tr.effectiveUid != callingUid) {
696 continue;
697 }
698 Intent intent = tr.getBaseIntent();
699 if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
700 continue;
701 }
702 ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr);
703 AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid);
704 list.add(taskImpl.asBinder());
705 }
706 return list;
707 }
708
709 /**
710 * @return the list of recent tasks for presentation.
711 */
712 ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
713 boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
Winson Chung1dbc8112017-09-28 18:05:31 -0700714 final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
715
716 if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
717 Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
718 return ParceledListSlice.emptyList();
719 }
720 loadUserRecentsLocked(userId);
721
Winson Chung5fa39752017-10-04 14:50:15 -0700722 final Set<Integer> includedUsers = mUserController.getProfileIds(userId);
Winson Chung1dbc8112017-09-28 18:05:31 -0700723 includedUsers.add(Integer.valueOf(userId));
724
725 final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
726 final int size = mTasks.size();
Winson Chungfb44d212017-10-04 11:39:10 -0700727 int numVisibleTasks = 0;
Winson Chung1dbc8112017-09-28 18:05:31 -0700728 for (int i = 0; i < size; i++) {
729 final TaskRecord tr = mTasks.get(i);
Winson Chungfb44d212017-10-04 11:39:10 -0700730
731 if (isVisibleRecentTask(tr)) {
732 numVisibleTasks++;
733 if (isInVisibleRange(tr, numVisibleTasks)) {
734 // Fall through
735 } else {
736 // Not in visible range
737 continue;
738 }
739 } else {
740 // Not visible
741 continue;
742 }
743
Winson Chung1dbc8112017-09-28 18:05:31 -0700744 // Skip remaining tasks once we reach the requested size
745 if (res.size() >= maxNum) {
746 continue;
747 }
748
749 // Only add calling user or related users recent tasks
750 if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
751 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
752 continue;
753 }
754
755 if (tr.realActivitySuspended) {
756 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
757 continue;
758 }
759
760 // Return the entry if desired by the caller. We always return
761 // the first entry, because callers always expect this to be the
762 // foreground app. We may filter others if the caller has
763 // not supplied RECENT_WITH_EXCLUDED and there is some reason
764 // we should exclude the entry.
765
766 if (i == 0
767 || withExcluded
768 || (tr.intent == null)
769 || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
770 == 0)) {
771 if (!getTasksAllowed) {
772 // If the caller doesn't have the GET_TASKS permission, then only
773 // allow them to see a small subset of tasks -- their own and home.
774 if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
775 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
776 continue;
777 }
778 }
Winson Chung1dbc8112017-09-28 18:05:31 -0700779 if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
780 // Don't include auto remove tasks that are finished or finishing.
781 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
782 "Skipping, auto-remove without activity: " + tr);
783 continue;
784 }
785 if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
786 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
787 "Skipping, unavail real act: " + tr);
788 continue;
789 }
790
791 if (!tr.mUserSetupComplete) {
792 // Don't include task launched while user is not done setting-up.
793 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
794 "Skipping, user setup not complete: " + tr);
795 continue;
796 }
797
Winson Chung61c9e5a2017-10-11 10:39:32 -0700798 final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
Winson Chung1dbc8112017-09-28 18:05:31 -0700799 if (!getDetailedTasks) {
800 rti.baseIntent.replaceExtras((Bundle)null);
801 }
802
803 res.add(rti);
804 }
805 }
806 return new ParceledListSlice<>(res);
807 }
808
809 /**
810 * @return the list of persistable task ids.
811 */
812 void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
813 final int size = mTasks.size();
814 for (int i = 0; i < size; i++) {
815 final TaskRecord task = mTasks.get(i);
816 if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
817 + " persistable=" + task.isPersistable);
818 final ActivityStack stack = task.getStack();
819 if ((task.isPersistable || task.inRecents)
820 && (stack == null || !stack.isHomeOrRecentsStack())) {
821 if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
822 persistentTaskIds.add(task.taskId);
823 } else {
824 if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
825 + task);
826 }
827 }
828 }
829
Winson Chungd6aa3db2017-10-05 17:18:43 -0700830 @VisibleForTesting
831 ArrayList<TaskRecord> getRawTasks() {
832 return mTasks;
833 }
834
Winson Chung1dbc8112017-09-28 18:05:31 -0700835 /**
Vadim Tryshev593e9562018-03-08 17:15:45 -0800836 * @return ids of tasks that are presented in Recents UI.
837 */
838 SparseBooleanArray getRecentTaskIds() {
839 final SparseBooleanArray res = new SparseBooleanArray();
840 final int size = mTasks.size();
841 int numVisibleTasks = 0;
842 for (int i = 0; i < size; i++) {
843 final TaskRecord tr = mTasks.get(i);
844 if (isVisibleRecentTask(tr)) {
845 numVisibleTasks++;
846 if (isInVisibleRange(tr, numVisibleTasks)) {
847 res.put(tr.taskId, true);
848 }
849 }
850 }
851 return res;
852 }
853
854 /**
Winson Chung1dbc8112017-09-28 18:05:31 -0700855 * @return the task in the task list with the given {@param id} if one exists.
856 */
857 TaskRecord getTask(int id) {
858 final int recentsCount = mTasks.size();
859 for (int i = 0; i < recentsCount; i++) {
860 TaskRecord tr = mTasks.get(i);
861 if (tr.taskId == id) {
862 return tr;
863 }
864 }
865 return null;
866 }
867
868 /**
869 * Add a new task to the recent tasks list.
870 */
871 void add(TaskRecord task) {
Winson Chungd6aa3db2017-10-05 17:18:43 -0700872 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
873
Winson Chung1dbc8112017-09-28 18:05:31 -0700874 final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
875 || task.mNextAffiliateTaskId != INVALID_TASK_ID
876 || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
877
878 int recentsCount = mTasks.size();
879 // Quick case: never add voice sessions.
880 // TODO: VI what about if it's just an activity?
881 // Probably nothing to do here
882 if (task.voiceSession != null) {
883 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
884 "addRecent: not adding voice interaction " + task);
885 return;
886 }
887 // Another quick case: check if the top-most recent task is the same.
888 if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
889 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
890 return;
891 }
892 // Another quick case: check if this is part of a set of affiliated
893 // tasks that are at the top.
894 if (isAffiliated && recentsCount > 0 && task.inRecents
895 && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
896 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
897 + " at top when adding " + task);
898 return;
899 }
900
901 boolean needAffiliationFix = false;
902
903 // Slightly less quick case: the task is already in recents, so all we need
904 // to do is move it.
905 if (task.inRecents) {
906 int taskIndex = mTasks.indexOf(task);
907 if (taskIndex >= 0) {
908 if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
909 // Simple case: this is not an affiliated task, so we just move it to the front.
910 mTasks.remove(taskIndex);
911 mTasks.add(0, task);
912 notifyTaskPersisterLocked(task, false);
913 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
914 + " from " + taskIndex);
915 return;
916 } else {
917 // More complicated: need to keep all affiliated tasks together.
918 if (moveAffiliatedTasksToFront(task, taskIndex)) {
919 // All went well.
920 return;
921 }
922
923 // Uh oh... something bad in the affiliation chain, try to rebuild
924 // everything and then go through our general path of adding a new task.
925 needAffiliationFix = true;
926 }
927 } else {
928 Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
929 needAffiliationFix = true;
930 }
931 }
932
933 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
Winson Chung079221f2017-12-13 17:43:34 -0800934 removeForAddTask(task);
Winson Chung1dbc8112017-09-28 18:05:31 -0700935
936 task.inRecents = true;
937 if (!isAffiliated || needAffiliationFix) {
938 // If this is a simple non-affiliated task, or we had some failure trying to
939 // handle it as part of an affilated task, then just place it at the top.
940 mTasks.add(0, task);
941 notifyTaskAdded(task);
942 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
943 } else if (isAffiliated) {
944 // If this is a new affiliated task, then move all of the affiliated tasks
945 // to the front and insert this new one.
946 TaskRecord other = task.mNextAffiliate;
947 if (other == null) {
948 other = task.mPrevAffiliate;
949 }
950 if (other != null) {
951 int otherIndex = mTasks.indexOf(other);
952 if (otherIndex >= 0) {
953 // Insert new task at appropriate location.
954 int taskIndex;
955 if (other == task.mNextAffiliate) {
956 // We found the index of our next affiliation, which is who is
957 // before us in the list, so add after that point.
958 taskIndex = otherIndex+1;
959 } else {
960 // We found the index of our previous affiliation, which is who is
961 // after us in the list, so add at their position.
962 taskIndex = otherIndex;
963 }
964 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
965 "addRecent: new affiliated task added at " + taskIndex + ": " + task);
966 mTasks.add(taskIndex, task);
967 notifyTaskAdded(task);
968
969 // Now move everything to the front.
970 if (moveAffiliatedTasksToFront(task, taskIndex)) {
971 // All went well.
972 return;
973 }
974
975 // Uh oh... something bad in the affiliation chain, try to rebuild
976 // everything and then go through our general path of adding a new task.
977 needAffiliationFix = true;
978 } else {
979 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
980 "addRecent: couldn't find other affiliation " + other);
981 needAffiliationFix = true;
982 }
983 } else {
984 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
985 "addRecent: adding affiliated task without next/prev:" + task);
986 needAffiliationFix = true;
987 }
988 }
989
990 if (needAffiliationFix) {
991 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
992 cleanupLocked(task.userId);
993 }
Winson Chungd6aa3db2017-10-05 17:18:43 -0700994
995 // Trim the set of tasks to the active set
996 trimInactiveRecentTasks();
Winson Chung1dbc8112017-09-28 18:05:31 -0700997 }
998
999 /**
1000 * Add the task to the bottom if possible.
1001 */
1002 boolean addToBottom(TaskRecord task) {
1003 if (!canAddTaskWithoutTrim(task)) {
1004 // Adding this task would cause the task to be removed (since it's appended at
1005 // the bottom and would be trimmed) so just return now
1006 return false;
1007 }
1008
1009 add(task);
1010 return true;
1011 }
1012
1013 /**
1014 * Remove a task from the recent tasks list.
1015 */
1016 void remove(TaskRecord task) {
1017 mTasks.remove(task);
1018 notifyTaskRemoved(task, !TRIMMED);
1019 }
1020
1021 /**
1022 * Trims the recents task list to the global max number of recents.
1023 */
Winson Chungd6aa3db2017-10-05 17:18:43 -07001024 private void trimInactiveRecentTasks() {
Winson Chung1dbc8112017-09-28 18:05:31 -07001025 int recentsCount = mTasks.size();
Winson Chungd6aa3db2017-10-05 17:18:43 -07001026
1027 // Remove from the end of the list until we reach the max number of recents
1028 while (recentsCount > mGlobalMaxNumTasks) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001029 final TaskRecord tr = mTasks.remove(recentsCount - 1);
Winson Chungd6aa3db2017-10-05 17:18:43 -07001030 notifyTaskRemoved(tr, TRIMMED);
Winson Chung1dbc8112017-09-28 18:05:31 -07001031 recentsCount--;
Winson Chungd6aa3db2017-10-05 17:18:43 -07001032 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
1033 + " max=" + mGlobalMaxNumTasks);
Winson Chung1dbc8112017-09-28 18:05:31 -07001034 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001035
1036 // Remove any tasks that belong to currently quiet profiles
1037 final int[] profileUserIds = mUserController.getCurrentProfileIds();
1038 mTmpQuietProfileUserIds.clear();
1039 for (int userId : profileUserIds) {
1040 final UserInfo userInfo = mUserController.getUserInfo(userId);
Winson Chung129771a02017-10-18 13:49:42 -07001041 if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001042 mTmpQuietProfileUserIds.put(userId, true);
1043 }
1044 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
1045 + " quiet=" + mTmpQuietProfileUserIds.get(userId));
1046 }
1047
1048 // Remove any inactive tasks, calculate the latest set of visible tasks
1049 int numVisibleTasks = 0;
1050 for (int i = 0; i < mTasks.size();) {
1051 final TaskRecord task = mTasks.get(i);
1052
1053 if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
1054 if (!mHasVisibleRecentTasks) {
1055 // Keep all active tasks if visible recent tasks is not supported
1056 i++;
1057 continue;
1058 }
1059
1060 if (!isVisibleRecentTask(task)) {
1061 // Keep all active-but-invisible tasks
1062 i++;
1063 continue;
1064 } else {
1065 numVisibleTasks++;
Winson Chung0ec2a352017-10-26 11:38:30 -07001066 if (isInVisibleRange(task, numVisibleTasks) || !isTrimmable(task)) {
Winson Chungd6aa3db2017-10-05 17:18:43 -07001067 // Keep visible tasks in range
1068 i++;
1069 continue;
1070 } else {
Winson Chung0ec2a352017-10-26 11:38:30 -07001071 // Fall through to trim visible tasks that are no longer in range and
1072 // trimmable
Winson Chungd6aa3db2017-10-05 17:18:43 -07001073 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
1074 "Trimming out-of-range visible task=" + task);
1075 }
1076 }
1077 } else {
1078 // Fall through to trim inactive tasks
1079 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
1080 }
1081
1082 // Task is no longer active, trim it from the list
1083 mTasks.remove(task);
1084 notifyTaskRemoved(task, TRIMMED);
1085 notifyTaskPersisterLocked(task, false /* flush */);
1086 }
1087 }
1088
1089 /**
1090 * @return whether the given task should be considered active.
1091 */
1092 private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
1093 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
1094 + " globalMax=" + mGlobalMaxNumTasks);
1095
1096 if (quietProfileUserIds.get(task.userId)) {
1097 // Quiet profile user's tasks are never active
1098 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
1099 return false;
1100 }
1101
1102 if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
1103 // Keep the task active if its affiliated task is also active
1104 final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
1105 if (affiliatedTask != null) {
1106 if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
1107 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
1108 "\taffiliatedWithTask=" + affiliatedTask + " is not active");
1109 return false;
1110 }
1111 }
1112 }
1113
1114 // All other tasks are considered active
1115 return true;
1116 }
1117
1118 /**
1119 * @return whether the given active task should be presented to the user through SystemUI.
1120 */
1121 private boolean isVisibleRecentTask(TaskRecord task) {
1122 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
1123 + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
1124 + " sessionDuration=" + mActiveTasksSessionDurationMs
1125 + " inactiveDuration=" + task.getInactiveDuration()
1126 + " activityType=" + task.getActivityType()
Vadim Trysheveff42d32018-03-05 18:33:48 -08001127 + " windowingMode=" + task.getWindowingMode()
1128 + " intentFlags=" + task.getBaseIntent().getFlags());
Winson Chungd6aa3db2017-10-05 17:18:43 -07001129
Winson Chungd6aa3db2017-10-05 17:18:43 -07001130 switch (task.getActivityType()) {
1131 case ACTIVITY_TYPE_HOME:
1132 case ACTIVITY_TYPE_RECENTS:
Vadim Trysheveff42d32018-03-05 18:33:48 -08001133 // Ignore certain activity types completely
Winson Chungd6aa3db2017-10-05 17:18:43 -07001134 return false;
Vadim Trysheveff42d32018-03-05 18:33:48 -08001135 case ACTIVITY_TYPE_ASSISTANT:
1136 // Ignore assistant that chose to be excluded from Recents, even if it's a top
1137 // task.
1138 if ((task.getBaseIntent().getFlags()
1139 & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1140 == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
1141 return false;
1142 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001143 }
1144
1145 // Ignore certain windowing modes
1146 switch (task.getWindowingMode()) {
1147 case WINDOWING_MODE_PINNED:
1148 return false;
1149 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
1150 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
1151 final ActivityStack stack = task.getStack();
1152 if (stack != null && stack.topTask() == task) {
1153 // Only the non-top task of the primary split screen mode is visible
1154 return false;
1155 }
1156 }
1157
Benjamin Franz7dcbfb02018-01-16 15:16:16 +00001158 // If we're in lock task mode, ignore the root task
Bryce Lee2b8e0372018-04-05 17:01:37 -07001159 if (task == mService.getLockTaskController().getRootTask()) {
Benjamin Franz7dcbfb02018-01-16 15:16:16 +00001160 return false;
1161 }
1162
Winson Chungd6aa3db2017-10-05 17:18:43 -07001163 return true;
1164 }
1165
1166 /**
1167 * @return whether the given visible task is within the policy range.
1168 */
1169 private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
1170 // Keep the last most task even if it is excluded from recents
1171 final boolean isExcludeFromRecents =
1172 (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
1173 == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
1174 if (isExcludeFromRecents) {
1175 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
1176 return numVisibleTasks == 1;
1177 }
1178
1179 if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
1180 // Always keep up to the min number of recent tasks, after that fall through to the
1181 // checks below
1182 return true;
1183 }
1184
1185 if (mMaxNumVisibleTasks >= 0) {
1186 // Always keep up to the max number of recent tasks, but return false afterwards
1187 return numVisibleTasks <= mMaxNumVisibleTasks;
1188 }
1189
1190 if (mActiveTasksSessionDurationMs > 0) {
1191 // Keep the task if the inactive time is within the session window, this check must come
1192 // after the checks for the min/max visible task range
1193 if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
1194 return true;
1195 }
1196 }
1197
1198 return false;
Winson Chung1dbc8112017-09-28 18:05:31 -07001199 }
1200
1201 /**
Winson Chung0ec2a352017-10-26 11:38:30 -07001202 * @return whether the given task can be trimmed even if it is outside the visible range.
1203 */
1204 protected boolean isTrimmable(TaskRecord task) {
1205 final ActivityStack stack = task.getStack();
1206 final ActivityStack homeStack = mService.mStackSupervisor.mHomeStack;
1207
1208 // No stack for task, just trim it
1209 if (stack == null) {
1210 return true;
1211 }
1212
1213 // Ignore tasks from different displays
1214 if (stack.getDisplay() != homeStack.getDisplay()) {
1215 return false;
1216 }
1217
1218 // Trim tasks that are in stacks that are behind the home stack
1219 final ActivityDisplay display = stack.getDisplay();
1220 return display.getIndexOf(stack) < display.getIndexOf(homeStack);
1221 }
1222
1223 /**
Winson Chung1dbc8112017-09-28 18:05:31 -07001224 * If needed, remove oldest existing entries in recents that are for the same kind
1225 * of task as the given one.
1226 */
Winson Chung079221f2017-12-13 17:43:34 -08001227 private void removeForAddTask(TaskRecord task) {
1228 final int removeIndex = findRemoveIndexForAddTask(task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001229 if (removeIndex == -1) {
1230 // Nothing to trim
1231 return;
1232 }
1233
Winson Chungd6aa3db2017-10-05 17:18:43 -07001234 // There is a similar task that will be removed for the addition of {@param task}, but it
1235 // can be the same task, and if so, the task will be re-added in add(), so skip the
1236 // callbacks here.
Winson Chung1dbc8112017-09-28 18:05:31 -07001237 final TaskRecord removedTask = mTasks.remove(removeIndex);
1238 if (removedTask != task) {
Winson Chung079221f2017-12-13 17:43:34 -08001239 notifyTaskRemoved(removedTask, !TRIMMED);
Winson Chungd6aa3db2017-10-05 17:18:43 -07001240 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
1241 + " for addition of task=" + task);
Winson Chung1dbc8112017-09-28 18:05:31 -07001242 }
Winson Chungd6aa3db2017-10-05 17:18:43 -07001243 notifyTaskPersisterLocked(removedTask, false /* flush */);
Winson Chung1dbc8112017-09-28 18:05:31 -07001244 }
1245
1246 /**
1247 * Find the task that would be removed if the given {@param task} is added to the recent tasks
1248 * list (if any).
1249 */
Winson Chung079221f2017-12-13 17:43:34 -08001250 private int findRemoveIndexForAddTask(TaskRecord task) {
Winson Chung6c9dcad2018-03-13 16:57:55 -07001251 final int recentsCount = mTasks.size();
Winson Chung1dbc8112017-09-28 18:05:31 -07001252 final Intent intent = task.intent;
1253 final boolean document = intent != null && intent.isDocument();
1254 int maxRecents = task.maxRecents - 1;
Winson Chung1dbc8112017-09-28 18:05:31 -07001255 for (int i = 0; i < recentsCount; i++) {
1256 final TaskRecord tr = mTasks.get(i);
Winson Chung1dbc8112017-09-28 18:05:31 -07001257 if (task != tr) {
Winson Chungb7667272018-04-06 10:18:39 -07001258 if (!task.hasCompatibleActivityType(tr) || task.userId != tr.userId) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001259 continue;
1260 }
1261 final Intent trIntent = tr.intent;
1262 final boolean sameAffinity =
1263 task.affinity != null && task.affinity.equals(tr.affinity);
1264 final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
1265 boolean multiTasksAllowed = false;
1266 final int flags = intent.getFlags();
1267 if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
1268 && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
1269 multiTasksAllowed = true;
1270 }
1271 final boolean trIsDocument = trIntent != null && trIntent.isDocument();
1272 final boolean bothDocuments = document && trIsDocument;
1273 if (!sameAffinity && !sameIntent && !bothDocuments) {
1274 continue;
1275 }
1276
1277 if (bothDocuments) {
1278 // Do these documents belong to the same activity?
1279 final boolean sameActivity = task.realActivity != null
1280 && tr.realActivity != null
1281 && task.realActivity.equals(tr.realActivity);
1282 if (!sameActivity) {
1283 // If the document is open in another app or is not the same document, we
1284 // don't need to trim it.
1285 continue;
1286 } else if (maxRecents > 0) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001287 --maxRecents;
1288 if (!sameIntent || multiTasksAllowed) {
1289 // We don't want to trim if we are not over the max allowed entries and
1290 // the tasks are not of the same intent filter, or multiple entries for
1291 // the task is allowed.
1292 continue;
1293 }
1294 }
1295 // Hit the maximum number of documents for this task. Fall through
1296 // and remove this document from recents.
1297 } else if (document || trIsDocument) {
1298 // Only one of these is a document. Not the droid we're looking for.
1299 continue;
1300 }
1301 }
1302 return i;
1303 }
1304 return -1;
1305 }
1306
1307 // Extract the affiliates of the chain containing recent at index start.
1308 private int processNextAffiliateChainLocked(int start) {
1309 final TaskRecord startTask = mTasks.get(start);
1310 final int affiliateId = startTask.mAffiliatedTaskId;
1311
1312 // Quick identification of isolated tasks. I.e. those not launched behind.
1313 if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
1314 startTask.mNextAffiliate == null) {
1315 // There is still a slim chance that there are other tasks that point to this task
1316 // and that the chain is so messed up that this task no longer points to them but
1317 // the gain of this optimization outweighs the risk.
1318 startTask.inRecents = true;
1319 return start + 1;
1320 }
1321
1322 // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
1323 mTmpRecents.clear();
1324 for (int i = mTasks.size() - 1; i >= start; --i) {
1325 final TaskRecord task = mTasks.get(i);
1326 if (task.mAffiliatedTaskId == affiliateId) {
1327 mTasks.remove(i);
1328 mTmpRecents.add(task);
1329 }
1330 }
1331
1332 // Sort them all by taskId. That is the order they were create in and that order will
1333 // always be correct.
1334 Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
1335
1336 // Go through and fix up the linked list.
1337 // The first one is the end of the chain and has no next.
1338 final TaskRecord first = mTmpRecents.get(0);
1339 first.inRecents = true;
1340 if (first.mNextAffiliate != null) {
1341 Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
1342 first.setNextAffiliate(null);
1343 notifyTaskPersisterLocked(first, false);
1344 }
1345 // Everything in the middle is doubly linked from next to prev.
1346 final int tmpSize = mTmpRecents.size();
1347 for (int i = 0; i < tmpSize - 1; ++i) {
1348 final TaskRecord next = mTmpRecents.get(i);
1349 final TaskRecord prev = mTmpRecents.get(i + 1);
1350 if (next.mPrevAffiliate != prev) {
1351 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
1352 " setting prev=" + prev);
1353 next.setPrevAffiliate(prev);
1354 notifyTaskPersisterLocked(next, false);
1355 }
1356 if (prev.mNextAffiliate != next) {
1357 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
1358 " setting next=" + next);
1359 prev.setNextAffiliate(next);
1360 notifyTaskPersisterLocked(prev, false);
1361 }
1362 prev.inRecents = true;
1363 }
1364 // The last one is the beginning of the list and has no prev.
1365 final TaskRecord last = mTmpRecents.get(tmpSize - 1);
1366 if (last.mPrevAffiliate != null) {
1367 Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
1368 last.setPrevAffiliate(null);
1369 notifyTaskPersisterLocked(last, false);
1370 }
1371
Winson Chungd6aa3db2017-10-05 17:18:43 -07001372 // Insert the group back into mTmpTasks at start.
Winson Chung1dbc8112017-09-28 18:05:31 -07001373 mTasks.addAll(start, mTmpRecents);
1374 mTmpRecents.clear();
1375
1376 // Let the caller know where we left off.
1377 return start + tmpSize;
1378 }
1379
1380 private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
1381 int recentsCount = mTasks.size();
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001382 TaskRecord top = task;
1383 int topIndex = taskIndex;
1384 while (top.mNextAffiliate != null && topIndex > 0) {
1385 top = top.mNextAffiliate;
1386 topIndex--;
1387 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001388 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001389 + topIndex + " from intial " + taskIndex);
1390 // Find the end of the chain, doing a sanity check along the way.
1391 boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
1392 int endIndex = topIndex;
1393 TaskRecord prev = top;
1394 while (endIndex < recentsCount) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001395 TaskRecord cur = mTasks.get(endIndex);
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001396 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001397 + endIndex + " " + cur);
1398 if (cur == top) {
1399 // Verify start of the chain.
1400 if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
1401 Slog.wtf(TAG, "Bad chain @" + endIndex
1402 + ": first task has next affiliate: " + prev);
1403 sane = false;
1404 break;
1405 }
1406 } else {
1407 // Verify middle of the chain's next points back to the one before.
1408 if (cur.mNextAffiliate != prev
1409 || cur.mNextAffiliateTaskId != prev.taskId) {
1410 Slog.wtf(TAG, "Bad chain @" + endIndex
1411 + ": middle task " + cur + " @" + endIndex
1412 + " has bad next affiliate "
1413 + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
1414 + ", expected " + prev);
1415 sane = false;
1416 break;
1417 }
1418 }
1419 if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
1420 // Chain ends here.
1421 if (cur.mPrevAffiliate != null) {
1422 Slog.wtf(TAG, "Bad chain @" + endIndex
1423 + ": last task " + cur + " has previous affiliate "
1424 + cur.mPrevAffiliate);
1425 sane = false;
1426 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001427 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001428 break;
1429 } else {
1430 // Verify middle of the chain's prev points to a valid item.
1431 if (cur.mPrevAffiliate == null) {
1432 Slog.wtf(TAG, "Bad chain @" + endIndex
1433 + ": task " + cur + " has previous affiliate "
1434 + cur.mPrevAffiliate + " but should be id "
1435 + cur.mPrevAffiliate);
1436 sane = false;
1437 break;
1438 }
1439 }
1440 if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
1441 Slog.wtf(TAG, "Bad chain @" + endIndex
1442 + ": task " + cur + " has affiliated id "
1443 + cur.mAffiliatedTaskId + " but should be "
1444 + task.mAffiliatedTaskId);
1445 sane = false;
1446 break;
1447 }
1448 prev = cur;
1449 endIndex++;
1450 if (endIndex >= recentsCount) {
1451 Slog.wtf(TAG, "Bad chain ran off index " + endIndex
1452 + ": last task " + prev);
1453 sane = false;
1454 break;
1455 }
1456 }
1457 if (sane) {
1458 if (endIndex < taskIndex) {
1459 Slog.wtf(TAG, "Bad chain @" + endIndex
1460 + ": did not extend to task " + task + " @" + taskIndex);
1461 sane = false;
1462 }
1463 }
1464 if (sane) {
1465 // All looks good, we can just move all of the affiliated tasks
1466 // to the top.
1467 for (int i=topIndex; i<=endIndex; i++) {
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001468 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001469 + " from " + i + " to " + (i-topIndex));
Winson Chung1dbc8112017-09-28 18:05:31 -07001470 TaskRecord cur = mTasks.remove(i);
1471 mTasks.add(i - topIndex, cur);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001472 }
Wale Ogunwaleee006da2015-03-30 14:49:25 -07001473 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001474 + " to " + endIndex);
1475 return true;
1476 }
1477
1478 // Whoops, couldn't do it.
1479 return false;
1480 }
1481
Winson Chung1dbc8112017-09-28 18:05:31 -07001482 void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
1483 pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
Winson Chung3f0e59a2017-10-25 10:19:05 -07001484 pw.println("mRecentsUid=" + mRecentsUid);
1485 pw.println("mRecentsComponent=" + mRecentsComponent);
Winson Chung1dbc8112017-09-28 18:05:31 -07001486 if (mTasks.isEmpty()) {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001487 return;
1488 }
1489
Winson Chung3f0e59a2017-10-25 10:19:05 -07001490 boolean printedAnything = false;
1491 boolean printedHeader = false;
Winson Chung1dbc8112017-09-28 18:05:31 -07001492 final int size = mTasks.size();
1493 for (int i = 0; i < size; i++) {
1494 final TaskRecord tr = mTasks.get(i);
1495 if (dumpPackage != null && (tr.realActivity == null ||
1496 !dumpPackage.equals(tr.realActivity.getPackageName()))) {
1497 continue;
1498 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001499
Winson Chung3f0e59a2017-10-25 10:19:05 -07001500 if (!printedHeader) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001501 pw.println(" Recent tasks:");
Winson Chung3f0e59a2017-10-25 10:19:05 -07001502 printedHeader = true;
1503 printedAnything = true;
Winson Chung1dbc8112017-09-28 18:05:31 -07001504 }
1505 pw.print(" * Recent #"); pw.print(i); pw.print(": ");
1506 pw.println(tr);
1507 if (dumpAll) {
1508 tr.dump(pw, " ");
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001509 }
1510 }
1511
Winson Chung3f0e59a2017-10-25 10:19:05 -07001512 if (!printedAnything) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001513 pw.println(" (nothing)");
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001514 }
1515 }
1516
1517 /**
Winson Chung1dbc8112017-09-28 18:05:31 -07001518 * Creates a new RecentTaskInfo from a TaskRecord.
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001519 */
Winson Chung61c9e5a2017-10-11 10:39:32 -07001520 ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
Winson Chung1dbc8112017-09-28 18:05:31 -07001521 // Compose the recent task info
1522 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
1523 rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
1524 rti.persistentId = tr.taskId;
1525 rti.baseIntent = new Intent(tr.getBaseIntent());
1526 rti.origActivity = tr.origActivity;
1527 rti.realActivity = tr.realActivity;
1528 rti.description = tr.lastDescription;
1529 rti.stackId = tr.getStackId();
1530 rti.userId = tr.userId;
1531 rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
Winson Chung1dbc8112017-09-28 18:05:31 -07001532 rti.lastActiveTime = tr.lastActiveTime;
1533 rti.affiliatedTaskId = tr.mAffiliatedTaskId;
1534 rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
1535 rti.numActivities = 0;
Bryce Leef3c6a472017-11-14 14:53:06 -08001536 if (!tr.matchParentBounds()) {
1537 rti.bounds = new Rect(tr.getOverrideBounds());
Winson Chung1dbc8112017-09-28 18:05:31 -07001538 }
1539 rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
1540 rti.resizeMode = tr.mResizeMode;
1541 rti.configuration.setTo(tr.getConfiguration());
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001542
Winson Chung61c9e5a2017-10-11 10:39:32 -07001543 tr.getNumRunningActivities(mTmpReport);
1544 rti.numActivities = mTmpReport.numActivities;
1545 rti.baseActivity = (mTmpReport.base != null) ? mTmpReport.base.intent.getComponent() : null;
1546 rti.topActivity = (mTmpReport.top != null) ? mTmpReport.top.intent.getComponent() : null;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001547
Winson Chung1dbc8112017-09-28 18:05:31 -07001548 return rti;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001549 }
Wale Ogunwalec82f2f52014-12-09 09:32:50 -08001550}