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             // This string can come from a binder IPC call where empty string is the default value
245             // for the auto-generated code. So, need to check for that.
246             if (userLocales != null && userLocales.trim().isEmpty()) {
247                 mUserLocales = null;
248             } else {
249                 mUserLocales = userLocales;
250             }
251             return this;
252         }
253 
254         /**
255          * Builds the object.
256          */
257         @NonNull
build()258         public InitialUserInfo build() {
259             return new InitialUserInfo(this);
260         }
261     }
262 
263     /**
264      * Object used to define the properties of the initial user (which can then be set by
265      * {@link InitialUserSetter#set(InitialUserInfo)});
266      */
267     public static final class InitialUserInfo {
268         public final @InitialUserInfoType int type;
269         public final boolean replaceGuest;
270         public final @UserIdInt int switchUserId;
271         public final @Nullable String newUserName;
272         public final int newUserFlags;
273         public final boolean supportsOverrideUserIdProperty;
274         public @Nullable String userLocales;
275 
InitialUserInfo(@onNull Builder builder)276         private InitialUserInfo(@NonNull Builder builder) {
277             type = builder.mType;
278             switchUserId = builder.mSwitchUserId;
279             replaceGuest = builder.mReplaceGuest;
280             newUserName = builder.mNewUserName;
281             newUserFlags = builder.mNewUserFlags;
282             supportsOverrideUserIdProperty = builder.mSupportsOverrideUserIdProperty;
283             userLocales = builder.mUserLocales;
284         }
285 
286         @Override
toString()287         public String toString() {
288             StringBuilder string = new StringBuilder("InitialUserInfo[type=");
289             switch(type) {
290                 case TYPE_DEFAULT_BEHAVIOR:
291                     string.append("DEFAULT_BEHAVIOR");
292                     break;
293                 case TYPE_REPLACE_GUEST:
294                     string.append("REPLACE_GUEST");
295                     break;
296                 case TYPE_SWITCH:
297                     string.append("SWITCH").append(",userId=").append(switchUserId);
298                     break;
299                 case TYPE_CREATE:
300                     string.append("CREATE").append(",flags=")
301                             .append(UserHalHelper.userFlagsToString(newUserFlags));
302                     if (newUserName != null) {
303                         string.append(",name=" + UserHelperLite.safeName(newUserName));
304                     }
305                     if (userLocales != null) {
306                         string.append(",locales=").append(userLocales);
307                     }
308                     break;
309                 default:
310                     string.append("UNKNOWN:").append(type);
311             }
312             if (replaceGuest) string.append(",replaceGuest");
313             if (supportsOverrideUserIdProperty) string.append(",supportsOverrideUserIdProperty");
314 
315             return string.append(']').toString();
316         }
317     }
318 
319     /**
320      * Sets the initial user.
321      */
set(@onNull InitialUserInfo info)322     public void set(@NonNull InitialUserInfo info) {
323         Preconditions.checkArgument(info != null, "info cannot be null");
324 
325         switch (info.type) {
326             case TYPE_DEFAULT_BEHAVIOR:
327                 executeDefaultBehavior(info, /* fallback= */ false);
328                 break;
329             case TYPE_SWITCH:
330                 try {
331                     switchUser(info, /* fallback= */ true);
332                 } catch (Exception e) {
333                     fallbackDefaultBehavior(info, /* fallback= */ true,
334                             "Exception switching user: " + e);
335                 }
336                 break;
337             case TYPE_CREATE:
338                 try {
339                     createAndSwitchUser(info, /* fallback= */ true);
340                 } catch (Exception e) {
341                     fallbackDefaultBehavior(info, /* fallback= */ true,
342                             "Exception createUser user with name "
343                                     + UserHelperLite.safeName(info.newUserName) + " and flags "
344                                     + UserHalHelper.userFlagsToString(info.newUserFlags) + ": "
345                                     + e);
346                 }
347                 break;
348             case TYPE_REPLACE_GUEST:
349                 try {
350                     replaceUser(info, /* fallback= */ true);
351                 } catch (Exception e) {
352                     fallbackDefaultBehavior(info, /* fallback= */ true,
353                             "Exception replace guest user: " + e);
354                 }
355                 break;
356             default:
357                 throw new IllegalArgumentException("invalid InitialUserInfo type: " + info.type);
358         }
359     }
360 
replaceUser(InitialUserInfo info, boolean fallback)361     private void replaceUser(InitialUserInfo info, boolean fallback) {
362         int currentUserId = ActivityManager.getCurrentUser();
363         UserInfo currentUser = mUm.getUserInfo(currentUserId);
364 
365         UserInfo newUser = replaceGuestIfNeeded(currentUser);
366         if (newUser == null) {
367             fallbackDefaultBehavior(info, fallback,
368                     "could not replace guest " + currentUser.toFullString());
369             return;
370         }
371 
372         switchUser(new Builder(TYPE_SWITCH)
373                 .setSwitchUserId(newUser.id)
374                 .build(), fallback);
375 
376         if (newUser.id != currentUser.id) {
377             Slog.i(TAG, "Removing old guest " + currentUser.id);
378             if (!mUm.removeUser(currentUser.id)) {
379                 Slog.w(TAG, "Could not remove old guest " + currentUser.id);
380             }
381         }
382     }
383 
executeDefaultBehavior(@onNull InitialUserInfo info, boolean fallback)384     private void executeDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback) {
385         if (!hasInitialUser()) {
386             if (DBG) Slog.d(TAG, "executeDefaultBehavior(): no initial user, creating it");
387             createAndSwitchUser(new Builder(TYPE_CREATE)
388                     .setNewUserName(mNewUserName)
389                     .setNewUserFlags(UserFlags.ADMIN)
390                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
391                     .setUserLocales(info.userLocales)
392                     .build(), fallback);
393         } else {
394             if (DBG) Slog.d(TAG, "executeDefaultBehavior(): switching to initial user");
395             int userId = getInitialUser(info.supportsOverrideUserIdProperty);
396             switchUser(new Builder(TYPE_SWITCH)
397                     .setSwitchUserId(userId)
398                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
399                     .setReplaceGuest(info.replaceGuest)
400                     .build(), fallback);
401         }
402     }
403 
404     @VisibleForTesting
fallbackDefaultBehavior(@onNull InitialUserInfo info, boolean fallback, @NonNull String reason)405     void fallbackDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback,
406             @NonNull String reason) {
407         if (!fallback) {
408             // Only log the error
409             Slog.w(TAG, reason);
410             // Must explicitly tell listener that initial user could not be determined
411             notifyListener(/*initialUser= */ null);
412             return;
413         }
414         Slog.w(TAG, "Falling back to default behavior. Reason: " + reason);
415         executeDefaultBehavior(info, /* fallback= */ false);
416     }
417 
switchUser(@onNull InitialUserInfo info, boolean fallback)418     private void switchUser(@NonNull InitialUserInfo info, boolean fallback) {
419         int userId = info.switchUserId;
420         boolean replaceGuest = info.replaceGuest;
421 
422         if (DBG) {
423             Slog.d(TAG, "switchUser(): userId=" + userId + ", replaceGuest=" + replaceGuest
424                     + ", fallback=" + fallback);
425         }
426 
427         UserInfo user = mUm.getUserInfo(userId);
428         if (user == null) {
429             fallbackDefaultBehavior(info, fallback, "user with id " + userId + " doesn't exist");
430             return;
431         }
432 
433         UserInfo actualUser = user;
434 
435         if (user.isGuest() && replaceGuest) {
436             actualUser = replaceGuestIfNeeded(user);
437 
438             if (actualUser == null) {
439                 fallbackDefaultBehavior(info, fallback, "could not replace guest "
440                         + user.toFullString());
441                 return;
442             }
443         }
444 
445         int actualUserId = actualUser.id;
446 
447         unlockSystemUserIfNecessary(actualUserId);
448 
449         int currentUserId = ActivityManager.getCurrentUser();
450         if (actualUserId != currentUserId) {
451             if (!startForegroundUser(actualUserId)) {
452                 fallbackDefaultBehavior(info, fallback,
453                         "am.switchUser(" + actualUserId + ") failed");
454                 return;
455             }
456             setLastActiveUser(actualUser.id);
457         }
458         notifyListener(actualUser);
459 
460         if (actualUserId != userId) {
461             Slog.i(TAG, "Removing old guest " + userId);
462             if (!mUm.removeUser(userId)) {
463                 Slog.w(TAG, "Could not remove old guest " + userId);
464             }
465         }
466     }
467 
unlockSystemUserIfNecessary(@serIdInt int userId)468     private void unlockSystemUserIfNecessary(@UserIdInt int userId) {
469         // If system user is the only user to unlock, it will be handled when boot is complete.
470         if (userId != UserHandle.USER_SYSTEM) {
471             unlockSystemUser();
472         }
473     }
474 
475     /**
476      * Check if the user is a guest and can be replaced.
477      */
canReplaceGuestUser(UserInfo user)478     public boolean canReplaceGuestUser(UserInfo user) {
479         if (!user.isGuest()) return false;
480 
481         if (mLockPatternUtils.isSecure(user.id)) {
482             if (DBG) {
483                 Slog.d(TAG, "replaceGuestIfNeeded(), skipped, since user "
484                         + user.id + " has secure lock pattern");
485             }
486             return false;
487         }
488 
489         return true;
490     }
491 
492     /**
493      * Replaces {@code user} by a new guest, if necessary.
494      *
495      * <p>If {@code user} is not a guest, it doesn't do anything and returns the same user.
496      *
497      * <p>Otherwise, it marks the current guest for deletion, creates a new one, and returns the
498      * new guest (or {@code null} if a new guest could not be created).
499      */
500 
501     @VisibleForTesting
502     @Nullable
replaceGuestIfNeeded(@onNull UserInfo user)503     UserInfo replaceGuestIfNeeded(@NonNull UserInfo user) {
504         Preconditions.checkArgument(user != null, "user cannot be null");
505 
506         if (!canReplaceGuestUser(user)) {
507             return user;
508         }
509 
510         Slog.i(TAG, "Replacing guest (" + user.toFullString() + ")");
511 
512         int halFlags = UserFlags.GUEST;
513         if (user.isEphemeral()) {
514             halFlags |= UserFlags.EPHEMERAL;
515         } else {
516             // TODO(b/150413515): decide whether we should allow it or not. Right now we're
517             // just logging, as UserManagerService will automatically set it to ephemeral if
518             // platform is set to do so.
519             Slog.w(TAG, "guest being replaced is not ephemeral: " + user.toFullString());
520         }
521 
522         if (!mUm.markGuestForDeletion(user.id)) {
523             // Don't need to recover in case of failure - most likely create new user will fail
524             // because there is already a guest
525             Slog.w(TAG, "failed to mark guest " + user.id + " for deletion");
526         }
527 
528         Pair<UserInfo, String> result = createNewUser(new Builder(TYPE_CREATE)
529                 .setNewUserName(mNewGuestName)
530                 .setNewUserFlags(halFlags)
531                 .build());
532 
533         String errorMessage = result.second;
534         if (errorMessage != null) {
535             Slog.w(TAG, "could not replace guest " + user.toFullString() + ": " + errorMessage);
536             return null;
537         }
538 
539         return result.first;
540     }
541 
createAndSwitchUser(@onNull InitialUserInfo info, boolean fallback)542     private void createAndSwitchUser(@NonNull InitialUserInfo info, boolean fallback) {
543         Pair<UserInfo, String> result = createNewUser(info);
544         String reason = result.second;
545         if (reason != null) {
546             fallbackDefaultBehavior(info, fallback, reason);
547             return;
548         }
549 
550         switchUser(new Builder(TYPE_SWITCH)
551                 .setSwitchUserId(result.first.id)
552                 .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
553                 .build(), fallback);
554     }
555 
556     /**
557      * Creates a new user.
558      *
559      * @return on success, first element is the new user; on failure, second element contains the
560      * error message.
561      */
562     @NonNull
createNewUser(@onNull InitialUserInfo info)563     private Pair<UserInfo, String> createNewUser(@NonNull InitialUserInfo info) {
564         String name = info.newUserName;
565         int halFlags = info.newUserFlags;
566 
567         if (DBG) {
568             Slog.d(TAG, "createUser(name=" + UserHelperLite.safeName(name) + ", flags="
569                     + userFlagsToString(halFlags) + ")");
570         }
571 
572         if (UserHalHelper.isSystem(halFlags)) {
573             return new Pair<>(null, "Cannot create system user");
574         }
575 
576         if (UserHalHelper.isAdmin(halFlags)) {
577             boolean validAdmin = true;
578             if (UserHalHelper.isGuest(halFlags)) {
579                 Slog.w(TAG, "Cannot create guest admin");
580                 validAdmin = false;
581             }
582             if (UserHalHelper.isEphemeral(halFlags)) {
583                 Slog.w(TAG, "Cannot create ephemeral admin");
584                 validAdmin = false;
585             }
586             if (!validAdmin) {
587                 return new Pair<>(null, "Invalid flags for admin user");
588             }
589         }
590         // TODO(b/150413515): decide what to if HAL requested a non-ephemeral guest but framework
591         // sets all guests as ephemeral - should it fail or just warn?
592 
593         int flags = UserHalHelper.toUserInfoFlags(halFlags);
594         String type = UserHalHelper.isGuest(halFlags) ? UserManager.USER_TYPE_FULL_GUEST
595                 : UserManager.USER_TYPE_FULL_SECONDARY;
596 
597         if (DBG) {
598             Slog.d(TAG, "calling am.createUser((name=" + UserHelperLite.safeName(name) + ", type="
599                     + type + ", flags=" + UserInfo.flagsToString(flags) + ")");
600         }
601 
602         UserInfo userInfo = mCarUserService.createUserEvenWhenDisallowed(name, type, flags);
603         if (userInfo == null) {
604             return new Pair<>(null, "createUser(name=" + UserHelperLite.safeName(name) + ", flags="
605                     + userFlagsToString(halFlags) + "): failed to create user");
606         }
607 
608         if (DBG) Slog.d(TAG, "user created: " + userInfo.id);
609 
610         if (info.userLocales != null) {
611             if (DBG) {
612                 Slog.d(TAG, "setting locale for user " + userInfo.id + " to " + info.userLocales);
613             }
614             Settings.System.putStringForUser(mContext.getContentResolver(),
615                     Settings.System.SYSTEM_LOCALES, info.userLocales, userInfo.id);
616         }
617 
618         return new Pair<>(userInfo, null);
619     }
620 
621     @VisibleForTesting
unlockSystemUser()622     void unlockSystemUser() {
623         Slog.i(TAG, "unlocking system user");
624         IActivityManager am = ActivityManager.getService();
625 
626         TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
627         t.traceBegin("UnlockSystemUser");
628         try {
629             // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
630             // update the state and USER_SYSTEM unlock happens twice.
631             t.traceBegin("am.startUser");
632             boolean started = am.startUserInBackground(UserHandle.USER_SYSTEM);
633             t.traceEnd();
634             if (!started) {
635                 Slog.w(TAG, "could not restart system user in foreground; trying unlock instead");
636                 t.traceBegin("am.unlockUser");
637                 boolean unlocked = am.unlockUser(UserHandle.USER_SYSTEM, /* token= */ null,
638                         /* secret= */ null, /* listener= */ null);
639                 t.traceEnd();
640                 if (!unlocked) {
641                     Slog.w(TAG, "could not unlock system user neither");
642                     return;
643                 }
644             }
645         } catch (RemoteException e) {
646             // should not happen for local call.
647             Slog.wtf("RemoteException from AMS", e);
648         } finally {
649             t.traceEnd();
650         }
651     }
652 
653     @VisibleForTesting
startForegroundUser(@serIdInt int userId)654     boolean startForegroundUser(@UserIdInt int userId) {
655         if (UserHelperLite.isHeadlessSystemUser(userId)) {
656             // System User doesn't associate with real person, can not be switched to.
657             return false;
658         }
659         try {
660             return ActivityManager.getService().startUserInForegroundWithListener(userId, null);
661         } catch (RemoteException e) {
662             Slog.w(TAG, "failed to start user " + userId, e);
663             return false;
664         }
665     }
666 
notifyListener(@ullable UserInfo initialUser)667     private void notifyListener(@Nullable UserInfo initialUser) {
668         if (DBG) Slog.d(TAG, "notifyListener(): " + initialUser);
669         mListener.accept(initialUser);
670     }
671 
672     /**
673      * Dumps it state.
674      */
675     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(@onNull PrintWriter writer)676     public void dump(@NonNull PrintWriter writer) {
677         writer.println("InitialUserSetter");
678         String indent = "  ";
679         writer.printf("%smNewUserName: %s\n", indent, mNewUserName);
680         writer.printf("%smNewGuestName: %s\n", indent, mNewGuestName);
681     }
682 
683     /**
684      * Sets the last active user.
685      */
setLastActiveUser(@serIdInt int userId)686     public void setLastActiveUser(@UserIdInt int userId) {
687         if (UserHelperLite.isHeadlessSystemUser(userId)) {
688             if (DBG) Slog.d(TAG, "setLastActiveUser(): ignoring headless system user " + userId);
689             return;
690         }
691         setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID, userId);
692 
693         // TODO(b/155918094): change method to receive a UserInfo instead
694         UserInfo user = mUm.getUserInfo(userId);
695         if (user == null) {
696             Slog.w(TAG, "setLastActiveUser(): user " + userId + " doesn't exist");
697             return;
698         }
699         if (!user.isEphemeral()) {
700             setUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID, userId);
701         }
702     }
703 
setUserIdGlobalProperty(@onNull String name, @UserIdInt int userId)704     private void setUserIdGlobalProperty(@NonNull String name, @UserIdInt int userId) {
705         if (DBG) Slog.d(TAG, "setting global property " + name + " to " + userId);
706 
707         Settings.Global.putInt(mContext.getContentResolver(), name, userId);
708     }
709 
710     /**
711      * Gets the user id for the initial user to boot into. This is only applicable for headless
712      * system user model. This method checks for a system property and will only work for system
713      * apps.
714      *
715      * This method checks for the initial user via three mechanisms in this order:
716      * <ol>
717      *     <li>Check for a boot user override via {@link CarProperties#boot_user_override_id()}</li>
718      *     <li>Check for the last active user in the system</li>
719      *     <li>Fallback to the smallest user id that is not {@link UserHandle.USER_SYSTEM}</li>
720      * </ol>
721      *
722      * If any step fails to retrieve the stored id or the retrieved id does not exist on device,
723      * then it will move onto the next step.
724      *
725      * @return user id of the initial user to boot into on the device, or
726      * {@link UserHandle#USER_NULL} if there is no user available.
727      */
728     @VisibleForTesting
getInitialUser(boolean usesOverrideUserIdProperty)729     int getInitialUser(boolean usesOverrideUserIdProperty) {
730 
731         List<Integer> allUsers = userInfoListToUserIdList(getAllUsers());
732 
733         if (allUsers.isEmpty()) {
734             return UserHandle.USER_NULL;
735         }
736 
737         //TODO(b/150416512): Check if it is still supported, if not remove it.
738         if (usesOverrideUserIdProperty) {
739             int bootUserOverride = CarProperties.boot_user_override_id()
740                     .orElse(BOOT_USER_NOT_FOUND);
741 
742             // If an override user is present and a real user, return it
743             if (bootUserOverride != BOOT_USER_NOT_FOUND
744                     && allUsers.contains(bootUserOverride)) {
745                 Slog.i(TAG, "Boot user id override found for initial user, user id: "
746                         + bootUserOverride);
747                 return bootUserOverride;
748             }
749         }
750 
751         // If the last active user is not the SYSTEM user and is a real user, return it
752         int lastActiveUser = getUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
753         if (allUsers.contains(lastActiveUser)) {
754             Slog.i(TAG, "Last active user loaded for initial user: " + lastActiveUser);
755             return lastActiveUser;
756         }
757         resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_USER_ID);
758 
759         int lastPersistentUser = getUserIdGlobalProperty(
760                 CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
761         if (allUsers.contains(lastPersistentUser)) {
762             Slog.i(TAG, "Last active, persistent user loaded for initial user: "
763                     + lastPersistentUser);
764             return lastPersistentUser;
765         }
766         resetUserIdGlobalProperty(CarSettings.Global.LAST_ACTIVE_PERSISTENT_USER_ID);
767 
768         // If all else fails, return the smallest user id
769         int returnId = Collections.min(allUsers);
770         // TODO(b/158101909): the smallest user id is not always the initial user; a better approach
771         // would be looking for the first ADMIN user, or keep track of all last active users (not
772         // just the very last)
773         Slog.w(TAG, "Last active user (" + lastActiveUser + ") not found. Returning smallest user "
774                 + "id instead: " + returnId);
775         return returnId;
776     }
777 
778     /**
779      * Gets all the users that can be brought to the foreground on the system.
780      *
781      * @return List of {@code UserInfo} for users that associated with a real person.
782      */
getAllUsers()783     private List<UserInfo> getAllUsers() {
784         if (UserManager.isHeadlessSystemUserMode()) {
785             return getAllUsersExceptSystemUserAndSpecifiedUser(UserHandle.USER_SYSTEM);
786         } else {
787             return mUm.getAliveUsers();
788         }
789     }
790 
791     /**
792      * Gets all the users except system user and the one with userId passed in.
793      *
794      * @param userId of the user not to be returned.
795      * @return All users other than system user and user with userId.
796      */
getAllUsersExceptSystemUserAndSpecifiedUser(@serIdInt int userId)797     private List<UserInfo> getAllUsersExceptSystemUserAndSpecifiedUser(@UserIdInt int userId) {
798         List<UserInfo> users = mUm.getAliveUsers();
799 
800         for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
801             UserInfo userInfo = iterator.next();
802             if (userInfo.id == userId || userInfo.id == UserHandle.USER_SYSTEM) {
803                 // Remove user with userId from the list.
804                 iterator.remove();
805             }
806         }
807         return users;
808     }
809 
810     /**
811      * Checks whether the device has an initial user that can be switched to.
812      */
hasInitialUser()813     public boolean hasInitialUser() {
814         List<UserInfo> allUsers = getAllUsers();
815         for (int i = 0; i < allUsers.size(); i++) {
816             UserInfo user = allUsers.get(i);
817             if (user.isManagedProfile()) continue;
818 
819             return true;
820         }
821         return false;
822     }
823 
userInfoListToUserIdList(List<UserInfo> allUsers)824     private static List<Integer> userInfoListToUserIdList(List<UserInfo> allUsers) {
825         ArrayList<Integer> list = new ArrayList<>(allUsers.size());
826         for (int i = 0; i < allUsers.size(); i++) {
827             list.add(allUsers.get(i).id);
828         }
829         return list;
830     }
831 
resetUserIdGlobalProperty(@onNull String name)832     private void resetUserIdGlobalProperty(@NonNull String name) {
833         if (DBG) Slog.d(TAG, "resetting global property " + name);
834 
835         Settings.Global.putInt(mContext.getContentResolver(), name, UserHandle.USER_NULL);
836     }
837 
getUserIdGlobalProperty(@onNull String name)838     private int getUserIdGlobalProperty(@NonNull String name) {
839         int userId = Settings.Global.getInt(mContext.getContentResolver(), name,
840                 UserHandle.USER_NULL);
841         if (DBG) Slog.d(TAG, "getting global property " + name + ": " + userId);
842 
843         return userId;
844     }
845 }
846