1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.user; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.car.builtin.os.UserManagerHelper; 22 import android.car.builtin.util.EventLogHelper; 23 import android.car.builtin.util.Slogf; 24 import android.content.Context; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.util.SparseBooleanArray; 28 29 import com.android.car.CarLog; 30 import com.android.car.internal.os.CarSystemProperties; 31 import com.android.internal.annotations.VisibleForTesting; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * Manages the pre-created users. 38 * 39 * <p> 40 * It creates (and remove) new pre-created users based on the values of {@code CarProperties} and 41 * the number of existing pre-created users. 42 */ 43 public final class UserPreCreator { 44 45 @VisibleForTesting 46 static final String TAG = CarLog.tagFor(UserPreCreator.class); 47 48 private final UserManager mUserManager; 49 private final Context mContext; 50 private final UserHandleHelper mUserHandleHelper; 51 UserPreCreator(Context context, UserManager userManager)52 UserPreCreator(Context context, UserManager userManager) { 53 this(context, userManager, new UserHandleHelper(context, userManager)); 54 } 55 56 @VisibleForTesting UserPreCreator(Context context, UserManager userManager, UserHandleHelper userHandleHelper)57 UserPreCreator(Context context, UserManager userManager, UserHandleHelper userHandleHelper) { 58 mUserManager = userManager; 59 mContext = context; 60 mUserHandleHelper = userHandleHelper; 61 } 62 63 /** 64 * Manages the required number of pre-created users. 65 */ managePreCreatedUsers()66 public void managePreCreatedUsers() { 67 // First gets how many pre-createad users are defined by the OEM 68 int numberRequestedGuests = CarSystemProperties.getNumberPreCreatedGuests().orElse(0); 69 int numberRequestedUsers = CarSystemProperties.getNumberPreCreatedUsers().orElse(0); 70 EventLogHelper.writeCarUserServicePreCreationRequested(numberRequestedUsers, 71 numberRequestedGuests); 72 Slogf.d(TAG, "managePreCreatedUsers(): OEM asked for " + numberRequestedGuests 73 + " guests and " + numberRequestedUsers + " users"); 74 75 if (numberRequestedGuests < 0 || numberRequestedUsers < 0) { 76 Slogf.w(TAG, "preCreateUsers(): invalid values provided by OEM; " 77 + "number_pre_created_guests=" + numberRequestedGuests 78 + ", number_pre_created_users=" + numberRequestedUsers); 79 return; 80 } 81 82 // Then checks how many exist already 83 List<UserHandle> allUsers = mUserHandleHelper.getUserHandles(/* excludePartial= */ true, 84 /* excludeDying= */ true, /* excludePreCreated= */ false); 85 86 int allUsersSize = allUsers.size(); 87 Slogf.d(TAG, "preCreateUsers: total users size is " + allUsersSize); 88 89 int numberExistingGuests = 0; 90 int numberExistingUsers = 0; 91 92 // List of pre-created users that were not properly initialized. Typically happens when 93 // the system crashed / rebooted before they were fully started. 94 SparseBooleanArray invalidPreCreatedUsers = new SparseBooleanArray(); 95 96 // List of all pre-created users - it will be used to remove unused ones (when needed) 97 SparseBooleanArray existingPrecreatedUsers = new SparseBooleanArray(); 98 99 // List of extra pre-created users and guests - they will be removed 100 List<Integer> extraPreCreatedUsers = new ArrayList<>(); 101 102 for (int i = 0; i < allUsersSize; i++) { 103 UserHandle user = allUsers.get(i); 104 int userId = user.getIdentifier(); 105 if (!mUserHandleHelper.isPreCreatedUser(user)) continue; 106 if (!mUserHandleHelper.isInitializedUser(user)) { 107 Slogf.w(TAG, "Found invalid pre-created user that needs to be removed: " 108 + user); 109 invalidPreCreatedUsers.append(userId, /* notUsed=*/ true); 110 continue; 111 } 112 boolean isGuest = mUserHandleHelper.isGuestUser(user); 113 existingPrecreatedUsers.put(userId, isGuest); 114 if (isGuest) { 115 numberExistingGuests++; 116 if (numberExistingGuests > numberRequestedGuests) { 117 extraPreCreatedUsers.add(userId); 118 } 119 } else { 120 numberExistingUsers++; 121 if (numberExistingUsers > numberRequestedUsers) { 122 extraPreCreatedUsers.add(userId); 123 } 124 } 125 } 126 Slogf.i(TAG, "managePreCreatedUsers(): system already has " + numberExistingGuests 127 + " pre-created guests," + numberExistingUsers + " pre-created users, and these" 128 + " invalid users: " + invalidPreCreatedUsers 129 + " and these extra pre-created users: " + extraPreCreatedUsers); 130 131 int numberGuestsToAdd = numberRequestedGuests - numberExistingGuests; 132 int numberUsersToAdd = numberRequestedUsers - numberExistingUsers; 133 int numberGuestsToRemove = numberExistingGuests - numberRequestedGuests; 134 int numberUsersToRemove = numberExistingUsers - numberRequestedUsers; 135 int numberInvalidUsersToRemove = invalidPreCreatedUsers.size(); 136 137 EventLogHelper.writeCarUserServicePreCreationStatus( 138 numberExistingUsers, numberUsersToAdd, numberUsersToRemove, 139 numberExistingGuests, numberGuestsToAdd, numberGuestsToRemove, 140 numberInvalidUsersToRemove); 141 142 if (numberGuestsToAdd == 0 && numberUsersToAdd == 0 && numberInvalidUsersToRemove == 0) { 143 Slogf.i(TAG, "managePreCreatedUsers(): everything in sync"); 144 return; 145 } 146 147 if (numberUsersToAdd > 0) { 148 preCreateUsers(numberUsersToAdd, /* isGuest= */ false); 149 } 150 if (numberGuestsToAdd > 0) { 151 preCreateUsers(numberGuestsToAdd, /* isGuest= */ true); 152 } 153 154 int totalNumberToRemove = extraPreCreatedUsers.size(); 155 Slogf.d(TAG, "Must delete " + totalNumberToRemove + " pre-created users"); 156 if (totalNumberToRemove > 0) { 157 int[] usersToRemove = new int[totalNumberToRemove]; 158 for (int i = 0; i < totalNumberToRemove; i++) { 159 usersToRemove[i] = extraPreCreatedUsers.get(i); 160 } 161 removePreCreatedUsers(usersToRemove); 162 } 163 164 if (numberInvalidUsersToRemove > 0) { 165 for (int i = 0; i < numberInvalidUsersToRemove; i++) { 166 int userId = invalidPreCreatedUsers.keyAt(i); 167 Slogf.w(TAG, "removing invalid pre-created user " + userId); 168 mUserManager.removeUser(UserHandle.of(userId)); 169 } 170 } 171 } 172 preCreateUsers(int size, boolean isGuest)173 private void preCreateUsers(int size, boolean isGuest) { 174 String msg = isGuest ? "preCreateGuests-" + size : "preCreateUsers-" + size; 175 Slogf.d(TAG, "preCreateUsers: " + msg); 176 for (int i = 1; i <= size; i++) { 177 UserHandle preCreated = preCreateUsers(isGuest); 178 if (preCreated == null) { 179 Slogf.w(TAG, "Could not pre-create" + (isGuest ? " guest" : "") 180 + " user #" + i); 181 continue; 182 } 183 } 184 } 185 186 @VisibleForTesting 187 @Nullable preCreateUsers(boolean isGuest)188 UserHandle preCreateUsers(boolean isGuest) { 189 String traceMsg = "pre-create" + (isGuest ? "-guest" : "-user"); 190 String userType = isGuest ? UserManager.USER_TYPE_FULL_GUEST 191 : UserManager.USER_TYPE_FULL_SECONDARY; 192 UserHandle user = null; 193 try { 194 user = UserManagerHelper.preCreateUser(mUserManager, userType); 195 } catch (Exception e) { 196 logPrecreationFailure(traceMsg, e); 197 return null; 198 } 199 200 if (user == null) { 201 logPrecreationFailure(traceMsg, /* cause= */ null); 202 } 203 return user; 204 } 205 removePreCreatedUsers(int[] usersToRemove)206 private void removePreCreatedUsers(int[] usersToRemove) { 207 for (int userId : usersToRemove) { 208 Slogf.i(TAG, "removing pre-created user with id " + userId); 209 mUserManager.removeUser(UserHandle.of(userId)); 210 } 211 } 212 213 /** 214 * Logs proper message when user pre-creation fails (most likely because there are too many). 215 */ 216 @VisibleForTesting logPrecreationFailure(@onNull String operation, @Nullable Exception cause)217 void logPrecreationFailure(@NonNull String operation, @Nullable Exception cause) { 218 int currentNumberUsers = mUserManager.getUserCount(); 219 String message = new StringBuilder(operation.length() + 100) 220 .append(operation).append(" failed. Number users: ").append(currentNumberUsers) 221 .toString(); 222 if (cause == null) { 223 Slogf.w(TAG, message); 224 } else { 225 Slogf.w(TAG, message, cause); 226 } 227 } 228 } 229