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.O; 10 // BEGIN-INTERNAL 11 import static android.os.Build.VERSION_CODES.Q; 12 import static android.os.Build.VERSION_CODES.R; 13 // END-INTERNAL 14 import static org.robolectric.shadow.api.Shadow.invokeConstructor; 15 import static org.robolectric.util.ReflectionHelpers.ClassParameter.from; 16 17 import android.annotation.Nullable; 18 import android.annotation.SuppressLint; 19 import android.app.ApplicationPackageManager; 20 import android.app.KeyguardManager; 21 import android.app.admin.DeviceAdminReceiver; 22 import android.app.admin.DevicePolicyManager; 23 import android.app.admin.IDevicePolicyManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.os.Build; 31 import android.os.Build.VERSION_CODES; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.Process; 35 import android.text.TextUtils; 36 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.Collections; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Set; 46 import org.robolectric.RuntimeEnvironment; 47 import org.robolectric.annotation.Implementation; 48 import org.robolectric.annotation.Implements; 49 import org.robolectric.annotation.RealObject; 50 import org.robolectric.shadow.api.Shadow; 51 52 @Implements(DevicePolicyManager.class) 53 @SuppressLint("NewApi") 54 public class ShadowDevicePolicyManager { 55 /** 56 * @see 57 * https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setOrganizationColor(android.content.ComponentName, 58 * int) 59 */ 60 private static final int DEFAULT_ORGANIZATION_COLOR = 0xFF008080; // teal 61 62 private ComponentName deviceOwner; 63 private ComponentName profileOwner; 64 private List<ComponentName> deviceAdmins = new ArrayList<>(); 65 private Map<Integer, String> profileOwnerNamesMap = new HashMap<>(); 66 private List<String> permittedAccessibilityServices = new ArrayList<>(); 67 private List<String> permittedInputMethods = new ArrayList<>(); 68 private Map<String, Bundle> applicationRestrictionsMap = new HashMap<>(); 69 private CharSequence organizationName; 70 private int organizationColor; 71 private boolean isAutoTimeRequired; 72 private int keyguardDisabledFeatures; 73 private String lastSetPassword; 74 private int requiredPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 75 private int userProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED; 76 77 private int passwordMinimumLength; 78 private int passwordMinimumLetters = 1; 79 private int passwordMinimumLowerCase; 80 private int passwordMinimumUpperCase; 81 private int passwordMinimumNonLetter; 82 private int passwordMinimumNumeric = 1; 83 private int passwordMinimumSymbols = 1; 84 85 private int wipeCalled; 86 private int storageEncryptionStatus; 87 private final Set<String> wasHiddenPackages = new HashSet<>(); 88 private final Set<String> accountTypesWithManagementDisabled = new HashSet<>(); 89 private final Set<String> systemAppsEnabled = new HashSet<>(); 90 private final Set<String> uninstallBlockedPackages = new HashSet<>(); 91 private final Set<String> suspendedPackages = new HashSet<>(); 92 private final Map<PackageAndPermission, Boolean> appPermissionGrantedMap = new HashMap<>(); 93 private final Map<PackageAndPermission, Integer> appPermissionGrantStateMap = new HashMap<>(); 94 private final Map<ComponentName, byte[]> passwordResetTokens = new HashMap<>(); 95 private final Set<ComponentName> componentsWithActivatedTokens = new HashSet<>(); 96 private Set<String> defaultCrossProfilePackages = new HashSet<>(); 97 private Collection<String> packagesToFailForSetApplicationHidden = Collections.emptySet(); 98 private Set<String> crossProfileCalendarPackages = Collections.emptySet(); 99 private Context context; 100 private ApplicationPackageManager applicationPackageManager; 101 102 private @RealObject DevicePolicyManager realObject; 103 104 private static class PackageAndPermission { 105 PackageAndPermission(String packageName, String permission)106 public PackageAndPermission(String packageName, String permission) { 107 this.packageName = packageName; 108 this.permission = permission; 109 } 110 111 private String packageName; 112 private String permission; 113 114 @Override equals(Object o)115 public boolean equals(Object o) { 116 if (!(o instanceof PackageAndPermission)) { 117 return false; 118 } 119 PackageAndPermission other = (PackageAndPermission) o; 120 return packageName.equals(other.packageName) && permission.equals(other.permission); 121 } 122 123 @Override hashCode()124 public int hashCode() { 125 int result = packageName.hashCode(); 126 result = 31 * result + permission.hashCode(); 127 return result; 128 } 129 } 130 131 @Implementation(maxSdk = M) __constructor__(Context context, Handler handler)132 protected void __constructor__(Context context, Handler handler) { 133 init(context); 134 invokeConstructor( 135 DevicePolicyManager.class, 136 realObject, 137 from(Context.class, context), 138 from(Handler.class, handler)); 139 } 140 141 @Implementation(minSdk = N, maxSdk = N_MR1) __constructor__(Context context, boolean parentInstance)142 protected void __constructor__(Context context, boolean parentInstance) { 143 init(context); 144 } 145 146 @Implementation(minSdk = O) __constructor__(Context context, IDevicePolicyManager service)147 protected void __constructor__(Context context, IDevicePolicyManager service) { 148 init(context); 149 } 150 init(Context context)151 private void init(Context context) { 152 this.context = context; 153 this.applicationPackageManager = 154 (ApplicationPackageManager) context.getApplicationContext().getPackageManager(); 155 organizationColor = DEFAULT_ORGANIZATION_COLOR; 156 storageEncryptionStatus = DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; 157 } 158 159 @Implementation(minSdk = JELLY_BEAN_MR2) isDeviceOwnerApp(String packageName)160 protected boolean isDeviceOwnerApp(String packageName) { 161 return deviceOwner != null && deviceOwner.getPackageName().equals(packageName); 162 } 163 164 @Implementation(minSdk = LOLLIPOP) isProfileOwnerApp(String packageName)165 protected boolean isProfileOwnerApp(String packageName) { 166 return profileOwner != null && profileOwner.getPackageName().equals(packageName); 167 } 168 169 @Implementation isAdminActive(ComponentName who)170 protected boolean isAdminActive(ComponentName who) { 171 return who != null && deviceAdmins.contains(who); 172 } 173 174 @Implementation getActiveAdmins()175 protected List<ComponentName> getActiveAdmins() { 176 return deviceAdmins; 177 } 178 179 @Implementation(minSdk = LOLLIPOP) addUserRestriction(ComponentName admin, String key)180 protected void addUserRestriction(ComponentName admin, String key) { 181 enforceActiveAdmin(admin); 182 getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, true); 183 } 184 185 @Implementation(minSdk = LOLLIPOP) clearUserRestriction(ComponentName admin, String key)186 protected void clearUserRestriction(ComponentName admin, String key) { 187 enforceActiveAdmin(admin); 188 getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, false); 189 } 190 191 @Implementation(minSdk = LOLLIPOP) setApplicationHidden(ComponentName admin, String packageName, boolean hidden)192 protected boolean setApplicationHidden(ComponentName admin, String packageName, boolean hidden) { 193 enforceActiveAdmin(admin); 194 if (packagesToFailForSetApplicationHidden.contains(packageName)) { 195 return false; 196 } 197 if (hidden) { 198 wasHiddenPackages.add(packageName); 199 } 200 return applicationPackageManager.setApplicationHiddenSettingAsUser( 201 packageName, hidden, Process.myUserHandle()); 202 } 203 204 /** 205 * Set package names for witch {@link DevicePolicyManager#setApplicationHidden} should fail. 206 * 207 * @param packagesToFail collection of package names or {@code null} to clear the packages. 208 */ failSetApplicationHiddenFor(Collection<String> packagesToFail)209 public void failSetApplicationHiddenFor(Collection<String> packagesToFail) { 210 if (packagesToFail == null) { 211 packagesToFail = Collections.emptySet(); 212 } 213 packagesToFailForSetApplicationHidden = packagesToFail; 214 } 215 216 @Implementation(minSdk = LOLLIPOP) isApplicationHidden(ComponentName admin, String packageName)217 protected boolean isApplicationHidden(ComponentName admin, String packageName) { 218 enforceActiveAdmin(admin); 219 return applicationPackageManager.getApplicationHiddenSettingAsUser( 220 packageName, Process.myUserHandle()); 221 } 222 223 /** Returns {@code true} if the given {@code packageName} was ever hidden. */ wasPackageEverHidden(String packageName)224 public boolean wasPackageEverHidden(String packageName) { 225 return wasHiddenPackages.contains(packageName); 226 } 227 228 @Implementation(minSdk = LOLLIPOP) enableSystemApp(ComponentName admin, String packageName)229 protected void enableSystemApp(ComponentName admin, String packageName) { 230 enforceActiveAdmin(admin); 231 systemAppsEnabled.add(packageName); 232 } 233 234 /** Returns {@code true} if the given {@code packageName} was a system app and was enabled. */ wasSystemAppEnabled(String packageName)235 public boolean wasSystemAppEnabled(String packageName) { 236 return systemAppsEnabled.contains(packageName); 237 } 238 239 @Implementation(minSdk = LOLLIPOP) setUninstallBlocked( ComponentName admin, String packageName, boolean uninstallBlocked)240 protected void setUninstallBlocked( 241 ComponentName admin, String packageName, boolean uninstallBlocked) { 242 enforceActiveAdmin(admin); 243 if (uninstallBlocked) { 244 uninstallBlockedPackages.add(packageName); 245 } else { 246 uninstallBlockedPackages.remove(packageName); 247 } 248 } 249 250 @Implementation(minSdk = LOLLIPOP) isUninstallBlocked(ComponentName admin, String packageName)251 protected boolean isUninstallBlocked(ComponentName admin, String packageName) { 252 enforceActiveAdmin(admin); 253 return uninstallBlockedPackages.contains(packageName); 254 } 255 256 /** @see #setDeviceOwner(ComponentName) */ 257 @Implementation(minSdk = JELLY_BEAN_MR2) getDeviceOwner()258 protected String getDeviceOwner() { 259 return deviceOwner != null ? deviceOwner.getPackageName() : null; 260 } 261 262 /** @see #setDeviceOwner(ComponentName) */ 263 @Implementation(minSdk = N) isDeviceManaged()264 public boolean isDeviceManaged() { 265 return getDeviceOwner() != null; 266 } 267 268 /** @see #setProfileOwner(ComponentName) */ 269 @Implementation(minSdk = LOLLIPOP) getProfileOwner()270 protected ComponentName getProfileOwner() { 271 return profileOwner; 272 } 273 274 @Implementation(minSdk = LOLLIPOP) getProfileOwnerAsUser(int userId)275 protected ComponentName getProfileOwnerAsUser(int userId) { 276 return profileOwner; 277 } 278 279 /** 280 * Returns the human-readable name of the profile owner for a user if set using 281 * {@link #setProfileOwnerName}, otherwise `null`. 282 */ 283 @Implementation(minSdk = LOLLIPOP) getProfileOwnerNameAsUser(int userId)284 protected String getProfileOwnerNameAsUser(int userId) { 285 return profileOwnerNamesMap.get(userId); 286 } 287 getShadowUserManager()288 private ShadowUserManager getShadowUserManager() { 289 return Shadow.extract(context.getSystemService(Context.USER_SERVICE)); 290 } 291 292 /** 293 * Sets the admin as active admin and device owner. 294 * 295 * @see DevicePolicyManager#getDeviceOwner() 296 */ setDeviceOwner(ComponentName admin)297 public void setDeviceOwner(ComponentName admin) { 298 setActiveAdmin(admin); 299 deviceOwner = admin; 300 } 301 302 /** 303 * Sets the admin as active admin and profile owner. 304 * 305 * @see DevicePolicyManager#getProfileOwner() 306 */ setProfileOwner(ComponentName admin)307 public void setProfileOwner(ComponentName admin) { 308 setActiveAdmin(admin); 309 profileOwner = admin; 310 } 311 setProfileOwnerName(int userId, String name)312 public void setProfileOwnerName(int userId, String name) { 313 profileOwnerNamesMap.put(userId, name); 314 } 315 316 /** Sets the given {@code componentName} as one of the active admins. */ setActiveAdmin(ComponentName componentName)317 public void setActiveAdmin(ComponentName componentName) { 318 deviceAdmins.add(componentName); 319 } 320 321 @Implementation removeActiveAdmin(ComponentName admin)322 protected void removeActiveAdmin(ComponentName admin) { 323 deviceAdmins.remove(admin); 324 } 325 326 @Implementation(minSdk = LOLLIPOP) clearProfileOwner(ComponentName admin)327 protected void clearProfileOwner(ComponentName admin) { 328 profileOwner = null; 329 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 330 removeActiveAdmin(admin); 331 } 332 } 333 334 @Implementation(minSdk = LOLLIPOP) getApplicationRestrictions(ComponentName admin, String packageName)335 protected Bundle getApplicationRestrictions(ComponentName admin, String packageName) { 336 enforceDeviceOwnerOrProfileOwner(admin); 337 return getApplicationRestrictions(packageName); 338 } 339 340 /** Returns all application restrictions of the {@code packageName} in a {@link Bundle}. */ getApplicationRestrictions(String packageName)341 public Bundle getApplicationRestrictions(String packageName) { 342 Bundle bundle = applicationRestrictionsMap.get(packageName); 343 // If no restrictions were saved, DPM method should return an empty Bundle as per JavaDoc. 344 return bundle != null ? new Bundle(bundle) : new Bundle(); 345 } 346 347 @Implementation(minSdk = LOLLIPOP) setApplicationRestrictions( ComponentName admin, String packageName, Bundle applicationRestrictions)348 protected void setApplicationRestrictions( 349 ComponentName admin, String packageName, Bundle applicationRestrictions) { 350 enforceDeviceOwnerOrProfileOwner(admin); 351 setApplicationRestrictions(packageName, applicationRestrictions); 352 } 353 354 /** 355 * Sets the application restrictions of the {@code packageName}. 356 * 357 * <p>The new {@code applicationRestrictions} always completely overwrites any existing ones. 358 */ setApplicationRestrictions(String packageName, Bundle applicationRestrictions)359 public void setApplicationRestrictions(String packageName, Bundle applicationRestrictions) { 360 applicationRestrictionsMap.put(packageName, new Bundle(applicationRestrictions)); 361 } 362 enforceProfileOwner(ComponentName admin)363 private void enforceProfileOwner(ComponentName admin) { 364 if (!admin.equals(profileOwner)) { 365 throw new SecurityException("[" + admin + "] is not a profile owner"); 366 } 367 } 368 enforceDeviceOwner(ComponentName admin)369 private void enforceDeviceOwner(ComponentName admin) { 370 if (!admin.equals(deviceOwner)) { 371 throw new SecurityException("[" + admin + "] is not a device owner"); 372 } 373 } 374 enforceDeviceOwnerOrProfileOwner(ComponentName admin)375 private void enforceDeviceOwnerOrProfileOwner(ComponentName admin) { 376 if (!admin.equals(deviceOwner) && !admin.equals(profileOwner)) { 377 throw new SecurityException("[" + admin + "] is neither a device owner nor a profile owner."); 378 } 379 } 380 enforceActiveAdmin(ComponentName admin)381 private void enforceActiveAdmin(ComponentName admin) { 382 if (!deviceAdmins.contains(admin)) { 383 throw new SecurityException("[" + admin + "] is not an active device admin"); 384 } 385 } 386 387 @Implementation(minSdk = LOLLIPOP) setAccountManagementDisabled( ComponentName admin, String accountType, boolean disabled)388 protected void setAccountManagementDisabled( 389 ComponentName admin, String accountType, boolean disabled) { 390 enforceDeviceOwnerOrProfileOwner(admin); 391 if (disabled) { 392 accountTypesWithManagementDisabled.add(accountType); 393 } else { 394 accountTypesWithManagementDisabled.remove(accountType); 395 } 396 } 397 398 @Implementation(minSdk = LOLLIPOP) getAccountTypesWithManagementDisabled()399 protected String[] getAccountTypesWithManagementDisabled() { 400 return accountTypesWithManagementDisabled.toArray(new String[0]); 401 } 402 403 /** 404 * Sets organization name. 405 * 406 * <p>The API can only be called by profile owner since Android N and can be called by both of 407 * profile owner and device owner since Android O. 408 */ 409 @Implementation(minSdk = N) setOrganizationName(ComponentName admin, @Nullable CharSequence name)410 protected void setOrganizationName(ComponentName admin, @Nullable CharSequence name) { 411 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 412 enforceDeviceOwnerOrProfileOwner(admin); 413 } else { 414 enforceProfileOwner(admin); 415 } 416 417 if (TextUtils.isEmpty(name)) { 418 organizationName = null; 419 } else { 420 organizationName = name; 421 } 422 } 423 424 @Implementation(minSdk = N) setPackagesSuspended( ComponentName admin, String[] packageNames, boolean suspended)425 protected String[] setPackagesSuspended( 426 ComponentName admin, String[] packageNames, boolean suspended) { 427 if (admin != null) { 428 enforceDeviceOwnerOrProfileOwner(admin); 429 } 430 if (packageNames == null) { 431 throw new NullPointerException("package names cannot be null"); 432 } 433 PackageManager pm = context.getPackageManager(); 434 ArrayList<String> packagesFailedToSuspend = new ArrayList<>(); 435 for (String packageName : packageNames) { 436 try { 437 // check if it is installed 438 pm.getPackageInfo(packageName, 0); 439 if (suspended) { 440 suspendedPackages.add(packageName); 441 } else { 442 suspendedPackages.remove(packageName); 443 } 444 } catch (NameNotFoundException e) { 445 packagesFailedToSuspend.add(packageName); 446 } 447 } 448 return packagesFailedToSuspend.toArray(new String[0]); 449 } 450 451 @Implementation(minSdk = N) isPackageSuspended(ComponentName admin, String packageName)452 protected boolean isPackageSuspended(ComponentName admin, String packageName) 453 throws NameNotFoundException { 454 if (admin != null) { 455 enforceDeviceOwnerOrProfileOwner(admin); 456 } 457 // Throws NameNotFoundException 458 context.getPackageManager().getPackageInfo(packageName, 0); 459 return suspendedPackages.contains(packageName); 460 } 461 462 @Implementation(minSdk = N) setOrganizationColor(ComponentName admin, int color)463 protected void setOrganizationColor(ComponentName admin, int color) { 464 enforceProfileOwner(admin); 465 organizationColor = color; 466 } 467 468 /** 469 * Returns organization name. 470 * 471 * <p>The API can only be called by profile owner since Android N. 472 * 473 * <p>Android framework has a hidden API for getting the organization name for device owner since 474 * Android O. This method, however, is extended to return the organization name for device owners 475 * too to make testing of {@link #setOrganizationName(ComponentName, CharSequence)} easier for 476 * device owner cases. 477 */ 478 @Implementation(minSdk = N) 479 @Nullable getOrganizationName(ComponentName admin)480 protected CharSequence getOrganizationName(ComponentName admin) { 481 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 482 enforceDeviceOwnerOrProfileOwner(admin); 483 } else { 484 enforceProfileOwner(admin); 485 } 486 487 return organizationName; 488 } 489 490 @Implementation(minSdk = N) getOrganizationColor(ComponentName admin)491 protected int getOrganizationColor(ComponentName admin) { 492 enforceProfileOwner(admin); 493 return organizationColor; 494 } 495 496 @Implementation(minSdk = LOLLIPOP) setAutoTimeRequired(ComponentName admin, boolean required)497 protected void setAutoTimeRequired(ComponentName admin, boolean required) { 498 enforceDeviceOwnerOrProfileOwner(admin); 499 isAutoTimeRequired = required; 500 } 501 502 @Implementation(minSdk = LOLLIPOP) getAutoTimeRequired()503 protected boolean getAutoTimeRequired() { 504 return isAutoTimeRequired; 505 } 506 507 /** 508 * Sets permitted accessibility services. 509 * 510 * <p>The API can be called by either a profile or device owner. 511 * 512 * <p>This method does not check already enabled non-system accessibility services, so will always 513 * set the restriction and return true. 514 */ 515 @Implementation(minSdk = LOLLIPOP) setPermittedAccessibilityServices( ComponentName admin, List<String> packageNames)516 protected boolean setPermittedAccessibilityServices( 517 ComponentName admin, List<String> packageNames) { 518 enforceDeviceOwnerOrProfileOwner(admin); 519 permittedAccessibilityServices = packageNames; 520 return true; 521 } 522 523 @Implementation(minSdk = LOLLIPOP) 524 @Nullable getPermittedAccessibilityServices(ComponentName admin)525 protected List<String> getPermittedAccessibilityServices(ComponentName admin) { 526 enforceDeviceOwnerOrProfileOwner(admin); 527 return permittedAccessibilityServices; 528 } 529 530 /** 531 * Sets permitted input methods. 532 * 533 * <p>The API can be called by either a profile or device owner. 534 * 535 * <p>This method does not check already enabled non-system input methods, so will always set the 536 * restriction and return true. 537 */ 538 @Implementation(minSdk = LOLLIPOP) setPermittedInputMethods(ComponentName admin, List<String> packageNames)539 protected boolean setPermittedInputMethods(ComponentName admin, List<String> packageNames) { 540 enforceDeviceOwnerOrProfileOwner(admin); 541 permittedInputMethods = packageNames; 542 return true; 543 } 544 545 @Implementation(minSdk = LOLLIPOP) 546 @Nullable getPermittedInputMethods(ComponentName admin)547 protected List<String> getPermittedInputMethods(ComponentName admin) { 548 enforceDeviceOwnerOrProfileOwner(admin); 549 return permittedInputMethods; 550 } 551 552 /** 553 * @return the previously set status; default is {@link 554 * DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} 555 * @see #setStorageEncryptionStatus(int) 556 */ 557 @Implementation getStorageEncryptionStatus()558 protected int getStorageEncryptionStatus() { 559 return storageEncryptionStatus; 560 } 561 562 /** Setter for {@link DevicePolicyManager#getStorageEncryptionStatus()}. */ setStorageEncryptionStatus(int status)563 public void setStorageEncryptionStatus(int status) { 564 switch (status) { 565 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE: 566 case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE: 567 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING: 568 case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED: 569 break; 570 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY: 571 if (RuntimeEnvironment.getApiLevel() < M) { 572 throw new IllegalArgumentException("status " + status + " requires API " + M); 573 } 574 break; 575 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER: 576 if (RuntimeEnvironment.getApiLevel() < N) { 577 throw new IllegalArgumentException("status " + status + " requires API " + N); 578 } 579 break; 580 default: 581 throw new IllegalArgumentException("Unknown status: " + status); 582 } 583 584 storageEncryptionStatus = status; 585 } 586 587 @Implementation(minSdk = VERSION_CODES.M) getPermissionGrantState( ComponentName admin, String packageName, String permission)588 protected int getPermissionGrantState( 589 ComponentName admin, String packageName, String permission) { 590 enforceDeviceOwnerOrProfileOwner(admin); 591 Integer state = 592 appPermissionGrantStateMap.get(new PackageAndPermission(packageName, permission)); 593 return state == null ? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT : state; 594 } 595 isPermissionGranted(String packageName, String permission)596 public boolean isPermissionGranted(String packageName, String permission) { 597 Boolean isGranted = 598 appPermissionGrantedMap.get(new PackageAndPermission(packageName, permission)); 599 return isGranted == null ? false : isGranted; 600 } 601 602 @Implementation(minSdk = VERSION_CODES.M) setPermissionGrantState( ComponentName admin, String packageName, String permission, int grantState)603 protected boolean setPermissionGrantState( 604 ComponentName admin, String packageName, String permission, int grantState) { 605 enforceDeviceOwnerOrProfileOwner(admin); 606 607 String selfPackageName = context.getPackageName(); 608 609 if (packageName.equals(selfPackageName)) { 610 PackageInfo packageInfo; 611 try { 612 packageInfo = 613 context 614 .getPackageManager() 615 .getPackageInfo(selfPackageName, PackageManager.GET_PERMISSIONS); 616 } catch (NameNotFoundException e) { 617 throw new RuntimeException(e); 618 } 619 if (Arrays.asList(packageInfo.requestedPermissions).contains(permission)) { 620 if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED) { 621 ShadowApplication.getInstance().grantPermissions(permission); 622 } 623 if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED) { 624 ShadowApplication.getInstance().denyPermissions(permission); 625 } 626 } else { 627 // the app does not require this permission 628 return false; 629 } 630 } 631 PackageAndPermission key = new PackageAndPermission(packageName, permission); 632 switch (grantState) { 633 case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: 634 appPermissionGrantedMap.put(key, true); 635 break; 636 case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: 637 appPermissionGrantedMap.put(key, false); 638 break; 639 default: 640 // no-op 641 } 642 appPermissionGrantStateMap.put(key, grantState); 643 return true; 644 } 645 646 @Implementation lockNow()647 protected void lockNow() { 648 KeyguardManager keyguardManager = 649 (KeyguardManager) this.context.getSystemService(Context.KEYGUARD_SERVICE); 650 ShadowKeyguardManager shadowKeyguardManager = Shadow.extract(keyguardManager); 651 shadowKeyguardManager.setKeyguardLocked(true); 652 shadowKeyguardManager.setIsDeviceLocked(true); 653 } 654 655 @Implementation wipeData(int flags)656 protected void wipeData(int flags) { 657 wipeCalled++; 658 } 659 660 @Implementation wipeData(int flags, CharSequence reason)661 protected void wipeData(int flags, CharSequence reason) { 662 wipeData(flags); 663 } 664 getWipeCalledTimes()665 public long getWipeCalledTimes() { 666 return wipeCalled; 667 } 668 669 @Implementation setPasswordQuality(ComponentName admin, int quality)670 protected void setPasswordQuality(ComponentName admin, int quality) { 671 enforceActiveAdmin(admin); 672 requiredPasswordQuality = quality; 673 } 674 675 @Implementation resetPassword(String password, int flags)676 protected boolean resetPassword(String password, int flags) { 677 if (!passwordMeetsRequirements(password)) { 678 return false; 679 } 680 lastSetPassword = password; 681 return true; 682 } 683 684 @Implementation(minSdk = O) resetPasswordWithToken( ComponentName admin, String password, byte[] token, int flags)685 protected boolean resetPasswordWithToken( 686 ComponentName admin, String password, byte[] token, int flags) { 687 enforceDeviceOwnerOrProfileOwner(admin); 688 if (!Arrays.equals(passwordResetTokens.get(admin), token) 689 || !componentsWithActivatedTokens.contains(admin)) { 690 throw new IllegalStateException("wrong or not activated token"); 691 } 692 resetPassword(password, flags); 693 return true; 694 } 695 696 @Implementation(minSdk = O) isResetPasswordTokenActive(ComponentName admin)697 protected boolean isResetPasswordTokenActive(ComponentName admin) { 698 enforceDeviceOwnerOrProfileOwner(admin); 699 return componentsWithActivatedTokens.contains(admin); 700 } 701 702 @Implementation(minSdk = O) setResetPasswordToken(ComponentName admin, byte[] token)703 protected boolean setResetPasswordToken(ComponentName admin, byte[] token) { 704 if (token.length < 32) { 705 throw new IllegalArgumentException("token too short: " + token.length); 706 } 707 enforceDeviceOwnerOrProfileOwner(admin); 708 passwordResetTokens.put(admin, token); 709 componentsWithActivatedTokens.remove(admin); 710 return true; 711 } 712 713 @Implementation setPasswordMinimumLength(ComponentName admin, int length)714 protected void setPasswordMinimumLength(ComponentName admin, int length) { 715 enforceActiveAdmin(admin); 716 passwordMinimumLength = length; 717 } 718 719 @Implementation setPasswordMinimumLetters(ComponentName admin, int length)720 protected void setPasswordMinimumLetters(ComponentName admin, int length) { 721 enforceActiveAdmin(admin); 722 passwordMinimumLetters = length; 723 } 724 725 @Implementation setPasswordMinimumLowerCase(ComponentName admin, int length)726 protected void setPasswordMinimumLowerCase(ComponentName admin, int length) { 727 enforceActiveAdmin(admin); 728 passwordMinimumLowerCase = length; 729 } 730 731 @Implementation setPasswordMinimumUpperCase(ComponentName admin, int length)732 protected void setPasswordMinimumUpperCase(ComponentName admin, int length) { 733 enforceActiveAdmin(admin); 734 passwordMinimumUpperCase = length; 735 } 736 737 @Implementation setPasswordMinimumNonLetter(ComponentName admin, int length)738 protected void setPasswordMinimumNonLetter(ComponentName admin, int length) { 739 enforceActiveAdmin(admin); 740 passwordMinimumNonLetter = length; 741 } 742 743 @Implementation setPasswordMinimumNumeric(ComponentName admin, int length)744 protected void setPasswordMinimumNumeric(ComponentName admin, int length) { 745 enforceActiveAdmin(admin); 746 passwordMinimumNumeric = length; 747 } 748 749 @Implementation setPasswordMinimumSymbols(ComponentName admin, int length)750 protected void setPasswordMinimumSymbols(ComponentName admin, int length) { 751 enforceActiveAdmin(admin); 752 passwordMinimumSymbols = length; 753 } 754 passwordMeetsRequirements(String password)755 private boolean passwordMeetsRequirements(String password) { 756 int digit = 0; 757 int alpha = 0; 758 int upper = 0; 759 int lower = 0; 760 int symbol = 0; 761 for (int i = 0; i < password.length(); i++) { 762 char c = password.charAt(i); 763 if (Character.isDigit(c)) { 764 digit++; 765 } 766 if (Character.isLetter(c)) { 767 alpha++; 768 } 769 if (Character.isUpperCase(c)) { 770 upper++; 771 } 772 if (Character.isLowerCase(c)) { 773 lower++; 774 } 775 if (!Character.isLetterOrDigit(c)) { 776 symbol++; 777 } 778 } 779 switch (requiredPasswordQuality) { 780 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: 781 case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: 782 case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: 783 return true; 784 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 785 return password.length() > 0; 786 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 787 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: // complexity not enforced 788 return digit > 0 && password.length() >= passwordMinimumLength; 789 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 790 return digit > 0 && alpha > 0 && password.length() >= passwordMinimumLength; 791 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 792 return password.length() >= passwordMinimumLength 793 && alpha >= passwordMinimumLetters 794 && lower >= passwordMinimumLowerCase 795 && upper >= passwordMinimumUpperCase 796 && digit + symbol >= passwordMinimumNonLetter 797 && digit >= passwordMinimumNumeric 798 && symbol >= passwordMinimumSymbols; 799 default: 800 return true; 801 } 802 } 803 804 /** 805 * Retrieves last password set through {@link DevicePolicyManager#resetPassword} or {@link 806 * DevicePolicyManager#resetPasswordWithToken}. 807 */ getLastSetPassword()808 public String getLastSetPassword() { 809 return lastSetPassword; 810 } 811 812 /** 813 * Activates reset token for given admin. 814 * 815 * @param admin Which {@link DeviceAdminReceiver} this request is associated with. 816 * @return if the activation state changed. 817 * @throws IllegalArgumentException if there is no token set for this admin. 818 */ activateResetToken(ComponentName admin)819 public boolean activateResetToken(ComponentName admin) { 820 if (!passwordResetTokens.containsKey(admin)) { 821 throw new IllegalArgumentException("No token set for comopnent: " + admin); 822 } 823 return componentsWithActivatedTokens.add(admin); 824 } 825 826 @Implementation(minSdk = LOLLIPOP) addPersistentPreferredActivity( ComponentName admin, IntentFilter filter, ComponentName activity)827 protected void addPersistentPreferredActivity( 828 ComponentName admin, IntentFilter filter, ComponentName activity) { 829 enforceDeviceOwnerOrProfileOwner(admin); 830 831 PackageManager packageManager = context.getPackageManager(); 832 packageManager.addPreferredActivity(filter, 0, null, activity); 833 } 834 835 @Implementation(minSdk = LOLLIPOP) clearPackagePersistentPreferredActivities( ComponentName admin, String packageName)836 protected void clearPackagePersistentPreferredActivities( 837 ComponentName admin, String packageName) { 838 enforceDeviceOwnerOrProfileOwner(admin); 839 PackageManager packageManager = context.getPackageManager(); 840 packageManager.clearPackagePreferredActivities(packageName); 841 } 842 843 @Implementation(minSdk = JELLY_BEAN_MR1) setKeyguardDisabledFeatures(ComponentName admin, int which)844 protected void setKeyguardDisabledFeatures(ComponentName admin, int which) { 845 enforceActiveAdmin(admin); 846 keyguardDisabledFeatures = which; 847 } 848 849 @Implementation(minSdk = JELLY_BEAN_MR1) getKeyguardDisabledFeatures(ComponentName admin)850 protected int getKeyguardDisabledFeatures(ComponentName admin) { 851 return keyguardDisabledFeatures; 852 } 853 854 /** 855 * Sets the user provisioning state. 856 * 857 * @param state to store provisioning state 858 */ setUserProvisioningState(int state)859 public void setUserProvisioningState(int state) { 860 userProvisioningState = state; 861 } 862 863 /** @return Returns the provisioning state for the current user. */ 864 @Implementation(minSdk = N) getUserProvisioningState()865 protected int getUserProvisioningState() { 866 return userProvisioningState; 867 } 868 869 // BEGIN-INTERNAL 870 @Implementation(minSdk = Q) getCrossProfileCalendarPackages()871 protected Set<String> getCrossProfileCalendarPackages() { 872 return crossProfileCalendarPackages; 873 } 874 875 @Implementation(minSdk = Q) setCrossProfileCalendarPackages(ComponentName admin, Set<String> packageNames)876 public void setCrossProfileCalendarPackages(ComponentName admin, Set<String> packageNames) { 877 enforceProfileOwner(admin); 878 crossProfileCalendarPackages = packageNames; 879 } 880 881 @Implementation(minSdk = R) getDefaultCrossProfilePackages()882 protected Set<String> getDefaultCrossProfilePackages() { 883 return new HashSet<>(defaultCrossProfilePackages); 884 } 885 addDefaultCrossProfilePackage(String packageName)886 public void addDefaultCrossProfilePackage(String packageName) { 887 defaultCrossProfilePackages.add(packageName); 888 } setDefaultCrossProfilePackages(Set<String> defaultCrossProfilePackages)889 public void setDefaultCrossProfilePackages(Set<String> defaultCrossProfilePackages) { 890 this.defaultCrossProfilePackages = new HashSet<>(defaultCrossProfilePackages); 891 } 892 // END-INTERNAL 893 } 894