diff options
Diffstat (limited to 'com/android/server/am/RecentTasks.java')
-rw-r--r-- | com/android/server/am/RecentTasks.java | 1040 |
1 files changed, 214 insertions, 826 deletions
diff --git a/com/android/server/am/RecentTasks.java b/com/android/server/am/RecentTasks.java index 78274bdc..365c5b1d 100644 --- a/com/android/server/am/RecentTasks.java +++ b/com/android/server/am/RecentTasks.java @@ -16,25 +16,15 @@ package com.android.server.am; -import static android.app.ActivityManager.FLAG_AND_UNLOCKED; -import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; - import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import com.google.android.collect.Sets; @@ -47,87 +37,43 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; -import android.content.pm.UserInfo; -import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Rect; -import android.os.Bundle; import android.os.Environment; -import android.os.IBinder; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArraySet; -import android.util.MutableBoolean; -import android.util.MutableInt; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.am.ActivityStack.ActivityState; - import java.io.File; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Set; -import java.util.concurrent.TimeUnit; /** - * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the - * least recent. + * Class for managing the recent tasks list. */ -class RecentTasks { +class RecentTasks extends ArrayList<TaskRecord> { private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; private static final String TAG_TASKS = TAG + POSTFIX_TASKS; - private static final boolean TRIMMED = true; + // Maximum number recent bitmaps to keep in memory. + private static final int MAX_RECENT_BITMAPS = 3; private static final int DEFAULT_INITIAL_CAPACITY = 5; // Whether or not to move all affiliated tasks to the front when one of the tasks is launched private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false; - // Comparator to sort by taskId - private static final Comparator<TaskRecord> TASK_ID_COMPARATOR = - (lhs, rhs) -> rhs.taskId - lhs.taskId; - - // Placeholder variables to keep track of activities/apps that are no longer avialble while - // iterating through the recents list - private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo(); - private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo(); - - /** - * Callbacks made when manipulating the list. - */ - interface Callbacks { - /** - * Called when a task is added to the recent tasks list. - */ - void onRecentTaskAdded(TaskRecord task); - - /** - * Called when a task is removed from the recent tasks list. - */ - void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed); - } - /** * Save recent tasks information across reboots. */ private final TaskPersister mTaskPersister; private final ActivityManagerService mService; - private final UserController mUserController; - - /** - * Mapping of user id -> whether recent tasks have been loaded for that user. - */ private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray( DEFAULT_INITIAL_CAPACITY); @@ -135,106 +81,21 @@ class RecentTasks { * Stores for each user task ids that are taken by tasks residing in persistent storage. These * tasks may or may not currently be in memory. */ - private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>( + final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>( DEFAULT_INITIAL_CAPACITY); - // List of all active recent tasks - private final ArrayList<TaskRecord> mTasks = new ArrayList<>(); - private final ArrayList<Callbacks> mCallbacks = new ArrayList<>(); - - // These values are generally loaded from resources, but can be set dynamically in the tests - private boolean mHasVisibleRecentTasks; - private int mGlobalMaxNumTasks; - private int mMinNumVisibleTasks; - private int mMaxNumVisibleTasks; - private long mActiveTasksSessionDurationMs; - // Mainly to avoid object recreation on multiple calls. - private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>(); + private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>(); private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); - private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); + private final ActivityInfo mTmpActivityInfo = new ActivityInfo(); + private final ApplicationInfo mTmpAppInfo = new ApplicationInfo(); - @VisibleForTesting - RecentTasks(ActivityManagerService service, TaskPersister taskPersister, - UserController userController) { + RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) { + File systemDir = Environment.getDataSystemDirectory(); mService = service; - mUserController = userController; - mTaskPersister = taskPersister; - mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic(); - mHasVisibleRecentTasks = true; - } - - RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) { - final File systemDir = Environment.getDataSystemDirectory(); - final Resources res = service.mContext.getResources(); - mService = service; - mUserController = service.mUserController; - mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this); - mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic(); - mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents); - loadParametersFromResources(service.mContext.getResources()); - } - - @VisibleForTesting - void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks, - long activeSessionDurationMs) { - mMinNumVisibleTasks = minNumVisibleTasks; - mMaxNumVisibleTasks = maxNumVisibleTasks; - mActiveTasksSessionDurationMs = activeSessionDurationMs; - } - - @VisibleForTesting - void setGlobalMaxNumTasks(int globalMaxNumTasks) { - mGlobalMaxNumTasks = globalMaxNumTasks; - } - - /** - * Loads the parameters from the system resources. - */ - @VisibleForTesting - void loadParametersFromResources(Resources res) { - if (ActivityManager.isLowRamDeviceStatic()) { - mMinNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam); - mMaxNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam); - } else if (SystemProperties.getBoolean("ro.recents.grid", false)) { - mMinNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid); - mMaxNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid); - } else { - mMinNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_minNumVisibleRecentTasks); - mMaxNumVisibleTasks = res.getInteger( - com.android.internal.R.integer.config_maxNumVisibleRecentTasks); - } - final int sessionDurationHrs = res.getInteger( - com.android.internal.R.integer.config_activeTaskDurationHours); - mActiveTasksSessionDurationMs = (sessionDurationHrs > 0) - ? TimeUnit.HOURS.toMillis(sessionDurationHrs) - : -1; - } - - void registerCallback(Callbacks callback) { - mCallbacks.add(callback); - } - - void unregisterCallback(Callbacks callback) { - mCallbacks.remove(callback); - } - - private void notifyTaskAdded(TaskRecord task) { - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onRecentTaskAdded(task); - } - } - - private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) { - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed); - } + mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this); + mStackSupervisor.setRecentTasks(this); } /** @@ -245,7 +106,6 @@ class RecentTasks { */ void loadUserRecentsLocked(int userId) { if (mUsersWithRecentsLoaded.get(userId)) { - // User already loaded, return early return; } @@ -254,14 +114,14 @@ class RecentTasks { // Check if any tasks are added before recents is loaded final SparseBooleanArray preaddedTasks = new SparseBooleanArray(); - for (final TaskRecord task : mTasks) { + for (final TaskRecord task : this) { if (task.userId == userId && shouldPersistTaskLocked(task)) { preaddedTasks.put(task.taskId, true); } } Slog.i(TAG, "Loading recents for user " + userId + " into memory."); - mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); + addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks)); cleanupLocked(userId); mUsersWithRecentsLoaded.put(userId, true); @@ -280,25 +140,11 @@ class RecentTasks { } } - /** - * @return whether the {@param taskId} is currently in use for the given user. - */ - boolean containsTaskId(int taskId, int userId) { + boolean taskIdTakenForUserLocked(int taskId, int userId) { loadPersistedTaskIdsForUserLocked(userId); return mPersistedTaskIds.get(userId).get(taskId); } - /** - * @return all the task ids for the user with the given {@param userId}. - */ - SparseBooleanArray getTaskIdsForUser(int userId) { - loadPersistedTaskIdsForUserLocked(userId); - return mPersistedTaskIds.get(userId); - } - - /** - * Kicks off the task persister to write any pending tasks to disk. - */ void notifyTaskPersisterLocked(TaskRecord task, boolean flush) { final ActivityStack stack = task != null ? task.getStack() : null; if (stack != null && stack.isHomeOrRecentsStack()) { @@ -318,8 +164,8 @@ class RecentTasks { mPersistedTaskIds.valueAt(i).clear(); } } - for (int i = mTasks.size() - 1; i >= 0; i--) { - final TaskRecord task = mTasks.get(i); + for (int i = size() - 1; i >= 0; i--) { + final TaskRecord task = get(i); if (shouldPersistTaskLocked(task)) { // Set of persisted taskIds for task.userId should not be null here // TODO Investigate why it can happen. For now initialize with an empty set @@ -334,12 +180,12 @@ class RecentTasks { } private static boolean shouldPersistTaskLocked(TaskRecord task) { - final ActivityStack stack = task.getStack(); + final ActivityStack<?> stack = task.getStack(); return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack()); } void onSystemReadyLocked() { - mTasks.clear(); + clear(); mTaskPersister.startPersisting(); } @@ -379,6 +225,14 @@ class RecentTasks { return usersWithRecentsLoaded; } + private void unloadUserRecentsLocked(int userId) { + if (mUsersWithRecentsLoaded.get(userId)) { + Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); + mUsersWithRecentsLoaded.delete(userId); + removeTasksForUserLocked(userId); + } + } + /** * Removes recent tasks and any other state kept in memory for the passed in user. Does not * touch the information present on persistent storage. @@ -386,36 +240,44 @@ class RecentTasks { * @param userId the id of the user */ void unloadUserDataFromMemoryLocked(int userId) { - if (mUsersWithRecentsLoaded.get(userId)) { - Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); - mUsersWithRecentsLoaded.delete(userId); - removeTasksForUserLocked(userId); - } + unloadUserRecentsLocked(userId); mPersistedTaskIds.delete(userId); mTaskPersister.unloadUserDataFromMemory(userId); } + TaskRecord taskForIdLocked(int id) { + final int recentsCount = size(); + for (int i = 0; i < recentsCount; i++) { + TaskRecord tr = get(i); + if (tr.taskId == id) { + return tr; + } + } + return null; + } + /** Remove recent tasks for a user. */ - private void removeTasksForUserLocked(int userId) { + void removeTasksForUserLocked(int userId) { if(userId <= 0) { Slog.i(TAG, "Can't remove recent task on user " + userId); return; } - for (int i = mTasks.size() - 1; i >= 0; --i) { - TaskRecord tr = mTasks.get(i); + for (int i = size() - 1; i >= 0; --i) { + TaskRecord tr = get(i); if (tr.userId == userId) { if(DEBUG_TASKS) Slog.i(TAG_TASKS, "remove RecentTask " + tr + " when finishing user" + userId); - remove(mTasks.get(i)); + remove(i); + tr.removedFromRecents(); } } } void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) { final Set<String> packageNames = Sets.newHashSet(packages); - for (int i = mTasks.size() - 1; i >= 0; --i) { - final TaskRecord tr = mTasks.get(i); + for (int i = size() - 1; i >= 0; --i) { + final TaskRecord tr = get(i); if (tr.realActivity != null && packageNames.contains(tr.realActivity.getPackageName()) && tr.userId == userId @@ -424,36 +286,7 @@ class RecentTasks { notifyTaskPersisterLocked(tr, false); } } - } - - void removeTasksByPackageName(String packageName, int userId) { - for (int i = mTasks.size() - 1; i >= 0; --i) { - final TaskRecord tr = mTasks.get(i); - final String taskPackageName = - tr.getBaseIntent().getComponent().getPackageName(); - if (tr.userId != userId) return; - if (!taskPackageName.equals(packageName)) return; - - mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS); - } - } - - void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, - int userId) { - for (int i = mTasks.size() - 1; i >= 0; --i) { - final TaskRecord tr = mTasks.get(i); - if (userId != UserHandle.USER_ALL && tr.userId != userId) { - continue; - } - ComponentName cn = tr.intent.getComponent(); - final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName) - && (filterByClasses == null || filterByClasses.contains(cn.getClassName())); - if (sameComponent) { - mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, - REMOVE_FROM_RECENTS); - } - } } /** @@ -462,28 +295,24 @@ class RecentTasks { * of affiliations. */ void cleanupLocked(int userId) { - int recentsCount = mTasks.size(); + int recentsCount = size(); if (recentsCount == 0) { // Happens when called from the packagemanager broadcast before boot, // or just any empty list. return; } - // Clear the temp lists - mTmpAvailActCache.clear(); - mTmpAvailAppCache.clear(); - final IPackageManager pm = AppGlobals.getPackageManager(); for (int i = recentsCount - 1; i >= 0; i--) { - final TaskRecord task = mTasks.get(i); + final TaskRecord task = get(i); if (userId != UserHandle.USER_ALL && task.userId != userId) { // Only look at tasks for the user ID of interest. continue; } if (task.autoRemoveRecents && task.getTopActivity() == null) { // This situation is broken, and we should just get rid of it now. - mTasks.remove(i); - notifyTaskRemoved(task, !TRIMMED); + remove(i); + task.removedFromRecents(); Slog.w(TAG, "Removing auto-remove without activity: " + task); continue; } @@ -502,11 +331,11 @@ class RecentTasks { continue; } if (ai == null) { - ai = NO_ACTIVITY_INFO_TOKEN; + ai = mTmpActivityInfo; } mTmpAvailActCache.put(task.realActivity, ai); } - if (ai == NO_ACTIVITY_INFO_TOKEN) { + if (ai == mTmpActivityInfo) { // This could be either because the activity no longer exists, or the // app is temporarily gone. For the former we want to remove the recents // entry; for the latter we want to mark it as unavailable. @@ -521,15 +350,15 @@ class RecentTasks { continue; } if (app == null) { - app = NO_APPLICATION_INFO_TOKEN; + app = mTmpAppInfo; } mTmpAvailAppCache.put(task.realActivity.getPackageName(), app); } - if (app == NO_APPLICATION_INFO_TOKEN + if (app == mTmpAppInfo || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { // Doesn't exist any more! Good-bye. - mTasks.remove(i); - notifyTaskRemoved(task, !TRIMMED); + remove(i); + task.removedFromRecents(); Slog.w(TAG, "Removing no longer valid recent: " + task); continue; } else { @@ -561,197 +390,121 @@ class RecentTasks { // Verify the affiliate chain for each task. int i = 0; - recentsCount = mTasks.size(); + recentsCount = size(); while (i < recentsCount) { i = processNextAffiliateChainLocked(i); } // recent tasks are now in sorted, affiliated order. } - /** - * @return whether the given {@param task} can be added to the list without causing another - * task to be trimmed as a result of that add. - */ - private boolean canAddTaskWithoutTrim(TaskRecord task) { - return findTrimIndexForAddTask(task) == -1; - } - - /** - * Returns the list of {@link ActivityManager.AppTask}s. - */ - ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) { - final ArrayList<IBinder> list = new ArrayList<>(); - final int size = mTasks.size(); - for (int i = 0; i < size; i++) { - final TaskRecord tr = mTasks.get(i); - // Skip tasks that do not match the caller. We don't need to verify - // callingPackage, because we are also limiting to callingUid and know - // that will limit to the correct security sandbox. - if (tr.effectiveUid != callingUid) { - continue; - } - Intent intent = tr.getBaseIntent(); - if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) { - continue; - } - ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr); - AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid); - list.add(taskImpl.asBinder()); - } - return list; - } - - /** - * @return the list of recent tasks for presentation. - */ - ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, - boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) { - final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0; - - if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) { - Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents"); - return ParceledListSlice.emptyList(); + private final boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) { + int recentsCount = size(); + TaskRecord top = task; + int topIndex = taskIndex; + while (top.mNextAffiliate != null && topIndex > 0) { + top = top.mNextAffiliate; + topIndex--; } - loadUserRecentsLocked(userId); - - final Set<Integer> includedUsers = mUserController.getProfileIds(userId); - includedUsers.add(Integer.valueOf(userId)); - - final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(); - final int size = mTasks.size(); - int numVisibleTasks = 0; - for (int i = 0; i < size; i++) { - final TaskRecord tr = mTasks.get(i); - - if (isVisibleRecentTask(tr)) { - numVisibleTasks++; - if (isInVisibleRange(tr, numVisibleTasks)) { - // Fall through - } else { - // Not in visible range - continue; + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " + + topIndex + " from intial " + taskIndex); + // Find the end of the chain, doing a sanity check along the way. + boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; + int endIndex = topIndex; + TaskRecord prev = top; + while (endIndex < recentsCount) { + TaskRecord cur = get(endIndex); + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @" + + endIndex + " " + cur); + if (cur == top) { + // Verify start of the chain. + if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": first task has next affiliate: " + prev); + sane = false; + break; } } else { - // Not visible - continue; - } - - // Skip remaining tasks once we reach the requested size - if (res.size() >= maxNum) { - continue; - } - - // Only add calling user or related users recent tasks - if (!includedUsers.contains(Integer.valueOf(tr.userId))) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr); - continue; - } - - if (tr.realActivitySuspended) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr); - continue; - } - - // Return the entry if desired by the caller. We always return - // the first entry, because callers always expect this to be the - // foreground app. We may filter others if the caller has - // not supplied RECENT_WITH_EXCLUDED and there is some reason - // we should exclude the entry. - - if (i == 0 - || withExcluded - || (tr.intent == null) - || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - == 0)) { - if (!getTasksAllowed) { - // If the caller doesn't have the GET_TASKS permission, then only - // allow them to see a small subset of tasks -- their own and home. - if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr); - continue; - } - } - if (tr.autoRemoveRecents && tr.getTopActivity() == null) { - // Don't include auto remove tasks that are finished or finishing. - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, auto-remove without activity: " + tr); - continue; - } - if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, unavail real act: " + tr); - continue; + // Verify middle of the chain's next points back to the one before. + if (cur.mNextAffiliate != prev + || cur.mNextAffiliateTaskId != prev.taskId) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": middle task " + cur + " @" + endIndex + + " has bad next affiliate " + + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId + + ", expected " + prev); + sane = false; + break; } - - if (!tr.mUserSetupComplete) { - // Don't include task launched while user is not done setting-up. - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, user setup not complete: " + tr); - continue; + } + if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) { + // Chain ends here. + if (cur.mPrevAffiliate != null) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": last task " + cur + " has previous affiliate " + + cur.mPrevAffiliate); + sane = false; } - - ActivityManager.RecentTaskInfo rti = RecentTasks.createRecentTaskInfo(tr); - if (!getDetailedTasks) { - rti.baseIntent.replaceExtras((Bundle)null); + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); + break; + } else { + // Verify middle of the chain's prev points to a valid item. + if (cur.mPrevAffiliate == null) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": task " + cur + " has previous affiliate " + + cur.mPrevAffiliate + " but should be id " + + cur.mPrevAffiliate); + sane = false; + break; } - - res.add(rti); + } + if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": task " + cur + " has affiliated id " + + cur.mAffiliatedTaskId + " but should be " + + task.mAffiliatedTaskId); + sane = false; + break; + } + prev = cur; + endIndex++; + if (endIndex >= recentsCount) { + Slog.wtf(TAG, "Bad chain ran off index " + endIndex + + ": last task " + prev); + sane = false; + break; } } - return new ParceledListSlice<>(res); - } - - /** - * @return the list of persistable task ids. - */ - void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) { - final int size = mTasks.size(); - for (int i = 0; i < size; i++) { - final TaskRecord task = mTasks.get(i); - if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task - + " persistable=" + task.isPersistable); - final ActivityStack stack = task.getStack(); - if ((task.isPersistable || task.inRecents) - && (stack == null || !stack.isHomeOrRecentsStack())) { - if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); - persistentTaskIds.add(task.taskId); - } else { - if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" - + task); + if (sane) { + if (endIndex < taskIndex) { + Slog.wtf(TAG, "Bad chain @" + endIndex + + ": did not extend to task " + task + " @" + taskIndex); + sane = false; } } - } - - @VisibleForTesting - ArrayList<TaskRecord> getRawTasks() { - return mTasks; - } - - /** - * @return the task in the task list with the given {@param id} if one exists. - */ - TaskRecord getTask(int id) { - final int recentsCount = mTasks.size(); - for (int i = 0; i < recentsCount; i++) { - TaskRecord tr = mTasks.get(i); - if (tr.taskId == id) { - return tr; + if (sane) { + // All looks good, we can just move all of the affiliated tasks + // to the top. + for (int i=topIndex; i<=endIndex; i++) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task + + " from " + i + " to " + (i-topIndex)); + TaskRecord cur = remove(i); + add(i - topIndex, cur); } + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex + + " to " + endIndex); + return true; } - return null; - } - /** - * Add a new task to the recent tasks list. - */ - void add(TaskRecord task) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task); + // Whoops, couldn't do it. + return false; + } + final void addLocked(TaskRecord task) { final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId || task.mNextAffiliateTaskId != INVALID_TASK_ID || task.mPrevAffiliateTaskId != INVALID_TASK_ID; - int recentsCount = mTasks.size(); + int recentsCount = size(); // Quick case: never add voice sessions. // TODO: VI what about if it's just an activity? // Probably nothing to do here @@ -761,15 +514,15 @@ class RecentTasks { return; } // Another quick case: check if the top-most recent task is the same. - if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) { + if (!isAffiliated && recentsCount > 0 && get(0) == task) { if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task); return; } // Another quick case: check if this is part of a set of affiliated // tasks that are at the top. if (isAffiliated && recentsCount > 0 && task.inRecents - && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0) + && task.mAffiliatedTaskId == get(0).mAffiliatedTaskId) { + if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + get(0) + " at top when adding " + task); return; } @@ -779,12 +532,12 @@ class RecentTasks { // Slightly less quick case: the task is already in recents, so all we need // to do is move it. if (task.inRecents) { - int taskIndex = mTasks.indexOf(task); + int taskIndex = indexOf(task); if (taskIndex >= 0) { - if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) { + if (!isAffiliated || MOVE_AFFILIATED_TASKS_TO_FRONT) { // Simple case: this is not an affiliated task, so we just move it to the front. - mTasks.remove(taskIndex); - mTasks.add(0, task); + remove(taskIndex); + add(0, task); notifyTaskPersisterLocked(task, false); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task + " from " + taskIndex); @@ -807,14 +560,20 @@ class RecentTasks { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task); - trimForAddTask(task); + trimForTaskLocked(task, true); + recentsCount = size(); + final int maxRecents = ActivityManager.getMaxRecentTasksStatic(); + while (recentsCount >= maxRecents) { + final TaskRecord tr = remove(recentsCount - 1); + tr.removedFromRecents(); + recentsCount--; + } task.inRecents = true; if (!isAffiliated || needAffiliationFix) { // If this is a simple non-affiliated task, or we had some failure trying to // handle it as part of an affilated task, then just place it at the top. - mTasks.add(0, task); - notifyTaskAdded(task); + add(0, task); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task); } else if (isAffiliated) { // If this is a new affiliated task, then move all of the affiliated tasks @@ -824,7 +583,7 @@ class RecentTasks { other = task.mPrevAffiliate; } if (other != null) { - int otherIndex = mTasks.indexOf(other); + int otherIndex = indexOf(other); if (otherIndex >= 0) { // Insert new task at appropriate location. int taskIndex; @@ -839,8 +598,7 @@ class RecentTasks { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: new affiliated task added at " + taskIndex + ": " + task); - mTasks.add(taskIndex, task); - notifyTaskAdded(task); + add(taskIndex, task); // Now move everything to the front. if (moveAffiliatedTasksToFront(task, taskIndex)) { @@ -867,235 +625,21 @@ class RecentTasks { if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations"); cleanupLocked(task.userId); } - - // Trim the set of tasks to the active set - trimInactiveRecentTasks(); - } - - /** - * Add the task to the bottom if possible. - */ - boolean addToBottom(TaskRecord task) { - if (!canAddTaskWithoutTrim(task)) { - // Adding this task would cause the task to be removed (since it's appended at - // the bottom and would be trimmed) so just return now - return false; - } - - add(task); - return true; - } - - /** - * Remove a task from the recent tasks list. - */ - void remove(TaskRecord task) { - mTasks.remove(task); - notifyTaskRemoved(task, !TRIMMED); - } - - /** - * Trims the recents task list to the global max number of recents. - */ - private void trimInactiveRecentTasks() { - int recentsCount = mTasks.size(); - - // Remove from the end of the list until we reach the max number of recents - while (recentsCount > mGlobalMaxNumTasks) { - final TaskRecord tr = mTasks.remove(recentsCount - 1); - notifyTaskRemoved(tr, TRIMMED); - recentsCount--; - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr - + " max=" + mGlobalMaxNumTasks); - } - - // Remove any tasks that belong to currently quiet profiles - final int[] profileUserIds = mUserController.getCurrentProfileIds(); - mTmpQuietProfileUserIds.clear(); - for (int userId : profileUserIds) { - final UserInfo userInfo = mUserController.getUserInfo(userId); - if (userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) { - mTmpQuietProfileUserIds.put(userId, true); - } - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo - + " quiet=" + mTmpQuietProfileUserIds.get(userId)); - } - - // Remove any inactive tasks, calculate the latest set of visible tasks - int numVisibleTasks = 0; - for (int i = 0; i < mTasks.size();) { - final TaskRecord task = mTasks.get(i); - - if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) { - if (!mHasVisibleRecentTasks) { - // Keep all active tasks if visible recent tasks is not supported - i++; - continue; - } - - if (!isVisibleRecentTask(task)) { - // Keep all active-but-invisible tasks - i++; - continue; - } else { - numVisibleTasks++; - if (isInVisibleRange(task, numVisibleTasks)) { - // Keep visible tasks in range - i++; - continue; - } else { - // Fall through to trim visible tasks that are no longer in range - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, - "Trimming out-of-range visible task=" + task); - } - } - } else { - // Fall through to trim inactive tasks - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task); - } - - // Task is no longer active, trim it from the list - mTasks.remove(task); - notifyTaskRemoved(task, TRIMMED); - notifyTaskPersisterLocked(task, false /* flush */); - } - } - - /** - * @return whether the given task should be considered active. - */ - private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task - + " globalMax=" + mGlobalMaxNumTasks); - - if (quietProfileUserIds.get(task.userId)) { - // Quiet profile user's tasks are never active - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true"); - return false; - } - - if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) { - // Keep the task active if its affiliated task is also active - final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId); - if (affiliatedTask != null) { - if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, - "\taffiliatedWithTask=" + affiliatedTask + " is not active"); - return false; - } - } - } - - // All other tasks are considered active - return true; - } - - /** - * @return whether the given active task should be presented to the user through SystemUI. - */ - private boolean isVisibleRecentTask(TaskRecord task) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task - + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks - + " sessionDuration=" + mActiveTasksSessionDurationMs - + " inactiveDuration=" + task.getInactiveDuration() - + " activityType=" + task.getActivityType() - + " windowingMode=" + task.getWindowingMode()); - - // Ignore certain activity types completely - switch (task.getActivityType()) { - case ACTIVITY_TYPE_HOME: - case ACTIVITY_TYPE_RECENTS: - return false; - } - - // Ignore certain windowing modes - switch (task.getWindowingMode()) { - case WINDOWING_MODE_PINNED: - return false; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask()); - final ActivityStack stack = task.getStack(); - if (stack != null && stack.topTask() == task) { - // Only the non-top task of the primary split screen mode is visible - return false; - } - } - - return true; - } - - /** - * @return whether the given visible task is within the policy range. - */ - private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) { - // Keep the last most task even if it is excluded from recents - final boolean isExcludeFromRecents = - (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; - if (isExcludeFromRecents) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true"); - return numVisibleTasks == 1; - } - - if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) { - // Always keep up to the min number of recent tasks, after that fall through to the - // checks below - return true; - } - - if (mMaxNumVisibleTasks >= 0) { - // Always keep up to the max number of recent tasks, but return false afterwards - return numVisibleTasks <= mMaxNumVisibleTasks; - } - - if (mActiveTasksSessionDurationMs > 0) { - // Keep the task if the inactive time is within the session window, this check must come - // after the checks for the min/max visible task range - if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) { - return true; - } - } - - return false; } /** * If needed, remove oldest existing entries in recents that are for the same kind * of task as the given one. */ - private void trimForAddTask(TaskRecord task) { - final int removeIndex = findTrimIndexForAddTask(task); - if (removeIndex == -1) { - // Nothing to trim - return; - } - - // There is a similar task that will be removed for the addition of {@param task}, but it - // can be the same task, and if so, the task will be re-added in add(), so skip the - // callbacks here. - final TaskRecord removedTask = mTasks.remove(removeIndex); - if (removedTask != task) { - notifyTaskRemoved(removedTask, TRIMMED); - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask - + " for addition of task=" + task); - } - notifyTaskPersisterLocked(removedTask, false /* flush */); - } - - /** - * Find the task that would be removed if the given {@param task} is added to the recent tasks - * list (if any). - */ - private int findTrimIndexForAddTask(TaskRecord task) { - int recentsCount = mTasks.size(); + int trimForTaskLocked(TaskRecord task, boolean doTrim) { + int recentsCount = size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); int maxRecents = task.maxRecents - 1; final ActivityStack stack = task.getStack(); for (int i = 0; i < recentsCount; i++) { - final TaskRecord tr = mTasks.get(i); + final TaskRecord tr = get(i); final ActivityStack trStack = tr.getStack(); - if (task != tr) { if (stack != null && trStack != null && stack != trStack) { continue; @@ -1106,7 +650,7 @@ class RecentTasks { final Intent trIntent = tr.intent; final boolean sameAffinity = task.affinity != null && task.affinity.equals(tr.affinity); - final boolean sameIntent = intent != null && intent.filterEquals(trIntent); + final boolean sameIntentFilter = intent != null && intent.filterEquals(trIntent); boolean multiTasksAllowed = false; final int flags = intent.getFlags(); if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0 @@ -1115,7 +659,7 @@ class RecentTasks { } final boolean trIsDocument = trIntent != null && trIntent.isDocument(); final boolean bothDocuments = document && trIsDocument; - if (!sameAffinity && !sameIntent && !bothDocuments) { + if (!sameAffinity && !sameIntentFilter && !bothDocuments) { continue; } @@ -1124,17 +668,17 @@ class RecentTasks { final boolean sameActivity = task.realActivity != null && tr.realActivity != null && task.realActivity.equals(tr.realActivity); + // If the document is open in another app or is not the same + // document, we don't need to trim it. if (!sameActivity) { - // If the document is open in another app or is not the same document, we - // don't need to trim it. continue; + // Otherwise only trim if we are over our max recents for this task } else if (maxRecents > 0) { - // Otherwise only trim if we are over our max recents for this task --maxRecents; - if (!sameIntent || multiTasksAllowed) { + if (!doTrim || !sameIntentFilter || multiTasksAllowed) { // We don't want to trim if we are not over the max allowed entries and - // the tasks are not of the same intent filter, or multiple entries for - // the task is allowed. + // the caller doesn't want us to trim, the tasks are not of the same + // intent filter, or multiple entries fot the task is allowed. continue; } } @@ -1145,14 +689,44 @@ class RecentTasks { continue; } } - return i; + + if (!doTrim) { + // If the caller is not actually asking for a trim, just tell them we reached + // a point where the trim would happen. + return i; + } + + // Either task and tr are the same or, their affinities match or their intents match + // and neither of them is a document, or they are documents using the same activity + // and their maxRecents has been reached. + remove(i); + if (task != tr) { + tr.removedFromRecents(); + } + i--; + recentsCount--; + if (task.intent == null) { + // If the new recent task we are adding is not fully + // specified, then replace it with the existing recent task. + task = tr; + } + notifyTaskPersisterLocked(tr, false); } + return -1; } + // Sort by taskId + private static Comparator<TaskRecord> sTaskRecordComparator = new Comparator<TaskRecord>() { + @Override + public int compare(TaskRecord lhs, TaskRecord rhs) { + return rhs.taskId - lhs.taskId; + } + }; + // Extract the affiliates of the chain containing recent at index start. private int processNextAffiliateChainLocked(int start) { - final TaskRecord startTask = mTasks.get(start); + final TaskRecord startTask = get(start); final int affiliateId = startTask.mAffiliatedTaskId; // Quick identification of isolated tasks. I.e. those not launched behind. @@ -1167,17 +741,17 @@ class RecentTasks { // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents. mTmpRecents.clear(); - for (int i = mTasks.size() - 1; i >= start; --i) { - final TaskRecord task = mTasks.get(i); + for (int i = size() - 1; i >= start; --i) { + final TaskRecord task = get(i); if (task.mAffiliatedTaskId == affiliateId) { - mTasks.remove(i); + remove(i); mTmpRecents.add(task); } } // Sort them all by taskId. That is the order they were create in and that order will // always be correct. - Collections.sort(mTmpRecents, TASK_ID_COMPARATOR); + Collections.sort(mTmpRecents, sTaskRecordComparator); // Go through and fix up the linked list. // The first one is the end of the chain and has no next. @@ -1215,197 +789,11 @@ class RecentTasks { notifyTaskPersisterLocked(last, false); } - // Insert the group back into mTmpTasks at start. - mTasks.addAll(start, mTmpRecents); + // Insert the group back into mRecentTasks at start. + addAll(start, mTmpRecents); mTmpRecents.clear(); // Let the caller know where we left off. return start + tmpSize; } - - private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) { - int recentsCount = mTasks.size(); - TaskRecord top = task; - int topIndex = taskIndex; - while (top.mNextAffiliate != null && topIndex > 0) { - top = top.mNextAffiliate; - topIndex--; - } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " - + topIndex + " from intial " + taskIndex); - // Find the end of the chain, doing a sanity check along the way. - boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; - int endIndex = topIndex; - TaskRecord prev = top; - while (endIndex < recentsCount) { - TaskRecord cur = mTasks.get(endIndex); - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @" - + endIndex + " " + cur); - if (cur == top) { - // Verify start of the chain. - if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": first task has next affiliate: " + prev); - sane = false; - break; - } - } else { - // Verify middle of the chain's next points back to the one before. - if (cur.mNextAffiliate != prev - || cur.mNextAffiliateTaskId != prev.taskId) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": middle task " + cur + " @" + endIndex - + " has bad next affiliate " - + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId - + ", expected " + prev); - sane = false; - break; - } - } - if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) { - // Chain ends here. - if (cur.mPrevAffiliate != null) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": last task " + cur + " has previous affiliate " - + cur.mPrevAffiliate); - sane = false; - } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); - break; - } else { - // Verify middle of the chain's prev points to a valid item. - if (cur.mPrevAffiliate == null) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": task " + cur + " has previous affiliate " - + cur.mPrevAffiliate + " but should be id " - + cur.mPrevAffiliate); - sane = false; - break; - } - } - if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": task " + cur + " has affiliated id " - + cur.mAffiliatedTaskId + " but should be " - + task.mAffiliatedTaskId); - sane = false; - break; - } - prev = cur; - endIndex++; - if (endIndex >= recentsCount) { - Slog.wtf(TAG, "Bad chain ran off index " + endIndex - + ": last task " + prev); - sane = false; - break; - } - } - if (sane) { - if (endIndex < taskIndex) { - Slog.wtf(TAG, "Bad chain @" + endIndex - + ": did not extend to task " + task + " @" + taskIndex); - sane = false; - } - } - if (sane) { - // All looks good, we can just move all of the affiliated tasks - // to the top. - for (int i=topIndex; i<=endIndex; i++) { - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task - + " from " + i + " to " + (i-topIndex)); - TaskRecord cur = mTasks.remove(i); - mTasks.add(i - topIndex, cur); - } - if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex - + " to " + endIndex); - return true; - } - - // Whoops, couldn't do it. - return false; - } - - void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { - pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)"); - if (mTasks.isEmpty()) { - return; - } - - final MutableBoolean printedAnything = new MutableBoolean(false); - final MutableBoolean printedHeader = new MutableBoolean(false); - final int size = mTasks.size(); - for (int i = 0; i < size; i++) { - final TaskRecord tr = mTasks.get(i); - if (dumpPackage != null && (tr.realActivity == null || - !dumpPackage.equals(tr.realActivity.getPackageName()))) { - continue; - } - - if (!printedHeader.value) { - pw.println(" Recent tasks:"); - printedHeader.value = true; - printedAnything.value = true; - } - pw.print(" * Recent #"); pw.print(i); pw.print(": "); - pw.println(tr); - if (dumpAll) { - tr.dump(pw, " "); - } - } - - if (!printedAnything.value) { - pw.println(" (nothing)"); - } - } - - /** - * Creates a new RecentTaskInfo from a TaskRecord. - */ - static ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) { - // Update the task description to reflect any changes in the task stack - tr.updateTaskDescription(); - - // Compose the recent task info - ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); - rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId; - rti.persistentId = tr.taskId; - rti.baseIntent = new Intent(tr.getBaseIntent()); - rti.origActivity = tr.origActivity; - rti.realActivity = tr.realActivity; - rti.description = tr.lastDescription; - rti.stackId = tr.getStackId(); - rti.userId = tr.userId; - rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription); - rti.lastActiveTime = tr.lastActiveTime; - rti.affiliatedTaskId = tr.mAffiliatedTaskId; - rti.affiliatedTaskColor = tr.mAffiliatedTaskColor; - rti.numActivities = 0; - if (tr.mBounds != null) { - rti.bounds = new Rect(tr.mBounds); - } - rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode(); - rti.resizeMode = tr.mResizeMode; - rti.configuration.setTo(tr.getConfiguration()); - - ActivityRecord base = null; - ActivityRecord top = null; - ActivityRecord tmp; - - for (int i = tr.mActivities.size() - 1; i >= 0; --i) { - tmp = tr.mActivities.get(i); - if (tmp.finishing) { - continue; - } - base = tmp; - if (top == null || (top.state == ActivityState.INITIALIZING)) { - top = base; - } - rti.numActivities++; - } - - rti.baseActivity = (base != null) ? base.intent.getComponent() : null; - rti.topActivity = (top != null) ? top.intent.getComponent() : null; - - return rti; - } } |