• 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.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