Exclude tasks manipulated before user setup is complete from Recents

We don't want any tasks manipulated before the user is done setting-up
their device included in the tasks list we give to the Recents activity.
However, the task can be included back in Recents if it is manipulated
after the user set-up is complete. E.g. you go into the gmail activity
during setup the task will be exclude, but if the user goes back into
gmail after setup then we start including the task.

Bug: 25959392
Change-Id: I421d48f0a9bcfc782d1ef19aa2f63e8b34a668e2
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54c4ced..db710c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8738,6 +8738,13 @@
                         continue;
                     }
 
+                    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;
+                    }
+
                     ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                     if (!detailed) {
                         rti.baseIntent.replaceExtras((Bundle)null);
@@ -12393,9 +12400,8 @@
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
 
-            // Make sure we have the current profile info, since it is needed for
-            // security checks.
-            mUserController.updateCurrentProfileIdsLocked();
+            // Make sure we have the current profile info, since it is needed for security checks.
+            mUserController.onSystemReady();
 
             mRecentTasks.onSystemReady();
             // Check to see if there are any update receivers to run.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a7d948c..e8476dd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -106,6 +106,7 @@
     private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
+    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
     private static final String ATTR_EFFECTIVE_UID = "effective_uid";
     private static final String ATTR_TASKTYPE = "task_type";
     private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
@@ -155,6 +156,8 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
+    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
+                                // was changed.
 
     int numFullscreen;      // Number of fullscreen activities.
 
@@ -311,7 +314,8 @@
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean privileged, boolean _realActivitySuspended) {
+            int resizeMode, boolean privileged, boolean _realActivitySuspended,
+            boolean userSetupComplete) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -334,6 +338,7 @@
         taskType = _taskType;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
+        mUserSetupComplete = userSetupComplete;
         effectiveUid = _effectiveUid;
         firstActiveTime = _firstActiveTime;
         lastActiveTime = _lastActiveTime;
@@ -434,6 +439,7 @@
         }
 
         userId = UserHandle.getUserId(info.applicationInfo.uid);
+        mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId);
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
@@ -1064,6 +1070,7 @@
         out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
         out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
         out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
@@ -1133,6 +1140,7 @@
         boolean askedCompatMode = false;
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         int userId = 0;
+        boolean userSetupComplete = true;
         int effectiveUid = -1;
         String lastDescription = null;
         long firstActiveTime = -1;
@@ -1179,6 +1187,8 @@
                 askedCompatMode = Boolean.valueOf(attrValue);
             } else if (ATTR_USERID.equals(attrName)) {
                 userId = Integer.valueOf(attrValue);
+            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
+                userSetupComplete = Boolean.valueOf(attrValue);
             } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
                 effectiveUid = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
@@ -1278,7 +1288,7 @@
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
                 taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
-                realActivitySuspended);
+                realActivitySuspended, userSetupComplete);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 551f332..2f63b2d3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -45,11 +46,14 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
@@ -66,10 +70,12 @@
 import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.R;
@@ -145,6 +151,34 @@
 
     private final LockPatternUtils mLockPatternUtils;
 
+    // Set of users who have completed the set-up process.
+    private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray();
+    private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver;
+
+    private class UserSetupCompleteContentObserver extends ContentObserver {
+        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE);
+
+        public UserSetupCompleteContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        void register(ContentResolver resolver) {
+            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+            synchronized (mService) {
+                updateCurrentUserSetupCompleteLocked();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mUserSetupComplete.equals(uri)) {
+                synchronized (mService) {
+                    updateCurrentUserSetupCompleteLocked();
+                }
+            }
+        }
+    }
+
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -154,6 +188,7 @@
         mUserLru.add(UserHandle.USER_SYSTEM);
         mLockPatternUtils = new LockPatternUtils(mService.mContext);
         updateStartedUserArrayLocked();
+        mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler);
     }
 
     void finishUserSwitch(UserState uss) {
@@ -424,6 +459,7 @@
                 mStartedUsers.remove(userId);
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLocked();
+                mSetupCompletedUsers.delete(userId);
 
                 mService.onUserStoppedLocked(userId);
                 // Clean up all state and processes associated with the user.
@@ -619,6 +655,7 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+                updateCurrentUserSetupCompleteLocked();
 
                 if (foreground) {
                     mCurrentUserId = userId;
@@ -833,6 +870,17 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    void updateCurrentUserSetupCompleteLocked() {
+        final ContentResolver cr = mService.mContext.getContentResolver();
+        final boolean setupComplete =
+                Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
+        mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+    }
+
+    boolean isUserSetupCompleteLocked(int userId) {
+        return mSetupCompletedUsers.get(userId);
+    }
+
     private void stopBackgroundUsersIfEnforced(int oldUserId) {
         // Never stop system user
         if (oldUserId == UserHandle.USER_SYSTEM) {
@@ -1141,12 +1189,17 @@
         }
     }
 
+    void onSystemReady() {
+        updateCurrentProfileIdsLocked();
+        mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver());
+    }
+
     /**
      * Refreshes the list of users related to the current user when either a
      * user switch happens or when a new related user is started in the
      * background.
      */
-    void updateCurrentProfileIdsLocked() {
+    private void updateCurrentProfileIdsLocked() {
         final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId,
                 false /* enabledOnly */);
         int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null