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