Home | History | Annotate | Download | only in shadows
      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
     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)
     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    */
     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    */
     98   public long addUserProfile(UserHandle userHandle) {
     99     long serialNumber = nextUserSerial++;
    100     userProfiles.put(userHandle, serialNumber);
    101     return serialNumber;
    102   }
    103 
    104   @Implementation(minSdk = LOLLIPOP)
    105   protected List<UserHandle> getUserProfiles() {
    106     return ImmutableList.copyOf(userProfiles.keySet());
    107   }
    108 
    109   @Implementation(minSdk = N)
    110   protected boolean isUserUnlocked() {
    111     return userUnlocked;
    112   }
    113 
    114   /**
    115    * Setter for {@link UserManager#isUserUnlocked()}
    116    */
    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)
    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 
    140   public void enforcePermissionChecks(boolean enforcePermissions) {
    141     this.enforcePermissions = enforcePermissions;
    142   }
    143 
    144   /**
    145    * Setter for {@link UserManager#isManagedProfile()}.
    146    */
    147   public void setManagedProfile(boolean managedProfile) {
    148     this.managedProfile = managedProfile;
    149   }
    150 
    151   @Implementation(minSdk = LOLLIPOP)
    152   protected boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
    153     Bundle bundle = userRestrictions.get(userHandle.getIdentifier());
    154     return bundle != null && bundle.getBoolean(restrictionKey);
    155   }
    156 
    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    */
    165   public void clearUserRestrictions(UserHandle userHandle) {
    166     userRestrictions.remove(userHandle.getIdentifier());
    167   }
    168 
    169   @Implementation(minSdk = JELLY_BEAN_MR2)
    170   protected Bundle getUserRestrictions(UserHandle userHandle) {
    171     return new Bundle(getUserRestrictionsForUser(userHandle));
    172   }
    173 
    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
    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
    199   public void setSerialNumberForUser(UserHandle userHandle, long serialNumber) {
    200     userProfiles.put(userHandle, serialNumber);
    201   }
    202 
    203   /**
    204    * @see #addUserProfile(UserHandle)
    205    */
    206   @Implementation
    207   protected UserHandle getUserForSerialNumber(long serialNumber) {
    208     return userProfiles.inverse().get(serialNumber);
    209   }
    210 
    211   private boolean hasManageUsersPermission() {
    212     return context.getPackageManager().checkPermission(permission.MANAGE_USERS, context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
    213   }
    214 
    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)
    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
    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)
    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    */
    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)
    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
    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
    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)
    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
    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
    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
    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
    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    */
    409   public void setUserState(UserHandle handle, UserState state) {
    410     userState.put(handle.getIdentifier(), state);
    411   }
    412 
    413   @Implementation
    414   protected List<UserInfo> getUsers() {
    415     return new ArrayList<UserInfo>(userInfoMap.values());
    416   }
    417 
    418   @Implementation
    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)
    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    */
    435   public void setCanSwitchUser(boolean canSwitchUser) {
    436     this.canSwitchUser = canSwitchUser;
    437   }
    438 
    439   @Implementation(minSdk = JELLY_BEAN_MR1)
    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    */
    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    */
    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
    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