Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
      4 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
      5 import static android.os.Build.VERSION_CODES.LOLLIPOP;
      6 import static android.os.Build.VERSION_CODES.M;
      7 import static android.os.Build.VERSION_CODES.N;
      8 import static android.os.Build.VERSION_CODES.N_MR1;
      9 import static 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 
    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
    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
    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)
    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)
    139   protected void __constructor__(Context context, boolean parentInstance) {
    140     init(context);
    141   }
    142 
    143   @Implementation(minSdk = O)
    144   protected void __constructor__(Context context, IDevicePolicyManager service) {
    145     init(context);
    146   }
    147 
    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)
    157   protected boolean isDeviceOwnerApp(String packageName) {
    158     return deviceOwner != null && deviceOwner.getPackageName().equals(packageName);
    159   }
    160 
    161   @Implementation(minSdk = LOLLIPOP)
    162   protected boolean isProfileOwnerApp(String packageName) {
    163     return profileOwner != null && profileOwner.getPackageName().equals(packageName);
    164   }
    165 
    166   @Implementation
    167   protected boolean isAdminActive(ComponentName who) {
    168     return who != null && deviceAdmins.contains(who);
    169   }
    170 
    171   @Implementation
    172   protected List<ComponentName> getActiveAdmins() {
    173     return deviceAdmins;
    174   }
    175 
    176   @Implementation(minSdk = LOLLIPOP)
    177   protected void addUserRestriction(ComponentName admin, String key) {
    178     enforceActiveAdmin(admin);
    179     getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, true);
    180   }
    181 
    182   @Implementation(minSdk = LOLLIPOP)
    183   protected void clearUserRestriction(ComponentName admin, String key) {
    184     enforceActiveAdmin(admin);
    185     getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, false);
    186   }
    187 
    188   @Implementation(minSdk = LOLLIPOP)
    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    */
    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)
    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. */
    221   public boolean wasPackageEverHidden(String packageName) {
    222     return wasHiddenPackages.contains(packageName);
    223   }
    224 
    225   @Implementation(minSdk = LOLLIPOP)
    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. */
    232   public boolean wasSystemAppEnabled(String packageName) {
    233     return systemAppsEnabled.contains(packageName);
    234   }
    235 
    236   @Implementation(minSdk = LOLLIPOP)
    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)
    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)
    255   protected String getDeviceOwner() {
    256     return deviceOwner != null ? deviceOwner.getPackageName() : null;
    257   }
    258 
    259   /** @see #setDeviceOwner(ComponentName) */
    260   @Implementation(minSdk = N)
    261   public boolean isDeviceManaged() {
    262     return getDeviceOwner() != null;
    263   }
    264 
    265   /** @see #setProfileOwner(ComponentName) */
    266   @Implementation(minSdk = LOLLIPOP)
    267   protected ComponentName getProfileOwner() {
    268     return profileOwner;
    269   }
    270 
    271   @Implementation(minSdk = LOLLIPOP)
    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)
    281   protected String getProfileOwnerNameAsUser(int userId) {
    282     return profileOwnerNamesMap.get(userId);
    283   }
    284 
    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    */
    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    */
    304   public void setProfileOwner(ComponentName admin) {
    305     setActiveAdmin(admin);
    306     profileOwner = admin;
    307   }
    308 
    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. */
    314   public void setActiveAdmin(ComponentName componentName) {
    315     deviceAdmins.add(componentName);
    316   }
    317 
    318   @Implementation
    319   protected void removeActiveAdmin(ComponentName admin) {
    320     deviceAdmins.remove(admin);
    321   }
    322 
    323   @Implementation(minSdk = LOLLIPOP)
    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)
    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}. */
    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)
    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    */
    356   public void setApplicationRestrictions(String packageName, Bundle applicationRestrictions) {
    357     applicationRestrictionsMap.put(packageName, new Bundle(applicationRestrictions));
    358   }
    359 
    360   private void enforceProfileOwner(ComponentName admin) {
    361     if (!admin.equals(profileOwner)) {
    362       throw new SecurityException("[" + admin + "] is not a profile owner");
    363     }
    364   }
    365 
    366   private void enforceDeviceOwner(ComponentName admin) {
    367     if (!admin.equals(deviceOwner)) {
    368       throw new SecurityException("[" + admin + "] is not a device owner");
    369     }
    370   }
    371 
    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 
    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)
    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)
    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)
    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)
    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)
    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)
    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
    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)
    488   protected int getOrganizationColor(ComponentName admin) {
    489     enforceProfileOwner(admin);
    490     return organizationColor;
    491   }
    492 
    493   @Implementation(minSdk = LOLLIPOP)
    494   protected void setAutoTimeRequired(ComponentName admin, boolean required) {
    495     enforceDeviceOwnerOrProfileOwner(admin);
    496     isAutoTimeRequired = required;
    497   }
    498 
    499   @Implementation(minSdk = LOLLIPOP)
    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)
    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
    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)
    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
    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
    555   protected int getStorageEncryptionStatus() {
    556     return storageEncryptionStatus;
    557   }
    558 
    559   /** Setter for {@link DevicePolicyManager#getStorageEncryptionStatus()}. */
    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)
    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 
    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)
    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
    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
    653   protected void wipeData(int flags) {
    654     wipeCalled++;
    655   }
    656 
    657   public long getWipeCalledTimes() {
    658     return wipeCalled;
    659   }
    660 
    661   @Implementation
    662   protected void setPasswordQuality(ComponentName admin, int quality) {
    663     enforceActiveAdmin(admin);
    664     requiredPasswordQuality = quality;
    665   }
    666 
    667   @Implementation
    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)
    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)
    689   protected boolean isResetPasswordTokenActive(ComponentName admin) {
    690     enforceDeviceOwnerOrProfileOwner(admin);
    691     return componentsWithActivatedTokens.contains(admin);
    692   }
    693 
    694   @Implementation(minSdk = O)
    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
    706   protected void setPasswordMinimumLength(ComponentName admin, int length) {
    707     enforceActiveAdmin(admin);
    708     passwordMinimumLength = length;
    709   }
    710 
    711   @Implementation
    712   protected void setPasswordMinimumLetters(ComponentName admin, int length) {
    713     enforceActiveAdmin(admin);
    714     passwordMinimumLetters = length;
    715   }
    716 
    717   @Implementation
    718   protected void setPasswordMinimumLowerCase(ComponentName admin, int length) {
    719     enforceActiveAdmin(admin);
    720     passwordMinimumLowerCase = length;
    721   }
    722 
    723   @Implementation
    724   protected void setPasswordMinimumUpperCase(ComponentName admin, int length) {
    725     enforceActiveAdmin(admin);
    726     passwordMinimumUpperCase = length;
    727   }
    728 
    729   @Implementation
    730   protected void setPasswordMinimumNonLetter(ComponentName admin, int length) {
    731     enforceActiveAdmin(admin);
    732     passwordMinimumNonLetter = length;
    733   }
    734 
    735   @Implementation
    736   protected void setPasswordMinimumNumeric(ComponentName admin, int length) {
    737     enforceActiveAdmin(admin);
    738     passwordMinimumNumeric = length;
    739   }
    740 
    741   @Implementation
    742   protected void setPasswordMinimumSymbols(ComponentName admin, int length) {
    743     enforceActiveAdmin(admin);
    744     passwordMinimumSymbols = length;
    745   }
    746 
    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    */
    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    */
    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)
    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)
    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)
    836   protected void setKeyguardDisabledFeatures(ComponentName admin, int which) {
    837     enforceActiveAdmin(admin);
    838     keyguardDisabledFeatures = which;
    839   }
    840 
    841   @Implementation(minSdk = JELLY_BEAN_MR1)
    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    */
    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)
    857   protected int getUserProvisioningState() {
    858     return userProvisioningState;
    859   }
    860 
    861   // BEGIN-INTERNAL
    862   @Implementation(minSdk = Q)
    863   protected Set<String> getCrossProfileCalendarPackages() {
    864     return crossProfileCalendarPackages;
    865   }
    866 
    867   @Implementation(minSdk = Q)
    868   public void setCrossProfileCalendarPackages(ComponentName admin, Set<String> packageNames) {
    869     enforceProfileOwner(admin);
    870     crossProfileCalendarPackages = packageNames;
    871   }
    872   // END-INTERNAL
    873 }
    874