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 android.car.userlib;
17 
18 import static android.car.userlib.UserHalHelper.userFlagsToString;
19 import static android.car.userlib.UserHelper.safeName;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.UserIdInt;
25 import android.app.ActivityManager;
26 import android.app.IActivityManager;
27 import android.content.Context;
28 import android.content.pm.UserInfo;
29 import android.hardware.automotive.vehicle.V2_0.UserFlags;
30 import android.os.RemoteException;
31 import android.os.Trace;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.provider.Settings;
35 import android.sysprop.CarProperties;
36 import android.util.Log;
37 import android.util.Pair;
38 import android.util.Slog;
39 import android.util.TimingsTraceLog;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.internal.util.Preconditions;
43 import com.android.internal.widget.LockPatternUtils;
44 
45 import java.io.PrintWriter;
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.function.Consumer;
49 
50 /**
51  * Helper used to set the initial Android user on boot or when resuming from RAM.
52  */
53 public final class InitialUserSetter {
54 
55     private static final String TAG = InitialUserSetter.class.getSimpleName();
56 
57     private static final boolean DBG = false;
58 
59     /**
60      * Sets the initial user using the default behavior.
61      *
62      * <p>The default behavior is:
63      *
64      * <ol>
65      *  <li>On first boot, it creates and switches to a new user.
66      *  <li>Otherwise, it will switch to either:
67      *  <ol>
68      *   <li>User defined by {@code android.car.systemuser.bootuseroverrideid} (when it was
69      * constructed with such option enabled).
70      *   <li>Last active user (as defined by
71      * {@link android.provider.Settings.Global.LAST_ACTIVE_USER_ID}.
72      *  </ol>
73      * </ol>
74      */
75     public static final int TYPE_DEFAULT_BEHAVIOR = 0;
76 
77     /**
78      * Switches to the given user, falling back to {@link #fallbackDefaultBehavior(String)} if it
79      * fails.
80      */
81     public static final int TYPE_SWITCH = 1;
82 
83     /**
84      * Creates a new user and switches to it, falling back to
85      * {@link #fallbackDefaultBehavior(String) if any of these steps fails.
86      *
87      * @param name (optional) name of the new user
88      * @param halFlags user flags as defined by Vehicle HAL ({@code UserFlags} enum).
89      */
90     public static final int TYPE_CREATE = 2;
91 
92     @IntDef(prefix = { "TYPE_" }, value = {
93             TYPE_DEFAULT_BEHAVIOR,
94             TYPE_SWITCH,
95             TYPE_CREATE,
96     })
97     @Retention(RetentionPolicy.SOURCE)
98     public @interface InitialUserInfoType { }
99 
100     private final Context mContext;
101 
102     // TODO(b/150413304): abstract AM / UM into interfaces, then provide local and remote
103     // implementation (where local is implemented by ActivityManagerInternal / UserManagerInternal)
104     private final CarUserManagerHelper mHelper;
105     private final UserManager mUm;
106     private final LockPatternUtils mLockPatternUtils;
107 
108     private final String mNewUserName;
109     private final String mNewGuestName;
110 
111     private final Consumer<UserInfo> mListener;
112 
InitialUserSetter(@onNull Context context, @NonNull Consumer<UserInfo> listener)113     public InitialUserSetter(@NonNull Context context, @NonNull Consumer<UserInfo> listener) {
114         this(context, listener, /* newGuestName= */ null);
115     }
116 
InitialUserSetter(@onNull Context context, @NonNull Consumer<UserInfo> listener, @Nullable String newGuestName)117     public InitialUserSetter(@NonNull Context context, @NonNull Consumer<UserInfo> listener,
118             @Nullable String newGuestName) {
119         this(context, new CarUserManagerHelper(context), UserManager.get(context), listener,
120                 new LockPatternUtils(context),
121                 context.getString(com.android.internal.R.string.owner_name), newGuestName);
122     }
123 
124     @VisibleForTesting
InitialUserSetter(@onNull Context context, @NonNull CarUserManagerHelper helper, @NonNull UserManager um, @NonNull Consumer<UserInfo> listener, @NonNull LockPatternUtils lockPatternUtils, @Nullable String newUserName, @Nullable String newGuestName)125     public InitialUserSetter(@NonNull Context context, @NonNull CarUserManagerHelper helper,
126             @NonNull UserManager um, @NonNull Consumer<UserInfo> listener,
127             @NonNull LockPatternUtils lockPatternUtils,
128             @Nullable String newUserName, @Nullable String newGuestName) {
129         mContext = context;
130         mHelper = helper;
131         mUm = um;
132         mListener = listener;
133         mLockPatternUtils = lockPatternUtils;
134         mNewUserName = newUserName;
135         mNewGuestName = newGuestName;
136     }
137 
138     /**
139      * Builder for {@link InitialUserInfo} objects.
140      *
141      */
142     public static final class Builder {
143 
144         private final @InitialUserInfoType int mType;
145         private boolean mReplaceGuest;
146         private @UserIdInt int mSwitchUserId;
147         private @Nullable String mNewUserName;
148         private int mNewUserFlags;
149         private boolean mSupportsOverrideUserIdProperty;
150         private @Nullable String mUserLocales;
151 
152         /**
153          * Constructor for the given type.
154          *
155          * @param type {@link #TYPE_DEFAULT_BEHAVIOR}, {@link #TYPE_SWITCH},
156          * or {@link #TYPE_CREATE}.
157          */
Builder(@nitialUserInfoType int type)158         public Builder(@InitialUserInfoType int type) {
159             Preconditions.checkArgument(
160                     type == TYPE_DEFAULT_BEHAVIOR || type == TYPE_SWITCH || type == TYPE_CREATE,
161                     "invalid builder type");
162             mType = type;
163         }
164 
165         /**
166          * Sets the id of the user to be switched to.
167          *
168          * @throws IllegalArgumentException if builder is not for {@link #TYPE_SWITCH}.
169          */
170         @NonNull
setSwitchUserId(@serIdInt int userId)171         public Builder setSwitchUserId(@UserIdInt int userId) {
172             Preconditions.checkArgument(mType == TYPE_SWITCH, "invalid builder type: " + mType);
173             mSwitchUserId = userId;
174             return this;
175         }
176 
177         /**
178          * Sets whether the current user should be replaced when it's a guest.
179          */
180         @NonNull
setReplaceGuest(boolean value)181         public Builder setReplaceGuest(boolean value) {
182             mReplaceGuest = value;
183             return this;
184         }
185 
186         /**
187          * Sets the name of the new user being created.
188          *
189          * @throws IllegalArgumentException if builder is not for {@link #TYPE_CREATE}.
190          */
191         @NonNull
setNewUserName(@ullable String name)192         public Builder setNewUserName(@Nullable String name) {
193             Preconditions.checkArgument(mType == TYPE_CREATE, "invalid builder type: " + mType);
194             mNewUserName = name;
195             return this;
196         }
197 
198         /**
199          * Sets the flags (as defined by {@link android.hardware.automotive.vehicle.V2_0.UserFlags})
200          * of the new user being created.
201          *
202          * @throws IllegalArgumentException if builder is not for {@link #TYPE_CREATE}.
203          */
204         @NonNull
setNewUserFlags(int flags)205         public Builder setNewUserFlags(int flags) {
206             Preconditions.checkArgument(mType == TYPE_CREATE, "invalid builder type: " + mType);
207             mNewUserFlags = flags;
208             return this;
209         }
210 
211         /**
212          * Sets whether the {@link CarProperties#boot_user_override_id()} should be taking in
213          * account when using the default behavior.
214          */
215         @NonNull
setSupportsOverrideUserIdProperty(boolean value)216         public Builder setSupportsOverrideUserIdProperty(boolean value) {
217             mSupportsOverrideUserIdProperty = value;
218             return this;
219         }
220 
221         /**
222          * Sets the system locales for the initial user (when it's created).
223          */
224         @NonNull
setUserLocales(@ullable String userLocales)225         public Builder setUserLocales(@Nullable String userLocales) {
226             mUserLocales = userLocales;
227             return this;
228         }
229 
230         /**
231          * Builds the object.
232          */
233         @NonNull
build()234         public InitialUserInfo build() {
235             return new InitialUserInfo(this);
236         }
237     }
238 
239     /**
240      * Object used to define the properties of the initial user (which can then be set by
241      * {@link InitialUserSetter#set(InitialUserInfo)});
242      */
243     public static final class InitialUserInfo {
244         public final @InitialUserInfoType int type;
245         public final boolean replaceGuest;
246         public final @UserIdInt int switchUserId;
247         public final @Nullable String newUserName;
248         public final int newUserFlags;
249         public final boolean supportsOverrideUserIdProperty;
250         public @Nullable String userLocales;
251 
InitialUserInfo(@onNull Builder builder)252         private InitialUserInfo(@NonNull Builder builder) {
253             type = builder.mType;
254             switchUserId = builder.mSwitchUserId;
255             replaceGuest = builder.mReplaceGuest;
256             newUserName = builder.mNewUserName;
257             newUserFlags = builder.mNewUserFlags;
258             supportsOverrideUserIdProperty = builder.mSupportsOverrideUserIdProperty;
259             userLocales = builder.mUserLocales;
260         }
261     }
262 
263     /**
264      * Sets the initial user.
265      */
set(@onNull InitialUserInfo info)266     public void set(@NonNull InitialUserInfo info) {
267         Preconditions.checkArgument(info != null, "info cannot be null");
268 
269         switch (info.type) {
270             case TYPE_DEFAULT_BEHAVIOR:
271                 executeDefaultBehavior(info, /* fallback= */ false);
272                 break;
273             case TYPE_SWITCH:
274                 try {
275                     switchUser(info, /* fallback= */ true);
276                 } catch (Exception e) {
277                     fallbackDefaultBehavior(info, /* fallback= */ true,
278                             "Exception switching user: " + e);
279                 }
280                 break;
281             case TYPE_CREATE:
282                 try {
283                     createAndSwitchUser(info, /* fallback= */ true);
284                 } catch (Exception e) {
285                     fallbackDefaultBehavior(info, /* fallback= */ true,
286                             "Exception createUser user with name "
287                                     + UserHelper.safeName(info.newUserName) + " and flags "
288                                     + UserHalHelper.userFlagsToString(info.newUserFlags) + ": "
289                                     + e);
290                 }
291                 break;
292             default:
293                 throw new IllegalArgumentException("invalid InitialUserInfo type: " + info.type);
294         }
295     }
296 
executeDefaultBehavior(@onNull InitialUserInfo info, boolean fallback)297     private void executeDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback) {
298         if (!mHelper.hasInitialUser()) {
299             if (DBG) Log.d(TAG, "executeDefaultBehavior(): no initial user, creating it");
300             createAndSwitchUser(new Builder(TYPE_CREATE)
301                     .setNewUserName(mNewUserName)
302                     .setNewUserFlags(UserFlags.ADMIN)
303                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
304                     .setUserLocales(info.userLocales)
305                     .build(), fallback);
306         } else {
307             if (DBG) Log.d(TAG, "executeDefaultBehavior(): switching to initial user");
308             int userId = mHelper.getInitialUser(info.supportsOverrideUserIdProperty);
309             switchUser(new Builder(TYPE_SWITCH)
310                     .setSwitchUserId(userId)
311                     .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
312                     .setReplaceGuest(info.replaceGuest)
313                     .build(), fallback);
314         }
315     }
316 
317     @VisibleForTesting
fallbackDefaultBehavior(@onNull InitialUserInfo info, boolean fallback, @NonNull String reason)318     void fallbackDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback,
319             @NonNull String reason) {
320         if (!fallback) {
321             // Only log the error
322             Log.w(TAG, reason);
323             // Must explicitly tell listener that initial user could not be determined
324             notifyListener(/*initialUser= */ null);
325             return;
326         }
327         Log.w(TAG, "Falling back to default behavior. Reason: " + reason);
328         executeDefaultBehavior(info, /* fallback= */ false);
329     }
330 
switchUser(@onNull InitialUserInfo info, boolean fallback)331     private void switchUser(@NonNull InitialUserInfo info, boolean fallback) {
332         int userId = info.switchUserId;
333         boolean replaceGuest = info.replaceGuest;
334 
335         if (DBG) {
336             Log.d(TAG, "switchUser(): userId=" + userId + ", replaceGuest=" + replaceGuest
337                     + ", fallback=" + fallback);
338         }
339 
340         UserInfo user = mUm.getUserInfo(userId);
341         if (user == null) {
342             fallbackDefaultBehavior(info, fallback, "user with id " + userId + " doesn't exist");
343             return;
344         }
345 
346         UserInfo actualUser = user;
347 
348         if (user.isGuest() && replaceGuest) {
349             actualUser = replaceGuestIfNeeded(user);
350 
351             if (actualUser == null) {
352                 fallbackDefaultBehavior(info, fallback, "could not replace guest "
353                         + user.toFullString());
354                 return;
355             }
356         }
357 
358         int actualUserId = actualUser.id;
359 
360         unlockSystemUserIfNecessary(actualUserId);
361 
362         int currentUserId = ActivityManager.getCurrentUser();
363         if (actualUserId != currentUserId) {
364             if (!startForegroundUser(actualUserId)) {
365                 fallbackDefaultBehavior(info, fallback,
366                         "am.switchUser(" + actualUserId + ") failed");
367                 return;
368             }
369             mHelper.setLastActiveUser(actualUserId);
370         }
371         notifyListener(actualUser);
372 
373         if (actualUserId != userId) {
374             Slog.i(TAG, "Removing old guest " + userId);
375             if (!mUm.removeUser(userId)) {
376                 Slog.w(TAG, "Could not remove old guest " + userId);
377             }
378         }
379     }
380 
unlockSystemUserIfNecessary(@serIdInt int userId)381     private void unlockSystemUserIfNecessary(@UserIdInt int userId) {
382         // If system user is the only user to unlock, it will be handled when boot is complete.
383         if (userId != UserHandle.USER_SYSTEM) {
384             unlockSystemUser();
385         }
386     }
387 
388     // TODO(b/151758646): move to CarUserManagerHelper
389     /**
390      * Replaces {@code user} by a new guest, if necessary.
391      *
392      * <p>If {@code user} is not a guest, it doesn't do anything and returns the same user.
393      *
394      * <p>Otherwise, it marks the current guest for deletion, creates a new one, and returns the
395      * new guest (or {@code null} if a new guest could not be created).
396      */
397     @Nullable
replaceGuestIfNeeded(@onNull UserInfo user)398     public UserInfo replaceGuestIfNeeded(@NonNull UserInfo user) {
399         Preconditions.checkArgument(user != null, "user cannot be null");
400 
401         if (!user.isGuest()) return user;
402 
403         if (mLockPatternUtils.isSecure(user.id)) {
404             if (DBG) {
405                 Log.d(TAG, "replaceGuestIfNeeded(), skipped, since user "
406                         + user.id + " has secure lock pattern");
407             }
408             return user;
409         }
410 
411         Log.i(TAG, "Replacing guest (" + user.toFullString() + ")");
412 
413         int halFlags = UserFlags.GUEST;
414         if (user.isEphemeral()) {
415             halFlags |= UserFlags.EPHEMERAL;
416         } else {
417             // TODO(b/150413515): decide whether we should allow it or not. Right now we're
418             // just logging, as UserManagerService will automatically set it to ephemeral if
419             // platform is set to do so.
420             Log.w(TAG, "guest being replaced is not ephemeral: " + user.toFullString());
421         }
422 
423         if (!mUm.markGuestForDeletion(user.id)) {
424             // Don't need to recover in case of failure - most likely create new user will fail
425             // because there is already a guest
426             Log.w(TAG, "failed to mark guest " + user.id + " for deletion");
427         }
428 
429         Pair<UserInfo, String> result = createNewUser(new Builder(TYPE_CREATE)
430                 .setNewUserName(mNewGuestName)
431                 .setNewUserFlags(halFlags)
432                 .build());
433 
434         String errorMessage = result.second;
435         if (errorMessage != null) {
436             Log.w(TAG, "could not replace guest " + user.toFullString() + ": " + errorMessage);
437             return null;
438         }
439 
440         return result.first;
441     }
442 
createAndSwitchUser(@onNull InitialUserInfo info, boolean fallback)443     private void createAndSwitchUser(@NonNull InitialUserInfo info, boolean fallback) {
444         Pair<UserInfo, String> result = createNewUser(info);
445         String reason = result.second;
446         if (reason != null) {
447             fallbackDefaultBehavior(info, fallback, reason);
448             return;
449         }
450 
451         switchUser(new Builder(TYPE_SWITCH)
452                 .setSwitchUserId(result.first.id)
453                 .setSupportsOverrideUserIdProperty(info.supportsOverrideUserIdProperty)
454                 .build(), fallback);
455     }
456 
457     /**
458      * Creates a new user.
459      *
460      * @return on success, first element is the new user; on failure, second element contains the
461      * error message.
462      */
463     @NonNull
createNewUser(@onNull InitialUserInfo info)464     private Pair<UserInfo, String> createNewUser(@NonNull InitialUserInfo info) {
465         String name = info.newUserName;
466         int halFlags = info.newUserFlags;
467 
468         if (DBG) {
469             Log.d(TAG, "createUser(name=" + safeName(name) + ", flags="
470                     + userFlagsToString(halFlags) + ")");
471         }
472 
473         if (UserHalHelper.isSystem(halFlags)) {
474             return new Pair<>(null, "Cannot create system user");
475         }
476 
477         if (UserHalHelper.isAdmin(halFlags)) {
478             boolean validAdmin = true;
479             if (UserHalHelper.isGuest(halFlags)) {
480                 Log.w(TAG, "Cannot create guest admin");
481                 validAdmin = false;
482             }
483             if (UserHalHelper.isEphemeral(halFlags)) {
484                 Log.w(TAG, "Cannot create ephemeral admin");
485                 validAdmin = false;
486             }
487             if (!validAdmin) {
488                 return new Pair<>(null, "Invalid flags for admin user");
489             }
490         }
491         // TODO(b/150413515): decide what to if HAL requested a non-ephemeral guest but framework
492         // sets all guests as ephemeral - should it fail or just warn?
493 
494         int flags = UserHalHelper.toUserInfoFlags(halFlags);
495         String type = UserHalHelper.isGuest(halFlags) ? UserManager.USER_TYPE_FULL_GUEST
496                 : UserManager.USER_TYPE_FULL_SECONDARY;
497 
498         if (DBG) {
499             Log.d(TAG, "calling am.createUser((name=" + safeName(name) + ", type=" + type
500                     + ", flags=" + UserInfo.flagsToString(flags) + ")");
501         }
502 
503         UserInfo userInfo = mUm.createUser(name, type, flags);
504         if (userInfo == null) {
505             return new Pair<>(null, "createUser(name=" + safeName(name) + ", flags="
506                     + userFlagsToString(halFlags) + "): failed to create user");
507         }
508 
509         if (DBG) Log.d(TAG, "user created: " + userInfo.id);
510 
511         if (info.userLocales != null) {
512             if (DBG) {
513                 Log.d(TAG, "setting locale for user " + userInfo.id + " to " + info.userLocales);
514             }
515             Settings.System.putStringForUser(mContext.getContentResolver(),
516                     Settings.System.SYSTEM_LOCALES, info.userLocales, userInfo.id);
517         }
518 
519         return new Pair<>(userInfo, null);
520     }
521 
522     @VisibleForTesting
unlockSystemUser()523     void unlockSystemUser() {
524         Log.i(TAG, "unlocking system user");
525         IActivityManager am = ActivityManager.getService();
526 
527         TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
528         t.traceBegin("UnlockSystemUser");
529         try {
530             // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
531             // update the state and USER_SYSTEM unlock happens twice.
532             t.traceBegin("am.startUser");
533             boolean started = am.startUserInBackground(UserHandle.USER_SYSTEM);
534             t.traceEnd();
535             if (!started) {
536                 Log.w(TAG, "could not restart system user in foreground; trying unlock instead");
537                 t.traceBegin("am.unlockUser");
538                 boolean unlocked = am.unlockUser(UserHandle.USER_SYSTEM, /* token= */ null,
539                         /* secret= */ null, /* listener= */ null);
540                 t.traceEnd();
541                 if (!unlocked) {
542                     Log.w(TAG, "could not unlock system user neither");
543                     return;
544                 }
545             }
546         } catch (RemoteException e) {
547             // should not happen for local call.
548             Log.wtf("RemoteException from AMS", e);
549         } finally {
550             t.traceEnd();
551         }
552     }
553 
554     @VisibleForTesting
startForegroundUser(@serIdInt int userId)555     boolean startForegroundUser(@UserIdInt int userId) {
556         if (UserHelper.isHeadlessSystemUser(userId)) {
557             // System User doesn't associate with real person, can not be switched to.
558             return false;
559         }
560         try {
561             return ActivityManager.getService().startUserInForegroundWithListener(userId, null);
562         } catch (RemoteException e) {
563             Log.w(TAG, "failed to start user " + userId, e);
564             return false;
565         }
566     }
567 
notifyListener(@ullable UserInfo initialUser)568     private void notifyListener(@Nullable UserInfo initialUser) {
569         if (DBG) Log.d(TAG, "notifyListener(): " + initialUser);
570         mListener.accept(initialUser);
571     }
572 
573     /**
574      * Dumps it state.
575      */
dump(@onNull PrintWriter writer)576     public void dump(@NonNull PrintWriter writer) {
577         writer.println("InitialUserSetter");
578         String indent = "  ";
579         writer.printf("%smNewUserName: %s\n", indent, mNewUserName);
580         writer.printf("%smNewGuestName: %s\n", indent, mNewGuestName);
581     }
582 }
583