Delay cleaning user tasks until user is removed

Fixes #24301208 No recent apps shows when switching
between users.

Instead of cleaning up when stopping a user, we
should remove tasks when removing a user, since
recents tasks should be persisted across reboots.
Reboots are similar to stopping and starting users.

Change-Id: I9a250792077cca5f18ae1a10bc36f7b97e8ea867
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd10c63..9753331 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11893,7 +11893,8 @@
             updateCurrentProfileIdsLocked();
 
             mRecentTasks.clear();
-            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
+                    getUserManagerLocked().getUserIds()));
             mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
             mTaskPersister.startPersisting();
 
@@ -20644,9 +20645,6 @@
                 // Kill all the processes for the user.
                 forceStopUserLocked(userId, "finish user");
             }
-
-            // Explicitly remove the old information in mRecentTasks.
-            mRecentTasks.removeTasksForUserLocked(userId);
         }
 
         for (int i=0; i<callbacks.size(); i++) {
@@ -20665,6 +20663,10 @@
         }
     }
 
+    void onUserRemovedLocked(int userId) {
+        mRecentTasks.removeTasksForUserLocked(userId);
+    }
+
     @Override
     public UserInfo getCurrentUser() {
         if ((checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20949,6 +20951,13 @@
                 return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
+
+        @Override
+        public void onUserRemoved(int userId) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.onUserRemovedLocked(userId);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index aa154a7..871331b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -35,6 +35,7 @@
 import android.util.Xml;
 import android.os.Process;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -330,7 +331,7 @@
         return null;
     }
 
-    ArrayList<TaskRecord> restoreTasksLocked() {
+    ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
         final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
@@ -362,15 +363,18 @@
                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
                                     task);
                             if (task != null) {
-                                task.isPersistable = true;
                                 // XXX Don't add to write queue... there is no reason to write
                                 // out the stuff we just read, if we don't write it we will
                                 // read the same thing again.
                                 //mWriteQueue.add(new TaskWriteQueueItem(task));
-                                tasks.add(task);
                                 final int taskId = task.taskId;
-                                recoveredTaskIds.add(taskId);
                                 mStackSupervisor.setNextTaskId(taskId);
+                                // Check if it's a valid user id. Don't add tasks for removed users.
+                                if (ArrayUtils.contains(validUserIds, task.userId)) {
+                                    task.isPersistable = true;
+                                    tasks.add(task);
+                                    recoveredTaskIds.add(taskId);
+                                }
                             } else {
                                 Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
                                         fileToString(taskFile));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0577d59..7d8dc04 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
@@ -64,7 +65,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountManagerService;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -242,13 +243,15 @@
             synchronized (mPackagesLock) {
                 // Prune out any partially created/partially removed users.
                 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
-                for (int i = 0; i < mUsers.size(); i++) {
+                final int userSize = mUsers.size();
+                for (int i = 0; i < userSize; i++) {
                     UserInfo ui = mUsers.valueAt(i);
                     if ((ui.partial || ui.guestToRemove) && i != 0) {
                         partials.add(ui);
                     }
                 }
-                for (int i = 0; i < partials.size(); i++) {
+                final int partialsSize = partials.size();
+                for (int i = 0; i < partialsSize; i++) {
                     UserInfo ui = partials.get(i);
                     Slog.w(LOG_TAG, "Removing partially created user " + ui.id
                             + " (name=" + ui.name + ")");
@@ -272,7 +275,8 @@
     public UserInfo getPrimaryUser() {
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.isPrimary()) {
                     return ui;
@@ -287,7 +291,8 @@
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.partial) {
                     continue;
@@ -323,7 +328,8 @@
             // Probably a dying user
             return users;
         }
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             UserInfo profile = mUsers.valueAt(i);
             if (!isProfileOf(user, profile)) {
                 continue;
@@ -978,7 +984,8 @@
             serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
             writeRestrictionsLocked(serializer, mGuestRestrictions);
             serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo user = mUsers.valueAt(i);
                 serializer.startTag(null, TAG_USER);
                 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
@@ -1555,6 +1562,9 @@
                             }
                             new Thread() {
                                 public void run() {
+                                    // Clean up any ActivityManager state
+                                    LocalServices.getService(ActivityManagerInternal.class)
+                                            .onUserRemoved(userHandle);
                                     synchronized (mInstallLock) {
                                         synchronized (mPackagesLock) {
                                             removeUserStateLocked(userHandle);
@@ -1919,14 +1929,15 @@
      */
     private void updateUserIdsLocked() {
         int num = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 num++;
             }
         }
         final int[] newUsers = new int[num];
         int n = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 newUsers[n++] = mUsers.keyAt(i);
             }