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