Snap for 5970985 from 167f5de804bb0aca128fc20291d6be1b5838d880 to qt-d4-release

Change-Id: I2c863e35a6b878d5a5affadbbdf0442c6381d3ae
diff --git a/src/com/android/internal/car/CarServiceHelperService.java b/src/com/android/internal/car/CarServiceHelperService.java
index 160d248..3710f82 100644
--- a/src/com/android/internal/car/CarServiceHelperService.java
+++ b/src/com/android/internal/car/CarServiceHelperService.java
@@ -17,6 +17,7 @@
 package com.android.internal.car;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
@@ -36,8 +37,11 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.sysprop.CarProperties;
 import android.util.Slog;
 import android.util.TimingsTraceLog;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -85,9 +89,11 @@
     private IBinder mCarService;
     @GuardedBy("mLock")
     private boolean mSystemBootCompleted;
+
     @GuardedBy("mLock")
     private final HashMap<Integer, Boolean> mUserUnlockedStatus = new HashMap<>();
     private final CarUserManagerHelper mCarUserManagerHelper;
+    private final UserManager mUserManager;
     private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
 
         @Override
@@ -113,6 +119,7 @@
         super(context);
         mContext = context;
         mCarUserManagerHelper = carUserManagerHelper;
+        mUserManager = UserManager.get(context);
     }
 
     @Override
@@ -127,6 +134,9 @@
             setupAndStartUsers();
             checkForCarServiceConnection();
         } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+            t.traceBegin("onBootPhase.completed");
+            managePreCreatedUsers();
             boolean shouldNotify = false;
             synchronized (mLock) {
                 mSystemBootCompleted = true;
@@ -137,6 +147,7 @@
             if (shouldNotify) {
                 notifyAllUnlockedUsers();
             }
+            t.traceEnd();
         }
     }
 
@@ -263,19 +274,21 @@
             }
             targetUserId = admin.id;
         } else {
-            Slog.i(TAG, "Switch to default user");
             targetUserId = mCarUserManagerHelper.getInitialUser();
+            Slog.i(TAG, "Switching to user " + targetUserId + " on boot");
+        }
+
+        IActivityManager am = ActivityManager.getService();
+        if (am == null) {
+            Slog.wtf(TAG, "cannot get ActivityManagerService");
+            return;
         }
 
         // If system user is the only user to unlock, handle it when system completes the boot.
         if (targetUserId == UserHandle.USER_SYSTEM) {
             return;
         }
-        IActivityManager am = ActivityManager.getService();
-        if (am == null) {
-            Slog.wtf(TAG, "cannot get ActivityManagerService");
-            return;
-        }
+
         TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
         unlockSystemUser(t, am);
 
@@ -320,6 +333,140 @@
         }
     }
 
