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