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