Merge tag 'android-security-10.0.0_r53' into int/10/fp2

Android security 10.0.0 release 53

* tag 'android-security-10.0.0_r53':

Change-Id: I425a2244ed8adac9d504c6538ff0c5ebb65b3e2c
diff --git a/src/com/android/internal/car/CarServiceHelperService.java b/src/com/android/internal/car/CarServiceHelperService.java
index f8842d4..5772e19 100644
--- a/src/com/android/internal/car/CarServiceHelperService.java
+++ b/src/com/android/internal/car/CarServiceHelperService.java
@@ -16,6 +16,8 @@
 
 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;
@@ -35,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;
@@ -84,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
@@ -112,6 +119,7 @@
         super(context);
         mContext = context;
         mCarUserManagerHelper = carUserManagerHelper;
+        mUserManager = UserManager.get(context);
     }
 
     @Override
@@ -126,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;
@@ -136,6 +147,7 @@
             if (shouldNotify) {
                 notifyAllUnlockedUsers();
             }
+            t.traceEnd();
         }
     }
 
@@ -262,44 +274,25 @@
             }
             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 traceLog = new TimingsTraceLog("SystemServerTiming",
-                Trace.TRACE_TAG_SYSTEM_SERVER);
-        traceLog.traceBegin("User0Unlock");
-        try {
-            // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
-            // update the state and user 0 unlock happens twice.
-            if (!am.startUserInBackground(UserHandle.USER_SYSTEM)) {
-                // cannot start user
-                Slog.w(TAG, "cannot start system user");
-            } else if (!am.unlockUser(UserHandle.USER_SYSTEM, null, null, null)) {
-                // unlocking system user failed. But still continue for other setup.
-                Slog.w(TAG, "cannot unlock system user");
-            } else {
-                // user 0 started and unlocked
-                handleUserLockStatusChange(UserHandle.USER_SYSTEM, true);
-            }
-        } catch (RemoteException e) {
-            // should not happen for local call.
-            Slog.wtf("RemoteException from AMS", e);
-        }
-        traceLog.traceEnd();
-        // Do not unlock here to allow other stuffs done. Unlock will happen
-        // when system completes the boot.
-        // TODO(b/124460424) Unlock earlier?
-        traceLog.traceBegin("ForegroundUserStart");
+
+        TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+        unlockSystemUser(t, am);
+
+        t.traceBegin("ForegroundUserStart" + targetUserId);
         try {
             if (!am.startUserInForegroundWithListener(targetUserId, null)) {
                 Slog.e(TAG, "cannot start foreground user:" + targetUserId);
@@ -310,9 +303,167 @@
             // should not happen for local call.
             Slog.wtf("RemoteException from AMS", e);
         }
-        traceLog.traceEnd();
+        t.traceEnd();
     }
 
+    private void unlockSystemUser(@NonNull TimingsTraceLog t, @NonNull IActivityManager am) {
+        t.traceBegin("UnlockSystemUser");
+        try {
+            // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
+            // update the state and user 0 unlock happens twice.
+            boolean started = am.startUserInBackground(UserHandle.USER_SYSTEM);
+            if (!started) {
+                Slog.w(TAG, "could not restart system user in foreground; trying unlock instead");
+                t.traceBegin("forceUnlockSystemUser");
+                boolean unlocked = am.unlockUser(UserHandle.USER_SYSTEM,
+                        /* token= */ null, /* secret= */ null, /* listner= */ null);
+                t.traceEnd();
+                if (!unlocked) {
+                    Slog.w(TAG, "could not unlock system user neither");
+                    return;
+                }
+            }
+        } catch (RemoteException e) {
+            // should not happen for local call.
+            Slog.wtf("RemoteException from AMS", e);
+        } finally {
+            t.traceEnd();
+        }
+    }
+
+    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