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