1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.testutils.shadow; 18 19 import static android.os.Build.VERSION_CODES.LOLLIPOP; 20 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; 21 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE; 22 23 import android.annotation.UserIdInt; 24 import android.content.pm.UserInfo; 25 import android.content.pm.UserProperties; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.os.UserManager; 29 import android.os.UserManager.EnforcingUser; 30 31 import com.google.android.collect.Maps; 32 import com.google.common.collect.BiMap; 33 import com.google.common.collect.HashBiMap; 34 import com.google.common.collect.ImmutableList; 35 36 import org.robolectric.RuntimeEnvironment; 37 import org.robolectric.annotation.Implementation; 38 import org.robolectric.annotation.Implements; 39 import org.robolectric.annotation.Resetter; 40 import org.robolectric.shadow.api.Shadow; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Collections; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Set; 50 51 @Implements(value = UserManager.class) 52 public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { 53 54 private static boolean sIsSupportsMultipleUsers; 55 private static boolean sIsMultipleAdminEnabled = false; 56 57 private static final int PRIMARY_USER_ID = 0; 58 59 private final List<String> mBaseRestrictions = new ArrayList<>(); 60 private final Map<String, List<EnforcingUser>> mRestrictionSources = new HashMap<>(); 61 private final List<UserInfo> mUserProfileInfos = new ArrayList<>(); 62 private final Set<Integer> mManagedProfiles = new HashSet<>(); 63 private final Map<Integer, Integer> mProfileToParent = new HashMap<>(); 64 private final Map<Integer, UserInfo> mUserInfoMap = new HashMap<>(); 65 private final Set<String> mEnabledTypes = new HashSet<>(); 66 private BiMap<UserHandle, Long> mUserProfiles = HashBiMap.create(); 67 private boolean mIsQuietModeEnabled = false; 68 private int[] mProfileIdsForUser = new int[0]; 69 private boolean mUserSwitchEnabled; 70 private Bundle mDefaultGuestUserRestriction = new Bundle(); 71 private boolean mIsGuestUser = false; 72 private long mNextUserSerial = 0; 73 74 private @UserManager.UserSwitchabilityResult int mSwitchabilityStatus = 75 UserManager.SWITCHABILITY_STATUS_OK; 76 private final Map<Integer, Integer> mSameProfileGroupIds = Maps.newHashMap(); 77 addProfile(UserInfo userInfo)78 public void addProfile(UserInfo userInfo) { 79 mUserProfileInfos.add(userInfo); 80 mUserInfoMap.put(userInfo.id, userInfo); 81 } 82 83 @Resetter reset()84 public static void reset() { 85 sIsSupportsMultipleUsers = false; 86 } 87 88 /** 89 * Creates a user with the specified name, userId and flags. 90 * 91 * @param id the unique id of user 92 * @param name name of the user 93 * @param flags 16 bits for user type. See {@link UserInfo#flags} 94 */ addUser(int id, String name, int flags)95 @Override public UserHandle addUser(int id, String name, int flags) { 96 UserHandle userHandle = super.addUser(id, name, flags); 97 mUserInfoMap.put(id, new UserInfo(id, name, flags)); 98 return userHandle; 99 } 100 101 /** Add a profile to be returned by {@link #getProfiles(int)}. */ addProfile( int userHandle, int profileUserHandle, String profileName, int profileFlags)102 public void addProfile( 103 int userHandle, int profileUserHandle, String profileName, int profileFlags) { 104 UserInfo profileUserInfo = new UserInfo(profileUserHandle, profileName, profileFlags); 105 addProfile(profileUserInfo); 106 mProfileToParent.put(profileUserHandle, userHandle); 107 if (profileFlags == UserInfo.FLAG_MANAGED_PROFILE) { 108 setManagedProfiles(new HashSet<>(Arrays.asList(profileUserHandle))); 109 } 110 } 111 112 @Implementation getProfiles(@serIdInt int userHandle)113 protected List<UserInfo> getProfiles(@UserIdInt int userHandle) { 114 return mUserProfileInfos; 115 } 116 117 /** 118 * If this profile has been added using {@link #addProfile}, return its parent. 119 */ 120 @Implementation(minSdk = LOLLIPOP) getProfileParent(int userHandle)121 protected UserInfo getProfileParent(int userHandle) { 122 if (!mProfileToParent.containsKey(userHandle)) { 123 return null; 124 } 125 return mUserInfoMap.get(mProfileToParent.get(userHandle)); 126 } 127 128 @Implementation getProfileIds(@serIdInt int userHandle, boolean enabledOnly)129 protected int[] getProfileIds(@UserIdInt int userHandle, boolean enabledOnly) { 130 int[] ids = new int[mUserProfileInfos.size()]; 131 for (int i = 0; i < mUserProfileInfos.size(); i++) { 132 ids[i] = mUserProfileInfos.get(i).id; 133 } 134 return ids; 135 } 136 137 @Implementation getCredentialOwnerProfile(@serIdInt int userHandle)138 protected int getCredentialOwnerProfile(@UserIdInt int userHandle) { 139 return userHandle; 140 } 141 142 @Implementation hasBaseUserRestriction(String restrictionKey, UserHandle userHandle)143 protected boolean hasBaseUserRestriction(String restrictionKey, UserHandle userHandle) { 144 return mBaseRestrictions.contains(restrictionKey); 145 } 146 addBaseUserRestriction(String restriction)147 public void addBaseUserRestriction(String restriction) { 148 mBaseRestrictions.add(restriction); 149 } 150 151 @Implementation getDefaultGuestRestrictions()152 protected Bundle getDefaultGuestRestrictions() { 153 return mDefaultGuestUserRestriction; 154 } 155 156 @Implementation setDefaultGuestRestrictions(Bundle restrictions)157 protected void setDefaultGuestRestrictions(Bundle restrictions) { 158 mDefaultGuestUserRestriction = restrictions; 159 } 160 161 @Implementation getUserProperties(UserHandle userHandle)162 protected UserProperties getUserProperties(UserHandle userHandle) { 163 return new UserProperties.Builder().build(); 164 } 165 166 addGuestUserRestriction(String restriction)167 public void addGuestUserRestriction(String restriction) { 168 mDefaultGuestUserRestriction.putBoolean(restriction, true); 169 } 170 hasGuestUserRestriction(String restriction, boolean expectedValue)171 public boolean hasGuestUserRestriction(String restriction, boolean expectedValue) { 172 return mDefaultGuestUserRestriction.containsKey(restriction) 173 && mDefaultGuestUserRestriction.getBoolean(restriction) == expectedValue; 174 } 175 176 177 @Implementation hasUserRestriction(String restrictionKey)178 protected boolean hasUserRestriction(String restrictionKey) { 179 return hasUserRestriction(restrictionKey, UserHandle.of(UserHandle.myUserId())); 180 } 181 getShadow()182 public static ShadowUserManager getShadow() { 183 return Shadow.extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); 184 } 185 186 @Implementation getUserRestrictionSources( String restrictionKey, UserHandle userHandle)187 protected List<EnforcingUser> getUserRestrictionSources( 188 String restrictionKey, UserHandle userHandle) { 189 // Return empty list when there is no enforcing user, otherwise might trigger 190 // NullPointer Exception in RestrictedLockUtils.checkIfRestrictionEnforced. 191 List<EnforcingUser> enforcingUsers = 192 mRestrictionSources.get(restrictionKey + userHandle.getIdentifier()); 193 return enforcingUsers == null ? Collections.emptyList() : enforcingUsers; 194 } 195 setUserRestrictionSources( String restrictionKey, UserHandle userHandle, List<EnforcingUser> enforcers)196 public void setUserRestrictionSources( 197 String restrictionKey, UserHandle userHandle, List<EnforcingUser> enforcers) { 198 mRestrictionSources.put(restrictionKey + userHandle.getIdentifier(), enforcers); 199 } 200 201 @Implementation isQuietModeEnabled(UserHandle userHandle)202 protected boolean isQuietModeEnabled(UserHandle userHandle) { 203 return mIsQuietModeEnabled; 204 } 205 setQuietModeEnabled(boolean enabled)206 public void setQuietModeEnabled(boolean enabled) { 207 mIsQuietModeEnabled = enabled; 208 } 209 210 @Implementation getProfileIdsWithDisabled(@serIdInt int userId)211 protected int[] getProfileIdsWithDisabled(@UserIdInt int userId) { 212 return mProfileIdsForUser; 213 } 214 setProfileIdsWithDisabled(int[] profileIds)215 public void setProfileIdsWithDisabled(int[] profileIds) { 216 mProfileIdsForUser = profileIds; 217 } 218 219 @Implementation isUserSwitcherEnabled()220 protected boolean isUserSwitcherEnabled() { 221 return mUserSwitchEnabled; 222 } 223 224 @Implementation isManagedProfile(int userId)225 protected boolean isManagedProfile(int userId) { 226 return mManagedProfiles.contains(userId); 227 } 228 setManagedProfiles(Set<Integer> profileIds)229 public void setManagedProfiles(Set<Integer> profileIds) { 230 mManagedProfiles.clear(); 231 mManagedProfiles.addAll(profileIds); 232 profileIds.forEach(id -> { 233 addProfile(new UserInfo(id, "managed" + id, null, 0, USER_TYPE_PROFILE_MANAGED)); 234 mProfileToParent.put(id, PRIMARY_USER_ID); 235 }); 236 addPrimaryUser(); 237 } 238 setPrivateProfile(int id, String name, int flags)239 public void setPrivateProfile(int id, String name, int flags) { 240 addProfile(new UserInfo(id, name, null, flags, USER_TYPE_PROFILE_PRIVATE)); 241 mProfileToParent.put(id, PRIMARY_USER_ID); 242 addPrimaryUser(); 243 } 244 addPrimaryUser()245 private void addPrimaryUser() { 246 if (mUserInfoMap.get(PRIMARY_USER_ID) == null) { 247 addProfile(getPrimaryUser()); 248 } 249 } 250 setUserSwitcherEnabled(boolean userSwitchEnabled)251 public void setUserSwitcherEnabled(boolean userSwitchEnabled) { 252 mUserSwitchEnabled = userSwitchEnabled; 253 } 254 255 @Implementation supportsMultipleUsers()256 protected static boolean supportsMultipleUsers() { 257 return sIsSupportsMultipleUsers; 258 } 259 260 @Implementation isSameProfileGroup(@serIdInt int userId, int otherUserId)261 protected boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) { 262 return mSameProfileGroupIds.containsKey(userId) 263 && mSameProfileGroupIds.get(userId) == otherUserId 264 || mSameProfileGroupIds.containsKey(otherUserId) 265 && mSameProfileGroupIds.get(otherUserId) == userId; 266 } 267 getSameProfileGroupIds()268 public Map<Integer, Integer> getSameProfileGroupIds() { 269 return mSameProfileGroupIds; 270 } 271 setSupportsMultipleUsers(boolean supports)272 public void setSupportsMultipleUsers(boolean supports) { 273 sIsSupportsMultipleUsers = supports; 274 } 275 276 @Implementation getUserInfo(@serIdInt int userId)277 protected UserInfo getUserInfo(@UserIdInt int userId) { 278 return mUserProfileInfos.stream() 279 .filter(userInfo -> userInfo.id == userId) 280 .findFirst() 281 .orElse(super.getUserInfo(userId)); 282 } 283 284 @Implementation getUserSwitchability()285 protected @UserManager.UserSwitchabilityResult int getUserSwitchability() { 286 return mSwitchabilityStatus; 287 } 288 setSwitchabilityStatus(@serManager.UserSwitchabilityResult int newStatus)289 public void setSwitchabilityStatus(@UserManager.UserSwitchabilityResult int newStatus) { 290 mSwitchabilityStatus = newStatus; 291 } 292 293 @Implementation isUserTypeEnabled(String userType)294 protected boolean isUserTypeEnabled(String userType) { 295 return mEnabledTypes.contains(userType); 296 } 297 setUserTypeEnabled(String type, boolean enabled)298 public void setUserTypeEnabled(String type, boolean enabled) { 299 if (enabled) { 300 mEnabledTypes.add(type); 301 } else { 302 mEnabledTypes.remove(type); 303 } 304 } 305 306 @Implementation getPrimaryUser()307 protected UserInfo getPrimaryUser() { 308 return new UserInfo(PRIMARY_USER_ID, null, null, 309 UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); 310 } 311 312 @Implementation getMainUser()313 protected UserHandle getMainUser() { 314 return UserHandle.of(PRIMARY_USER_ID); 315 } 316 setUserEphemeral(@serIdInt int userId, boolean enableEphemeral)317 protected boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { 318 UserInfo userInfo = mUserProfileInfos.stream() 319 .filter(user -> user.id == userId) 320 .findFirst() 321 .orElse(super.getUserInfo(userId)); 322 323 boolean isSuccess = false; 324 boolean isEphemeralUser = 325 (userInfo.flags & UserInfo.FLAG_EPHEMERAL) != 0; 326 boolean isEphemeralOnCreateUser = 327 (userInfo.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) 328 != 0; 329 // when user is created in ephemeral mode via FLAG_EPHEMERAL 330 // its state cannot be changed. 331 // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state 332 if (!isEphemeralOnCreateUser) { 333 isSuccess = true; 334 if (isEphemeralUser != enableEphemeral) { 335 if (enableEphemeral) { 336 userInfo.flags |= UserInfo.FLAG_EPHEMERAL; 337 } else { 338 userInfo.flags &= ~UserInfo.FLAG_EPHEMERAL; 339 } 340 } 341 } 342 return isSuccess; 343 } 344 345 @Implementation setUserAdmin(@serIdInt int userId)346 protected void setUserAdmin(@UserIdInt int userId) { 347 for (int i = 0; i < mUserProfileInfos.size(); i++) { 348 mUserProfileInfos.get(i).flags |= UserInfo.FLAG_ADMIN; 349 } 350 } 351 352 /** 353 * Sets that the current user is an admin user; controls the return value of 354 * {@link UserManager#isAdminUser}. 355 */ setIsAdminUser(boolean isAdminUser)356 public void setIsAdminUser(boolean isAdminUser) { 357 UserInfo userInfo = getUserInfo(UserHandle.myUserId()); 358 if (isAdminUser) { 359 userInfo.flags |= UserInfo.FLAG_ADMIN; 360 } else { 361 userInfo.flags &= ~UserInfo.FLAG_ADMIN; 362 } 363 } 364 365 @Implementation isGuestUser()366 protected boolean isGuestUser() { 367 return mIsGuestUser; 368 } 369 setGuestUser(boolean isGuestUser)370 public void setGuestUser(boolean isGuestUser) { 371 mIsGuestUser = isGuestUser; 372 } 373 setIsMultipleAdminEnabled(boolean enableMultipleAdmin)374 public static void setIsMultipleAdminEnabled(boolean enableMultipleAdmin) { 375 sIsMultipleAdminEnabled = enableMultipleAdmin; 376 } 377 378 /** 379 * Adds a profile associated for the user that the calling process is running on. 380 * 381 * <p>The user is assigned an arbitrary unique serial number. 382 * 383 * @return the user's serial number 384 * @deprecated use either addUser() or addProfile() 385 */ 386 @Deprecated addUserProfile(UserHandle userHandle)387 public long addUserProfile(UserHandle userHandle) { 388 long serialNumber = mNextUserSerial++; 389 mUserProfiles.put(userHandle, serialNumber); 390 return serialNumber; 391 } 392 getUserProfiles()393 protected List<UserHandle> getUserProfiles() { 394 return ImmutableList.copyOf(mUserProfiles.keySet()); 395 } 396 } 397