1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; 4 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; 5 import static android.os.Build.VERSION_CODES.LOLLIPOP; 6 import static android.os.Build.VERSION_CODES.M; 7 import static android.os.Build.VERSION_CODES.N; 8 import static android.os.Build.VERSION_CODES.N_MR1; 9 import static android.os.Build.VERSION_CODES.R; 10 import static android.os.Build.VERSION_CODES.TIRAMISU; 11 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 12 13 import static org.robolectric.shadow.api.Shadow.directlyOn; 14 15 import android.Manifest.permission; 16 import android.annotation.NonNull; 17 import android.annotation.UserIdInt; 18 import android.content.Context; 19 import android.content.pm.PackageManager; 20 import android.content.pm.UserInfo; 21 import android.content.pm.UserProperties; 22 import android.os.Bundle; 23 import android.os.IUserManager; 24 import android.os.Process; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 28 import com.google.common.collect.BiMap; 29 import com.google.common.collect.HashBiMap; 30 import com.google.common.collect.ImmutableList; 31 32 import org.robolectric.annotation.Implementation; 33 import org.robolectric.annotation.Implements; 34 import org.robolectric.annotation.RealObject; 35 import org.robolectric.annotation.Resetter; 36 import org.robolectric.util.ReflectionHelpers.ClassParameter; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.stream.Collectors; 44 45 /** 46 * Robolectric implementation of {@link android.os.UserManager}. 47 */ 48 @Implements(value = UserManager.class, minSdk = JELLY_BEAN_MR1) 49 public class ShadowUserManager { 50 51 /** 52 * The default user ID user for secondary user testing, when the ID is not otherwise specified. 53 */ 54 public static final int DEFAULT_SECONDARY_USER_ID = 10; 55 56 public static final int FLAG_PRIMARY = UserInfo.FLAG_PRIMARY; 57 public static final int FLAG_ADMIN = UserInfo.FLAG_ADMIN; 58 public static final int FLAG_GUEST = UserInfo.FLAG_GUEST; 59 public static final int FLAG_RESTRICTED = UserInfo.FLAG_RESTRICTED; 60 public static final int FLAG_MANAGED_PROFILE = UserInfo.FLAG_MANAGED_PROFILE; 61 62 private static Map<Integer, Integer> userPidMap = new HashMap<>(); 63 64 @RealObject private UserManager realObject; 65 66 private boolean userUnlocked = true; 67 private boolean managedProfile = false; 68 private boolean isSystemUser = true; 69 private static boolean isHeadlessSystemUserMode = false; 70 private static boolean isMultipleAdminEnabled = false; 71 72 73 private Map<Integer, Bundle> userRestrictions = new HashMap<>(); 74 private BiMap<UserHandle, Long> userProfiles = HashBiMap.create(); 75 private Map<String, Bundle> applicationRestrictions = new HashMap<>(); 76 private long nextUserSerial = 0; 77 private Map<Integer, UserState> userState = new HashMap<>(); 78 private Map<Integer, UserInfo> userInfoMap = new HashMap<>(); 79 private Map<Integer, List<UserInfo>> profiles = new HashMap<>(); 80 private Map<Integer, Integer> profileToParent = new HashMap<>(); 81 private Map<Integer, UserProperties> mUserPropertiesMap = new HashMap<>(); 82 83 private Context context; 84 private boolean enforcePermissions; 85 private boolean canSwitchUser = false; 86 87 @Implementation __constructor__(Context context, IUserManager service)88 protected void __constructor__(Context context, IUserManager service) { 89 this.context = context; 90 addUser(UserHandle.USER_SYSTEM, "system_user", UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN); 91 } 92 93 /** 94 * Compared to real Android, there is no check that the package name matches the application 95 * package name and the method returns instantly. 96 * 97 * @see #setApplicationRestrictions(String, Bundle) 98 */ 99 @Implementation(minSdk = JELLY_BEAN_MR2) getApplicationRestrictions(String packageName)100 protected Bundle getApplicationRestrictions(String packageName) { 101 Bundle bundle = applicationRestrictions.get(packageName); 102 return bundle != null ? bundle : new Bundle(); 103 } 104 105 /** 106 * Sets the value returned by {@link UserManager#getApplicationRestrictions(String)}. 107 */ setApplicationRestrictions(String packageName, Bundle restrictions)108 public void setApplicationRestrictions(String packageName, Bundle restrictions) { 109 applicationRestrictions.put(packageName, restrictions); 110 } 111 112 /** 113 * Adds a profile associated for the user that the calling process is running on. 114 * 115 * The user is assigned an arbitrary unique serial number. 116 * 117 * @return the user's serial number 118 */ addUserProfile(UserHandle userHandle)119 public long addUserProfile(UserHandle userHandle) { 120 long serialNumber = nextUserSerial++; 121 userProfiles.put(userHandle, serialNumber); 122 return serialNumber; 123 } 124 125 @Implementation(minSdk = LOLLIPOP) getUserProfiles()126 protected List<UserHandle> getUserProfiles() { 127 return ImmutableList.copyOf(userProfiles.keySet()); 128 } 129 130 @Implementation getAllProfiles()131 public List<UserHandle> getAllProfiles() { 132 return getUserProfiles(); 133 } 134 135 /** 136 * If any profiles have been added using {@link #addProfile}, return those profiles. 137 * 138 * Otherwise follow real android behaviour. 139 */ 140 @Implementation(minSdk = LOLLIPOP) getProfiles(int userHandle)141 protected List<UserInfo> getProfiles(int userHandle) { 142 if (profiles.containsKey(userHandle)) { 143 return ImmutableList.copyOf(profiles.get(userHandle)); 144 } 145 146 if (profileToParent.containsKey(userHandle) 147 && profiles.containsKey(profileToParent.get(userHandle))) { 148 return ImmutableList.copyOf(profiles.get(profileToParent.get(userHandle))); 149 } 150 151 return directlyOn( 152 realObject, UserManager.class, "getProfiles", ClassParameter.from(int.class, userHandle)); 153 } 154 155 /** Add a profile to be returned by {@link #getProfiles(int)}.**/ addProfile( int userHandle, int profileUserHandle, String profileName, int profileFlags)156 public void addProfile( 157 int userHandle, int profileUserHandle, String profileName, int profileFlags) { 158 UserInfo userInfo = new UserInfo(profileUserHandle, profileName, profileFlags); 159 profiles.putIfAbsent(userHandle, new ArrayList<>()); 160 profiles.get(userHandle).add(userInfo); 161 userInfoMap.put(profileUserHandle, userInfo); 162 profileToParent.put(profileUserHandle, userHandle); 163 } 164 165 /** 166 * If this profile has been added using {@link #addProfile}, return its parent. 167 */ 168 @Implementation(minSdk = LOLLIPOP) getProfileParent(int userHandle)169 protected UserInfo getProfileParent(int userHandle) { 170 if (!profileToParent.containsKey(userHandle)) { 171 return null; 172 } 173 return userInfoMap.get(profileToParent.get(userHandle)); 174 } 175 176 @Implementation(minSdk = N) isUserUnlocked()177 protected boolean isUserUnlocked() { 178 return userUnlocked; 179 } 180 181 /** 182 * Setter for {@link UserManager#isUserUnlocked()} 183 */ setUserUnlocked(boolean userUnlocked)184 public void setUserUnlocked(boolean userUnlocked) { 185 this.userUnlocked = userUnlocked; 186 } 187 188 /** 189 * If permissions are enforced (see {@link #enforcePermissionChecks(boolean)}) and the application 190 * doesn't have the {@link android.Manifest.permission#MANAGE_USERS} permission, throws a 191 * {@link SecurityManager} exception. 192 * 193 * @return `false` by default, or the value specified via {@link #setManagedProfile(boolean)} 194 * @see #enforcePermissionChecks(boolean) 195 * @see #setManagedProfile(boolean) 196 */ 197 @Implementation(minSdk = LOLLIPOP) isManagedProfile()198 protected boolean isManagedProfile() { 199 if (enforcePermissions && !hasManageUsersPermission()) { 200 throw new SecurityException( 201 "You need MANAGE_USERS permission to: check if specified user a " + 202 "managed profile outside your profile group"); 203 } 204 return managedProfile; 205 } 206 207 /** 208 * If permissions are enforced (see {@link #enforcePermissionChecks(boolean)}) and the application 209 * doesn't have the {@link android.Manifest.permission#MANAGE_USERS} permission, throws a {@link 210 * SecurityManager} exception. 211 * 212 * @return true if the profile added has FLAG_MANAGED_PROFILE 213 * @see #enforcePermissionChecks(boolean) 214 * @see #addProfile(int, int, String, int) 215 * @see #addUser(int, String, int) 216 */ 217 @Implementation(minSdk = N) isManagedProfile(int userHandle)218 protected boolean isManagedProfile(int userHandle) { 219 if (enforcePermissions && !hasManageUsersPermission()) { 220 throw new SecurityException( 221 "You need MANAGE_USERS permission to: check if specified user a " 222 + "managed profile outside your profile group"); 223 } 224 UserInfo info = getUserInfo(userHandle); 225 return info != null && ((info.flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE); 226 } 227 228 // BEGIN-INTERNAL 229 @Implementation(minSdk = R) isProfile()230 protected boolean isProfile() { 231 return isManagedProfile(); 232 } 233 234 /** 235 * Compared to real Android, userId is not used, instead 236 * managedProfile determines if user has badge. 237 * 238 * @param userId ignored, uses managedProfile field 239 * @return true if managedProfile field is true 240 */ 241 @Implementation(minSdk = R) hasBadge(int userId)242 protected boolean hasBadge(int userId) { 243 return isProfile(); 244 } 245 // END-INTERNAL 246 enforcePermissionChecks(boolean enforcePermissions)247 public void enforcePermissionChecks(boolean enforcePermissions) { 248 this.enforcePermissions = enforcePermissions; 249 } 250 251 /** 252 * Setter for {@link UserManager#isManagedProfile()}. 253 */ setManagedProfile(boolean managedProfile)254 public void setManagedProfile(boolean managedProfile) { 255 this.managedProfile = managedProfile; 256 } 257 258 @Implementation(minSdk = LOLLIPOP) hasUserRestriction(String restrictionKey, UserHandle userHandle)259 protected boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { 260 Bundle bundle = userRestrictions.get(userHandle.getIdentifier()); 261 return bundle != null && bundle.getBoolean(restrictionKey); 262 } 263 264 // BEGIN-INTERNAL 265 @Implementation(minSdk = R) hasUserRestrictionForUser(String restrictionKey, UserHandle userHandle)266 protected boolean hasUserRestrictionForUser(String restrictionKey, UserHandle userHandle) { 267 return hasUserRestriction(restrictionKey, userHandle); 268 } 269 270 @Implementation(minSdk = TIRAMISU) hasUserRestrictionForUser(String restrictionKey, int userId)271 protected boolean hasUserRestrictionForUser(String restrictionKey, int userId) { 272 Bundle bundle = userRestrictions.get(userId); 273 return bundle != null && bundle.getBoolean(restrictionKey); 274 } 275 // END-INTERNAL 276 setUserRestriction(UserHandle userHandle, String restrictionKey, boolean value)277 public void setUserRestriction(UserHandle userHandle, String restrictionKey, boolean value) { 278 Bundle bundle = getUserRestrictionsForUser(userHandle); 279 bundle.putBoolean(restrictionKey, value); 280 } 281 282 @Implementation(minSdk = JELLY_BEAN_MR2) setUserRestriction(String key, boolean value)283 protected void setUserRestriction(String key, boolean value) { 284 Bundle bundle = getUserRestrictionsForUser(Process.myUserHandle()); 285 bundle.putBoolean(key, value); 286 } 287 288 /** 289 * Removes all user restrictions set of a user identified by {@code userHandle}. 290 */ clearUserRestrictions(UserHandle userHandle)291 public void clearUserRestrictions(UserHandle userHandle) { 292 userRestrictions.remove(userHandle.getIdentifier()); 293 } 294 295 @Implementation(minSdk = JELLY_BEAN_MR2) getUserRestrictions(UserHandle userHandle)296 protected Bundle getUserRestrictions(UserHandle userHandle) { 297 return new Bundle(getUserRestrictionsForUser(userHandle)); 298 } 299 getUserRestrictionsForUser(UserHandle userHandle)300 private Bundle getUserRestrictionsForUser(UserHandle userHandle) { 301 Bundle bundle = userRestrictions.get(userHandle.getIdentifier()); 302 if (bundle == null) { 303 bundle = new Bundle(); 304 userRestrictions.put(userHandle.getIdentifier(), bundle); 305 } 306 return bundle; 307 } 308 309 /** 310 * @see #addUserProfile(UserHandle) 311 */ 312 @Implementation getSerialNumberForUser(UserHandle userHandle)313 protected long getSerialNumberForUser(UserHandle userHandle) { 314 Long result = userProfiles.get(userHandle); 315 return result == null ? -1L : result; 316 } 317 318 /** 319 * @deprecated prefer {@link #addUserProfile(UserHandle)} to ensure consistency of profiles known 320 * to the {@link UserManager}. Furthermore, calling this method for the current user, i.e: {@link 321 * Process#myUserHandle()} is no longer necessary as this user is always known to UserManager and 322 * has a preassigned serial number. 323 */ 324 @Deprecated setSerialNumberForUser(UserHandle userHandle, long serialNumber)325 public void setSerialNumberForUser(UserHandle userHandle, long serialNumber) { 326 userProfiles.put(userHandle, serialNumber); 327 } 328 329 /** 330 * @see #addUserProfile(UserHandle) 331 */ 332 @Implementation getUserForSerialNumber(long serialNumber)333 protected UserHandle getUserForSerialNumber(long serialNumber) { 334 return userProfiles.inverse().get(serialNumber); 335 } 336 337 /** 338 * @see #addProfile(int, int, String, int) 339 * @see #addUser(int, String, int) 340 */ 341 @Implementation getUserSerialNumber(@serIdInt int userHandle)342 protected int getUserSerialNumber(@UserIdInt int userHandle) { 343 Long result = userProfiles.get(UserHandle.of(userHandle)); 344 return result != null ? result.intValue() : -1; 345 } 346 hasManageUsersPermission()347 private boolean hasManageUsersPermission() { 348 return context.getPackageManager().checkPermission(permission.MANAGE_USERS, context.getPackageName()) == PackageManager.PERMISSION_GRANTED; 349 } 350 setIsMultipleAdminEnabled(boolean enableMultipleAdmin)351 public static void setIsMultipleAdminEnabled(boolean enableMultipleAdmin) { 352 isMultipleAdminEnabled = enableMultipleAdmin; 353 } 354 355 @Implementation(minSdk = UPSIDE_DOWN_CAKE) isMultipleAdminEnabled()356 protected static boolean isMultipleAdminEnabled() { 357 return isMultipleAdminEnabled; 358 } 359 setIsHeadlessSystemUserMode(boolean isHSUM)360 public static void setIsHeadlessSystemUserMode(boolean isHSUM) { 361 isHeadlessSystemUserMode = isHSUM; 362 } 363 364 @Implementation isHeadlessSystemUserMode()365 protected static boolean isHeadlessSystemUserMode() { 366 return isHeadlessSystemUserMode; 367 } 368 checkPermissions()369 private void checkPermissions() { 370 // TODO Ensure permisions 371 // throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS 372 // permission " 373 // + "to: check " + name);throw new SecurityException(); 374 } 375 376 /** 377 * @return `false` by default, or the value specified via {@link #setIsDemoUser(boolean)} 378 */ 379 @Implementation(minSdk = N_MR1) isDemoUser()380 protected boolean isDemoUser() { 381 return getUserInfo(UserHandle.myUserId()).isDemo(); 382 } 383 384 /** 385 * Sets that the current user is a demo user; controls the return value of {@link 386 * UserManager#isDemoUser()}. 387 * 388 * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a demo user 389 * instead of changing default user flags. 390 */ 391 @Deprecated setIsDemoUser(boolean isDemoUser)392 public void setIsDemoUser(boolean isDemoUser) { 393 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 394 if (isDemoUser) { 395 userInfo.flags |= UserInfo.FLAG_DEMO; 396 } else { 397 userInfo.flags &= ~UserInfo.FLAG_DEMO; 398 } 399 } 400 401 /** 402 * @return {@code false} by default, or the value specified via {@link #setIsAdminUser(boolean)} 403 */ 404 @Implementation(minSdk = N_MR1) isAdminUser()405 public boolean isAdminUser() { 406 return getUserInfo(UserHandle.myUserId()).isAdmin(); 407 } 408 409 /** 410 * Sets that the current user is an admin user; controls the return value of 411 * {@link UserManager#isAdminUser}. 412 */ setIsAdminUser(boolean isAdminUser)413 public void setIsAdminUser(boolean isAdminUser) { 414 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 415 if (isAdminUser) { 416 userInfo.flags |= UserInfo.FLAG_ADMIN; 417 } else { 418 userInfo.flags &= ~UserInfo.FLAG_ADMIN; 419 } 420 } 421 422 /** 423 * @return 'true' by default, or the value specified via {@link #setIsSystemUser(boolean)} 424 */ 425 @Implementation(minSdk = M) isSystemUser()426 protected boolean isSystemUser() { 427 if (isSystemUser == false) { 428 return false; 429 } else { 430 return directlyOn(realObject, UserManager.class, "isSystemUser"); 431 } 432 } 433 434 /** 435 * Sets that the current user is the system user; controls the return value of {@link 436 * UserManager#isSystemUser()}. 437 * 438 * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a system user 439 * instead of changing default user flags. 440 */ 441 @Deprecated setIsSystemUser(boolean isSystemUser)442 public void setIsSystemUser(boolean isSystemUser) { 443 this.isSystemUser = isSystemUser; 444 } 445 446 /** 447 * Sets that the current user is the primary user; controls the return value of {@link 448 * UserManager#isPrimaryUser()}. 449 * 450 * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a primary user 451 * instead of changing default user flags. 452 */ 453 @Deprecated setIsPrimaryUser(boolean isPrimaryUser)454 public void setIsPrimaryUser(boolean isPrimaryUser) { 455 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 456 if (isPrimaryUser) { 457 userInfo.flags |= UserInfo.FLAG_PRIMARY; 458 } else { 459 userInfo.flags &= ~UserInfo.FLAG_PRIMARY; 460 } 461 } 462 463 /** 464 * @return 'false' by default, or the value specified via {@link #setIsLinkedUser(boolean)} 465 */ 466 @Implementation(minSdk = JELLY_BEAN_MR2) isLinkedUser()467 protected boolean isLinkedUser() { 468 return getUserInfo(UserHandle.myUserId()).isRestricted(); 469 } 470 471 /** 472 * Sets that the current user is the linked user; controls the return value of {@link 473 * UserManager#isLinkedUser()}. 474 * 475 * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a linked user 476 * instead of changing default user flags. 477 */ 478 @Deprecated setIsLinkedUser(boolean isLinkedUser)479 public void setIsLinkedUser(boolean isLinkedUser) { 480 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 481 if (isLinkedUser) { 482 userInfo.flags |= UserInfo.FLAG_RESTRICTED; 483 } else { 484 userInfo.flags &= ~UserInfo.FLAG_RESTRICTED; 485 } 486 } 487 488 /** 489 * Sets that the current user is the guest user; controls the return value of {@link 490 * UserManager#isGuestUser()}. 491 * 492 * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a guest user 493 * instead of changing default user flags. 494 */ 495 @Deprecated setIsGuestUser(boolean isGuestUser)496 public void setIsGuestUser(boolean isGuestUser) { 497 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 498 if (isGuestUser) { 499 userInfo.flags |= UserInfo.FLAG_GUEST; 500 } else { 501 userInfo.flags &= ~UserInfo.FLAG_GUEST; 502 } 503 } 504 505 /** 506 * @see #setUserState(UserHandle, UserState) 507 */ 508 @Implementation isUserRunning(UserHandle handle)509 protected boolean isUserRunning(UserHandle handle) { 510 checkPermissions(); 511 UserState state = userState.get(handle.getIdentifier()); 512 513 if (state == UserState.STATE_RUNNING_LOCKED 514 || state == UserState.STATE_RUNNING_UNLOCKED 515 || state == UserState.STATE_RUNNING_UNLOCKING) { 516 return true; 517 } else { 518 return false; 519 } 520 } 521 522 /** 523 * @see #setUserState(UserHandle, UserState) 524 */ 525 @Implementation isUserRunningOrStopping(UserHandle handle)526 protected boolean isUserRunningOrStopping(UserHandle handle) { 527 checkPermissions(); 528 UserState state = userState.get(handle.getIdentifier()); 529 530 if (state == UserState.STATE_RUNNING_LOCKED 531 || state == UserState.STATE_RUNNING_UNLOCKED 532 || state == UserState.STATE_RUNNING_UNLOCKING 533 || state == UserState.STATE_STOPPING) { 534 return true; 535 } else { 536 return false; 537 } 538 } 539 540 /** 541 * Describes the current state of the user. State can be set using 542 * {@link #setUserState(UserHandle, UserState)}. 543 */ 544 public enum UserState { 545 // User is first coming up. 546 STATE_BOOTING, 547 // User is in the locked state. 548 STATE_RUNNING_LOCKED, 549 // User is in the unlocking state. 550 STATE_RUNNING_UNLOCKING, 551 // User is in the running state. 552 STATE_RUNNING_UNLOCKED, 553 // User is in the initial process of being stopped. 554 STATE_STOPPING, 555 // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN. 556 STATE_SHUTDOWN 557 } 558 559 /** 560 * Sets the current state for a given user, see {@link UserManager#isUserRunning(UserHandle)} 561 * and {@link UserManager#isUserRunningOrStopping(UserHandle)} 562 */ setUserState(UserHandle handle, UserState state)563 public void setUserState(UserHandle handle, UserState state) { 564 userState.put(handle.getIdentifier(), state); 565 } 566 567 @Implementation getUsers()568 protected List<UserInfo> getUsers() { 569 return new ArrayList<UserInfo>(userInfoMap.values()); 570 } 571 572 @Implementation getUserInfo(int userHandle)573 protected UserInfo getUserInfo(int userHandle) { 574 return userInfoMap.get(userHandle); 575 } 576 577 /** 578 * Returns {@code true} by default, or the value specified via {@link #setCanSwitchUser(boolean)}. 579 */ 580 @Implementation(minSdk = N) canSwitchUsers()581 protected boolean canSwitchUsers() { 582 return canSwitchUser; 583 } 584 585 /** 586 * Sets whether switching users is allowed or not; controls the return value of {@link 587 * UserManager#canSwitchUser()} 588 */ setCanSwitchUser(boolean canSwitchUser)589 public void setCanSwitchUser(boolean canSwitchUser) { 590 this.canSwitchUser = canSwitchUser; 591 } 592 593 @Implementation(minSdk = JELLY_BEAN_MR1) removeUser(int userHandle)594 protected boolean removeUser(int userHandle) { 595 userInfoMap.remove(userHandle); 596 return true; 597 } 598 599 // BEGIN-INTERNAL 600 @Implementation(minSdk = R) createProfileForUserEvenWhenDisallowed(String name, @NonNull String userType, @UserInfo.UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages)601 protected UserInfo createProfileForUserEvenWhenDisallowed(String name, 602 @NonNull String userType, @UserInfo.UserInfoFlag int flags, @UserIdInt int userId, 603 String[] disallowedPackages) throws UserManager.UserOperationException { 604 List<UserInfo> userIdProfiles = profiles.computeIfAbsent(userId, ignored -> new ArrayList<>()); 605 int profileUserId = userIdProfiles.isEmpty() ? 10 : findMaxProfileId(userIdProfiles) + 1; 606 UserInfo profileUserInfo = new UserInfo(profileUserId, name, flags); 607 userIdProfiles.add(profileUserInfo); 608 profileToParent.put(profileUserId, userId); 609 addUserProfile(UserHandle.of(profileUserId)); 610 return profileUserInfo; 611 } 612 613 /** Assumes the given list of profile infos is non-empty. */ findMaxProfileId(List<UserInfo> userIdProfiles)614 private int findMaxProfileId(List<UserInfo> userIdProfiles) { 615 return Collections.max( 616 userIdProfiles.stream() 617 .map(userInfo -> userInfo.id) 618 .collect(Collectors.toList())); 619 } 620 // END-INTERNAL 621 622 /** 623 * Switches the current user to {@code userHandle}. 624 * 625 * @param userId the integer handle of the user, where 0 is the primary user. 626 */ switchUser(int userId)627 public void switchUser(int userId) { 628 if (!userInfoMap.containsKey(userId)) { 629 throw new UnsupportedOperationException("Must add user before switching to it"); 630 } 631 632 ShadowProcess.setUid(userPidMap.get(userId)); 633 } 634 635 /** 636 * Creates a user with the specified name, userId and flags. 637 * 638 * @param id the unique id of user 639 * @param name name of the user 640 * @param flags 16 bits for user type. See {@link UserInfo#flags} 641 */ addUser(int id, String name, int flags)642 public void addUser(int id, String name, int flags) { 643 UserHandle userHandle = 644 id == UserHandle.USER_SYSTEM ? Process.myUserHandle() : new UserHandle(id); 645 addUserProfile(userHandle); 646 setSerialNumberForUser(userHandle, (long) id); 647 profiles.putIfAbsent(id, new ArrayList<>()); 648 userInfoMap.put(id, new UserInfo(id, name, flags)); 649 userPidMap.put( 650 id, 651 id == UserHandle.USER_SYSTEM 652 ? Process.myUid() 653 : id * UserHandle.PER_USER_RANGE + ShadowProcess.getRandomApplicationUid()); 654 } 655 656 @Resetter reset()657 public static void reset() { 658 if (userPidMap != null && userPidMap.isEmpty() == false) { 659 ShadowProcess.setUid(userPidMap.get(UserHandle.USER_SYSTEM)); 660 661 userPidMap.clear(); 662 userPidMap.put(UserHandle.USER_SYSTEM, Process.myUid()); 663 } 664 isMultipleAdminEnabled = false; 665 } 666 setupUserProperty(int userId, int showInSettings)667 public void setupUserProperty(int userId, int showInSettings) { 668 UserProperties userProperties = new UserProperties(new UserProperties.Builder() 669 .setShowInSettings(showInSettings).build()); 670 mUserPropertiesMap.putIfAbsent(userId, userProperties); 671 } 672 673 @Implementation(minSdk = UPSIDE_DOWN_CAKE) getUserProperties(UserHandle user)674 protected UserProperties getUserProperties(UserHandle user) { 675 return mUserPropertiesMap.getOrDefault(user.getIdentifier(), 676 new UserProperties(new UserProperties.Builder().build())); 677 } 678 } 679