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