Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
      4 import static android.os.Build.VERSION_CODES.LOLLIPOP;
      5 import static android.os.Build.VERSION_CODES.N;
      6 import static org.robolectric.Shadows.shadowOf;
      7 
      8 import android.annotation.Nullable;
      9 import android.app.admin.DevicePolicyManager;
     10 import android.content.ComponentName;
     11 import android.content.Context;
     12 import android.os.Build;
     13 import android.os.Bundle;
     14 import android.os.Process;
     15 import android.os.UserManager;
     16 import android.text.TextUtils;
     17 import java.util.ArrayList;
     18 import java.util.HashMap;
     19 import java.util.HashSet;
     20 import java.util.List;
     21 import java.util.Map;
     22 import java.util.Set;
     23 import org.robolectric.RuntimeEnvironment;
     24 import org.robolectric.annotation.Implementation;
     25 import org.robolectric.annotation.Implements;
     26 
     27 /** Shadow for {@link DevicePolicyManager} */
     28 @Implements(DevicePolicyManager.class)
     29 public class ShadowDevicePolicyManager {
     30   /**
     31    * @see
     32    *     https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setOrganizationColor(android.content.ComponentName,
     33    *     int)
     34    */
     35   private static final int DEFAULT_ORGANIZATION_COLOR = 0xFF008080; // teal
     36 
     37   private ComponentName deviceOwner;
     38   private ComponentName profileOwner;
     39   private List<ComponentName> deviceAdmins = new ArrayList<>();
     40   private List<String> permittedAccessibilityServices = new ArrayList<>();
     41   private List<String> permittedInputMethods = new ArrayList<>();
     42   private Map<String, Bundle> applicationRestrictionsMap = new HashMap<>();
     43   private CharSequence organizationName;
     44   private int organizationColor;
     45   private boolean isAutoTimeRequired;
     46 
     47   private final Set<String> hiddenPackages = new HashSet<>();
     48   private final Set<String> wasHiddenPackages = new HashSet<>();
     49   private final Set<String> accountTypesWithManagementDisabled = new HashSet<>();
     50   private final Set<String> systemAppsEnabled = new HashSet<>();
     51   private final Set<String> uninstallBlockedPackages = new HashSet<>();
     52 
     53   public ShadowDevicePolicyManager() {
     54     organizationColor = DEFAULT_ORGANIZATION_COLOR;
     55   }
     56 
     57   @Implementation
     58   public boolean isDeviceOwnerApp(String packageName) {
     59     return deviceOwner != null && deviceOwner.getPackageName().equals(packageName);
     60   }
     61 
     62   @Implementation
     63   public boolean isProfileOwnerApp(String packageName) {
     64     return profileOwner != null && profileOwner.getPackageName().equals(packageName);
     65   }
     66 
     67   @Implementation
     68   public boolean isAdminActive(ComponentName who) {
     69     return who != null && deviceAdmins.contains(who);
     70   }
     71 
     72   @Implementation
     73   public List<ComponentName> getActiveAdmins() {
     74     return deviceAdmins;
     75   }
     76 
     77   @Implementation
     78   public void addUserRestriction(ComponentName admin, String key) {
     79     enforceActiveAdmin(admin);
     80     getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, true);
     81   }
     82 
     83   @Implementation
     84   public void clearUserRestriction(ComponentName admin, String key) {
     85     enforceActiveAdmin(admin);
     86     getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, false);
     87   }
     88 
     89   @Implementation
     90   public void setApplicationHidden(ComponentName admin, String packageName, boolean hidden) {
     91     enforceActiveAdmin(admin);
     92     if (hidden) {
     93       hiddenPackages.add(packageName);
     94       wasHiddenPackages.add(packageName);
     95     } else {
     96       hiddenPackages.remove(packageName);
     97     }
     98   }
     99 
    100   @Implementation
    101   public boolean isApplicationHidden(ComponentName admin, String packageName) {
    102     enforceActiveAdmin(admin);
    103     return hiddenPackages.contains(packageName);
    104   }
    105 
    106   /** Returns {@code true} if the given {@code packageName} was ever hidden. */
    107   public boolean wasPackageEverHidden(String packageName) {
    108     return wasHiddenPackages.contains(packageName);
    109   }
    110 
    111   @Implementation
    112   public int enableSystemApp(ComponentName admin, String packageName) {
    113     enforceActiveAdmin(admin);
    114     systemAppsEnabled.add(packageName);
    115     return 1;
    116   }
    117 
    118   /** Returns {@code true} if the given {@code packageName} was a system app and was enabled. */
    119   public boolean wasSystemAppEnabled(String packageName) {
    120     return systemAppsEnabled.contains(packageName);
    121   }
    122 
    123   @Implementation
    124   public void setUninstallBlocked(
    125       ComponentName admin, String packageName, boolean uninstallBlocked) {
    126     enforceActiveAdmin(admin);
    127     if (uninstallBlocked) {
    128       uninstallBlockedPackages.add(packageName);
    129     } else {
    130       uninstallBlockedPackages.remove(packageName);
    131     }
    132   }
    133 
    134   @Implementation
    135   public boolean isUninstallBlocked(ComponentName admin, String packageName) {
    136     enforceActiveAdmin(admin);
    137     return uninstallBlockedPackages.contains(packageName);
    138   }
    139 
    140   @Implementation(minSdk = JELLY_BEAN_MR2)
    141   public String getDeviceOwner() {
    142     return deviceOwner != null ? deviceOwner.getPackageName() : null;
    143   }
    144 
    145   @Implementation(minSdk = LOLLIPOP)
    146   public ComponentName getProfileOwner() {
    147     return profileOwner;
    148   }
    149 
    150   private ShadowUserManager getShadowUserManager() {
    151     return shadowOf(
    152         (UserManager) RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE));
    153   }
    154 
    155   /** Sets the admin as active admin and device owner. */
    156   public void setDeviceOwner(ComponentName admin) {
    157     setActiveAdmin(admin);
    158     deviceOwner = admin;
    159   }
    160 
    161   /** Sets the admin as active admin and profile owner. */
    162   public void setProfileOwner(ComponentName admin) {
    163     setActiveAdmin(admin);
    164     profileOwner = admin;
    165   }
    166 
    167   /** Sets the given {@code componentName} as one of the active admins. */
    168   public void setActiveAdmin(ComponentName componentName) {
    169     deviceAdmins.add(componentName);
    170   }
    171 
    172   @Implementation
    173   public Bundle getApplicationRestrictions(ComponentName admin, String packageName) {
    174     enforceDeviceOwnerOrProfileOwner(admin);
    175     return getApplicationRestrictions(packageName);
    176   }
    177 
    178   /** Returns all application restrictions of the {@code packageName} in a {@link Bundle}. */
    179   public Bundle getApplicationRestrictions(String packageName) {
    180     Bundle bundle = applicationRestrictionsMap.get(packageName);
    181     // If no restrictions were saved, DPM method should return an empty Bundle as per JavaDoc.
    182     return bundle != null ? bundle : Bundle.EMPTY;
    183   }
    184 
    185   @Implementation
    186   public void setApplicationRestrictions(
    187       ComponentName admin, String packageName, Bundle applicationRestrictions) {
    188     enforceDeviceOwnerOrProfileOwner(admin);
    189     setApplicationRestrictions(packageName, applicationRestrictions);
    190   }
    191 
    192   /**
    193    * Sets the application restrictions of the {@code packageName}.
    194    *
    195    * The new {@code applicationRestrictions} always completely overwrites any existing ones.
    196    */
    197   public void setApplicationRestrictions(String packageName, Bundle applicationRestrictions) {
    198     applicationRestrictionsMap.put(packageName, applicationRestrictions);
    199   }
    200 
    201   private void enforceProfileOwner(ComponentName admin) {
    202     if (!admin.equals(profileOwner)) {
    203       throw new SecurityException("[" + admin + "] is not a profile owner");
    204     }
    205   }
    206 
    207   private void enforceDeviceOwner(ComponentName admin) {
    208     if (!admin.equals(deviceOwner)) {
    209       throw new SecurityException("[" + admin + "] is not a device owner");
    210     }
    211   }
    212 
    213   private void enforceDeviceOwnerOrProfileOwner(ComponentName admin) {
    214     if (!admin.equals(deviceOwner) && !admin.equals(profileOwner)) {
    215       throw new SecurityException("[" + admin + "] is neither a device owner nor a profile owner.");
    216     }
    217   }
    218 
    219   private void enforceActiveAdmin(ComponentName admin) {
    220     if (!deviceAdmins.contains(admin)) {
    221       throw new SecurityException("[" + admin + "] is not an active device admin");
    222     }
    223   }
    224 
    225   @Implementation
    226   public void setAccountManagementDisabled(
    227       ComponentName admin, String accountType, boolean disabled) {
    228     enforceDeviceOwnerOrProfileOwner(admin);
    229     if (disabled) {
    230       accountTypesWithManagementDisabled.add(accountType);
    231     } else {
    232       accountTypesWithManagementDisabled.remove(accountType);
    233     }
    234   }
    235 
    236   @Implementation
    237   public String[] getAccountTypesWithManagementDisabled() {
    238     return accountTypesWithManagementDisabled.toArray(new String[0]);
    239   }
    240 
    241   /**
    242    * Sets organization name.
    243    *
    244    * The API can only be called by profile owner since Android N and can be called by both of
    245    * profile owner and device owner since Android O.
    246    */
    247   @Implementation(minSdk = N)
    248   public void setOrganizationName(ComponentName admin, @Nullable CharSequence name) {
    249     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    250       enforceDeviceOwnerOrProfileOwner(admin);
    251     } else {
    252       enforceProfileOwner(admin);
    253     }
    254 
    255     if (TextUtils.isEmpty(name)) {
    256       organizationName = null;
    257     } else {
    258       organizationName = name;
    259     }
    260   }
    261 
    262   @Implementation(minSdk = N)
    263   public void setOrganizationColor(ComponentName admin, int color) {
    264     enforceProfileOwner(admin);
    265     organizationColor = color;
    266   }
    267 
    268   /**
    269    * Returns organization name.
    270    *
    271    * The API can only be called by profile owner since Android N.
    272    *
    273    * Android framework has a hidden API for getting the organization name for device owner since
    274    * Android O. This method, however, is extended to return the organization name for device owners
    275    * too to make testing of {@link #setOrganizationName(ComponentName, CharSequence)} easier for
    276    * device owner cases.
    277    */
    278   @Implementation(minSdk = N)
    279   @Nullable
    280   public CharSequence getOrganizationName(ComponentName admin) {
    281     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    282       enforceDeviceOwnerOrProfileOwner(admin);
    283     } else {
    284       enforceProfileOwner(admin);
    285     }
    286 
    287     return organizationName;
    288   }
    289 
    290   @Implementation(minSdk = N)
    291   public int getOrganizationColor(ComponentName admin) {
    292     enforceProfileOwner(admin);
    293     return organizationColor;
    294   }
    295 
    296   @Implementation
    297   public void setAutoTimeRequired(ComponentName admin, boolean required) {
    298     enforceDeviceOwnerOrProfileOwner(admin);
    299     isAutoTimeRequired = required;
    300   }
    301 
    302   @Implementation
    303   public boolean getAutoTimeRequired() {
    304     return isAutoTimeRequired;
    305   }
    306 
    307   /**
    308    * Sets permitted accessibility services.
    309    *
    310    * The API can be called by either a profile or device owner.
    311    *
    312    * This method does not check already enabled non-system accessibility services, so will always
    313    * set the restriction and return true.
    314    */
    315   @Implementation
    316   public boolean setPermittedAccessibilityServices(ComponentName admin, List<String> packageNames) {
    317     enforceDeviceOwnerOrProfileOwner(admin);
    318     permittedAccessibilityServices = packageNames;
    319     return true;
    320   }
    321 
    322   @Implementation
    323   @Nullable
    324   public List<String> getPermittedAccessibilityServices(ComponentName admin) {
    325     enforceDeviceOwnerOrProfileOwner(admin);
    326     return permittedAccessibilityServices;
    327   }
    328 
    329   /**
    330    * Sets permitted input methods.
    331    *
    332    * The API can be called by either a profile or device owner.
    333    *
    334    * This method does not check already enabled non-system input methods, so will always set the
    335    * restriction and return true.
    336    */
    337   @Implementation
    338   public boolean setPermittedInputMethods(ComponentName admin, List<String> packageNames) {
    339     enforceDeviceOwnerOrProfileOwner(admin);
    340     permittedInputMethods = packageNames;
    341     return true;
    342   }
    343 
    344   @Implementation
    345   @Nullable
    346   public List<String> getPermittedInputMethods(ComponentName admin) {
    347     enforceDeviceOwnerOrProfileOwner(admin);
    348     return permittedInputMethods;
    349   }
    350 }
    351