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.N; 7 import static android.os.Build.VERSION_CODES.N_MR1; 8 9 import android.Manifest.permission; 10 import android.content.Context; 11 import android.content.pm.PackageManager; 12 import android.content.pm.UserInfo; 13 import android.os.Bundle; 14 import android.os.IUserManager; 15 import android.os.Process; 16 import android.os.UserHandle; 17 import android.os.UserManager; 18 19 import com.google.common.collect.BiMap; 20 import com.google.common.collect.HashBiMap; 21 import com.google.common.collect.ImmutableList; 22 23 import org.robolectric.annotation.Implementation; 24 import org.robolectric.annotation.Implements; 25 26 import java.util.Collections; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 31 /** 32 * Robolectric implementation of {@link android.os.UserManager}. 33 */ 34 @Implements(value = UserManager.class, minSdk = JELLY_BEAN_MR1) 35 public class ShadowUserManager { 36 37 private boolean userUnlocked = true; 38 private boolean managedProfile = false; 39 private boolean isDemoUser = false; 40 private boolean isAdminUser = false; 41 private Map<UserHandle, Bundle> userRestrictions = new HashMap<>(); 42 private BiMap<UserHandle, Long> userProfiles = HashBiMap.create(); 43 private Map<String, Bundle> applicationRestrictions = new HashMap<>(); 44 private int nextUserSerial = 0; 45 private Map<UserHandle, UserState> userState = new HashMap<>(); 46 private Context context; 47 private boolean enforcePermissions; 48 49 @Implementation 50 public void __constructor__(Context context, IUserManager service) { 51 this.context = context; 52 } 53 54 public ShadowUserManager() { 55 addUserProfile(Process.myUserHandle()); 56 } 57 58 public void enforcePermissionChecks(boolean enforcePermissions) { 59 this.enforcePermissions = enforcePermissions; 60 } 61 62 /** 63 * Compared to real Android, there is no check that the package name matches the application 64 * package name and the method returns instantly. 65 */ 66 @Implementation(minSdk = JELLY_BEAN_MR2) 67 public Bundle getApplicationRestrictions(String packageName) { 68 Bundle bundle = applicationRestrictions.get(packageName); 69 return bundle != null ? bundle : new Bundle(); 70 } 71 72 /** 73 * Setter for {@link #getApplicationRestrictions(String)} 74 */ 75 public void setApplicationRestrictions(String packageName, Bundle restrictions) { 76 applicationRestrictions.put(packageName, restrictions); 77 } 78 79 /** 80 * Adds a profile associated for the user that the calling process is running on. 81 */ 82 public void addUserProfile(UserHandle userHandle) { 83 setSerialNumberForUser(userHandle, nextUserSerial++); 84 } 85 86 @Implementation(minSdk = LOLLIPOP) 87 public List<UserHandle> getUserProfiles(){ 88 return ImmutableList.copyOf(userProfiles.keySet()); 89 } 90 91 @Implementation(minSdk = LOLLIPOP) 92 protected List<UserInfo> getProfiles(int userHandle) { 93 return Collections.emptyList(); 94 } 95 96 @Implementation(minSdk = LOLLIPOP) 97 protected UserInfo getProfileParent(int userHandle) { 98 return null; 99 } 100 101 @Implementation(minSdk = N) 102 public boolean isUserUnlocked() { 103 return userUnlocked; 104 } 105 106 /** 107 * Setter for {@link UserManager#isUserUnlocked()} 108 */ 109 public void setUserUnlocked(boolean userUnlocked) { 110 this.userUnlocked = userUnlocked; 111 } 112 113 @Implementation(minSdk = LOLLIPOP) 114 public boolean isManagedProfile() { 115 if (enforcePermissions && !hasManageUsersPermission()) { 116 throw new SecurityException( 117 "You need MANAGE_USERS permission to: check if specified user a " + 118 "managed profile outside your profile group"); 119 } 120 return managedProfile; 121 } 122 123 /** 124 * Setter for {@link UserManager#isManagedProfile()} 125 */ 126 public void setManagedProfile(boolean managedProfile) { 127 this.managedProfile = managedProfile; 128 } 129 130 @Implementation(minSdk = LOLLIPOP) 131 public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { 132 Bundle bundle = userRestrictions.get(userHandle); 133 return bundle != null && bundle.getBoolean(restrictionKey); 134 } 135 136 public void setUserRestriction(UserHandle userHandle, String restrictionKey, boolean value) { 137 Bundle bundle = getUserRestrictionsForUser(userHandle); 138 bundle.putBoolean(restrictionKey, value); 139 } 140 141 /** 142 * Removes all user restrictions set of a user identified by {@code userHandle}. 143 */ 144 public void clearUserRestrictions(UserHandle userHandle) { 145 if (userRestrictions.containsKey(userHandle)) { 146 userRestrictions.remove(userHandle); 147 } 148 } 149 150 @Implementation(minSdk = JELLY_BEAN_MR2) 151 public Bundle getUserRestrictions(UserHandle userHandle) { 152 return getUserRestrictionsForUser(userHandle); 153 } 154 155 private Bundle getUserRestrictionsForUser(UserHandle userHandle) { 156 Bundle bundle = userRestrictions.get(userHandle); 157 if (bundle == null) { 158 bundle = new Bundle(); 159 userRestrictions.put(userHandle, bundle); 160 } 161 return bundle; 162 } 163 164 @Implementation 165 public long getSerialNumberForUser(UserHandle userHandle) { 166 Long result = userProfiles.get(userHandle); 167 return result == null ? -1L : result; 168 } 169 170 /** 171 * @deprecated prefer {@link #addUserProfile()} to ensure consistency of profiles known to 172 * UserManager. Furthermore, calling this method for the current user, i.e: 173 * {@link Process.myUserHandle()} is no longer necessary as this user is always known to 174 * UserManager and has a preassigned serial number. 175 */ 176 @Deprecated 177 public void setSerialNumberForUser(UserHandle userHandle, long serialNumber) { 178 userProfiles.put(userHandle, serialNumber); 179 } 180 181 @Implementation 182 public UserHandle getUserForSerialNumber(long serialNumber) { 183 return userProfiles.inverse().get(serialNumber); 184 } 185 186 private boolean hasManageUsersPermission() { 187 return context.getPackageManager().checkPermission(permission.MANAGE_USERS, context.getPackageName()) == PackageManager.PERMISSION_GRANTED; 188 } 189 190 private void checkPermissions() { 191 // TODO Ensure permisions 192 // throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS 193 // permission " 194 // + "to: check " + name);throw new SecurityException(); 195 } 196 197 @Implementation(minSdk = N_MR1) 198 public boolean isDemoUser() { 199 return isDemoUser; 200 } 201 202 /** 203 * Sets that the current user is a demo user; controls the return value of 204 * {@link UserManager#isDemoUser}. 205 */ 206 public void setIsDemoUser(boolean isDemoUser) { 207 this.isDemoUser = isDemoUser; 208 } 209 210 @Implementation(minSdk = N_MR1) 211 public boolean isAdminUser() { 212 return isAdminUser; 213 } 214 215 /** 216 * Sets that the current user is an admin user; controls the return value of 217 * {@link UserManager#isAdminUser}. 218 */ 219 public void setIsAdminUser(boolean isAdminUser) { 220 this.isAdminUser = isAdminUser; 221 } 222 223 @Implementation 224 public boolean isUserRunning(UserHandle handle) { 225 checkPermissions(); 226 UserState state = userState.get(handle); 227 228 if (state == UserState.STATE_RUNNING_LOCKED 229 || state == UserState.STATE_RUNNING_UNLOCKED 230 || state == UserState.STATE_RUNNING_UNLOCKING) { 231 return true; 232 } else { 233 return false; 234 } 235 } 236 237 @Implementation 238 public boolean isUserRunningOrStopping(UserHandle handle) { 239 checkPermissions(); 240 UserState state = userState.get(handle); 241 242 if (state == UserState.STATE_RUNNING_LOCKED 243 || state == UserState.STATE_RUNNING_UNLOCKED 244 || state == UserState.STATE_RUNNING_UNLOCKING 245 || state == UserState.STATE_STOPPING) { 246 return true; 247 } else { 248 return false; 249 } 250 } 251 252 /** 253 * Describes the current state of the user. State can be set using 254 * {@link UserManager#setUserState()} 255 */ 256 public enum UserState { 257 // User is first coming up. 258 STATE_BOOTING, 259 // User is in the locked state. 260 STATE_RUNNING_LOCKED, 261 // User is in the unlocking state. 262 STATE_RUNNING_UNLOCKING, 263 // User is in the running state. 264 STATE_RUNNING_UNLOCKED, 265 // User is in the initial process of being stopped. 266 STATE_STOPPING, 267 // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN. 268 STATE_SHUTDOWN 269 } 270 271 /** 272 * Sets the current state for a given user, see {@link #isUserRunning()} 273 * and {@link #isUserRunningOrStopping()} 274 */ 275 public void setUserState(UserHandle handle, UserState state) { 276 userState.put(handle, state); 277 } 278 279 @Implementation 280 public List<UserInfo> getUsers() { 281 // Implement this - return empty list to avoid NPE from call to getUserCount() 282 return ImmutableList.of(); 283 } 284 } 285