• 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 org.robolectric.shadow.api.Shadow.directlyOn;
10 
11 import android.Manifest.permission;
12 import android.content.Context;
13 import android.content.pm.PackageManager;
14 import android.content.pm.UserInfo;
15 import android.os.Bundle;
16 import android.os.IUserManager;
17 import android.os.Process;
18 import android.os.UserHandle;
19 import android.os.UserManager;
20 import com.google.common.collect.BiMap;
21 import com.google.common.collect.HashBiMap;
22 import com.google.common.collect.ImmutableList;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import org.robolectric.annotation.Implementation;
28 import org.robolectric.annotation.Implements;
29 import org.robolectric.annotation.RealObject;
30 import org.robolectric.annotation.Resetter;
31 
32 /**
33  * Robolectric implementation of {@link android.os.UserManager}.
34  */
35 @Implements(value = UserManager.class, minSdk = JELLY_BEAN_MR1)
36 public class ShadowUserManager {
37 
38   /**
39    * The default user ID user for secondary user testing, when the ID is not otherwise specified.
40    */
41   public static final int DEFAULT_SECONDARY_USER_ID = 10;
42 
43   public static final int FLAG_PRIMARY = UserInfo.FLAG_PRIMARY;
44   public static final int FLAG_ADMIN = UserInfo.FLAG_ADMIN;
45   public static final int FLAG_GUEST = UserInfo.FLAG_GUEST;
46   public static final int FLAG_RESTRICTED = UserInfo.FLAG_RESTRICTED;
47 
48   private static Map<Integer, Integer> userPidMap = new HashMap<>();
49 
50   @RealObject private UserManager realObject;
51 
52   private boolean userUnlocked = true;
53   private boolean managedProfile = false;
54   private boolean isSystemUser = true;
55   private Map<Integer, Bundle> userRestrictions = new HashMap<>();
56   private BiMap<UserHandle, Long> userProfiles = HashBiMap.create();
57   private Map<String, Bundle> applicationRestrictions = new HashMap<>();
58   private long nextUserSerial = 0;
59   private Map<Integer, UserState> userState = new HashMap<>();
60   private Map<Integer, UserInfo> userInfoMap = new HashMap<>();
61 
62   private Context context;
63   private boolean enforcePermissions;
64   private boolean canSwitchUser = false;
65 
66   @Implementation
__constructor__(Context context, IUserManager service)67   protected void __constructor__(Context context, IUserManager service) {
68     this.context = context;
69     addUser(UserHandle.USER_SYSTEM, "system_user", UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN);
70   }
71 
72   /**
73    * Compared to real Android, there is no check that the package name matches the application
74    * package name and the method returns instantly.
75    *
76    * @see #setApplicationRestrictions(String, Bundle)
77    */
78   @Implementation(minSdk = JELLY_BEAN_MR2)
getApplicationRestrictions(String packageName)79   protected Bundle getApplicationRestrictions(String packageName) {
80     Bundle bundle = applicationRestrictions.get(packageName);
81     return bundle != null ? bundle : new Bundle();
82   }
83 
84   /**
85    * Sets the value returned by {@link UserManager#getApplicationRestrictions(String)}.
86    */
setApplicationRestrictions(String packageName, Bundle restrictions)87   public void setApplicationRestrictions(String packageName, Bundle restrictions) {
88     applicationRestrictions.put(packageName, restrictions);
89   }
90 
91   /**
92    * Adds a profile associated for the user that the calling process is running on.
93    *
94    * The user is assigned an arbitrary unique serial number.
95    *
96    * @return the user's serial number
97    */
addUserProfile(UserHandle userHandle)98   public long addUserProfile(UserHandle userHandle) {
99     long serialNumber = nextUserSerial++;
100     userProfiles.put(userHandle, serialNumber);
101     return serialNumber;
102   }
103 
104   @Implementation(minSdk = LOLLIPOP)
getUserProfiles()105   protected List<UserHandle> getUserProfiles() {
106     return ImmutableList.copyOf(userProfiles.keySet());
107   }
108 
109   @Implementation(minSdk = N)
isUserUnlocked()110   protected boolean isUserUnlocked() {
111     return userUnlocked;
112   }
113 
114   /**
115    * Setter for {@link UserManager#isUserUnlocked()}
116    */
setUserUnlocked(boolean userUnlocked)117   public void setUserUnlocked(boolean userUnlocked) {
118     this.userUnlocked = userUnlocked;
119   }
120 
121   /**
122    * If permissions are enforced (see {@link #enforcePermissionChecks(boolean)}) and the application
123    * doesn't have the {@link android.Manifest.permission#MANAGE_USERS} permission, throws a
124    * {@link SecurityManager} exception.
125    *
126    * @return `false` by default, or the value specified via {@link #setManagedProfile(boolean)}
127    * @see #enforcePermissionChecks(boolean)
128    * @see #setManagedProfile(boolean)
129    */
130   @Implementation(minSdk = LOLLIPOP)
isManagedProfile()131   protected boolean isManagedProfile() {
132     if (enforcePermissions && !hasManageUsersPermission()) {
133       throw new SecurityException(
134           "You need MANAGE_USERS permission to: check if specified user a " +
135               "managed profile outside your profile group");
136     }
137     return managedProfile;
138   }
139 
enforcePermissionChecks(boolean enforcePermissions)140   public void enforcePermissionChecks(boolean enforcePermissions) {
141     this.enforcePermissions = enforcePermissions;
142   }
143 
144   /**
145    * Setter for {@link UserManager#isManagedProfile()}.
146    */
setManagedProfile(boolean managedProfile)147   public void setManagedProfile(boolean managedProfile) {
148     this.managedProfile = managedProfile;
149   }
150 
151   @Implementation(minSdk = LOLLIPOP)
hasUserRestriction(String restrictionKey, UserHandle userHandle)152   protected boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
153     Bundle bundle = userRestrictions.get(userHandle.getIdentifier());
154     return bundle != null && bundle.getBoolean(restrictionKey);
155   }
156 
setUserRestriction(UserHandle userHandle, String restrictionKey, boolean value)157   public void setUserRestriction(UserHandle userHandle, String restrictionKey, boolean value) {
158     Bundle bundle = getUserRestrictionsForUser(userHandle);
159     bundle.putBoolean(restrictionKey, value);
160   }
161 
162   /**
163    * Removes all user restrictions set of a user identified by {@code userHandle}.
164    */
clearUserRestrictions(UserHandle userHandle)165   public void clearUserRestrictions(UserHandle userHandle) {
166     userRestrictions.remove(userHandle.getIdentifier());
167   }
168 
169   @Implementation(minSdk = JELLY_BEAN_MR2)
getUserRestrictions(UserHandle userHandle)170   protected Bundle getUserRestrictions(UserHandle userHandle) {
171     return new Bundle(getUserRestrictionsForUser(userHandle));
172   }
173 
getUserRestrictionsForUser(UserHandle userHandle)174   private Bundle getUserRestrictionsForUser(UserHandle userHandle) {
175     Bundle bundle = userRestrictions.get(userHandle.getIdentifier());
176     if (bundle == null) {
177       bundle = new Bundle();
178       userRestrictions.put(userHandle.getIdentifier(), bundle);
179     }
180     return bundle;
181   }
182 
183   /**
184    * @see #addUserProfile(UserHandle)
185    */
186   @Implementation
getSerialNumberForUser(UserHandle userHandle)187   protected long getSerialNumberForUser(UserHandle userHandle) {
188     Long result = userProfiles.get(userHandle);
189     return result == null ? -1L : result;
190   }
191 
192   /**
193    * @deprecated prefer {@link #addUserProfile(UserHandle)} to ensure consistency of profiles known
194    * to the {@link UserManager}. Furthermore, calling this method for the current user, i.e: {@link
195    * Process#myUserHandle()} is no longer necessary as this user is always known to UserManager and
196    * has a preassigned serial number.
197    */
198   @Deprecated
setSerialNumberForUser(UserHandle userHandle, long serialNumber)199   public void setSerialNumberForUser(UserHandle userHandle, long serialNumber) {
200     userProfiles.put(userHandle, serialNumber);
201   }
202 
203   /**
204    * @see #addUserProfile(UserHandle)
205    */
206   @Implementation
getUserForSerialNumber(long serialNumber)207   protected UserHandle getUserForSerialNumber(long serialNumber) {
208     return userProfiles.inverse().get(serialNumber);
209   }
210 
hasManageUsersPermission()211   private boolean hasManageUsersPermission() {
212     return context.getPackageManager().checkPermission(permission.MANAGE_USERS, context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
213   }
214 
checkPermissions()215   private void checkPermissions() {
216     // TODO Ensure permisions
217     //              throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS
218     // permission "
219     //                + "to: check " + name);throw new SecurityException();
220   }
221 
222   /**
223    * @return `false` by default, or the value specified via {@link #setIsDemoUser(boolean)}
224    */
225   @Implementation(minSdk = N_MR1)
isDemoUser()226   protected boolean isDemoUser() {
227     return getUserInfo(UserHandle.myUserId()).isDemo();
228   }
229 
230   /**
231    * Sets that the current user is a demo user; controls the return value of {@link
232    * UserManager#isDemoUser()}.
233    *
234    * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a demo user
235    *     instead of changing default user flags.
236    */
237   @Deprecated
setIsDemoUser(boolean isDemoUser)238   public void setIsDemoUser(boolean isDemoUser) {
239     UserInfo userInfo = getUserInfo(UserHandle.myUserId());
240     if (isDemoUser) {
241       userInfo.flags |= UserInfo.FLAG_DEMO;
242     } else {
243       userInfo.flags &= ~UserInfo.FLAG_DEMO;
244     }
245   }
246 
247   /**
248    * @return {@code false} by default, or the value specified via {@link #setIsAdminUser(boolean)}
249    */
250   @Implementation(minSdk = N_MR1)
isAdminUser()251   public boolean isAdminUser() {
252     return getUserInfo(UserHandle.myUserId()).isAdmin();
253   }
254 
255   /**
256    * Sets that the current user is an admin user; controls the return value of
257    * {@link UserManager#isAdminUser}.
258    */
setIsAdminUser(boolean isAdminUser)259   public void setIsAdminUser(boolean isAdminUser) {
260     UserInfo userInfo = getUserInfo(UserHandle.myUserId());
261     if (isAdminUser) {
262       userInfo.flags |= UserInfo.FLAG_ADMIN;
263     } else {
264       userInfo.flags &= ~UserInfo.FLAG_ADMIN;
265     }
266   }
267 
268   /**
269    * @return 'true' by default, or the value specified via {@link #setIsSystemUser(boolean)}
270    */
271   @Implementation(minSdk = M)
isSystemUser()272   protected boolean isSystemUser() {
273     if (isSystemUser == false) {
274       return false;
275     } else {
276       return directlyOn(realObject, UserManager.class, "isSystemUser");
277     }
278   }
279 
280   /**
281    * Sets that the current user is the system user; controls the return value of {@link
282    * UserManager#isSystemUser()}.
283    *
284    * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a system user
285    *     instead of changing default user flags.
286    */
287   @Deprecated
setIsSystemUser(boolean isSystemUser)288   public void setIsSystemUser(boolean isSystemUser) {
289     this.isSystemUser = isSystemUser;
290   }
291 
292   /**
293    * Sets that the current user is the primary user; controls the return value of {@link
294    * UserManager#isPrimaryUser()}.
295    *
296    * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a primary user
297    *     instead of changing default user flags.
298    */
299   @Deprecated
setIsPrimaryUser(boolean isPrimaryUser)300   public void setIsPrimaryUser(boolean isPrimaryUser) {
301     UserInfo userInfo = getUserInfo(UserHandle.myUserId());
302     if (isPrimaryUser) {
303       userInfo.flags |= UserInfo.FLAG_PRIMARY;
304     } else {
305       userInfo.flags &= ~UserInfo.FLAG_PRIMARY;
306     }
307   }
308 
309   /**
310    * @return 'false' by default, or the value specified via {@link #setIsLinkedUser(boolean)}
311    */
312   @Implementation(minSdk = JELLY_BEAN_MR2)
isLinkedUser()313   protected boolean isLinkedUser() {
314     return getUserInfo(UserHandle.myUserId()).isRestricted();
315   }
316 
317   /**
318    * Sets that the current user is the linked user; controls the return value of {@link
319    * UserManager#isLinkedUser()}.
320    *
321    * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a linked user
322    *     instead of changing default user flags.
323    */
324   @Deprecated
setIsLinkedUser(boolean isLinkedUser)325   public void setIsLinkedUser(boolean isLinkedUser) {
326     UserInfo userInfo = getUserInfo(UserHandle.myUserId());
327     if (isLinkedUser) {
328       userInfo.flags |= UserInfo.FLAG_RESTRICTED;
329     } else {
330       userInfo.flags &= ~UserInfo.FLAG_RESTRICTED;
331     }
332   }
333 
334   /**
335    * Sets that the current user is the guest user; controls the return value of {@link
336    * UserManager#isGuestUser()}.
337    *
338    * @deprecated Use {@link ShadowUserManager#addUser(int, String, int)} to create a guest user
339    *     instead of changing default user flags.
340    */
341   @Deprecated
setIsGuestUser(boolean isGuestUser)342   public void setIsGuestUser(boolean isGuestUser) {
343     UserInfo userInfo = getUserInfo(UserHandle.myUserId());
344     if (isGuestUser) {
345       userInfo.flags |= UserInfo.FLAG_GUEST;
346     } else {
347       userInfo.flags &= ~UserInfo.FLAG_GUEST;
348     }
349   }
350 
351   /**
352    * @see #setUserState(UserHandle, UserState)
353    */
354   @Implementation
isUserRunning(UserHandle handle)355   protected boolean isUserRunning(UserHandle handle) {
356     checkPermissions();
357     UserState state = userState.get(handle.getIdentifier());
358 
359     if (state == UserState.STATE_RUNNING_LOCKED
360         || state == UserState.STATE_RUNNING_UNLOCKED
361         || state == UserState.STATE_RUNNING_UNLOCKING) {
362       return true;
363     } else {
364       return false;
365     }
366   }
367 
368   /**
369    * @see #setUserState(UserHandle, UserState)
370    */
371   @Implementation
isUserRunningOrStopping(UserHandle handle)372   protected boolean isUserRunningOrStopping(UserHandle handle) {
373     checkPermissions();
374     UserState state = userState.get(handle.getIdentifier());
375 
376     if (state == UserState.STATE_RUNNING_LOCKED
377         || state == UserState.STATE_RUNNING_UNLOCKED
378         || state == UserState.STATE_RUNNING_UNLOCKING
379         || state == UserState.STATE_STOPPING) {
380       return true;
381     } else {
382       return false;
383     }
384   }
385 
386   /**
387    * Describes the current state of the user. State can be set using
388    * {@link #setUserState(UserHandle, UserState)}.
389    */
390   public enum UserState {
391     // User is first coming up.
392     STATE_BOOTING,
393     // User is in the locked state.
394     STATE_RUNNING_LOCKED,
395     // User is in the unlocking state.
396     STATE_RUNNING_UNLOCKING,
397     // User is in the running state.
398     STATE_RUNNING_UNLOCKED,
399     // User is in the initial process of being stopped.
400     STATE_STOPPING,
401     // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN.
402     STATE_SHUTDOWN
403   }
404 
405   /**
406    * Sets the current state for a given user, see {@link UserManager#isUserRunning(UserHandle)}
407    * and {@link UserManager#isUserRunningOrStopping(UserHandle)}
408    */
setUserState(UserHandle handle, UserState state)409   public void setUserState(UserHandle handle, UserState state) {
410     userState.put(handle.getIdentifier(), state);
411   }
412 
413   @Implementation
getUsers()414   protected List<UserInfo> getUsers() {
415     return new ArrayList<UserInfo>(userInfoMap.values());
416   }
417 
418   @Implementation
getUserInfo(int userHandle)419   protected UserInfo getUserInfo(int userHandle) {
420     return userInfoMap.get(userHandle);
421   }
422 
423   /**
424    * Returns {@code true} by default, or the value specified via {@link #setCanSwitchUser(boolean)}.
425    */
426   @Implementation(minSdk = N)
canSwitchUsers()427   protected boolean canSwitchUsers() {
428     return canSwitchUser;
429   }
430 
431   /**
432    * Sets whether switching users is allowed or not; controls the return value of {@link
433    * UserManager#canSwitchUser()}
434    */
setCanSwitchUser(boolean canSwitchUser)435   public void setCanSwitchUser(boolean canSwitchUser) {
436     this.canSwitchUser = canSwitchUser;
437   }
438 
439   @Implementation(minSdk = JELLY_BEAN_MR1)
removeUser(int userHandle)440   protected boolean removeUser(int userHandle) {
441     userInfoMap.remove(userHandle);
442     return true;
443   }
444 
445   /**
446    * Switches the current user to {@code userHandle}.
447    *
448    * @param userId the integer handle of the user, where 0 is the primary user.
449    */
switchUser(int userId)450   public void switchUser(int userId) {
451     if (!userInfoMap.containsKey(userId)) {
452       throw new UnsupportedOperationException("Must add user before switching to it");
453     }
454 
455     ShadowProcess.setUid(userPidMap.get(userId));
456   }
457 
458   /**
459    * Creates a user with the specified name, userId and flags.
460    *
461    * @param id the unique id of user
462    * @param name name of the user
463    * @param flags 16 bits for user type. See {@link UserInfo#flags}
464    */
addUser(int id, String name, int flags)465   public void addUser(int id, String name, int flags) {
466     UserHandle userHandle =
467         id == UserHandle.USER_SYSTEM ? Process.myUserHandle() : new UserHandle(id);
468     addUserProfile(userHandle);
469     setSerialNumberForUser(userHandle, (long) id);
470     userInfoMap.put(id, new UserInfo(id, name, flags));
471     userPidMap.put(
472         id,
473         id == UserHandle.USER_SYSTEM
474             ? Process.myUid()
475             : id * UserHandle.PER_USER_RANGE + ShadowProcess.getRandomApplicationUid());
476   }
477 
478   @Resetter
reset()479   public static void reset() {
480     if (userPidMap != null && userPidMap.isEmpty() == false) {
481       ShadowProcess.setUid(userPidMap.get(UserHandle.USER_SYSTEM));
482 
483       userPidMap.clear();
484       userPidMap.put(UserHandle.USER_SYSTEM, Process.myUid());
485     }
486   }
487 }
488