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