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