+    private void managePreCreatedUsers() {
+
+        // First gets how many pre-createad users are defined by the OEM
+        int numberRequestedGuests = CarProperties.number_pre_created_guests().orElse(0);
+        int numberRequestedUsers = CarProperties.number_pre_created_users().orElse(0);
+        Slog.i(TAG, "managePreCreatedUsers(): OEM asked for " + numberRequestedGuests
+                + " guests and " + numberRequestedUsers + " users");
+        if (numberRequestedGuests < 0 || numberRequestedUsers < 0) {
+            Slog.w(TAG, "preCreateUsers(): invalid values provided by OEM; "
+                    + "number_pre_created_guests=" + numberRequestedGuests
+                    + ", number_pre_created_users=" + numberRequestedUsers);
+            return;
+        }
+
+        if (numberRequestedGuests == 0 && numberRequestedUsers == 0) {
+            Slog.i(TAG, "managePreCreatedUsers(): not defined by OEM");
+            return;
+        }
+
+        // Then checks how many exist already
+        List<UserInfo> allUsers = mUserManager.getUsers(/* excludePartial= */ true,
+                /* excludeDying= */ true, /* excludePreCreated= */ false);
+
+        int allUsersSize = allUsers.size();
+        if (DBG) Slog.d(TAG, "preCreateUsers: total users size is "  + allUsersSize);
+
+        int numberExistingGuests = 0;
+        int numberExistingUsers = 0;
+
+        // List of pre-created users that were not properly initialized. Typically happens when
+        // the system crashed / rebooted before they were fully started.
+        SparseBooleanArray invalidUsers = new SparseBooleanArray();
+
+        for (int i = 0; i < allUsersSize; i++) {
+            UserInfo user = allUsers.get(i);
+            if (!user.preCreated) continue;
+            if (!user.isInitialized()) {
+                Slog.w(TAG, "Found invalid pre-created user that needs to be removed: "
+                        + user.toFullString());
+                invalidUsers.append(user.id, /* notUsed=*/ true);
+                continue;
+            }
+            if (user.isGuest()) {
+                numberExistingGuests++;
+            } else {
+                numberExistingUsers++;
+            }
+        }
+        if (DBG) {
+            Slog.i(TAG, "managePreCreatedUsers(): system already has " + numberExistingGuests
+                    + " pre-created guests," + numberExistingUsers + " pre-created users, and these"
+                    + " invalid users: " + invalidUsers );
+        }
+
+        int numberGuests = numberRequestedGuests - numberExistingGuests;
+        int numberUsers = numberRequestedUsers - numberExistingUsers;
+        int numberInvalidUsers = invalidUsers.size();
+
+        if (numberGuests <= 0 && numberUsers <= 0 && numberInvalidUsers == 0) {
+            Slog.i(TAG, "managePreCreatedUsers(): all pre-created and no invalid ones");
+            return;
+        }
+
+        // Finally, manage them....
+
+        // In theory, we could submit multiple user pre-creations in parallel, but we're
+        // submitting just 1 task, for 2 reasons:
+        //   1.To minimize it's effect on other system server initialization tasks.
+        //   2.The pre-created users will be unlocked in parallel anyways.
+        new Thread( () -> {
+            TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+
+            t.traceBegin("preCreateUsers");
+            if (numberUsers > 0) {
+                preCreateUsers(t, numberUsers, /* isGuest= */ false);
+            }
+            if (numberGuests > 0) {
+                preCreateUsers(t, numberGuests, /* isGuest= */ true);
+            }
+            t.traceEnd();
+
+            if (numberInvalidUsers > 0) {
+                t.traceBegin("removeInvalidPreCreatedUsers");
+                for (int i = 0; i < numberInvalidUsers; i++) {
+                    int userId = invalidUsers.keyAt(i);
+                    Slog.i(TAG, "removing invalid pre-created user " + userId);
+                    mUserManager.removeUser(userId);
+                }
+                t.traceEnd();
+            }
+        }, "CarServiceHelperManagePreCreatedUsers").start();
+    }
+
+    private void preCreateUsers(@NonNull TimingsTraceLog t, int size, boolean isGuest) {
+        String msg = isGuest ? "preCreateGuests-" + size : "preCreateUsers-" + size;
+        t.traceBegin(msg);
+        for (int i = 1; i <= size; i++) {
+            UserInfo preCreated = preCreateUsers(t, isGuest);
+            if (preCreated == null) {
+                Slog.w(TAG, "Could not pre-create " + (isGuest ? " guest " : "")
+                        + " user #" + i);
+                continue;
+            }
+        }
+        t.traceEnd();
+    }
+
+    // TODO(b/111451156): add unit test?
+    @Nullable
+    public UserInfo preCreateUsers(@NonNull TimingsTraceLog t, boolean isGuest) {
+        int flags = 0;
+        String traceMsg =  "pre-create";
+        if (isGuest) {
+            flags |= UserInfo.FLAG_GUEST;
+            traceMsg += "-guest";
+        } else {
+            traceMsg += "-user";
+        }
+        t.traceBegin(traceMsg);
+        // NOTE: we want to get rid of UserManagerHelper, so let's call UserManager directly
+        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        UserInfo user = um.preCreateUser(flags);
+        try {
+            if (user == null) {
+                // Couldn't create user, most likely because there are too many.
+                Slog.w(TAG, "couldn't " + traceMsg);
+                return null;
+            }
+        } finally {
+            t.traceEnd();
+        }
+        return user;
+    }
+
     private void notifyAllUnlockedUsers() {
         // only care about unlocked users
         LinkedList<Integer> users = new LinkedList<>();