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