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