diff options
12 files changed, 368 insertions, 182 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 093a33dc6049..6ece56fe6e3d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18,7 +18,6 @@ package com.android.server.am; import com.google.android.collect.Lists; import com.google.android.collect.Maps; - import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.AssistUtils; @@ -202,6 +201,7 @@ import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; import android.view.Display; @@ -565,7 +565,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * List of intents that were used to start the most recent tasks. */ - private final RecentTasks mRecentTasks; + final RecentTasks mRecentTasks; /** * For addAppTask: cached of the last activity component that was added. @@ -1053,11 +1053,6 @@ public final class ActivityManagerService extends ActivityManagerNative final AppOpsService mAppOpsService; /** - * Save recent tasks information across reboots. - */ - final TaskPersister mTaskPersister; - - /** * Current configuration information. HistoryRecord objects are given * a reference to this object to indicate which configuration they are * currently running in, so this object must be kept immutable. @@ -2509,10 +2504,10 @@ public final class ActivityManagerService extends ActivityManagerNative mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); - mRecentTasks = new RecentTasks(this); - mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks); + mStackSupervisor = new ActivityStackSupervisor(this); mActivityStarter = new ActivityStarter(this, mStackSupervisor); - mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks); + mRecentTasks = new RecentTasks(this, mStackSupervisor); + mProcessCpuThread = new Thread("CpuTracker") { @Override @@ -2566,6 +2561,10 @@ public final class ActivityManagerService extends ActivityManagerNative LocalServices.addService(ActivityManagerInternal.class, new LocalService()); } + void onUserStoppedLocked(int userId) { + mRecentTasks.unloadUserRecentsLocked(userId); + } + public void initPowerManagement() { mStackSupervisor.initPowerManagement(); mBatteryStatsService.initPowerManagement(); @@ -8851,6 +8850,8 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.GET_DETAILED_TASKS) == PackageManager.PERMISSION_GRANTED; + mRecentTasks.loadUserRecentsLocked(userId); + final int recentsCount = mRecentTasks.size(); ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount); @@ -9013,8 +9014,9 @@ public final class ActivityManagerService extends ActivityManagerNative thumbnailInfo.taskHeight = displaySize.y; thumbnailInfo.screenOrientation = mConfiguration.orientation; - TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo, - intent, description, thumbnailInfo); + TaskRecord task = new TaskRecord(this, + mStackSupervisor.getNextTaskIdForUserLocked(r.userId), + ainfo, intent, description, thumbnailInfo); int trimIdx = mRecentTasks.trimForTaskLocked(task, false); if (trimIdx >= 0) { @@ -9173,9 +9175,10 @@ public final class ActivityManagerService extends ActivityManagerNative passedIconFile.getName()); if (!legitIconFile.getPath().equals(filePath) || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) { - throw new IllegalArgumentException("Bad file path: " + filePath); + throw new IllegalArgumentException("Bad file path: " + filePath + + " passed for userId " + userId); } - return mTaskPersister.getTaskDescriptionIcon(filePath); + return mRecentTasks.getTaskDescriptionIcon(filePath); } @Override @@ -11145,11 +11148,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** Pokes the task persister. */ void notifyTaskPersisterLocked(TaskRecord task, boolean flush) { - if (task != null && task.stack != null && task.stack.isHomeStack()) { - // Never persist the home stack. - return; - } - mTaskPersister.wakeup(task, flush); + mRecentTasks.notifyTaskPersisterLocked(task, flush); } /** Notifies all listeners when the task stack has changed. */ @@ -12562,11 +12561,7 @@ public final class ActivityManagerService extends ActivityManagerNative // security checks. mUserController.updateCurrentProfileIdsLocked(); - mRecentTasks.clear(); - mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(mUserController.getUserIds())); - mRecentTasks.cleanupLocked(UserHandle.USER_ALL); - mTaskPersister.startPersisting(); - + mRecentTasks.onSystemReady(); // Check to see if there are any update receivers to run. if (!mDidUpdate) { if (mWaitingUpdate) { @@ -20818,10 +20813,6 @@ public final class ActivityManagerService extends ActivityManagerNative return mUserController.stopUser(userId, force, callback); } - void onUserRemovedLocked(int userId) { - mRecentTasks.removeTasksForUserLocked(userId); - } - @Override public UserInfo getCurrentUser() { return mUserController.getCurrentUser(); @@ -20876,6 +20867,10 @@ public final class ActivityManagerService extends ActivityManagerNative return newInfo; } + public boolean isUserStopped(int userId) { + return mUserController.getStartedUserStateLocked(userId) == null; + } + ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) { if (aInfo == null || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) { @@ -21028,7 +21023,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void onUserRemoved(int userId) { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.onUserRemovedLocked(userId); + ActivityManagerService.this.onUserStoppedLocked(userId); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 13c14176e670..8c997395da46 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -21,6 +21,8 @@ import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; +import com.android.internal.util.ArrayUtils; + import java.io.PrintWriter; class ActivityManagerShellCommand extends ShellCommand { @@ -58,6 +60,8 @@ class ActivityManagerShellCommand extends ShellCommand { return runTrackAssociations(pw); case "untrack-associations": return runUntrackAssociations(pw); + case "is-user-stopped": + return runIsUserStopped(pw); default: return handleDefaultCommands(cmd); } @@ -67,6 +71,13 @@ class ActivityManagerShellCommand extends ShellCommand { return -1; } + int runIsUserStopped(PrintWriter pw) { + int userId = UserHandle.parseUserArg(getNextArgRequired()); + boolean stopped = mInternal.isUserStopped(userId); + pw.println(stopped); + return 0; + } + int runForceStop(PrintWriter pw) throws RemoteException { int userId = UserHandle.USER_ALL; @@ -107,7 +118,7 @@ class ActivityManagerShellCommand extends ShellCommand { int runWrite(PrintWriter pw) { mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "registerUidObserver()"); - mInternal.mTaskPersister.flush(); + mInternal.mRecentTasks.flush(); pw.println("All tasks persisted."); return 0; } @@ -190,6 +201,8 @@ class ActivityManagerShellCommand extends ShellCommand { pw.println(" Enable association tracking."); pw.println(" untrack-associations"); pw.println(" Disable and clear association tracking."); + pw.println(" is-user-stopped <USER_ID>"); + pw.println(" returns whether <USER_ID> has been stopped or not"); } } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index eb0945bfe5f1..b16bd2b1c279 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1268,7 +1268,7 @@ final class ActivityRecord { final String iconFilename = createImageFilename(createTime, task.taskId); final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename); final String iconFilePath = iconFile.getAbsolutePath(); - mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilePath); + service.mRecentTasks.saveImage(icon, iconFilePath); _taskDescription.setIconFilename(iconFilePath); } taskDescription = _taskDescription; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 312e309e3950..07cc505b6450 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2590,8 +2590,9 @@ final class ActivityStack { if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to bottom task " + bottom.task); } else { - targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info, - null, null, null, false); + targetTask = createTaskRecord( + mStackSupervisor.getNextTaskIdForUserLocked(target.userId), + target.info, null, null, null, false); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to new task " + target.task); @@ -4798,7 +4799,8 @@ final class ActivityStack { final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r); final TaskRecord task = createTaskRecord( - mStackSupervisor.getNextTaskId(), r.info, r.intent, null, null, true); + mStackSupervisor.getNextTaskIdForUserLocked(r.userId), + r.info, r.intent, null, null, true); r.setTask(task, null); task.addActivityToTop(r); setAppTask(r, task); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c634e0e6a6ac..03f5c41ca349 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -262,10 +262,12 @@ public final class ActivityStackSupervisor implements DisplayListener { // For debugging to make sure the caller when acquiring/releasing our // wake lock is the system process. static final boolean VALIDATE_WAKE_LOCK_CALLER = false; + /** The number of distinct task ids that can be assigned to the tasks of a single user */ + private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE; final ActivityManagerService mService; - private final RecentTasks mRecentTasks; + private RecentTasks mRecentTasks; final ActivityStackSupervisorHandler mHandler; @@ -276,9 +278,11 @@ public final class ActivityStackSupervisor implements DisplayListener { /** Counter for next free stack ID to use for dynamic activity stacks. */ private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID; - /** Task identifier that activities are currently being started in. Incremented each time a - * new task is created. */ - private int mCurTaskId = 0; + /** + * Maps the task identifier that activities are currently being started in to the userId of the + * task. Each time a new task is created, the entry for the userId of the task is incremented + */ + private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20); /** The current user */ int mCurrentUser; @@ -430,14 +434,17 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - public ActivityStackSupervisor(ActivityManagerService service, RecentTasks recentTasks) { + public ActivityStackSupervisor(ActivityManagerService service) { mService = service; - mRecentTasks = recentTasks; mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler); } + void setRecentTasks(RecentTasks recentTasks) { + mRecentTasks = recentTasks; + } + /** * At the time when the constructor runs, the power manager has not yet been * initialized. So we initialize our wakelocks afterwards. @@ -679,20 +686,37 @@ public final class ActivityStackSupervisor implements DisplayListener { return null; } - void setNextTaskId(int taskId) { - if (taskId > mCurTaskId) { - mCurTaskId = taskId; + void setNextTaskIdForUserLocked(int taskId, int userId) { + final int currentTaskId = mCurTaskIdForUser.get(userId, -1); + if (taskId > currentTaskId) { + mCurTaskIdForUser.put(userId, taskId); } } - int getNextTaskId() { - do { - mCurTaskId++; - if (mCurTaskId <= 0) { - mCurTaskId = 1; + int getNextTaskIdForUserLocked(int userId) { + mRecentTasks.loadUserRecentsLocked(userId); + final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER); + // for a userId u, a taskId can only be in the range + // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER + // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. + int candidateTaskId = currentTaskId; + while (anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS, + INVALID_STACK_ID) != null) { + candidateTaskId++; + if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) { + // Wrap around as there will be smaller task ids that are available now. + candidateTaskId -= MAX_TASK_IDS_PER_USER; } - } while (anyTaskForIdLocked(mCurTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null); - return mCurTaskId; + if (candidateTaskId == currentTaskId) { + // Something wrong! + // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user + throw new IllegalStateException("Cannot get an available task id." + + " Reached limit of " + MAX_TASK_IDS_PER_USER + + " running tasks per user."); + } + } + mCurTaskIdForUser.put(userId, candidateTaskId); + return candidateTaskId; } ActivityRecord resumedAppLocked() { @@ -2796,7 +2820,8 @@ public final class ActivityStackSupervisor implements DisplayListener { pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack); pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack); pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout); - pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId); + pw.print(prefix); + pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront); pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers); pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString()); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index cfa44335f6eb..9bcff884c331 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1420,7 +1420,8 @@ class ActivityStarter { } if (mReuseTask == null) { - final TaskRecord task = mTargetStack.createTaskRecord(mSupervisor.getNextTaskId(), + final TaskRecord task = mTargetStack.createTaskRecord( + mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */); @@ -1552,9 +1553,9 @@ class ActivityStarter { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.topActivity(); - final TaskRecord task = prev != null ? prev.task - : mTargetStack.createTaskRecord( - mSupervisor.getNextTaskId(), mStartActivity.info, mIntent, null, null, true); + final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord( + mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), + mStartActivity.info, mIntent, null, null, true); mStartActivity.setTask(task, null); mWindowManager.moveTaskToTop(mStartActivity.task.taskId); if (DEBUG_TASKS) Slog.v(TAG_TASKS, diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 52d23cf05888..a3c26cb8f963 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -16,7 +16,12 @@ package com.android.server.am; -import static com.android.server.am.ActivityManagerDebugConfig.*; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; +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.TaskRecord.INVALID_TASK_ID; import android.app.ActivityManager; @@ -27,11 +32,16 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.os.Environment; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; +import android.util.SparseBooleanArray; +import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -47,18 +57,106 @@ class RecentTasks extends ArrayList<TaskRecord> { // Maximum number recent bitmaps to keep in memory. private static final int MAX_RECENT_BITMAPS = 3; - // Activity manager service. - private final ActivityManagerService mService; + /** + * Save recent tasks information across reboots. + */ + private final TaskPersister mTaskPersister; + private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(5); // Mainly to avoid object recreation on multiple calls. private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>(); - private final HashMap<ComponentName, ActivityInfo> tmpAvailActCache = new HashMap<>(); - private final HashMap<String, ApplicationInfo> tmpAvailAppCache = new HashMap<>(); - private final ActivityInfo tmpActivityInfo = new ActivityInfo(); - private final ApplicationInfo tmpAppInfo = new ApplicationInfo(); + private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); + private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); + private final ActivityInfo mTmpActivityInfo = new ActivityInfo(); + private final ApplicationInfo mTmpAppInfo = new ApplicationInfo(); + + RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) { + File systemDir = Environment.getDataSystemDirectory(); + mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this); + mStackSupervisor.setRecentTasks(this); + } + + /** + * Loads the persistent recentTasks for {@code userId} into {@link #mRecentTasks} from + * persistent storage. Does nothing if they are already loaded. + * + * @param userId the user Id + */ + void loadUserRecentsLocked(int userId) { + if (!mUsersWithRecentsLoaded.get(userId)) { + Slog.i(TAG, "Loading recents for user " + userId + " into memory."); + addAll(mTaskPersister.restoreTasksForUserLocked(userId)); + cleanupLocked(userId); + mUsersWithRecentsLoaded.put(userId, true); + } + } - RecentTasks(ActivityManagerService service) { - mService = service; + void notifyTaskPersisterLocked(TaskRecord task, boolean flush) { + if (task != null && task.stack != null && task.stack.isHomeStack()) { + // Never persist the home stack. + return; + } + mTaskPersister.wakeup(task, flush); + } + + void onSystemReady() { + clear(); + loadUserRecentsLocked(UserHandle.USER_SYSTEM); + startPersisting(); + } + + void startPersisting() { + mTaskPersister.startPersisting(); + } + + Bitmap getTaskDescriptionIcon(String path) { + return mTaskPersister.getTaskDescriptionIcon(path); + } + + Bitmap getImageFromWriteQueue(String path) { + return mTaskPersister.getImageFromWriteQueue(path); + } + + void saveImage(Bitmap image, String path) { + mTaskPersister.saveImage(image, path); + } + + void flush() { + mTaskPersister.flush(); + } + + /** + * Returns all userIds for which recents from storage are loaded + * + * @return an array of userIds. + */ + int[] usersWithRecentsLoadedLocked() { + int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()]; + int len = 0; + for (int i = 0; i < usersWithRecentsLoaded.length; i++) { + int userId = mUsersWithRecentsLoaded.keyAt(i); + if (mUsersWithRecentsLoaded.valueAt(i)) { + usersWithRecentsLoaded[len++] = userId; + } + } + if (len < usersWithRecentsLoaded.length) { + // should never happen. + return Arrays.copyOf(usersWithRecentsLoaded, len); + } + return usersWithRecentsLoaded; + } + + /** + * Removes recent tasks for this user if they are loaded, does not do anything otherwise. + * + * @param userId the user id. + */ + void unloadUserRecentsLocked(int userId) { + if (mUsersWithRecentsLoaded.get(userId)) { + Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); + mUsersWithRecentsLoaded.delete(userId); + removeTasksForUserLocked(userId); + } } TaskRecord taskForIdLocked(int id) { @@ -88,9 +186,6 @@ class RecentTasks extends ArrayList<TaskRecord> { tr.removedFromRecents(); } } - - // Remove tasks from persistent storage. - mService.notifyTaskPersisterLocked(null, true); } /** @@ -107,87 +202,86 @@ class RecentTasks extends ArrayList<TaskRecord> { } final IPackageManager pm = AppGlobals.getPackageManager(); - final int[] users = (userId == UserHandle.USER_ALL) - ? mService.mUserController.getUsers() : new int[] { userId }; - for (int userIdx = 0; userIdx < users.length; userIdx++) { - final int user = users[userIdx]; - recentsCount = size() - 1; - for (int i = recentsCount; i >= 0; i--) { - TaskRecord task = get(i); - if (task.userId != user) { - // 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. - remove(i); - task.removedFromRecents(); - Slog.w(TAG, "Removing auto-remove without activity: " + task); - continue; - } - // Check whether this activity is currently available. - if (task.realActivity != null) { - ActivityInfo ai = tmpAvailActCache.get(task.realActivity); + for (int i = recentsCount - 1; i >= 0; 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. + remove(i); + task.removedFromRecents(); + Slog.w(TAG, "Removing auto-remove without activity: " + task); + continue; + } + // Check whether this activity is currently available. + if (task.realActivity != null) { + ActivityInfo ai = mTmpAvailActCache.get(task.realActivity); + if (ai == null) { + try { + // At this first cut, we're only interested in + // activities that are fully runnable based on + // current system state. + ai = pm.getActivityInfo(task.realActivity, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); + } catch (RemoteException e) { + // Will never happen. + continue; + } if (ai == null) { + ai = mTmpActivityInfo; + } + mTmpAvailActCache.put(task.realActivity, ai); + } + 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. + ApplicationInfo app = mTmpAvailAppCache + .get(task.realActivity.getPackageName()); + if (app == null) { try { - // At this first cut, we're only interested in - // activities that are fully runnable based on - // current system state. - ai = pm.getActivityInfo(task.realActivity, - PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user); + app = pm.getApplicationInfo(task.realActivity.getPackageName(), + PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); } catch (RemoteException e) { // Will never happen. continue; } - if (ai == null) { - ai = tmpActivityInfo; - } - tmpAvailActCache.put(task.realActivity, ai); - } - if (ai == tmpActivityInfo) { - // 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. - ApplicationInfo app = tmpAvailAppCache.get(task.realActivity.getPackageName()); if (app == null) { - try { - app = pm.getApplicationInfo(task.realActivity.getPackageName(), - PackageManager.MATCH_UNINSTALLED_PACKAGES, user); - } catch (RemoteException e) { - // Will never happen. - continue; - } - if (app == null) { - app = tmpAppInfo; - } - tmpAvailAppCache.put(task.realActivity.getPackageName(), app); - } - if (app == tmpAppInfo || (app.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { - // Doesn't exist any more! Good-bye. - remove(i); - task.removedFromRecents(); - Slog.w(TAG, "Removing no longer valid recent: " + task); - continue; - } else { - // Otherwise just not available for now. - if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, - "Making recent unavailable: " + task); - task.isAvailable = false; + app = mTmpAppInfo; } + mTmpAvailAppCache.put(task.realActivity.getPackageName(), app); + } + if (app == mTmpAppInfo + || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { + // Doesn't exist any more! Good-bye. + remove(i); + task.removedFromRecents(); + Slog.w(TAG, "Removing no longer valid recent: " + task); + continue; } else { - if (!ai.enabled || !ai.applicationInfo.enabled - || (ai.applicationInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { - if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, - "Making recent unavailable: " + task - + " (enabled=" + ai.enabled + "/" + ai.applicationInfo.enabled - + " flags=" + Integer.toHexString(ai.applicationInfo.flags) - + ")"); - task.isAvailable = false; - } else { - if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS, - "Making recent available: " + task); - task.isAvailable = true; - } + // Otherwise just not available for now. + if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, + "Making recent unavailable: " + task); + task.isAvailable = false; + } + } else { + if (!ai.enabled || !ai.applicationInfo.enabled + || (ai.applicationInfo.flags + & ApplicationInfo.FLAG_INSTALLED) == 0) { + if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS, + "Making recent unavailable: " + task + + " (enabled=" + ai.enabled + "/" + + ai.applicationInfo.enabled + + " flags=" + + Integer.toHexString(ai.applicationInfo.flags) + + ")"); + task.isAvailable = false; + } else { + if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS, + "Making recent available: " + task); + task.isAvailable = true; } } } @@ -341,7 +435,7 @@ class RecentTasks extends ArrayList<TaskRecord> { // Simple case: this is not an affiliated task, so we just move it to the front. remove(taskIndex); add(0, task); - mService.notifyTaskPersisterLocked(task, false); + notifyTaskPersisterLocked(task, false); if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task + " from " + taskIndex); return; @@ -501,7 +595,7 @@ class RecentTasks extends ArrayList<TaskRecord> { // specified, then replace it with the existing recent task. task = tr; } - mService.notifyTaskPersisterLocked(tr, false); + notifyTaskPersisterLocked(tr, false); } return -1; @@ -551,7 +645,7 @@ class RecentTasks extends ArrayList<TaskRecord> { if (first.mNextAffiliate != null) { Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate); first.setNextAffiliate(null); - mService.notifyTaskPersisterLocked(first, false); + notifyTaskPersisterLocked(first, false); } // Everything in the middle is doubly linked from next to prev. final int tmpSize = mTmpRecents.size(); @@ -562,13 +656,13 @@ class RecentTasks extends ArrayList<TaskRecord> { Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate + " setting prev=" + prev); next.setPrevAffiliate(prev); - mService.notifyTaskPersisterLocked(next, false); + notifyTaskPersisterLocked(next, false); } if (prev.mNextAffiliate != next) { Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate + " setting next=" + next); prev.setNextAffiliate(next); - mService.notifyTaskPersisterLocked(prev, false); + notifyTaskPersisterLocked(prev, false); } prev.inRecents = true; } @@ -577,7 +671,7 @@ class RecentTasks extends ArrayList<TaskRecord> { if (last.mPrevAffiliate != null) { Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate); last.setPrevAffiliate(null); - mService.notifyTaskPersisterLocked(last, false); + notifyTaskPersisterLocked(last, false); } // Insert the group back into mRecentTasks at start. diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 9a0007555eb5..283939e29229 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -21,16 +21,18 @@ import android.graphics.BitmapFactory; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; +import android.os.Process; import android.os.SystemClock; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; -import android.os.Process; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -42,12 +44,10 @@ import java.io.FileReader; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; -import libcore.io.IoUtils; - public class TaskPersister { static final String TAG = "TaskPersister"; static final boolean DEBUG = false; @@ -113,7 +113,7 @@ public class TaskPersister { ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>(); TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor, - RecentTasks recentTasks) { + ActivityManagerService service, RecentTasks recentTasks) { final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME); if (legacyImagesDir.exists()) { @@ -130,7 +130,7 @@ public class TaskPersister { } mStackSupervisor = stackSupervisor; - mService = stackSupervisor.mService; + mService = service; mRecentTasks = recentTasks; mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread"); } @@ -145,11 +145,15 @@ public class TaskPersister { final String taskString = Integer.toString(task.taskId); for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) { final WriteQueueItem item = mWriteQueue.get(queueNdx); - if (item instanceof ImageWriteQueueItem && - ((ImageWriteQueueItem) item).mFilePath.startsWith(taskString)) { - if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath + - " from write queue"); - mWriteQueue.remove(queueNdx); + if (item instanceof ImageWriteQueueItem) { + final File thumbnailFile = new File(((ImageWriteQueueItem) item).mFilePath); + if (thumbnailFile.getName().startsWith(taskString)) { + if (DEBUG) { + Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath + + " from write queue"); + } + mWriteQueue.remove(queueNdx); + } } } } @@ -185,7 +189,8 @@ public class TaskPersister { mWriteQueue.add(new TaskWriteQueueItem(task)); } } else { - // Dummy. + // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is + // notified. mWriteQueue.add(new WriteQueueItem()); } if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) { @@ -323,8 +328,8 @@ public class TaskPersister { return null; } - private List<TaskRecord> restoreTasksForUserLocked(final int userId) { - final List<TaskRecord> tasks = new ArrayList<TaskRecord>(); + List<TaskRecord> restoreTasksForUserLocked(final int userId) { + final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>(); ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>(); File userTasksDir = getUserTasksDir(userId); @@ -351,10 +356,10 @@ public class TaskPersister { event != XmlPullParser.END_TAG) { final String name = in.getName(); if (event == XmlPullParser.START_TAG) { - if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name); + if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name); if (TAG_TASK.equals(name)) { final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor); - if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task=" + task); if (task != null) { // XXX Don't add to write queue... there is no reason to write @@ -362,7 +367,7 @@ public class TaskPersister { // read the same thing again. // mWriteQueue.add(new TaskWriteQueueItem(task)); final int taskId = task.taskId; - mStackSupervisor.setNextTaskId(taskId); + mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId); // Check if it's a valid user id. Don't add tasks for removed users. if (userId == task.userId) { task.isPersistable = true; @@ -396,15 +401,6 @@ public class TaskPersister { if (!DEBUG) { removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles()); } - return tasks; - } - - ArrayList<TaskRecord> restoreTasksLocked(final int[] validUserIds) { - final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>(); - - for (int userId : validUserIds) { - tasks.addAll(restoreTasksForUserLocked(userId)); - } // Fix up task affiliation from taskIds for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { @@ -413,9 +409,7 @@ public class TaskPersister { task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks)); } - TaskRecord[] tasksArray = new TaskRecord[tasks.size()]; - tasks.toArray(tasksArray); - Arrays.sort(tasksArray, new Comparator<TaskRecord>() { + Collections.sort(tasks, new Comparator<TaskRecord>() { @Override public int compare(TaskRecord lhs, TaskRecord rhs) { final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved; @@ -428,8 +422,7 @@ public class TaskPersister { } } }); - - return new ArrayList<TaskRecord>(Arrays.asList(tasksArray)); + return tasks; } private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) { @@ -462,7 +455,13 @@ public class TaskPersister { } private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) { - for (int userId : mService.getRunningUserIds()) { + int[] candidateUserIds; + synchronized (mService) { + // Remove only from directories of the users who have recents in memory synchronized + // with persistent storage. + candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked(); + } + for (int userId : candidateUserIds) { removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles()); removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles()); } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index ae987e6f4402..c49aff674316 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -551,7 +551,7 @@ final class TaskRecord { mLastThumbnailFile.delete(); } } else { - mService.mTaskPersister.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath()); + mService.mRecentTasks.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath()); } return true; } @@ -563,7 +563,7 @@ final class TaskRecord { thumbs.thumbnailInfo = mLastThumbnailInfo; thumbs.thumbnailFileDescriptor = null; if (mLastThumbnail == null) { - thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue( + thumbs.mainThumbnail = mService.mRecentTasks.getImageFromWriteQueue( mLastThumbnailFile.getAbsolutePath()); } // Only load the thumbnail file if we don't have a thumbnail diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 717285930f5b..ed7186459109 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -419,6 +419,7 @@ final class UserController { mUserLru.remove(Integer.valueOf(userId)); updateStartedUserArrayLocked(); + mService.onUserStoppedLocked(userId); // Clean up all state and processes associated with the user. // Kill all the processes for the user. forceStopUserLocked(userId, "finish user"); @@ -1121,8 +1122,7 @@ final class UserController { UserState uss = mStartedUsers.valueAt(i); if (uss.state != UserState.STATE_STOPPING && uss.state != UserState.STATE_SHUTDOWN) { - mStartedUserArray[num] = mStartedUsers.keyAt(i); - num++; + mStartedUserArray[num++] = mStartedUsers.keyAt(i); } } } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index eed326e4a4c4..ad2b2a24ab5b 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -27,7 +27,9 @@ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> - + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.REAL_GET_TASKS" /> + <uses-permission android:name="android.permission.GET_DETAILED_TASKS" /> <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" /> diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java new file mode 100644 index 000000000000..625fe7768990 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.RemoteException; +import android.test.AndroidTestCase; + +import java.util.List; + +public class ActivityManagerTest extends AndroidTestCase { + + IActivityManager service; + @Override + public void setUp() throws Exception { + super.setUp(); + service = ActivityManagerNative.getDefault(); + } + + public void testTaskIdsForRunningUsers() throws RemoteException { + for(int userId : service.getRunningUserIds()) { + testTaskIdsForUser(userId); + } + } + + private void testTaskIdsForUser(int userId) throws RemoteException { + List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks( + 100, 0, userId); + if(recentTasks != null) { + for(ActivityManager.RecentTaskInfo recentTask : recentTasks) { + int taskId = recentTask.persistentId; + assertEquals("The task id " + taskId + " should not belong to user " + userId, + taskId / UserHandle.PER_USER_RANGE, userId); + } + } + } +}
\ No newline at end of file |