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