• 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 package com.android.car.user;
17 
18 import static com.android.car.hal.UserHalHelper.userFlagsToString;
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.UserIdInt;
26 import android.app.ActivityManager;
27 import android.car.builtin.app.ActivityManagerHelper;
28 import android.car.builtin.os.TraceHelper;
29 import android.car.builtin.os.UserManagerHelper;
30 import android.car.builtin.provider.SettingsHelper;
31 import android.car.builtin.util.Slogf;
32 import android.car.builtin.util.TimingsTraceLog;
33 import android.car.builtin.widget.LockPatternHelper;
34 import android.car.settings.CarSettings;
35 import android.content.Context;
36 import android.hardware.automotive.vehicle.UserInfo;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Settings;
40 import android.util.Pair;
41 
42 import com.android.car.CarLog;
43 import com.android.car.R;
44 import com.android.car.hal.UserHalHelper;
45 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
46 import com.android.car.internal.common.UserHelperLite;
47 import com.android.car.internal.os.CarSystemProperties;
48 import com.android.car.util.Utils;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.util.Preconditions;
51 
52 import java.io.PrintWriter;
53 import java.lang.annotation.Retention;
54 import java.lang.annotation.RetentionPolicy;
55 import java.util.ArrayList;
56 import java.util.Collections;
57 import java.util.Iterator;
58 import java.util.List;
59 import java.util.function.Consumer;
60 
61 /**
62  * Helper used to set the initial Android user on boot or when resuming from RAM.
63  */
64 final class InitialUserSetter {
65 
66     @VisibleForTesting
67     static final String TAG = CarLog.tagFor(InitialUserSetter.class);
68 
69     private static final boolean DBG = false;
70     private static final int BOOT_USER_NOT_FOUND = -1;
71 
72     /**
73      * Sets the initial user using the default behavior.
74      *
75      * <p>The default behavior is:
76      *
77      * <ol>
78      *  <li>On first boot, it creates and switches to a new user.
79      *  <li>Otherwise, it will switch to either:
80      *  <ol>
81      *   <li>User defined by {@code android.car.systemuser.bootuseroverrideid} (when it was
82      * constructed with such option enabled).
83      *   <li>Last active user (as defined by
84      * {@link android.provider.Settings.Global.LAST_ACTIVE_USER_ID}.
85      *  </ol>
86      * </ol>
87      */
88     public static final int TYPE_DEFAULT_BEHAVIOR = 0;
89 
90     /**
91      * Switches to the given user, falling back to {@link #fallbackDefaultBehavior(String)} if it
92      * fails.
93      */
94     public static final int TYPE_SWITCH = 1;
95 
96     /**
97      * Creates a new user and switches to it, falling back to
98      * {@link #fallbackDefaultBehavior(String) if any of these steps fails.
99      *
100      * @param name (optional) name of the new user
101      * @param halFlags user flags as defined by Vehicle HAL ({@code UserFlags} enum).
102      */
103     public static final int TYPE_CREATE = 2;
104 
105     /**
106      * Creates a new guest user and switches to it, if current user is unlocked guest user.
107      * Does not fallback if any of these steps fails. falling back to
108      * {@link #fallbackDefaultBehavior(String) if any of these steps fails
109      */
110     public static final int TYPE_REPLACE_GUEST = 3;
111 
112     @IntDef(prefix = { "TYPE_" }, value = {
113             TYPE_DEFAULT_BEHAVIOR,
114             TYPE_SWITCH,
115             TYPE_CREATE,
116             TYPE_REPLACE_GUEST
117     })
118     @Retention(RetentionPolicy.SOURCE)
119     public @interface InitialUserInfoType { }
120 
121     private final Context mContext;
122 
123     // TODO(b/150413304): abstract AM / UM into interfaces, then provide local and remote
124     // implementation (where local is implemented by ActivityManagerInternal / UserManagerInternal)
125     private final UserManager mUm;
126     private final CarUserService mCarUserService;
127 
128     private final String mNewUserName;
129     private final String mNewGuestName;
130 
131     private final Consumer<UserHandle> mListener;
132 
133     private final UserHandleHelper mUserHandleHelper;
134 
InitialUserSetter(@onNull Context context, @NonNull CarUserService carUserService, @NonNull Consumer<UserHandle> listener, @NonNull UserHandleHelper userHandleHelper)135     InitialUserSetter(@NonNull Context context, @NonNull CarUserService carUserService,
136             @NonNull Consumer<UserHandle> listener, @NonNull UserHandleHelper userHandleHelper) {
137         this(context, carUserService, listener, userHandleHelper,
138                 context.getString(R.string.default_guest_name));
139     }
140 
InitialUserSetter(@onNull Context context, @NonNull CarUserService carUserService, @NonNull Consumer<UserHandle> listener, @NonNull UserHandleHelper userHandleHelper, @Nullable String newGuestName)141     InitialUserSetter(@NonNull Context context, @NonNull CarUserService carUserService,
142             @NonNull Consumer<UserHandle> listener, @NonNull UserHandleHelper userHandleHelper,
143             @Nullable String newGuestName) {
144         this(context, context.getSystemService(UserManager.class), carUserService, listener,
145                 userHandleHelper, UserManagerHelper.getDefaultUserName(context), newGuestName);
146     }
147 
148     @VisibleForTesting
InitialUserSetter(@onNull Context context, @NonNull UserManager um, @NonNull CarUserService carUserService, @NonNull Consumer<UserHandle> listener, @NonNull UserHandleHelper userHandleHelper, @Nullable String newUserName, @Nullable String newGuestName)149     InitialUserSetter(@NonNull Context context, @NonNull UserManager um,
150             @NonNull CarUserService carUserService, @NonNull Consumer<UserHandle> listener,
151             @NonNull UserHandleHelper userHandleHelper, @Nullable String newUserName,
152             @Nullable String newGuestName) {
153         mContext = context;
154         mUm = um;
155         mCarUserService = carUserService;
156         mListener = listener;
157         mUserHandleHelper = userHandleHelper;
158         mNewUserName = newUserName;
159         mNewGuestName = newGuestName;
160     }
161 
162     /**
163      * Builder for {@link InitialUserInfo} objects.
164      *
165      */
166     public static final class Builder {
167 
168         private final @InitialUserInfoType int mType;
169         private boolean mReplaceGuest;
170         private @UserIdInt int mSwitchUserId;
171         private @Nullable String mNewUserName;
172         private int mNewUserFlags;
173         private boolean mSupportsOverrideUserIdProperty;
174         private @Nullable String mUserLocales;
175 
176         /**
177          * Constructor for the given type.
178          *
179          * @param type {@link #TYPE_DEFAULT_BEHAVIOR}, {@link #TYPE_SWITCH},
180          * {@link #TYPE_CREATE} or {@link #TYPE_REPLACE_GUEST}.
181          */
Builder(@nitialUserInfoType int type)182         public Builder(@InitialUserInfoType int type) {
183             Preconditions.checkArgument(type == TYPE_DEFAULT_BEHAVIOR || type == TYPE_SWITCH
184                     || type == TYPE_CREATE || type == TYPE_REPLACE_GUEST, "invalid builder type");
185             mType = type;
186         }
187 
188         /**
189          * Sets the id of the user to be switched to.
190          *
191          * @throws IllegalArgumentException if builder is not for {@link #TYPE_SWITCH}.
192          */
193         @NonNull
setSwitchUserId(@serIdInt int userId)194         public Builder setSwitchUserId(@UserIdInt int userId) {
195             Preconditions.checkArgument(mType == TYPE_SWITCH, "invalid builder type: " + mType);
196             mSwitchUserId = userId;
197             return this;
198         }
199 
200         /**
201          * Sets whether the current user should be replaced when it's a guest.
202          */
203         @NonNull
setReplaceGuest(boolean value)204         public Builder setReplaceGuest(boolean value) {
205             mReplaceGuest = value;
206             return this;
207         }
208 
209         /**
210          * Sets the name of the new user being created.
211          *
212          * @throws IllegalArgumentException if builder is not for {@link #TYPE_CREATE}.
213          */
214         @NonNull
setNewUserName(@ullable String name)215         public Builder setNewUserName(@Nullable String name) {
216             Preconditions.checkArgument(mType == TYPE_CREATE, "invalid builder type: " + mType);
217             mNewUserName = name;
218             return this;
219         }
220 
221         /**
222          * Sets the flags (as defined by {@link android.hardware.automotive.vehicle.UserInfo})
223          * of the new user being created.
224          *
225          * @throws IllegalArgumentException if builder is not for {@link #TYPE_CREATE}.
226          */
227         @NonNull
setNewUserFlags(int flags)228         public Builder setNewUserFlags(int flags) {
229             Preconditions.checkArgument(mType == TYPE_CREATE, "invalid builder type: " + mType);
230             mNewUserFlags = flags;
231             return this;
232         }
233 
234         /**
235          * Sets whether the {@code CarProperties#boot_user_override_id()} should be taking in
236          * account when using the default behavior.
237          */
238         @NonNull
setSupportsOverrideUserIdProperty(boolean value)239         public Builder setSupportsOverrideUserIdProperty(boolean value) {
240             mSupportsOverrideUserIdProperty = value;
241             return this;
242         }
243 
244         /**
245          * Sets the system locales for the initial user (when it's created).
246          */
247         @NonNull
setUserLocales(@ullable String userLocales)248         public Builder setUserLocales(@Nullable String userLocales) {
249             // This string can come from a binder IPC call where empty string is the default value
250             // for the auto-generated code. So, need to check for that.
251             if (userLocales != null && userLocales.trim().isEmpty()) {
252                 mUserLocales = null;
253             } else {
254                 mUserLocales = userLocales;
255             }
256             return this;
257         }
258 
259         /**
260          * Builds the object.
261          */
262         @NonNull
build()263         public InitialUserInfo build() {
264             return new InitialUserInfo(this);
265         }
266     }
267 
268     /**
269      * Object used to define the properties of the initial user (which can then be set by
270      * {@link InitialUserSetter#set(InitialUserInfo)});
271      */
272     public static final class InitialUserInfo {
273         public final @InitialUserInfoType int type;
274         public final boolean replaceGuest;
275         public final @UserIdInt int switchUserId;
276         public final @Nullable String newUserName;
277         public final int newUserFlags;
278         public final boolean supportsOverrideUserIdProperty;
279         public @Nullable String userLocales;
280 
InitialUserInfo(@onNull Builder builder)281         private InitialUserInfo(@NonNull Builder builder) {
282             type = builder.mType;
283             switchUserId = builder.mSwitchUserId;
284             replaceGuest = builder.mReplaceGuest;
285             newUserName = builder.mNewUserName;
286             newUserFlags = builder.mNewUserFlags;
287             supportsOverrideUserIdProperty = builder.mSupportsOverrideUserIdProperty;
288             userLocales = builder.mUserLocales;
289         }
290 
291         @Override
292         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
toString()293         public String toString() {
294             StringBuilder string = new StringBuilder("InitialUserInfo[type=");
295             switch(type) {
296                 case TYPE_DEFAULT_BEHAVIOR:
297                     string.append("DEFAULT_BEHAVIOR");
298                     break;
299                 case TYPE_REPLACE_GUEST:
300                     string.append("REPLACE_GUEST");
301                     break;
302                 case TYPE_SWITCH:
303                     string.append("SWITCH").append(",userId=").append(switchUserId);
304                     break;
305                 case TYPE_CREATE:
306                     string.append("CREATE").append(",flags=")
307                             .append(UserHalHelper.userFlagsToString(newUserFlags));
308                     if (newUserName != null) {
309                         string.append(",name=" + UserHelperLite.safeName(newUserName));
310                     }
311                     if (userLocales != null) {
312                         string.append(",locales=").append(userLocales);
313                     }
314                     break;
315                 default:
316                     string.append("UNKNOWN:").append(type);
317             }
318             if (replaceGuest) string.append(",replaceGuest");
319             if (supportsOverrideUserIdProperty) string.append(",supportsOverrideUserIdProperty");
320 
321             return string.append(']').toString();
322         }
323     }
324 
325     /**
326      * Sets the initial user.
327      */
set(@onNull InitialUserInfo info)328     public void set(@NonNull InitialUserInfo info) {
329         Preconditions.checkArgument(info != null, "info cannot be null");
330 
331         switch (info.type) {
332             case TYPE_DEFAULT_BEHAVIOR:
333                 executeDefaultBehavior(info, /* fallback= */ false);
334                 break;
335             case TYPE_SWITCH:
336                 try {
337                     switchUser(info, /* fallback= */ true);
338                 } catch (Exception e) {
339                     fallbackDefaultBehavior(info, /* fallback= */ true,
340                             "Exception switching user: " + e);
341                 }
342                 break;
343             case TYPE_CREATE:
344                 try {
345                     createAndSwitchUser(info, /* fallback= */ true);
346                 } catch (Exception e) {
347                     fallbackDefaultBehavior(info, /* fallback= */ true,
348                             "Exception createUser user with name "
349                                     + UserHelperLite.safeName(info.newUserName) + " and flags "
350                                     + UserHalHelper.userFlagsToString(info.newUserFlags) + ": "
351                                     + e);
352                 }
353                 break;
354             case TYPE_REPLACE_GUEST:
355                 try {
356                     replaceUser(info, /* fallback= */ true);
357                 } catch (Exception e) {
358                     fallbackDefaultBehavior(info, /* fallback= */ true,
359                             "Exception replace guest user: " + e);
360                 }
361                 break;
362             default:
363                 throw new IllegalArgumentException("invalid InitialUserInfo type: " + info.type);
364         }
365     }
366 
replaceUser(InitialUserInfo info, boolean fallback)367     private void replaceUser(InitialUserInfo info, boolean fallback) {
368         int currentUserId = ActivityManager.getCurrentUser();
369         UserHandle currentUser = mUserHandleHelper.getExistingUserHandle(currentUserId);
370 
371         if (currentUser == null) {
372             Slogf.wtf(TAG, "Current user %d handle doesn't exits ", currentUserId);
373         }
374 
375         UserHandle newUser = replaceGuestIfNeeded(currentUser);
376         if (newUser == null) {
377             fallbackDefaultBehavior(info, fallback,
378                     "could not replace guest " + currentUser);
379             return;
380         }
381 
382         switchUser(new Builder(TYPE_SWITCH)
383                 .setSwitchUserId(newUser.getIdentifier())
384                 .build(), fallback);
385 
386         if (newUser.getIdentifier() != currentUser.getIdentifier()) {
387             Slogf.i(TAG, "Removing old guest %d", currentUser.getIdentifier());
388             if (!mUm.removeUser(currentUser)) {
389                 Slogf.w(TAG, "Could not remove old guest " + currentUser.getIdentifier());
390             }
391         }
392     }
393 
executeDefaultBehavior(@onNull InitialUserInfo info, boolean fallback)394     private void executeDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback) {
395         if (!hasValidInitialUser()) {
396             if (DBG) Slogf.d(TAG, "executeDefaultBehavior(): no initial user, creating it");
397             createAndSwitchUser(new Builder(TYPE_CREATE)
398                     .setNewUserName(mNewUserName)
399                     .setNewUserFlags(UserInfo.USER_FLAG_ADMIN)
400                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
401                     .setUserLocales(info.userLocales)
402                     .build(), fallback);
403         } else {
404             if (DBG) Slogf.d(TAG, "executeDefaultBehavior(): switching to initial user");
405             int userId = getInitialUser(info.supportsOverrideUserIdProperty);
406             switchUser(new Builder(TYPE_SWITCH)
407                     .setSwitchUserId(userId)
408                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
409                     .setReplaceGuest(info.replaceGuest)
410                     .build(), fallback);
411         }
412     }
413 
414     @VisibleForTesting
fallbackDefaultBehavior(@onNull InitialUserInfo info, boolean fallback, @NonNull String reason)415     void fallbackDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback,
416             @NonNull String reason) {
417         if (!fallback) {
418             // Only log the error
419             Slogf.w(TAG, reason);
420             // Must explicitly tell listener that initial user could not be determined
421             notifyListener(/*initialUser= */ null);
422             return;
423         }
424         Slogf.w(TAG, "Falling back to default behavior. Reason: " + reason);
425         executeDefaultBehavior(info, /* fallback= */ false);
426     }
427 
switchUser(@onNull InitialUserInfo info, boolean fallback)428     private void switchUser(@NonNull InitialUserInfo info, boolean fallback) {
429         int userId = info.switchUserId;
430         boolean replaceGuest = info.replaceGuest;
431 
432         if (DBG) {
433             Slogf.d(TAG, "switchUser(): userId=" + userId + ", replaceGuest=" + replaceGuest
434                     + ", fallback=" + fallback);
435         }
436 
437         UserHandle user = mUserHandleHelper.getExistingUserHandle(userId);
438         if (user == null) {
439             fallbackDefaultBehavior(info, fallback, "user with id " + userId + " doesn't exist");
440             return;
441         }
442 
443         UserHandle actualUser = user;
444 
445         if (mUserHandleHelper.isGuestUser(user) && replaceGuest) {
446             actualUser = replaceGuestIfNeeded(user);
447 
448             if (actualUser == null) {
449                 fallbackDefaultBehavior(info, fallback, "could not replace guest " + user);
450                 return;
451             }
452         }
453 
454         int actualUserId = actualUser.getIdentifier();
455 
456         unlockSystemUserIfNecessary(actualUserId);
457 
458         int currentUserId = ActivityManager.getCurrentUser();
459         if (actualUserId != currentUserId) {
460             if (!startForegroundUser(actualUserId)) {
461                 fallbackDefaultBehavior(info, fallback,
462                         "am.switchUser(" + actualUserId + ") failed");
463                 return;
464             }
465             setLastActiveUser(actualUserId);
466         }
467         notifyListener(actualUser);
468 
469         if (actualUserId != userId) {
470             Slogf.i(TAG, "Removing old guest " + userId);
471             if (!mUm.removeUser(user)) {
472                 Slogf.w(TAG, "Could not remove old guest " + userId);
473             }
474         }
475     }
476 
unlockSystemUserIfNecessary(@serIdInt int userId)477     private void unlockSystemUserIfNecessary(@UserIdInt int userId) {
478         // If system user is the only user to unlock, it will be handled when boot is complete.
479         if (userId != UserHandle.SYSTEM.getIdentifier()) {
480             unlockSystemUser();
481         }
482     }
483 
484     /**
485      * Check if the user is a guest and can be replaced.
486      */
canReplaceGuestUser(UserHandle user)487     public boolean canReplaceGuestUser(UserHandle user) {
488         if (!mUserHandleHelper.isGuestUser(user)) return false;
489 
490         if (LockPatternHelper.isSecure(mContext, user.getIdentifier())) {
491             if (DBG) {
492                 Slogf.d(TAG, "replaceGuestIfNeeded(), skipped, since user "
493                         + user.getIdentifier() + " has secure lock pattern");
494             }
495             return false;
496         }
497 
498         return true;
499     }
500 
501     /**
502      * Replaces {@code user} by a new guest, if necessary.
503      *
504      * <p>If {@code user} is not a guest, it doesn't do anything and returns the same user.
505      *
506      * <p>Otherwise, it marks the current guest for deletion, creates a new one, and returns the
507      * new guest (or {@code null} if a new guest could not be created).
508      */
509 
510     @VisibleForTesting
511     @Nullable
replaceGuestIfNeeded(@onNull UserHandle user)512     UserHandle replaceGuestIfNeeded(@NonNull UserHandle user) {
513         Preconditions.checkArgument(user != null, "user cannot be null");
514 
515         if (!canReplaceGuestUser(user)) {
516             return user;
517         }
518 
519         Slogf.i(TAG, "Replacing guest (" + user + ")");
520 
521         int halFlags = UserInfo.USER_FLAG_GUEST;
522         if (mUserHandleHelper.isEphemeralUser(user)) {
523             halFlags |= UserInfo.USER_FLAG_EPHEMERAL;
524         } else {
525             // TODO(b/150413515): decide whether we should allow it or not. Right now we're
526             // just logging, as UserManagerService will automatically set it to ephemeral if
527             // platform is set to do so.
528             Slogf.w(TAG, "guest being replaced is not ephemeral: " + user);
529         }
530 
531         if (!UserManagerHelper.markGuestForDeletion(mUm, user)) {
532             // Don't need to recover in case of failure - most likely create new user will fail
533             // because there is already a guest
534             Slogf.w(TAG, "failed to mark guest " + user.getIdentifier() + " for deletion");
535         }
536 
537         Pair<UserHandle, String> result = createNewUser(new Builder(TYPE_CREATE)
538                 .setNewUserName(mNewGuestName)
539                 .setNewUserFlags(halFlags)
540                 .build());
541 
542         String errorMessage = result.second;
543         if (errorMessage != null) {
544             Slogf.w(TAG, "could not replace guest " + user + ": " + errorMessage);
545             return null;
546         }
547 
548         return result.first;
549     }
550 
createAndSwitchUser(@onNull InitialUserInfo info, boolean fallback)551     private void createAndSwitchUser(@NonNull InitialUserInfo info, boolean fallback) {
552         Pair<UserHandle, String> result = createNewUser(info);
553         String reason = result.second;
554         if (reason != null) {
555             fallbackDefaultBehavior(info, fallback, reason);
556             return;
557         }
558 
559         switchUser(new Builder(TYPE_SWITCH)
560                 .setSwitchUserId(result.first.getIdentifier())
561                 .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
562                 .build(), fallback);
563     }
564 
565     /**
566      * Creates a new user.
567      *
568      * @return on success, first element is the new user; on failure, second element contains the
569      * error message.
570      */
571     @NonNull
createNewUser(@onNull InitialUserInfo info)572     private Pair<UserHandle, String> createNewUser(@NonNull InitialUserInfo info) {
573         String name = info.newUserName;
574         int halFlags = info.newUserFlags;
575 
576         if (DBG) {
577             Slogf.d(TAG, "createUser(name=" + UserHelperLite.safeName(name) + ", flags="
578                     + userFlagsToString(halFlags) + ")");
579         }
580 
581         if (UserHalHelper.isSystem(halFlags)) {
582             return new Pair<>(null, "Cannot create system user");
583         }
584 
585         if (UserHalHelper.isAdmin(halFlags)) {
586             boolean validAdmin = true;
587             if (UserHalHelper.isGuest(halFlags)) {
588                 Slogf.w(TAG, "Cannot create guest admin");
589                 validAdmin = false;
590             }
591             if (UserHalHelper.isEphemeral(halFlags)) {
592                 Slogf.w(TAG, "Cannot create ephemeral admin");
593                 validAdmin = false;
594             }
595             if (!validAdmin) {
596                 return new Pair<>(null, "Invalid flags for admin user");
597             }
598         }
599         // TODO(b/150413515): decide what to if HAL requested a non-ephemeral guest but framework
600         // sets all guests as ephemeral - should it fail or just warn?
601 
602         int flags = UserHalHelper.toUserInfoFlags(halFlags);
603         String type = UserHalHelper.isGuest(halFlags) ? UserManager.USER_TYPE_FULL_GUEST
604                 : UserManager.USER_TYPE_FULL_SECONDARY;
605 
606         if (DBG) {
607             Slogf.d(TAG, "calling am.createUser((name=" + UserHelperLite.safeName(name) + ", type="
608                     + type + ", flags=" + flags + ")");
609         }
610 
611         UserHandle user = mCarUserService.createUserEvenWhenDisallowed(name, type, flags);
612         if (user == null) {
613             return new Pair<>(null, "createUser(name=" + UserHelperLite.safeName(name) + ", flags="
614                     + userFlagsToString(halFlags) + "): failed to create user");
615         }
616 
617         if (DBG) Slogf.d(TAG, "user created: " + user.getIdentifier());
618 
619         if (info.userLocales != null) {
620             if (DBG) {
621                 Slogf.d(TAG, "setting locale for user " + user.getIdentifier() + " to "
622                         + info.userLocales);
623             }
624             Settings.System.putString(
625                     Utils.getContentResolverForUser(mContext, user.getIdentifier()),
626                     SettingsHelper.SYSTEM_LOCALES, info.userLocales);
627         }
628 
629         return new Pair<>(user, null);
630     }
631 
632     @VisibleForTesting
unlockSystemUser()633     void unlockSystemUser() {
634         Slogf.i(TAG, "unlocking system user");
635         TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE);
636         t.traceBegin("UnlockSystemUser");
637         // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
638         // update the state and USER_SYSTEM unlock happens twice.
639         t.traceBegin("am.startUser");
640         boolean started = ActivityManagerHelper.startUserInBackground(
641                 UserHandle.SYSTEM.getIdentifier());
642         t.traceEnd();
643         if (!started) {
644             Slogf.w(TAG, "could not restart system user in foreground; trying unlock instead");
645             t.traceBegin("am.unlockUser");
646             boolean unlocked = ActivityManagerHelper.unlockUser(UserHandle.SYSTEM.getIdentifier());
647             t.traceEnd();
648             if (!unlocked) {
649                 Slogf.w(TAG, "could not unlock system user neither");
650                 return;
651             }
652         }
653         t.traceEnd();
654     }
655 
656     @VisibleForTesting
startForegroundUser(@serIdInt int userId)657     boolean startForegroundUser(@UserIdInt int userId) {
658         if (UserHelperLite.isHeadlessSystemUser(userId)) {
659             // System User doesn't associate with real person, can not be switched to.
660             return false;
661         }
662         return ActivityManagerHelper.startUserInForeground(userId);
663     }
664 
notifyListener(@ullable UserHandle initialUser)665     private void notifyListener(@Nullable UserHandle initialUser) {
666         if (DBG) Slogf.d(TAG, "notifyListener(): " + initialUser);
667         mListener.accept(initialUser);
668     }
669 
670     /**
671      * Dumps it state.
672      */
673     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(@onNull PrintWriter writer)674     public void dump(@NonNull PrintWriter writer) {
675         writer.println("InitialUserSetter");
676         String indent = "  ";
677         writer.printf("%smNewUserName: %s\n", indent, mNewUserName);
678         writer.printf("%smNewGuestName: %s\n", indent, mNewGuestName);
679     }
680 
681     /**
682      * Sets the last active user.
683      */
setLastActiveUser(@serIdInt int userId)684     public void setLastActiveUser(@UserIdInt int userId) {
685         if (UserHelperLite.isHeadlessSystemUser(userId)) {
686             if (DBG) Slogf.d(TAG, "setLastActiveUser(): ignoring headless system user " + userId);
687             return;
688         }
689         setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID, userId);
690 
691         UserHandle user = mUserHandleHelper.getExistingUserHandle(userId);
692         if (user == null) {
693             Slogf.w(TAG, "setLastActiveUser(): user " + userId + " doesn't exist");
694             return;
695         }
696         if (!mUserHandleHelper.isEphemeralUser(user)) {
697             setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID, userId);
698         }
699     }
700 
setUserIdGlobalProperty(@onNull String name, @UserIdInt int userId)701     private void setUserIdGlobalProperty(@NonNull String name, @UserIdInt int userId) {
702         if (DBG) Slogf.d(TAG, "setting global property " + name + " to " + userId);
703 
704         Settings.Global.putInt(mContext.getContentResolver(), name, userId);
705     }
706 
707     /**
708      * Gets the user id for the initial user to boot into. This is only applicable for headless
709      * system user model. This method checks for a system property and will only work for system
710      * apps.
711      *
712      * This method checks for the initial user via three mechanisms in this order:
713      * <ol>
714      *     <li>Check for a boot user override via {@code CarProperties#boot_user_override_id()}</li>
715      *     <li>Check for the last active user in the system</li>
716      *     <li>Fallback to the smallest user id that is not {@link UserHandle.SYSTEM}</li>
717      * </ol>
718      *
719      * If any step fails to retrieve the stored id or the retrieved id does not exist on device,
720      * then it will move onto the next step.
721      *
722      * @return user id of the initial user to boot into on the device, or
723      * {@link UserHandle#USER_NULL} if there is no user available.
724      */
725     @VisibleForTesting
getInitialUser(boolean usesOverrideUserIdProperty)726     int getInitialUser(boolean usesOverrideUserIdProperty) {
727 
728         List<Integer> allUsers = userListToUserIdList(getAllUsers());
729 
730         if (allUsers.isEmpty()) {
731             return UserManagerHelper.USER_NULL;
732         }
733 
734         //TODO(b/150416512): Check if it is still supported, if not remove it.
735         if (usesOverrideUserIdProperty) {
736             int bootUserOverride = CarSystemProperties.getBootUserOverrideId()
737                     .orElse(BOOT_USER_NOT_FOUND);
738 
739             // If an override user is present and a real user, return it
740             if (bootUserOverride != BOOT_USER_NOT_FOUND
741                     && allUsers.contains(bootUserOverride)) {
742                 Slogf.i(TAG, "Boot user id override found for initial user, user id: "
743                         + bootUserOverride);
744                 return bootUserOverride;
745             }
746         }
747 
748         // If the last active user is not the SYSTEM user and is a real user, return it
749         int lastActiveUser = getUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
750         if (allUsers.contains(lastActiveUser)) {
751             Slogf.i(TAG, "Last active user loaded for initial user: " + lastActiveUser);
752             return lastActiveUser;
753         }
754         resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
755 
756         int lastPersistentUser = getUserIdGlobalProperty(
757                 CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
758         if (allUsers.contains(lastPersistentUser)) {
759             Slogf.i(TAG, "Last active, persistent user loaded for initial user: "
760                     + lastPersistentUser);
761             return lastPersistentUser;
762         }
763         resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
764 
765         // If all else fails, return the smallest user id
766         int returnId = Collections.min(allUsers);
767         // TODO(b/158101909): the smallest user id is not always the initial user; a better approach
768         // would be looking for the first ADMIN user, or keep track of all last active users (not
769         // just the very last)
770         Slogf.w(TAG, "Last active user (" + lastActiveUser + ") not found. Returning smallest user "
771                 + "id instead: " + returnId);
772         return returnId;
773     }
774 
775     /**
776      * Gets all the users that can be brought to the foreground on the system.
777      *
778      * @return List of {@code UserHandle} for users that associated with a real person.
779      */
getAllUsers()780     private List<UserHandle> getAllUsers() {
781         if (UserManager.isHeadlessSystemUserMode()) {
782             return getAllUsersExceptSystemUserAndSpecifiedUser(UserHandle.SYSTEM.getIdentifier());
783         } else {
784             return UserManagerHelper.getUserHandles(mUm, /* excludePartial= */ false,
785                     /* excludeDying= */ false, /* excludePreCreated */ true);
786         }
787     }
788 
789     /**
790      * Gets all the users except system user and the one with userId passed in.
791      *
792      * @param userId of the user not to be returned.
793      * @return All users other than system user and user with userId.
794      */
getAllUsersExceptSystemUserAndSpecifiedUser(@serIdInt int userId)795     private List<UserHandle> getAllUsersExceptSystemUserAndSpecifiedUser(@UserIdInt int userId) {
796         List<UserHandle> users = UserManagerHelper.getUserHandles(mUm, /* excludePartial= */ false,
797                 /* excludeDying= */ false, /* excludePreCreated */ true);
798 
799         for (Iterator<UserHandle> iterator = users.iterator(); iterator.hasNext(); ) {
800             UserHandle user = iterator.next();
801             if (user.getIdentifier() == userId
802                     || user.getIdentifier() == UserHandle.SYSTEM.getIdentifier()) {
803                 // Remove user with userId from the list.
804                 iterator.remove();
805             }
806         }
807         return users;
808     }
809 
810     // TODO(b/231473748): this method should NOT be used to define if it's the first boot - we
811     // should create a new method for that instead (which would check the proper signals) and change
812     // CarUserService.getInitialUserInfoRequestType() to use it instead
813     /**
814      * Checks whether the device has an initial user that can be switched to.
815      */
hasInitialUser()816     public boolean hasInitialUser() {
817         List<UserHandle> allUsers = getAllUsers();
818         for (int i = 0; i < allUsers.size(); i++) {
819             UserHandle user = allUsers.get(i);
820             if (mUserHandleHelper.isManagedProfile(user)) continue;
821 
822             return true;
823         }
824         return false;
825     }
826 
827     // TODO(b/231473748): temporary method that ignores ephemeral user while hasInitialUser() is
828     // used to define if it's first boot - once there is an isInitialBoot() for that purpose, this
829     // method should be removed (and its logic moved to hasInitialUser())
830     @VisibleForTesting
hasValidInitialUser()831     boolean hasValidInitialUser() {
832         // TODO(b/231473748): should call method that ignores partial, dying, or pre-created
833         List<UserHandle> allUsers = getAllUsers();
834         for (int i = 0; i < allUsers.size(); i++) {
835             UserHandle user = allUsers.get(i);
836             if (mUserHandleHelper.isManagedProfile(user)
837                     || mUserHandleHelper.isEphemeralUser(user)) {
838                 continue;
839             }
840 
841             return true;
842         }
843         return false;
844     }
845 
userListToUserIdList(List<UserHandle> allUsers)846     private static List<Integer> userListToUserIdList(List<UserHandle> allUsers) {
847         ArrayList<Integer> list = new ArrayList<>(allUsers.size());
848         for (int i = 0; i < allUsers.size(); i++) {
849             list.add(allUsers.get(i).getIdentifier());
850         }
851         return list;
852     }
853 
resetUserIdGlobalProperty(@onNull String name)854     private void resetUserIdGlobalProperty(@NonNull String name) {
855         if (DBG) Slogf.d(TAG, "resetting global property " + name);
856 
857         Settings.Global.putInt(mContext.getContentResolver(), name, UserManagerHelper.USER_NULL);
858     }
859 
getUserIdGlobalProperty(@onNull String name)860     private int getUserIdGlobalProperty(@NonNull String name) {
861         int userId = Settings.Global.getInt(mContext.getContentResolver(), name,
862                 UserManagerHelper.USER_NULL);
863         if (DBG) Slogf.d(TAG, "getting global property " + name + ": " + userId);
864         return userId;
865     }
866 }
867