• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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