• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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