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.content.pm.UserInfo; 22 import android.os.UserManager; 23 import android.sysprop.CarProperties; 24 import android.util.EventLog; 25 import android.util.Slog; 26 import android.util.SparseBooleanArray; 27 28 import com.android.car.CarLog; 29 import com.android.car.internal.common.EventLogTags; 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * Manages the pre-created users. 37 * 38 * <p> 39 * It creates (and remove) new pre-created users based on the values of {@link CarProperties} and 40 * the number of existing pre-created users. 41 */ 42 public final class UserPreCreator { 43 44 private static final String TAG = CarLog.tagFor(UserPreCreator.class); 45 46 private final UserManager mUserManager; 47 UserPreCreator(UserManager userManager)48 UserPreCreator(UserManager userManager) { 49 mUserManager = userManager; 50 } 51 52 /** 53 * Manages the required number of pre-created users. 54 */ managePreCreatedUsers()55 public void managePreCreatedUsers() { 56 // First gets how many pre-createad users are defined by the OEM 57 int numberRequestedGuests = CarProperties.number_pre_created_guests().orElse(0); 58 int numberRequestedUsers = CarProperties.number_pre_created_users().orElse(0); 59 EventLog.writeEvent(EventLogTags.CAR_USER_SVC_PRE_CREATION_REQUESTED, numberRequestedUsers, 60 numberRequestedGuests); 61 Slog.d(TAG, "managePreCreatedUsers(): OEM asked for " + numberRequestedGuests 62 + " guests and " + numberRequestedUsers + " users"); 63 64 if (numberRequestedGuests < 0 || numberRequestedUsers < 0) { 65 Slog.w(TAG, "preCreateUsers(): invalid values provided by OEM; " 66 + "number_pre_created_guests=" + numberRequestedGuests 67 + ", number_pre_created_users=" + numberRequestedUsers); 68 return; 69 } 70 71 // Then checks how many exist already 72 List<UserInfo> allUsers = mUserManager.getUsers(/* excludePartial= */ true, 73 /* excludeDying= */ true, /* excludePreCreated= */ false); 74 75 int allUsersSize = allUsers.size(); 76 Slog.d(TAG, "preCreateUsers: total users size is " + allUsersSize); 77 78 int numberExistingGuests = 0; 79 int numberExistingUsers = 0; 80 81 // List of pre-created users that were not properly initialized. Typically happens when 82 // the system crashed / rebooted before they were fully started. 83 SparseBooleanArray invalidPreCreatedUsers = new SparseBooleanArray(); 84 85 // List of all pre-created users - it will be used to remove unused ones (when needed) 86 SparseBooleanArray existingPrecreatedUsers = new SparseBooleanArray(); 87 88 // List of extra pre-created users and guests - they will be removed 89 List<Integer> extraPreCreatedUsers = new ArrayList<>(); 90 91 for (int i = 0; i < allUsersSize; i++) { 92 UserInfo user = allUsers.get(i); 93 if (!user.preCreated) continue; 94 if (!user.isInitialized()) { 95 Slog.w(TAG, "Found invalid pre-created user that needs to be removed: " 96 + user.toFullString()); 97 invalidPreCreatedUsers.append(user.id, /* notUsed=*/ true); 98 continue; 99 } 100 boolean isGuest = user.isGuest(); 101 existingPrecreatedUsers.put(user.id, isGuest); 102 if (isGuest) { 103 numberExistingGuests++; 104 if (numberExistingGuests > numberRequestedGuests) { 105 extraPreCreatedUsers.add(user.id); 106 } 107 } else { 108 numberExistingUsers++; 109 if (numberExistingUsers > numberRequestedUsers) { 110 extraPreCreatedUsers.add(user.id); 111 } 112 } 113 } 114 Slog.i(TAG, "managePreCreatedUsers(): system already has " + numberExistingGuests 115 + " pre-created guests," + numberExistingUsers + " pre-created users, and these" 116 + " invalid users: " + invalidPreCreatedUsers 117 + " and these extra pre-created users: " + extraPreCreatedUsers); 118 119 int numberGuestsToAdd = numberRequestedGuests - numberExistingGuests; 120 int numberUsersToAdd = numberRequestedUsers - numberExistingUsers; 121 int numberGuestsToRemove = numberExistingGuests - numberRequestedGuests; 122 int numberUsersToRemove = numberExistingUsers - numberRequestedUsers; 123 int numberInvalidUsersToRemove = invalidPreCreatedUsers.size(); 124 125 EventLog.writeEvent(EventLogTags.CAR_USER_SVC_PRE_CREATION_STATUS, 126 numberExistingUsers, numberUsersToAdd, numberUsersToRemove, 127 numberExistingGuests, numberGuestsToAdd, numberGuestsToRemove, 128 numberInvalidUsersToRemove); 129 130 if (numberGuestsToAdd == 0 && numberUsersToAdd == 0 && numberInvalidUsersToRemove == 0) { 131 Slog.i(TAG, "managePreCreatedUsers(): everything in sync"); 132 return; 133 } 134 135 if (numberUsersToAdd > 0) { 136 preCreateUsers(numberUsersToAdd, /* isGuest= */ false); 137 } 138 if (numberGuestsToAdd > 0) { 139 preCreateUsers(numberGuestsToAdd, /* isGuest= */ true); 140 } 141 142 int totalNumberToRemove = extraPreCreatedUsers.size(); 143 Slog.d(TAG, "Must delete " + totalNumberToRemove + " pre-created users"); 144 if (totalNumberToRemove > 0) { 145 int[] usersToRemove = new int[totalNumberToRemove]; 146 for (int i = 0; i < totalNumberToRemove; i++) { 147 usersToRemove[i] = extraPreCreatedUsers.get(i); 148 } 149 removePreCreatedUsers(usersToRemove); 150 } 151 152 if (numberInvalidUsersToRemove > 0) { 153 for (int i = 0; i < numberInvalidUsersToRemove; i++) { 154 int userId = invalidPreCreatedUsers.keyAt(i); 155 Slog.w(TAG, "removing invalid pre-created user " + userId); 156 mUserManager.removeUser(userId); 157 } 158 } 159 } 160 preCreateUsers(int size, boolean isGuest)161 private void preCreateUsers(int size, boolean isGuest) { 162 String msg = isGuest ? "preCreateGuests-" + size : "preCreateUsers-" + size; 163 Slog.d(TAG, "preCreateUsers: " + msg); 164 for (int i = 1; i <= size; i++) { 165 UserInfo preCreated = preCreateUsers(isGuest); 166 if (preCreated == null) { 167 Slog.w(TAG, "Could not pre-create" + (isGuest ? " guest" : "") 168 + " user #" + i); 169 continue; 170 } 171 } 172 } 173 174 @VisibleForTesting 175 @Nullable preCreateUsers(boolean isGuest)176 UserInfo preCreateUsers(boolean isGuest) { 177 String traceMsg = "pre-create" + (isGuest ? "-guest" : "-user"); 178 // NOTE: we want to get rid of UserManagerHelper, so let's call UserManager directly 179 String userType = 180 isGuest ? UserManager.USER_TYPE_FULL_GUEST : UserManager.USER_TYPE_FULL_SECONDARY; 181 UserInfo user = null; 182 try { 183 user = mUserManager.preCreateUser(userType); 184 if (user == null) { 185 logPrecreationFailure(traceMsg, /* cause= */ null); 186 } 187 } catch (Exception e) { 188 logPrecreationFailure(traceMsg, e); 189 } 190 return user; 191 } 192 removePreCreatedUsers(int[] usersToRemove)193 private void removePreCreatedUsers(int[] usersToRemove) { 194 for (int userId : usersToRemove) { 195 Slog.i(TAG, "removing pre-created user with id " + userId); 196 mUserManager.removeUser(userId); 197 } 198 } 199 200 /** 201 * Logs proper message when user pre-creation fails (most likely because there are too many). 202 */ 203 @VisibleForTesting logPrecreationFailure(@onNull String operation, @Nullable Exception cause)204 void logPrecreationFailure(@NonNull String operation, @Nullable Exception cause) { 205 int maxNumberUsers = UserManager.getMaxSupportedUsers(); 206 int currentNumberUsers = mUserManager.getUserCount(); 207 String message = new StringBuilder(operation.length() + 100) 208 .append(operation).append(" failed. Number users: ").append(currentNumberUsers) 209 .append(" Max: ").append(maxNumberUsers).toString(); 210 if (cause == null) { 211 Slog.w(TAG, message); 212 } else { 213 Slog.w(TAG, message, cause); 214 } 215 } 216 } 217