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