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