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 && !mService.getLockTaskController().isTaskWhitelisted(task)) { 659 remove(task); 660 } 661 } 662 } 663 removeTasksByPackageName(String packageName, int userId)664 void removeTasksByPackageName(String packageName, int userId) { 665 for (int i = mTasks.size() - 1; i >= 0; --i) { 666 final Task task = mTasks.get(i); 667 final String taskPackageName = 668 task.getBaseIntent().getComponent().getPackageName(); 669 if (task.mUserId != userId) continue; 670 if (!taskPackageName.equals(packageName)) continue; 671 672 mSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-package-task"); 673 } 674 } 675 removeAllVisibleTasks(int userId)676 void removeAllVisibleTasks(int userId) { 677 Set<Integer> profileIds = getProfileIds(userId); 678 for (int i = mTasks.size() - 1; i >= 0; --i) { 679 final Task task = mTasks.get(i); 680 if (!profileIds.contains(task.mUserId)) continue; 681 if (isVisibleRecentTask(task)) { 682 mTasks.remove(i); 683 notifyTaskRemoved(task, true /* wasTrimmed */, true /* killProcess */); 684 } 685 } 686 } 687 cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, int userId)688 void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, 689 int userId) { 690 for (int i = mTasks.size() - 1; i >= 0; --i) { 691 final Task task = mTasks.get(i); 692 if (userId != UserHandle.USER_ALL && task.mUserId != userId) { 693 continue; 694 } 695 696 ComponentName cn = task.intent != null ? task.intent.getComponent() : null; 697 final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName) 698 && (filterByClasses == null || filterByClasses.contains(cn.getClassName())); 699 if (sameComponent) { 700 mSupervisor.removeTask(task, false, REMOVE_FROM_RECENTS, "disabled-package"); 701 } 702 } 703 } 704 705 /** 706 * Update the recent tasks lists: make sure tasks should still be here (their 707 * applications / activities still exist), update their availability, fix-up ordering 708 * of affiliations. 709 */ cleanupLocked(int userId)710 void cleanupLocked(int userId) { 711 int recentsCount = mTasks.size(); 712 if (recentsCount == 0) { 713 // Happens when called from the packagemanager broadcast before boot, 714 // or just any empty list. 715 return; 716 } 717 718 // Clear the temp lists 719 mTmpAvailActCache.clear(); 720 mTmpAvailAppCache.clear(); 721 722 final IPackageManager pm = AppGlobals.getPackageManager(); 723 for (int i = recentsCount - 1; i >= 0; i--) { 724 final Task task = mTasks.get(i); 725 if (userId != UserHandle.USER_ALL && task.mUserId != userId) { 726 // Only look at tasks for the user ID of interest. 727 continue; 728 } 729 if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) { 730 // This situation is broken, and we should just get rid of it now. 731 remove(task); 732 Slog.w(TAG, "Removing auto-remove without activity: " + task); 733 continue; 734 } 735 // Check whether this activity is currently available. 736 if (task.realActivity != null) { 737 ActivityInfo ai = mTmpAvailActCache.get(task.realActivity); 738 if (ai == null) { 739 try { 740 // At this first cut, we're only interested in 741 // activities that are fully runnable based on 742 // current system state. 743 ai = pm.getActivityInfo(task.realActivity, 744 PackageManager.MATCH_DEBUG_TRIAGED_MISSING 745 | ActivityManagerService.STOCK_PM_FLAGS, userId); 746 } catch (RemoteException e) { 747 // Will never happen. 748 continue; 749 } 750 if (ai == null) { 751 ai = NO_ACTIVITY_INFO_TOKEN; 752 } 753 mTmpAvailActCache.put(task.realActivity, ai); 754 } 755 if (ai == NO_ACTIVITY_INFO_TOKEN) { 756 // This could be either because the activity no longer exists, or the 757 // app is temporarily gone. For the former we want to remove the recents 758 // entry; for the latter we want to mark it as unavailable. 759 ApplicationInfo app = mTmpAvailAppCache 760 .get(task.realActivity.getPackageName()); 761 if (app == null) { 762 try { 763 app = pm.getApplicationInfo(task.realActivity.getPackageName(), 764 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 765 } catch (RemoteException e) { 766 // Will never happen. 767 continue; 768 } 769 if (app == null) { 770 app = NO_APPLICATION_INFO_TOKEN; 771 } 772 mTmpAvailAppCache.put(task.realActivity.getPackageName(), app); 773 } 774 if (app == NO_APPLICATION_INFO_TOKEN 775 || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 776 // Doesn't exist any more! Good-bye. 777 remove(task); 778 Slog.w(TAG, "Removing no longer valid recent: " + task); 779 continue; 780 } else { 781 // Otherwise just not available for now. 782 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, 783 "Making recent unavailable: " + task); 784 task.isAvailable = false; 785 } 786 } else { 787 if (!ai.enabled || !ai.applicationInfo.enabled 788 || (ai.applicationInfo.flags 789 & ApplicationInfo.FLAG_INSTALLED) == 0) { 790 if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, 791 "Making recent unavailable: " + task 792 + " (enabled=" + ai.enabled + "/" 793 + ai.applicationInfo.enabled 794 + " flags=" 795 + Integer.toHexString(ai.applicationInfo.flags) 796 + ")"); 797 task.isAvailable = false; 798 } else { 799 if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS, 800 "Making recent available: " + task); 801 task.isAvailable = true; 802 } 803 } 804 } 805 } 806 807 // Verify the affiliate chain for each task. 808 int i = 0; 809 recentsCount = mTasks.size(); 810 while (i < recentsCount) { 811 i = processNextAffiliateChainLocked(i); 812 } 813 // recent tasks are now in sorted, affiliated order. 814 } 815 816 /** 817 * @return whether the given {@param task} can be added to the list without causing another 818 * task to be trimmed as a result of that add. 819 */ canAddTaskWithoutTrim(Task task)820 private boolean canAddTaskWithoutTrim(Task task) { 821 return findRemoveIndexForAddTask(task) == -1; 822 } 823 824 /** 825 * Returns the list of {@link ActivityManager.AppTask}s. 826 */ getAppTasksList(int callingUid, String callingPackage)827 ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) { 828 final ArrayList<IBinder> list = new ArrayList<>(); 829 final int size = mTasks.size(); 830 for (int i = 0; i < size; i++) { 831 final Task task = mTasks.get(i); 832 // Skip tasks that do not match the caller. We don't need to verify 833 // callingPackage, because we are also limiting to callingUid and know 834 // that will limit to the correct security sandbox. 835 if (task.effectiveUid != callingUid) { 836 continue; 837 } 838 Intent intent = task.getBaseIntent(); 839 if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) { 840 continue; 841 } 842 AppTaskImpl taskImpl = new AppTaskImpl(mService, task.mTaskId, callingUid); 843 list.add(taskImpl.asBinder()); 844 } 845 return list; 846 } 847 848 @VisibleForTesting getProfileIds(int userId)849 Set<Integer> getProfileIds(int userId) { 850 Set<Integer> userIds = new ArraySet<>(); 851 int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */); 852 for (int i = 0; i < profileIds.length; i++) { 853 userIds.add(Integer.valueOf(profileIds[i])); 854 } 855 return userIds; 856 } 857 858 @VisibleForTesting getUserInfo(int userId)859 UserInfo getUserInfo(int userId) { 860 return mService.getUserManager().getUserInfo(userId); 861 } 862 863 @VisibleForTesting getCurrentProfileIds()864 int[] getCurrentProfileIds() { 865 return mService.mAmInternal.getCurrentProfileIds(); 866 } 867 868 @VisibleForTesting isUserRunning(int userId, int flags)869 boolean isUserRunning(int userId, int flags) { 870 return mService.mAmInternal.isUserRunning(userId, flags); 871 } 872 873 /** 874 * @return the list of recent tasks for presentation. 875 */ getRecentTasks(int maxNum, int flags, boolean getTasksAllowed, int userId, int callingUid)876 ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, 877 boolean getTasksAllowed, int userId, int callingUid) { 878 return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed, 879 userId, callingUid)); 880 } 881 882 /** 883 * @return the list of recent tasks for presentation. 884 */ getRecentTasksImpl(int maxNum, int flags, boolean getTasksAllowed, int userId, int callingUid)885 private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags, 886 boolean getTasksAllowed, int userId, int callingUid) { 887 final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0; 888 889 if (!isUserRunning(userId, FLAG_AND_UNLOCKED)) { 890 Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents"); 891 return new ArrayList<>(); 892 } 893 loadUserRecentsLocked(userId); 894 895 final Set<Integer> includedUsers = getProfileIds(userId); 896 includedUsers.add(Integer.valueOf(userId)); 897 898 final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); 899 final int size = mTasks.size(); 900 int numVisibleTasks = 0; 901 for (int i = 0; i < size; i++) { 902 final Task task = mTasks.get(i); 903 904 if (isVisibleRecentTask(task)) { 905 numVisibleTasks++; 906 if (isInVisibleRange(task, i, numVisibleTasks, withExcluded)) { 907 // Fall through 908 } else { 909 // Not in visible range 910 continue; 911 } 912 } else { 913 // Not visible 914 continue; 915 } 916 917 // Skip remaining tasks once we reach the requested size 918 if (res.size() >= maxNum) { 919 continue; 920 } 921 922 // Only add calling user or related users recent tasks 923 if (!includedUsers.contains(Integer.valueOf(task.mUserId))) { 924 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + task); 925 continue; 926 } 927 928 if (task.realActivitySuspended) { 929 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + task); 930 continue; 931 } 932 933 if (!getTasksAllowed) { 934 // If the caller doesn't have the GET_TASKS permission, then only 935 // allow them to see a small subset of tasks -- their own and home. 936 if (!task.isActivityTypeHome() && task.effectiveUid != callingUid) { 937 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + task); 938 continue; 939 } 940 } 941 942 if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) { 943 // Don't include auto remove tasks that are finished or finishing. 944 if (DEBUG_RECENTS) { 945 Slog.d(TAG_RECENTS, "Skipping, auto-remove without activity: " + task); 946 } 947 continue; 948 } 949 if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !task.isAvailable) { 950 if (DEBUG_RECENTS) { 951 Slog.d(TAG_RECENTS, "Skipping, unavail real act: " + task); 952 } 953 continue; 954 } 955 956 if (!task.mUserSetupComplete) { 957 // Don't include task launched while user is not done setting-up. 958 if (DEBUG_RECENTS) { 959 Slog.d(TAG_RECENTS, "Skipping, user setup not complete: " + task); 960 } 961 continue; 962 } 963 964 res.add(createRecentTaskInfo(task, true /* stripExtras */)); 965 } 966 return res; 967 } 968 969 /** 970 * @return the list of persistable task ids. 971 */ getPersistableTaskIds(ArraySet<Integer> persistentTaskIds)972 void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) { 973 final int size = mTasks.size(); 974 for (int i = 0; i < size; i++) { 975 final Task task = mTasks.get(i); 976 if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task 977 + " persistable=" + task.isPersistable); 978 final ActivityStack stack = task.getStack(); 979 if ((task.isPersistable || task.inRecents) 980 && (stack == null || !stack.isHomeOrRecentsStack())) { 981 if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); 982 persistentTaskIds.add(task.mTaskId); 983 } else { 984 if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" 985 + task); 986 } 987 } 988 } 989 990 @VisibleForTesting getRawTasks()991 ArrayList<Task> getRawTasks() { 992 return mTasks; 993 } 994 995 /** 996 * @return ids of tasks that are presented in Recents UI. 997 */ getRecentTaskIds()998 SparseBooleanArray getRecentTaskIds() { 999 final SparseBooleanArray res = new SparseBooleanArray(); 1000 final int size = mTasks.size(); 1001 int numVisibleTasks = 0; 1002 for (int i = 0; i < size; i++) { 1003 final Task task = mTasks.get(i); 1004 if (isVisibleRecentTask(task)) { 1005 numVisibleTasks++; 1006 if (isInVisibleRange(task, i, numVisibleTasks, false /* skipExcludedCheck */)) { 1007 res.put(task.mTaskId, true); 1008 } 1009 } 1010 } 1011 return res; 1012 } 1013 1014 /** 1015 * @return the task in the task list with the given {@param id} if one exists. 1016 */ getTask(int id)1017 Task getTask(int id) { 1018 final int recentsCount = mTasks.size(); 1019 for (int i = 0; i < recentsCount; i++) { 1020 Task task = mTasks.get(i); 1021 if (task.mTaskId == id) { 1022 return task; 1023 } 1024 } 1025 return null; 1026 } 1027 1028 /** 1029 * Add a new task to the recent tasks list. 1030 */ add(Task task)1031 void add(Task task) { 1032 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task); 1033 1034 // Only allow trimming task if it is not updating visibility for activities, so the caller 1035 // doesn't need to handle unexpected size and index when looping task containers. 1036 final boolean canTrimTask = !mSupervisor.inActivityVisibilityUpdate(); 1037 1038 // Clean up the hidden tasks when going to home because the user may not be unable to return 1039 // to the task from recents. 1040 if (canTrimTask && !mHiddenTasks.isEmpty() && task.isActivityTypeHome()) { 1041 removeUnreachableHiddenTasks(task.getWindowingMode()); 1042 } 1043 1044 final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId 1045 || task.mNextAffiliateTaskId != INVALID_TASK_ID 1046 || task.mPrevAffiliateTaskId != INVALID_TASK_ID; 1047 1048 int recentsCount = mTasks.size(); 1049 // Quick case: never add voice sessions. 1050 // TODO: VI what about if it's just an activity? 1051 // Probably nothing to do here 1052 if (task.voiceSession != null) { 1053 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, 1054 "addRecent: not adding voice interaction " + task); 1055 return; 1056 } 1057 // Another quick case: check if the top-most recent task is the same. 1058 if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) { 1059 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task); 1060 return; 1061 } 1062 // Another quick case: check if this is part of a set of affiliated 1063 // tasks that are at the top. 1064 if (isAffiliated && recentsCount > 0 && task.inRecents 1065 && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) { 1066 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0) 1067 + " at top when adding " + task); 1068 return; 1069 } 1070 1071 boolean needAffiliationFix = false; 1072 1073 // Slightly less quick case: the task is already in recents, so all we need 1074 // to do is move it. 1075 if (task.inRecents) { 1076 int taskIndex = mTasks.indexOf(task); 1077 if (taskIndex >= 0) { 1078 if (!isAffiliated) { 1079 if (!mFreezeTaskListReordering) { 1080 // Simple case: this is not an affiliated task, so we just move it to the 1081 // front unless overridden by the provided activity options 1082 mTasks.remove(taskIndex); 1083 mTasks.add(0, task); 1084 1085 if (DEBUG_RECENTS) { 1086 Slog.d(TAG_RECENTS, "addRecent: moving to top " + task 1087 + " from " + taskIndex); 1088 } 1089 } 1090 notifyTaskPersisterLocked(task, false); 1091 return; 1092 } 1093 } else { 1094 Slog.wtf(TAG, "Task with inRecent not in recents: " + task); 1095 needAffiliationFix = true; 1096 } 1097 } 1098 1099 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task); 1100 removeForAddTask(task); 1101 1102 task.inRecents = true; 1103 if (!isAffiliated || needAffiliationFix) { 1104 // If this is a simple non-affiliated task, or we had some failure trying to 1105 // handle it as part of an affilated task, then just place it at the top. 1106 mTasks.add(0, task); 1107 notifyTaskAdded(task); 1108 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task); 1109 } else if (isAffiliated) { 1110 // If this is a new affiliated task, then move all of the affiliated tasks 1111 // to the front and insert this new one. 1112 Task other = task.mNextAffiliate; 1113 if (other == null) { 1114 other = task.mPrevAffiliate; 1115 } 1116 if (other != null) { 1117 int otherIndex = mTasks.indexOf(other); 1118 if (otherIndex >= 0) { 1119 // Insert new task at appropriate location. 1120 int taskIndex; 1121 if (other == task.mNextAffiliate) { 1122 // We found the index of our next affiliation, which is who is 1123 // before us in the list, so add after that point. 1124 taskIndex = otherIndex+1; 1125 } else { 1126 // We found the index of our previous affiliation, which is who is 1127 // after us in the list, so add at their position. 1128 taskIndex = otherIndex; 1129 } 1130 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, 1131 "addRecent: new affiliated task added at " + taskIndex + ": " + task); 1132 mTasks.add(taskIndex, task); 1133 notifyTaskAdded(task); 1134 1135 // Now move everything to the front. 1136 if (moveAffiliatedTasksToFront(task, taskIndex)) { 1137 // All went well. 1138 return; 1139 } 1140 1141 // Uh oh... something bad in the affiliation chain, try to rebuild 1142 // everything and then go through our general path of adding a new task. 1143 needAffiliationFix = true; 1144 } else { 1145 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, 1146 "addRecent: couldn't find other affiliation " + other); 1147 needAffiliationFix = true; 1148 } 1149 } else { 1150 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, 1151 "addRecent: adding affiliated task without next/prev:" + task); 1152 needAffiliationFix = true; 1153 } 1154 } 1155 1156 if (needAffiliationFix) { 1157 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations"); 1158 cleanupLocked(task.mUserId); 1159 } 1160 1161 // Trim the set of tasks to the active set 1162 if (canTrimTask) { 1163 trimInactiveRecentTasks(); 1164 } 1165 notifyTaskPersisterLocked(task, false /* flush */); 1166 } 1167 1168 /** 1169 * Add the task to the bottom if possible. 1170 */ addToBottom(Task task)1171 boolean addToBottom(Task task) { 1172 if (!canAddTaskWithoutTrim(task)) { 1173 // Adding this task would cause the task to be removed (since it's appended at 1174 // the bottom and would be trimmed) so just return now 1175 return false; 1176 } 1177 1178 add(task); 1179 return true; 1180 } 1181 1182 /** 1183 * Remove a task from the recent tasks list. 1184 */ remove(Task task)1185 void remove(Task task) { 1186 mTasks.remove(task); 1187 notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */); 1188 } 1189 1190 /** 1191 * Trims the recents task list to the global max number of recents. 1192 */ trimInactiveRecentTasks()1193 private void trimInactiveRecentTasks() { 1194 if (mFreezeTaskListReordering) { 1195 // Defer trimming inactive recent tasks until we are unfrozen 1196 return; 1197 } 1198 1199 int recentsCount = mTasks.size(); 1200 1201 // Remove from the end of the list until we reach the max number of recents 1202 while (recentsCount > mGlobalMaxNumTasks) { 1203 final Task task = mTasks.remove(recentsCount - 1); 1204 notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */); 1205 recentsCount--; 1206 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + task 1207 + " max=" + mGlobalMaxNumTasks); 1208 } 1209 1210 // Remove any tasks that belong to currently quiet profiles 1211 final int[] profileUserIds = getCurrentProfileIds(); 1212 mTmpQuietProfileUserIds.clear(); 1213 for (int userId : profileUserIds) { 1214 final UserInfo userInfo = getUserInfo(userId); 1215 if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) { 1216 mTmpQuietProfileUserIds.put(userId, true); 1217 } 1218 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo 1219 + " quiet=" + mTmpQuietProfileUserIds.get(userId)); 1220 } 1221 1222 // Remove any inactive tasks, calculate the latest set of visible tasks. 1223 int numVisibleTasks = 0; 1224 for (int i = 0; i < mTasks.size();) { 1225 final Task task = mTasks.get(i); 1226 1227 if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) { 1228 if (!mHasVisibleRecentTasks) { 1229 // Keep all active tasks if visible recent tasks is not supported 1230 i++; 1231 continue; 1232 } 1233 1234 if (!isVisibleRecentTask(task)) { 1235 // Keep all active-but-invisible tasks 1236 i++; 1237 continue; 1238 } else { 1239 numVisibleTasks++; 1240 if (isInVisibleRange(task, i, numVisibleTasks, false /* skipExcludedCheck */) 1241 || !isTrimmable(task)) { 1242 // Keep visible tasks in range 1243 i++; 1244 continue; 1245 } else { 1246 // Fall through to trim visible tasks that are no longer in range and 1247 // trimmable 1248 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, 1249 "Trimming out-of-range visible task=" + task); 1250 } 1251 } 1252 } else { 1253 // Fall through to trim inactive tasks 1254 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task); 1255 } 1256 1257 // Task is no longer active, trim it from the list 1258 mTasks.remove(task); 1259 notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */); 1260 notifyTaskPersisterLocked(task, false /* flush */); 1261 } 1262 } 1263 1264 /** 1265 * @return whether the given task should be considered active. 1266 */ isActiveRecentTask(Task task, SparseBooleanArray quietProfileUserIds)1267 private boolean isActiveRecentTask(Task task, SparseBooleanArray quietProfileUserIds) { 1268 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task 1269 + " globalMax=" + mGlobalMaxNumTasks); 1270 1271 if (quietProfileUserIds.get(task.mUserId)) { 1272 // Quiet profile user's tasks are never active 1273 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true"); 1274 return false; 1275 } 1276 1277 if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.mTaskId) { 1278 // Keep the task active if its affiliated task is also active 1279 final Task affiliatedTask = getTask(task.mAffiliatedTaskId); 1280 if (affiliatedTask != null) { 1281 if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) { 1282 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, 1283 "\taffiliatedWithTask=" + affiliatedTask + " is not active"); 1284 return false; 1285 } 1286 } 1287 } 1288 1289 // All other tasks are considered active 1290 return true; 1291 } 1292 1293 /** 1294 * @return whether the given active task should be presented to the user through SystemUI. 1295 */ 1296 @VisibleForTesting isVisibleRecentTask(Task task)1297 boolean isVisibleRecentTask(Task task) { 1298 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task 1299 + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks 1300 + " sessionDuration=" + mActiveTasksSessionDurationMs 1301 + " inactiveDuration=" + task.getInactiveDuration() 1302 + " activityType=" + task.getActivityType() 1303 + " windowingMode=" + task.getWindowingMode() 1304 + " intentFlags=" + task.getBaseIntent().getFlags()); 1305 1306 switch (task.getActivityType()) { 1307 case ACTIVITY_TYPE_HOME: 1308 case ACTIVITY_TYPE_RECENTS: 1309 case ACTIVITY_TYPE_DREAM: 1310 // Ignore certain activity types completely 1311 return false; 1312 case ACTIVITY_TYPE_ASSISTANT: 1313 // Ignore assistant that chose to be excluded from Recents, even if it's a top 1314 // task. 1315 if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 1316 == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) { 1317 return false; 1318 } 1319 break; 1320 } 1321 1322 // Ignore certain windowing modes 1323 switch (task.getWindowingMode()) { 1324 case WINDOWING_MODE_PINNED: 1325 return false; 1326 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: 1327 if (DEBUG_RECENTS_TRIM_TASKS) { 1328 Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask()); 1329 } 1330 final ActivityStack stack = task.getStack(); 1331 if (stack != null && stack.getTopMostTask() == task) { 1332 // Only the non-top task of the primary split screen mode is visible 1333 return false; 1334 } 1335 break; 1336 case WINDOWING_MODE_MULTI_WINDOW: 1337 // Ignore tasks that are always on top 1338 if (task.isAlwaysOnTop()) { 1339 return false; 1340 } 1341 break; 1342 } 1343 1344 // Tasks managed by/associated with an ActivityView should be excluded from recents. 1345 // singleTaskInstance is set on the VirtualDisplay managed by ActivityView 1346 // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance 1347 final ActivityStack stack = task.getStack(); 1348 if (stack != null) { 1349 DisplayContent display = stack.getDisplay(); 1350 if (display != null && display.isSingleTaskInstance()) { 1351 return false; 1352 } 1353 } 1354 1355 // If we're in lock task mode, ignore the root task 1356 if (task == mService.getLockTaskController().getRootTask()) { 1357 return false; 1358 } 1359 1360 return true; 1361 } 1362 1363 /** 1364 * @return whether the given visible task is within the policy range. 1365 */ isInVisibleRange(Task task, int taskIndex, int numVisibleTasks, boolean skipExcludedCheck)1366 private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks, 1367 boolean skipExcludedCheck) { 1368 if (!skipExcludedCheck) { 1369 // Keep the most recent task even if it is excluded from recents 1370 final boolean isExcludeFromRecents = 1371 (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 1372 == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 1373 if (isExcludeFromRecents) { 1374 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true"); 1375 return taskIndex == 0; 1376 } 1377 } 1378 1379 if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) { 1380 // Always keep up to the min number of recent tasks, after that fall through to the 1381 // checks below 1382 return true; 1383 } 1384 1385 if (mMaxNumVisibleTasks >= 0) { 1386 // Always keep up to the max number of recent tasks, but return false afterwards 1387 return numVisibleTasks <= mMaxNumVisibleTasks; 1388 } 1389 1390 if (mActiveTasksSessionDurationMs > 0) { 1391 // Keep the task if the inactive time is within the session window, this check must come 1392 // after the checks for the min/max visible task range 1393 if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) { 1394 return true; 1395 } 1396 } 1397 1398 return false; 1399 } 1400 1401 /** @return whether the given task can be trimmed even if it is outside the visible range. */ isTrimmable(Task task)1402 protected boolean isTrimmable(Task task) { 1403 final ActivityStack stack = task.getStack(); 1404 1405 // No stack for task, just trim it 1406 if (stack == null) { 1407 return true; 1408 } 1409 1410 // Ignore tasks from different displays 1411 // TODO (b/115289124): No Recents on non-default displays. 1412 if (!stack.isOnHomeDisplay()) { 1413 return false; 1414 } 1415 1416 final ActivityStack rootHomeTask = stack.getDisplayArea().getRootHomeTask(); 1417 // Home stack does not exist. Don't trim the task. 1418 if (rootHomeTask == null) { 1419 return false; 1420 } 1421 // Trim tasks that are behind the home task. 1422 return task.compareTo(rootHomeTask) < 0; 1423 } 1424 1425 /** Remove the tasks that user may not be able to return. */ removeUnreachableHiddenTasks(int windowingMode)1426 private void removeUnreachableHiddenTasks(int windowingMode) { 1427 for (int i = mHiddenTasks.size() - 1; i >= 0; i--) { 1428 final Task hiddenTask = mHiddenTasks.get(i); 1429 if (!hiddenTask.hasChild() || hiddenTask.inRecents) { 1430 // The task was removed by other path or it became reachable (added to recents). 1431 mHiddenTasks.remove(i); 1432 continue; 1433 } 1434 if (hiddenTask.getWindowingMode() != windowingMode 1435 || hiddenTask.getTopVisibleActivity() != null) { 1436 // The task may be reachable from the back stack of other windowing mode or it is 1437 // currently in use. Keep the task in the hidden list to avoid losing track, e.g. 1438 // after dismissing primary split screen. 1439 continue; 1440 } 1441 mHiddenTasks.remove(i); 1442 mSupervisor.removeTask(hiddenTask, false /* killProcess */, 1443 !REMOVE_FROM_RECENTS, "remove-hidden-task"); 1444 } 1445 } 1446 1447 /** 1448 * If needed, remove oldest existing entries in recents that are for the same kind 1449 * of task as the given one. 1450 */ removeForAddTask(Task task)1451 private void removeForAddTask(Task task) { 1452 // The adding task will be in recents so it is not hidden. 1453 mHiddenTasks.remove(task); 1454 1455 final int removeIndex = findRemoveIndexForAddTask(task); 1456 if (removeIndex == -1) { 1457 // Nothing to trim 1458 return; 1459 } 1460 1461 // There is a similar task that will be removed for the addition of {@param task}, but it 1462 // can be the same task, and if so, the task will be re-added in add(), so skip the 1463 // callbacks here. 1464 final Task removedTask = mTasks.remove(removeIndex); 1465 if (removedTask != task) { 1466 if (removedTask.hasChild()) { 1467 // A non-empty task is replaced by a new task. Because the removed task is no longer 1468 // managed by the recent tasks list, add it to the hidden list to prevent the task 1469 // from becoming dangling. 1470 mHiddenTasks.add(removedTask); 1471 } 1472 notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */); 1473 if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask 1474 + " for addition of task=" + task); 1475 } 1476 notifyTaskPersisterLocked(removedTask, false /* flush */); 1477 } 1478 1479 /** 1480 * Find the task that would be removed if the given {@param task} is added to the recent tasks 1481 * list (if any). 1482 */ findRemoveIndexForAddTask(Task task)1483 private int findRemoveIndexForAddTask(Task task) { 1484 if (mFreezeTaskListReordering) { 1485 // Defer removing tasks due to the addition of new tasks until the task list is unfrozen 1486 return -1; 1487 } 1488 1489 final int recentsCount = mTasks.size(); 1490 final Intent intent = task.intent; 1491 final boolean document = intent != null && intent.isDocument(); 1492 int maxRecents = task.maxRecents - 1; 1493 for (int i = 0; i < recentsCount; i++) { 1494 final Task t = mTasks.get(i); 1495 if (task != t) { 1496 if (!hasCompatibleActivityTypeAndWindowingMode(task, t) 1497 || task.mUserId != t.mUserId) { 1498 continue; 1499 } 1500 final Intent trIntent = t.intent; 1501 final boolean sameAffinity = 1502 task.affinity != null && task.affinity.equals(t.affinity); 1503 final boolean sameIntent = intent != null && intent.filterEquals(trIntent); 1504 boolean multiTasksAllowed = false; 1505 final int flags = intent.getFlags(); 1506 if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0 1507 && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { 1508 multiTasksAllowed = true; 1509 } 1510 final boolean trIsDocument = trIntent != null && trIntent.isDocument(); 1511 final boolean bothDocuments = document && trIsDocument; 1512 if (!sameAffinity && !sameIntent && !bothDocuments) { 1513 continue; 1514 } 1515 1516 if (bothDocuments) { 1517 // Do these documents belong to the same activity? 1518 final boolean sameActivity = task.realActivity != null 1519 && t.realActivity != null 1520 && task.realActivity.equals(t.realActivity); 1521 if (!sameActivity) { 1522 // If the document is open in another app or is not the same document, we 1523 // don't need to trim it. 1524 continue; 1525 } else if (maxRecents > 0) { 1526 --maxRecents; 1527 if (!sameIntent || multiTasksAllowed) { 1528 // We don't want to trim if we are not over the max allowed entries and 1529 // the tasks are not of the same intent filter, or multiple entries for 1530 // the task is allowed. 1531 continue; 1532 } 1533 } 1534 // Hit the maximum number of documents for this task. Fall through 1535 // and remove this document from recents. 1536 } else if (document || trIsDocument) { 1537 // Only one of these is a document. Not the droid we're looking for. 1538 continue; 1539 } 1540 } 1541 return i; 1542 } 1543 return -1; 1544 } 1545 1546 // Extract the affiliates of the chain containing recent at index start. processNextAffiliateChainLocked(int start)1547 private int processNextAffiliateChainLocked(int start) { 1548 final Task startTask = mTasks.get(start); 1549 final int affiliateId = startTask.mAffiliatedTaskId; 1550 1551 // Quick identification of isolated tasks. I.e. those not launched behind. 1552 if (startTask.mTaskId == affiliateId && startTask.mPrevAffiliate == null && 1553 startTask.mNextAffiliate == null) { 1554 // There is still a slim chance that there are other tasks that point to this task 1555 // and that the chain is so messed up that this task no longer points to them but 1556 // the gain of this optimization outweighs the risk. 1557 startTask.inRecents = true; 1558 return start + 1; 1559 } 1560 1561 // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents. 1562 mTmpRecents.clear(); 1563 for (int i = mTasks.size() - 1; i >= start; --i) { 1564 final Task task = mTasks.get(i); 1565 if (task.mAffiliatedTaskId == affiliateId) { 1566 mTasks.remove(i); 1567 mTmpRecents.add(task); 1568 } 1569 } 1570 1571 // Sort them all by taskId. That is the order they were create in and that order will 1572 // always be correct. 1573 Collections.sort(mTmpRecents, TASK_ID_COMPARATOR); 1574 1575 // Go through and fix up the linked list. 1576 // The first one is the end of the chain and has no next. 1577 final Task first = mTmpRecents.get(0); 1578 first.inRecents = true; 1579 if (first.mNextAffiliate != null) { 1580 Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate); 1581 first.setNextAffiliate(null); 1582 notifyTaskPersisterLocked(first, false); 1583 } 1584 // Everything in the middle is doubly linked from next to prev. 1585 final int tmpSize = mTmpRecents.size(); 1586 for (int i = 0; i < tmpSize - 1; ++i) { 1587 final Task next = mTmpRecents.get(i); 1588 final Task prev = mTmpRecents.get(i + 1); 1589 if (next.mPrevAffiliate != prev) { 1590 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate + 1591 " setting prev=" + prev); 1592 next.setPrevAffiliate(prev); 1593 notifyTaskPersisterLocked(next, false); 1594 } 1595 if (prev.mNextAffiliate != next) { 1596 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate + 1597 " setting next=" + next); 1598 prev.setNextAffiliate(next); 1599 notifyTaskPersisterLocked(prev, false); 1600 } 1601 prev.inRecents = true; 1602 } 1603 // The last one is the beginning of the list and has no prev. 1604 final Task last = mTmpRecents.get(tmpSize - 1); 1605 if (last.mPrevAffiliate != null) { 1606 Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate); 1607 last.setPrevAffiliate(null); 1608 notifyTaskPersisterLocked(last, false); 1609 } 1610 1611 // Insert the group back into mTmpTasks at start. 1612 mTasks.addAll(start, mTmpRecents); 1613 mTmpRecents.clear(); 1614 1615 // Let the caller know where we left off. 1616 return start + tmpSize; 1617 } 1618 moveAffiliatedTasksToFront(Task task, int taskIndex)1619 private boolean moveAffiliatedTasksToFront(Task task, int taskIndex) { 1620 int recentsCount = mTasks.size(); 1621 Task top = task; 1622 int topIndex = taskIndex; 1623 while (top.mNextAffiliate != null && topIndex > 0) { 1624 top = top.mNextAffiliate; 1625 topIndex--; 1626 } 1627 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " 1628 + topIndex + " from intial " + taskIndex); 1629 // Find the end of the chain, doing a sanity check along the way. 1630 boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; 1631 int endIndex = topIndex; 1632 Task prev = top; 1633 while (endIndex < recentsCount) { 1634 Task cur = mTasks.get(endIndex); 1635 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @" 1636 + endIndex + " " + cur); 1637 if (cur == top) { 1638 // Verify start of the chain. 1639 if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { 1640 Slog.wtf(TAG, "Bad chain @" + endIndex 1641 + ": first task has next affiliate: " + prev); 1642 sane = false; 1643 break; 1644 } 1645 } else { 1646 // Verify middle of the chain's next points back to the one before. 1647 if (cur.mNextAffiliate != prev 1648 || cur.mNextAffiliateTaskId != prev.mTaskId) { 1649 Slog.wtf(TAG, "Bad chain @" + endIndex 1650 + ": middle task " + cur + " @" + endIndex 1651 + " has bad next affiliate " 1652 + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId 1653 + ", expected " + prev); 1654 sane = false; 1655 break; 1656 } 1657 } 1658 if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) { 1659 // Chain ends here. 1660 if (cur.mPrevAffiliate != null) { 1661 Slog.wtf(TAG, "Bad chain @" + endIndex 1662 + ": last task " + cur + " has previous affiliate " 1663 + cur.mPrevAffiliate); 1664 sane = false; 1665 } 1666 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); 1667 break; 1668 } else { 1669 // Verify middle of the chain's prev points to a valid item. 1670 if (cur.mPrevAffiliate == null) { 1671 Slog.wtf(TAG, "Bad chain @" + endIndex 1672 + ": task " + cur + " has previous affiliate " 1673 + cur.mPrevAffiliate + " but should be id " 1674 + cur.mPrevAffiliate); 1675 sane = false; 1676 break; 1677 } 1678 } 1679 if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) { 1680 Slog.wtf(TAG, "Bad chain @" + endIndex 1681 + ": task " + cur + " has affiliated id " 1682 + cur.mAffiliatedTaskId + " but should be " 1683 + task.mAffiliatedTaskId); 1684 sane = false; 1685 break; 1686 } 1687 prev = cur; 1688 endIndex++; 1689 if (endIndex >= recentsCount) { 1690 Slog.wtf(TAG, "Bad chain ran off index " + endIndex 1691 + ": last task " + prev); 1692 sane = false; 1693 break; 1694 } 1695 } 1696 if (sane) { 1697 if (endIndex < taskIndex) { 1698 Slog.wtf(TAG, "Bad chain @" + endIndex 1699 + ": did not extend to task " + task + " @" + taskIndex); 1700 sane = false; 1701 } 1702 } 1703 if (sane) { 1704 // All looks good, we can just move all of the affiliated tasks 1705 // to the top. 1706 for (int i=topIndex; i<=endIndex; i++) { 1707 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task 1708 + " from " + i + " to " + (i-topIndex)); 1709 Task cur = mTasks.remove(i); 1710 mTasks.add(i - topIndex, cur); 1711 } 1712 if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex 1713 + " to " + endIndex); 1714 return true; 1715 } 1716 1717 // Whoops, couldn't do it. 1718 return false; 1719 } 1720 dump(PrintWriter pw, boolean dumpAll, String dumpPackage)1721 void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { 1722 pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); 1723 pw.println("mRecentsUid=" + mRecentsUid); 1724 pw.println("mRecentsComponent=" + mRecentsComponent); 1725 pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering); 1726 pw.println("mFreezeTaskListReorderingPendingTimeout=" 1727 + mService.mH.hasCallbacks(mResetFreezeTaskListOnTimeoutRunnable)); 1728 if (!mHiddenTasks.isEmpty()) { 1729 pw.println("mHiddenTasks=" + mHiddenTasks); 1730 } 1731 if (mTasks.isEmpty()) { 1732 return; 1733 } 1734 1735 // Dump raw recent task list 1736 boolean printedAnything = false; 1737 boolean printedHeader = false; 1738 final int size = mTasks.size(); 1739 for (int i = 0; i < size; i++) { 1740 final Task task = mTasks.get(i); 1741 if (dumpPackage != null) { 1742 boolean match = task.intent != null 1743 && task.intent.getComponent() != null 1744 && dumpPackage.equals( 1745 task.intent.getComponent().getPackageName()); 1746 if (!match) { 1747 match |= task.affinityIntent != null 1748 && task.affinityIntent.getComponent() != null 1749 && dumpPackage.equals( 1750 task.affinityIntent.getComponent().getPackageName()); 1751 } 1752 if (!match) { 1753 match |= task.origActivity != null 1754 && dumpPackage.equals(task.origActivity.getPackageName()); 1755 } 1756 if (!match) { 1757 match |= task.realActivity != null 1758 && dumpPackage.equals(task.realActivity.getPackageName()); 1759 } 1760 if (!match) { 1761 match |= dumpPackage.equals(task.mCallingPackage); 1762 } 1763 if (!match) { 1764 continue; 1765 } 1766 } 1767 1768 if (!printedHeader) { 1769 pw.println(" Recent tasks:"); 1770 printedHeader = true; 1771 printedAnything = true; 1772 } 1773 pw.print(" * Recent #"); pw.print(i); pw.print(": "); 1774 pw.println(task); 1775 if (dumpAll) { 1776 task.dump(pw, " "); 1777 } 1778 } 1779 1780 // Dump visible recent task list 1781 if (mHasVisibleRecentTasks) { 1782 // Reset the header flag for the next block 1783 printedHeader = false; 1784 ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE, 1785 0, true /* getTasksAllowed */, mService.getCurrentUserId(), SYSTEM_UID); 1786 for (int i = 0; i < tasks.size(); i++) { 1787 final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i); 1788 if (dumpPackage != null) { 1789 boolean match = taskInfo.baseIntent != null 1790 && taskInfo.baseIntent.getComponent() != null 1791 && dumpPackage.equals( 1792 taskInfo.baseIntent.getComponent().getPackageName()); 1793 if (!match) { 1794 match |= taskInfo.baseActivity != null 1795 && dumpPackage.equals(taskInfo.baseActivity.getPackageName()); 1796 } 1797 if (!match) { 1798 match |= taskInfo.topActivity != null 1799 && dumpPackage.equals(taskInfo.topActivity.getPackageName()); 1800 } 1801 if (!match) { 1802 match |= taskInfo.origActivity != null 1803 && dumpPackage.equals(taskInfo.origActivity.getPackageName()); 1804 } 1805 if (!match) { 1806 match |= taskInfo.realActivity != null 1807 && dumpPackage.equals(taskInfo.realActivity.getPackageName()); 1808 } 1809 if (!match) { 1810 continue; 1811 } 1812 } 1813 if (!printedHeader) { 1814 if (printedAnything) { 1815 // Separate from the last block if it printed 1816 pw.println(); 1817 } 1818 pw.println(" Visible recent tasks (most recent first):"); 1819 printedHeader = true; 1820 printedAnything = true; 1821 } 1822 1823 pw.print(" * RecentTaskInfo #"); pw.print(i); pw.print(": "); 1824 taskInfo.dump(pw, " "); 1825 } 1826 } 1827 1828 if (!printedAnything) { 1829 pw.println(" (nothing)"); 1830 } 1831 } 1832 1833 /** 1834 * Creates a new RecentTaskInfo from a Task. 1835 */ createRecentTaskInfo(Task tr, boolean stripExtras)1836 ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { 1837 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); 1838 tr.fillTaskInfo(rti, stripExtras); 1839 // Fill in some deprecated values 1840 rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; 1841 rti.persistentId = rti.taskId; 1842 return rti; 1843 } 1844 1845 /** 1846 * @return Whether the activity types and windowing modes of the two tasks are considered 1847 * compatible. This is necessary because we currently don't persist the activity type 1848 * or the windowing mode with the task, so they can be undefined when restored. 1849 */ hasCompatibleActivityTypeAndWindowingMode(Task t1, Task t2)1850 private boolean hasCompatibleActivityTypeAndWindowingMode(Task t1, Task t2) { 1851 final int activityType = t1.getActivityType(); 1852 final int windowingMode = t1.getWindowingMode(); 1853 final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED; 1854 final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED; 1855 final int otherActivityType = t2.getActivityType(); 1856 final int otherWindowingMode = t2.getWindowingMode(); 1857 final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED; 1858 final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED; 1859 1860 // An activity type and windowing mode is compatible if they are the exact same type/mode, 1861 // or if one of the type/modes is undefined 1862 final boolean isCompatibleType = activityType == otherActivityType 1863 || isUndefinedType || isOtherUndefinedType; 1864 final boolean isCompatibleMode = windowingMode == otherWindowingMode 1865 || isUndefinedMode || isOtherUndefinedMode; 1866 1867 return isCompatibleType && isCompatibleMode; 1868 } 1869 } 1870