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