• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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