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