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