| package org.robolectric.shadows; |
| |
| import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; |
| import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; |
| import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; |
| import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; |
| import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; |
| import static android.os.Build.VERSION_CODES.LOLLIPOP; |
| import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; |
| import static android.os.Build.VERSION_CODES.M; |
| import static android.os.Build.VERSION_CODES.N; |
| import static android.os.Build.VERSION_CODES.N_MR1; |
| import static android.os.Build.VERSION_CODES.O; |
| import static android.os.Build.VERSION_CODES.P; |
| import static android.os.Build.VERSION_CODES.Q; |
| import static android.os.Build.VERSION_CODES.R; |
| import static android.os.Build.VERSION_CODES.S; |
| import static android.os.Build.VERSION_CODES.S_V2; |
| import static android.os.Build.VERSION_CODES.TIRAMISU; |
| import static org.robolectric.Shadows.shadowOf; |
| import static org.robolectric.shadow.api.Shadow.invokeConstructor; |
| import static org.robolectric.util.ReflectionHelpers.ClassParameter.from; |
| |
| import android.accounts.Account; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.RequiresPermission; |
| import android.annotation.SuppressLint; |
| import android.annotation.SystemApi; |
| import android.app.ApplicationPackageManager; |
| import android.app.KeyguardManager; |
| import android.app.admin.DeviceAdminReceiver; |
| import android.app.admin.DevicePolicyManager; |
| import android.app.admin.DevicePolicyManager.NearbyStreamingPolicy; |
| import android.app.admin.DevicePolicyManager.PasswordComplexity; |
| import android.app.admin.DevicePolicyManager.UserProvisioningState; |
| import android.app.admin.IDevicePolicyManager; |
| import android.app.admin.SystemUpdatePolicy; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.ServiceConnection; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.os.Build; |
| import android.os.Build.VERSION_CODES; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.PersistableBundle; |
| import android.os.Process; |
| import android.os.UserHandle; |
| import android.text.TextUtils; |
| import com.android.internal.util.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import org.robolectric.RuntimeEnvironment; |
| import org.robolectric.annotation.Implementation; |
| import org.robolectric.annotation.Implements; |
| import org.robolectric.annotation.RealObject; |
| import org.robolectric.shadow.api.Shadow; |
| |
| @Implements(DevicePolicyManager.class) |
| @SuppressLint("NewApi") |
| public class ShadowDevicePolicyManager { |
| /** |
| * @see |
| * https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setOrganizationColor(android.content.ComponentName, |
| * int) |
| */ |
| private static final int DEFAULT_ORGANIZATION_COLOR = 0xFF008080; // teal |
| |
| private ComponentName deviceOwner; |
| private ComponentName profileOwner; |
| private List<ComponentName> deviceAdmins = new ArrayList<>(); |
| private Map<Integer, String> profileOwnerNamesMap = new HashMap<>(); |
| private List<String> permittedAccessibilityServices = new ArrayList<>(); |
| private List<String> permittedInputMethods = new ArrayList<>(); |
| private Map<String, Bundle> applicationRestrictionsMap = new HashMap<>(); |
| private CharSequence organizationName; |
| private int organizationColor; |
| private boolean isAutoTimeRequired; |
| private boolean isAutoTimeZoneEnabled; |
| private String timeZone; |
| private int keyguardDisabledFeatures; |
| private String lastSetPassword; |
| private int requiredPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; |
| |
| private int passwordMinimumLength; |
| private int passwordMinimumLetters = 1; |
| private int passwordMinimumLowerCase; |
| private int passwordMinimumUpperCase; |
| private int passwordMinimumNonLetter; |
| private int passwordMinimumNumeric = 1; |
| private int passwordMinimumSymbols = 1; |
| private int passwordHistoryLength = 0; |
| private long passwordExpiration = 0; |
| private long passwordExpirationTimeout = 0; |
| private int maximumFailedPasswordsForWipe = 0; |
| private long maximumTimeToLock = 0; |
| private boolean cameraDisabled; |
| private boolean isActivePasswordSufficient; |
| private boolean isUniqueDeviceAttestationSupported; |
| @PasswordComplexity private int passwordComplexity; |
| |
| private int wipeCalled; |
| private int storageEncryptionStatus; |
| private int permissionPolicy; |
| private boolean storageEncryptionRequested; |
| private final Set<String> wasHiddenPackages = new HashSet<>(); |
| private final Set<String> accountTypesWithManagementDisabled = new HashSet<>(); |
| private final Set<String> systemAppsEnabled = new HashSet<>(); |
| private final Set<String> uninstallBlockedPackages = new HashSet<>(); |
| private final Set<String> suspendedPackages = new HashSet<>(); |
| private final Set<String> affiliationIds = new HashSet<>(); |
| private final Map<PackageAndPermission, Boolean> appPermissionGrantedMap = new HashMap<>(); |
| private final Map<PackageAndPermission, Integer> appPermissionGrantStateMap = new HashMap<>(); |
| private final Map<ComponentName, byte[]> passwordResetTokens = new HashMap<>(); |
| private final Map<ComponentName, Set<Integer>> adminPolicyGrantedMap = new HashMap<>(); |
| private final Map<ComponentName, CharSequence> shortSupportMessageMap = new HashMap<>(); |
| private final Map<ComponentName, CharSequence> longSupportMessageMap = new HashMap<>(); |
| private final Set<ComponentName> componentsWithActivatedTokens = new HashSet<>(); |
| private Collection<String> packagesToFailForSetApplicationHidden = Collections.emptySet(); |
| private int lockTaskFeatures; |
| private final List<String> lockTaskPackages = new ArrayList<>(); |
| private Context context; |
| private ApplicationPackageManager applicationPackageManager; |
| private SystemUpdatePolicy policy; |
| private List<UserHandle> bindDeviceAdminTargetUsers = ImmutableList.of(); |
| private boolean isDeviceProvisioned; |
| private boolean isDeviceProvisioningConfigApplied; |
| private volatile boolean organizationOwnedDeviceWithManagedProfile = false; |
| private int nearbyNotificationStreamingPolicy = |
| DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY; |
| private int nearbyAppStreamingPolicy = |
| DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY; |
| private boolean isUsbDataSignalingEnabled = true; |
| @Nullable private String devicePolicyManagementRoleHolderPackage; |
| private final Map<UserHandle, Account> finalizedWorkProfileProvisioningMap = new HashMap<>(); |
| private List<UserHandle> policyManagedProfiles = new ArrayList<>(); |
| private final Map<Integer, Integer> userProvisioningStatesMap = new HashMap<>(); |
| @Nullable private PersistableBundle lastTransferOwnershipBundle; |
| |
| private @RealObject DevicePolicyManager realObject; |
| |
| private static class PackageAndPermission { |
| |
| public PackageAndPermission(String packageName, String permission) { |
| this.packageName = packageName; |
| this.permission = permission; |
| } |
| |
| private String packageName; |
| private String permission; |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof PackageAndPermission)) { |
| return false; |
| } |
| PackageAndPermission other = (PackageAndPermission) o; |
| return packageName.equals(other.packageName) && permission.equals(other.permission); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = packageName.hashCode(); |
| result = 31 * result + permission.hashCode(); |
| return result; |
| } |
| } |
| |
| @Implementation(maxSdk = M) |
| protected void __constructor__(Context context, Handler handler) { |
| init(context); |
| invokeConstructor( |
| DevicePolicyManager.class, |
| realObject, |
| from(Context.class, context), |
| from(Handler.class, handler)); |
| } |
| |
| @Implementation(minSdk = N, maxSdk = N_MR1) |
| protected void __constructor__(Context context, boolean parentInstance) { |
| init(context); |
| } |
| |
| @Implementation(minSdk = O) |
| protected void __constructor__(Context context, IDevicePolicyManager service) { |
| init(context); |
| } |
| |
| private void init(Context context) { |
| this.context = context; |
| this.applicationPackageManager = |
| (ApplicationPackageManager) context.getApplicationContext().getPackageManager(); |
| organizationColor = DEFAULT_ORGANIZATION_COLOR; |
| storageEncryptionStatus = DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; |
| } |
| |
| @Implementation(minSdk = JELLY_BEAN_MR2) |
| protected boolean isDeviceOwnerApp(String packageName) { |
| return deviceOwner != null && deviceOwner.getPackageName().equals(packageName); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean isProfileOwnerApp(String packageName) { |
| return profileOwner != null && profileOwner.getPackageName().equals(packageName); |
| } |
| |
| @Implementation |
| protected boolean isAdminActive(ComponentName who) { |
| return who != null && deviceAdmins.contains(who); |
| } |
| |
| @Implementation |
| protected List<ComponentName> getActiveAdmins() { |
| return deviceAdmins; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void addUserRestriction(ComponentName admin, String key) { |
| enforceActiveAdmin(admin); |
| getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, true); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void clearUserRestriction(ComponentName admin, String key) { |
| enforceActiveAdmin(admin); |
| getShadowUserManager().setUserRestriction(Process.myUserHandle(), key, false); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean setApplicationHidden(ComponentName admin, String packageName, boolean hidden) { |
| enforceActiveAdmin(admin); |
| if (packagesToFailForSetApplicationHidden.contains(packageName)) { |
| return false; |
| } |
| if (hidden) { |
| wasHiddenPackages.add(packageName); |
| } |
| return applicationPackageManager.setApplicationHiddenSettingAsUser( |
| packageName, hidden, Process.myUserHandle()); |
| } |
| |
| /** |
| * Set package names for witch {@link DevicePolicyManager#setApplicationHidden} should fail. |
| * |
| * @param packagesToFail collection of package names or {@code null} to clear the packages. |
| */ |
| public void failSetApplicationHiddenFor(Collection<String> packagesToFail) { |
| if (packagesToFail == null) { |
| packagesToFail = Collections.emptySet(); |
| } |
| packagesToFailForSetApplicationHidden = packagesToFail; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean isApplicationHidden(ComponentName admin, String packageName) { |
| enforceActiveAdmin(admin); |
| return applicationPackageManager.getApplicationHiddenSettingAsUser( |
| packageName, Process.myUserHandle()); |
| } |
| |
| /** Returns {@code true} if the given {@code packageName} was ever hidden. */ |
| public boolean wasPackageEverHidden(String packageName) { |
| return wasHiddenPackages.contains(packageName); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void enableSystemApp(ComponentName admin, String packageName) { |
| enforceActiveAdmin(admin); |
| systemAppsEnabled.add(packageName); |
| } |
| |
| /** Returns {@code true} if the given {@code packageName} was a system app and was enabled. */ |
| public boolean wasSystemAppEnabled(String packageName) { |
| return systemAppsEnabled.contains(packageName); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void setUninstallBlocked( |
| ComponentName admin, String packageName, boolean uninstallBlocked) { |
| enforceActiveAdmin(admin); |
| if (uninstallBlocked) { |
| uninstallBlockedPackages.add(packageName); |
| } else { |
| uninstallBlockedPackages.remove(packageName); |
| } |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) { |
| if (admin == null) { |
| // Starting from LOLLIPOP_MR1, the behavior of this API is changed such that passing null as |
| // the admin parameter will return if any admin has blocked the uninstallation. Before L MR1, |
| // passing null will cause a NullPointerException to be raised. |
| if (Build.VERSION.SDK_INT < LOLLIPOP_MR1) { |
| throw new NullPointerException("ComponentName is null"); |
| } |
| } else { |
| enforceActiveAdmin(admin); |
| } |
| return uninstallBlockedPackages.contains(packageName); |
| } |
| |
| public void setIsUniqueDeviceAttestationSupported(boolean supported) { |
| isUniqueDeviceAttestationSupported = supported; |
| } |
| |
| @Implementation(minSdk = R) |
| protected boolean isUniqueDeviceAttestationSupported() { |
| return isUniqueDeviceAttestationSupported; |
| } |
| |
| /** Sets USB signaling device restriction. */ |
| public void setIsUsbDataSignalingEnabled(boolean isEnabled) { |
| isUsbDataSignalingEnabled = isEnabled; |
| } |
| |
| @Implementation(minSdk = S) |
| protected boolean isUsbDataSignalingEnabled() { |
| return isUsbDataSignalingEnabled; |
| } |
| |
| /** |
| * @see #setDeviceOwner(ComponentName) |
| */ |
| @Implementation(minSdk = JELLY_BEAN_MR2) |
| protected String getDeviceOwner() { |
| return deviceOwner != null ? deviceOwner.getPackageName() : null; |
| } |
| |
| /** |
| * @see #setDeviceOwner(ComponentName) |
| */ |
| @Implementation(minSdk = N) |
| public boolean isDeviceManaged() { |
| return getDeviceOwner() != null; |
| } |
| |
| /** |
| * @see #setProfileOwner(ComponentName) |
| */ |
| @Implementation(minSdk = LOLLIPOP) |
| protected ComponentName getProfileOwner() { |
| return profileOwner; |
| } |
| |
| /** |
| * Returns the human-readable name of the profile owner for a user if set using {@link |
| * #setProfileOwnerName}, otherwise null. |
| */ |
| @Implementation(minSdk = LOLLIPOP) |
| protected String getProfileOwnerNameAsUser(int userId) { |
| return profileOwnerNamesMap.get(userId); |
| } |
| |
| @Implementation(minSdk = P) |
| protected void transferOwnership( |
| ComponentName admin, ComponentName target, @Nullable PersistableBundle bundle) { |
| Objects.requireNonNull(admin, "ComponentName is null"); |
| Objects.requireNonNull(target, "Target cannot be null."); |
| Preconditions.checkArgument( |
| !admin.equals(target), "Provided administrator and target are the same object."); |
| Preconditions.checkArgument( |
| !admin.getPackageName().equals(target.getPackageName()), |
| "Provided administrator and target have the same package name."); |
| try { |
| context.getPackageManager().getReceiverInfo(target, 0); |
| } catch (PackageManager.NameNotFoundException e) { |
| throw new IllegalArgumentException("Unknown admin: " + target); |
| } |
| if (admin.equals(deviceOwner)) { |
| deviceOwner = target; |
| } else if (admin.equals(profileOwner)) { |
| profileOwner = target; |
| } else { |
| throw new SecurityException("Calling identity is not authorized"); |
| } |
| lastTransferOwnershipBundle = bundle; |
| } |
| |
| @Implementation(minSdk = P) |
| @Nullable |
| protected PersistableBundle getTransferOwnershipBundle() { |
| return lastTransferOwnershipBundle; |
| } |
| |
| private ShadowUserManager getShadowUserManager() { |
| return Shadow.extract(context.getSystemService(Context.USER_SERVICE)); |
| } |
| |
| /** |
| * Sets the admin as active admin and device owner. |
| * |
| * @see DevicePolicyManager#getDeviceOwner() |
| */ |
| @Implementation(minSdk = N, maxSdk = S_V2) |
| public boolean setDeviceOwner(ComponentName admin) { |
| setActiveAdmin(admin); |
| deviceOwner = admin; |
| return true; |
| } |
| |
| /** |
| * Sets the admin as active admin and profile owner. |
| * |
| * @see DevicePolicyManager#getProfileOwner() |
| */ |
| public void setProfileOwner(ComponentName admin) { |
| setActiveAdmin(admin); |
| profileOwner = admin; |
| } |
| |
| public void setProfileOwnerName(int userId, String name) { |
| profileOwnerNamesMap.put(userId, name); |
| } |
| |
| /** Sets the given {@code componentName} as one of the active admins. */ |
| public void setActiveAdmin(ComponentName componentName) { |
| deviceAdmins.add(componentName); |
| } |
| |
| @Implementation |
| protected void removeActiveAdmin(ComponentName admin) { |
| deviceAdmins.remove(admin); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void clearProfileOwner(ComponentName admin) { |
| profileOwner = null; |
| lastTransferOwnershipBundle = null; |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
| removeActiveAdmin(admin); |
| } |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected Bundle getApplicationRestrictions(ComponentName admin, String packageName) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return getApplicationRestrictions(packageName); |
| } |
| |
| /** Returns all application restrictions of the {@code packageName} in a {@link Bundle}. */ |
| public Bundle getApplicationRestrictions(String packageName) { |
| Bundle bundle = applicationRestrictionsMap.get(packageName); |
| // If no restrictions were saved, DPM method should return an empty Bundle as per JavaDoc. |
| return bundle != null ? new Bundle(bundle) : new Bundle(); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void setApplicationRestrictions( |
| ComponentName admin, String packageName, Bundle applicationRestrictions) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| setApplicationRestrictions(packageName, applicationRestrictions); |
| } |
| |
| /** |
| * Sets the application restrictions of the {@code packageName}. |
| * |
| * <p>The new {@code applicationRestrictions} always completely overwrites any existing ones. |
| */ |
| public void setApplicationRestrictions(String packageName, Bundle applicationRestrictions) { |
| applicationRestrictionsMap.put(packageName, new Bundle(applicationRestrictions)); |
| } |
| |
| private void enforceProfileOwner(ComponentName admin) { |
| if (!admin.equals(profileOwner)) { |
| throw new SecurityException("[" + admin + "] is not a profile owner"); |
| } |
| } |
| |
| private void enforceDeviceOwnerOrProfileOwner(ComponentName admin) { |
| if (!admin.equals(deviceOwner) && !admin.equals(profileOwner)) { |
| throw new SecurityException("[" + admin + "] is neither a device owner nor a profile owner."); |
| } |
| } |
| |
| private void enforceActiveAdmin(ComponentName admin) { |
| if (!deviceAdmins.contains(admin)) { |
| throw new SecurityException("[" + admin + "] is not an active device admin"); |
| } |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void setAccountManagementDisabled( |
| ComponentName admin, String accountType, boolean disabled) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| if (disabled) { |
| accountTypesWithManagementDisabled.add(accountType); |
| } else { |
| accountTypesWithManagementDisabled.remove(accountType); |
| } |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected String[] getAccountTypesWithManagementDisabled() { |
| return accountTypesWithManagementDisabled.toArray(new String[0]); |
| } |
| |
| /** |
| * Sets organization name. |
| * |
| * <p>The API can only be called by profile owner since Android N and can be called by both of |
| * profile owner and device owner since Android O. |
| */ |
| @Implementation(minSdk = N) |
| protected void setOrganizationName(ComponentName admin, @Nullable CharSequence name) { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| } else { |
| enforceProfileOwner(admin); |
| } |
| |
| if (TextUtils.isEmpty(name)) { |
| organizationName = null; |
| } else { |
| organizationName = name; |
| } |
| } |
| |
| @Implementation(minSdk = N) |
| protected String[] setPackagesSuspended( |
| ComponentName admin, String[] packageNames, boolean suspended) { |
| if (admin != null) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| } |
| if (packageNames == null) { |
| throw new NullPointerException("package names cannot be null"); |
| } |
| PackageManager pm = context.getPackageManager(); |
| ArrayList<String> packagesFailedToSuspend = new ArrayList<>(); |
| for (String packageName : packageNames) { |
| try { |
| // check if it is installed |
| pm.getPackageInfo(packageName, 0); |
| if (suspended) { |
| suspendedPackages.add(packageName); |
| } else { |
| suspendedPackages.remove(packageName); |
| } |
| } catch (NameNotFoundException e) { |
| packagesFailedToSuspend.add(packageName); |
| } |
| } |
| return packagesFailedToSuspend.toArray(new String[0]); |
| } |
| |
| @Implementation(minSdk = N) |
| protected boolean isPackageSuspended(ComponentName admin, String packageName) |
| throws NameNotFoundException { |
| if (admin != null) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| } |
| // Throws NameNotFoundException |
| context.getPackageManager().getPackageInfo(packageName, 0); |
| return suspendedPackages.contains(packageName); |
| } |
| |
| @Implementation(minSdk = N) |
| protected void setOrganizationColor(ComponentName admin, int color) { |
| enforceProfileOwner(admin); |
| organizationColor = color; |
| } |
| |
| /** |
| * Returns organization name. |
| * |
| * <p>The API can only be called by profile owner since Android N. |
| * |
| * <p>Android framework has a hidden API for getting the organization name for device owner since |
| * Android O. This method, however, is extended to return the organization name for device owners |
| * too to make testing of {@link #setOrganizationName(ComponentName, CharSequence)} easier for |
| * device owner cases. |
| */ |
| @Implementation(minSdk = N) |
| @Nullable |
| protected CharSequence getOrganizationName(ComponentName admin) { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| } else { |
| enforceProfileOwner(admin); |
| } |
| |
| return organizationName; |
| } |
| |
| @Implementation(minSdk = N) |
| protected int getOrganizationColor(ComponentName admin) { |
| enforceProfileOwner(admin); |
| return organizationColor; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void setAutoTimeRequired(ComponentName admin, boolean required) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| isAutoTimeRequired = required; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean getAutoTimeRequired() { |
| return isAutoTimeRequired; |
| } |
| |
| @Implementation(minSdk = R) |
| protected void setAutoTimeZoneEnabled(ComponentName admin, boolean enabled) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| isAutoTimeZoneEnabled = enabled; |
| } |
| |
| @Implementation(minSdk = R) |
| protected boolean getAutoTimeZoneEnabled(ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return isAutoTimeZoneEnabled; |
| } |
| |
| @Implementation(minSdk = P) |
| protected boolean setTimeZone(ComponentName admin, String timeZone) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| if (isAutoTimeZoneEnabled) { |
| return false; |
| } |
| this.timeZone = timeZone; |
| return true; |
| } |
| |
| /** Returns the time zone set by setTimeZone. */ |
| public String getTimeZone() { |
| return timeZone; |
| } |
| |
| /** |
| * Sets permitted accessibility services. |
| * |
| * <p>The API can be called by either a profile or device owner. |
| * |
| * <p>This method does not check already enabled non-system accessibility services, so will always |
| * set the restriction and return true. |
| */ |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean setPermittedAccessibilityServices( |
| ComponentName admin, List<String> packageNames) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| permittedAccessibilityServices = packageNames; |
| return true; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| @Nullable |
| protected List<String> getPermittedAccessibilityServices(ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return permittedAccessibilityServices; |
| } |
| |
| /** |
| * Sets permitted input methods. |
| * |
| * <p>The API can be called by either a profile or device owner. |
| * |
| * <p>This method does not check already enabled non-system input methods, so will always set the |
| * restriction and return true. |
| */ |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean setPermittedInputMethods(ComponentName admin, List<String> packageNames) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| permittedInputMethods = packageNames; |
| return true; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| @Nullable |
| protected List<String> getPermittedInputMethods(ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return permittedInputMethods; |
| } |
| |
| /** |
| * @return the previously set status; default is {@link |
| * DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} |
| * @see #setStorageEncryptionStatus(int) |
| */ |
| @Implementation |
| protected int getStorageEncryptionStatus() { |
| return storageEncryptionStatus; |
| } |
| |
| /** Setter for {@link DevicePolicyManager#getStorageEncryptionStatus()}. */ |
| public void setStorageEncryptionStatus(int status) { |
| switch (status) { |
| case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE: |
| case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE: |
| case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING: |
| case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED: |
| break; |
| case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY: |
| if (RuntimeEnvironment.getApiLevel() < M) { |
| throw new IllegalArgumentException("status " + status + " requires API " + M); |
| } |
| break; |
| case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER: |
| if (RuntimeEnvironment.getApiLevel() < N) { |
| throw new IllegalArgumentException("status " + status + " requires API " + N); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown status: " + status); |
| } |
| |
| storageEncryptionStatus = status; |
| } |
| |
| @Implementation |
| protected int setStorageEncryption(ComponentName admin, boolean encrypt) { |
| enforceActiveAdmin(admin); |
| this.storageEncryptionRequested = encrypt; |
| return storageEncryptionStatus; |
| } |
| |
| @Implementation |
| protected boolean getStorageEncryption(ComponentName admin) { |
| return storageEncryptionRequested; |
| } |
| |
| @Implementation(minSdk = VERSION_CODES.M) |
| protected int getPermissionGrantState( |
| ComponentName admin, String packageName, String permission) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| Integer state = |
| appPermissionGrantStateMap.get(new PackageAndPermission(packageName, permission)); |
| return state == null ? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT : state; |
| } |
| |
| public boolean isPermissionGranted(String packageName, String permission) { |
| Boolean isGranted = |
| appPermissionGrantedMap.get(new PackageAndPermission(packageName, permission)); |
| return isGranted == null ? false : isGranted; |
| } |
| |
| @Implementation(minSdk = VERSION_CODES.M) |
| protected boolean setPermissionGrantState( |
| ComponentName admin, String packageName, String permission, int grantState) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| |
| String selfPackageName = context.getPackageName(); |
| |
| if (packageName.equals(selfPackageName)) { |
| PackageInfo packageInfo; |
| try { |
| packageInfo = |
| context |
| .getPackageManager() |
| .getPackageInfo(selfPackageName, PackageManager.GET_PERMISSIONS); |
| } catch (NameNotFoundException e) { |
| throw new RuntimeException(e); |
| } |
| if (Arrays.asList(packageInfo.requestedPermissions).contains(permission)) { |
| if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED) { |
| ShadowApplication.getInstance().grantPermissions(permission); |
| } |
| if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED) { |
| ShadowApplication.getInstance().denyPermissions(permission); |
| } |
| } else { |
| // the app does not require this permission |
| return false; |
| } |
| } |
| PackageAndPermission key = new PackageAndPermission(packageName, permission); |
| switch (grantState) { |
| case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: |
| appPermissionGrantedMap.put(key, true); |
| break; |
| case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: |
| appPermissionGrantedMap.put(key, false); |
| break; |
| default: |
| // no-op |
| } |
| appPermissionGrantStateMap.put(key, grantState); |
| return true; |
| } |
| |
| @Implementation |
| protected void lockNow() { |
| KeyguardManager keyguardManager = |
| (KeyguardManager) this.context.getSystemService(Context.KEYGUARD_SERVICE); |
| ShadowKeyguardManager shadowKeyguardManager = Shadow.extract(keyguardManager); |
| shadowKeyguardManager.setKeyguardLocked(true); |
| shadowKeyguardManager.setIsDeviceLocked(true); |
| } |
| |
| @Implementation |
| protected void wipeData(int flags) { |
| wipeCalled++; |
| } |
| |
| public long getWipeCalledTimes() { |
| return wipeCalled; |
| } |
| |
| @Implementation |
| protected void setPasswordQuality(ComponentName admin, int quality) { |
| enforceActiveAdmin(admin); |
| requiredPasswordQuality = quality; |
| } |
| |
| @Implementation |
| protected int getPasswordQuality(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return requiredPasswordQuality; |
| } |
| |
| @Implementation |
| protected boolean resetPassword(String password, int flags) { |
| if (!passwordMeetsRequirements(password)) { |
| return false; |
| } |
| lastSetPassword = password; |
| boolean secure = !password.isEmpty(); |
| KeyguardManager keyguardManager = |
| (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); |
| shadowOf(keyguardManager).setIsDeviceSecure(secure); |
| shadowOf(keyguardManager).setIsKeyguardSecure(secure); |
| return true; |
| } |
| |
| @Implementation(minSdk = O) |
| protected boolean resetPasswordWithToken( |
| ComponentName admin, String password, byte[] token, int flags) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| if (!Arrays.equals(passwordResetTokens.get(admin), token) |
| || !componentsWithActivatedTokens.contains(admin)) { |
| throw new IllegalStateException("wrong or not activated token"); |
| } |
| resetPassword(password, flags); |
| return true; |
| } |
| |
| @Implementation(minSdk = O) |
| protected boolean isResetPasswordTokenActive(ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return componentsWithActivatedTokens.contains(admin); |
| } |
| |
| @Implementation(minSdk = O) |
| protected boolean setResetPasswordToken(ComponentName admin, byte[] token) { |
| if (token.length < 32) { |
| throw new IllegalArgumentException("token too short: " + token.length); |
| } |
| enforceDeviceOwnerOrProfileOwner(admin); |
| passwordResetTokens.put(admin, token); |
| componentsWithActivatedTokens.remove(admin); |
| KeyguardManager keyguardManager = |
| (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); |
| if (!keyguardManager.isDeviceSecure()) { |
| activateResetToken(admin); |
| } |
| return true; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumLength(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumLength = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumLength(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumLength; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumLetters(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumLetters = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumLetters(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumLetters; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumLowerCase(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumLowerCase = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumLowerCase(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumLowerCase; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumUpperCase(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumUpperCase = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumUpperCase(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumUpperCase; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumNonLetter(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumNonLetter = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumNonLetter(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumNonLetter; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumNumeric(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumNumeric = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumNumeric(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumNumeric; |
| } |
| |
| @Implementation |
| protected void setPasswordMinimumSymbols(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordMinimumSymbols = length; |
| } |
| |
| @Implementation |
| protected int getPasswordMinimumSymbols(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordMinimumSymbols; |
| } |
| |
| @Implementation |
| protected void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) { |
| enforceActiveAdmin(admin); |
| maximumFailedPasswordsForWipe = num; |
| } |
| |
| @Implementation |
| protected int getMaximumFailedPasswordsForWipe(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return maximumFailedPasswordsForWipe; |
| } |
| |
| @Implementation |
| protected void setCameraDisabled(ComponentName admin, boolean disabled) { |
| enforceActiveAdmin(admin); |
| cameraDisabled = disabled; |
| } |
| |
| @Implementation |
| protected boolean getCameraDisabled(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return cameraDisabled; |
| } |
| |
| @Implementation |
| protected void setPasswordExpirationTimeout(ComponentName admin, long timeout) { |
| enforceActiveAdmin(admin); |
| passwordExpirationTimeout = timeout; |
| } |
| |
| @Implementation |
| protected long getPasswordExpirationTimeout(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordExpirationTimeout; |
| } |
| |
| /** |
| * Sets the password expiration time for a particular admin. |
| * |
| * @param admin which DeviceAdminReceiver this request is associated with. |
| * @param timeout the password expiration time, in milliseconds since epoch. |
| */ |
| public void setPasswordExpiration(ComponentName admin, long timeout) { |
| enforceActiveAdmin(admin); |
| passwordExpiration = timeout; |
| } |
| |
| @Implementation |
| protected long getPasswordExpiration(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordExpiration; |
| } |
| |
| @Implementation |
| protected void setMaximumTimeToLock(ComponentName admin, long timeMs) { |
| enforceActiveAdmin(admin); |
| maximumTimeToLock = timeMs; |
| } |
| |
| @Implementation |
| protected long getMaximumTimeToLock(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return maximumTimeToLock; |
| } |
| |
| @Implementation |
| protected void setPasswordHistoryLength(ComponentName admin, int length) { |
| enforceActiveAdmin(admin); |
| passwordHistoryLength = length; |
| } |
| |
| @Implementation |
| protected int getPasswordHistoryLength(ComponentName admin) { |
| if (admin != null) { |
| enforceActiveAdmin(admin); |
| } |
| return passwordHistoryLength; |
| } |
| |
| /** |
| * Sets if the password meets the current requirements. |
| * |
| * @param sufficient indicates the password meets the current requirements |
| */ |
| public void setActivePasswordSufficient(boolean sufficient) { |
| isActivePasswordSufficient = sufficient; |
| } |
| |
| @Implementation |
| protected boolean isActivePasswordSufficient() { |
| return isActivePasswordSufficient; |
| } |
| |
| /** Sets whether the device is provisioned. */ |
| public void setDeviceProvisioned(boolean isProvisioned) { |
| isDeviceProvisioned = isProvisioned; |
| } |
| |
| @Implementation(minSdk = O) |
| @SystemApi |
| @RequiresPermission(android.Manifest.permission.MANAGE_USERS) |
| protected boolean isDeviceProvisioned() { |
| return isDeviceProvisioned; |
| } |
| |
| @Implementation(minSdk = O) |
| @SystemApi |
| @RequiresPermission(android.Manifest.permission.MANAGE_USERS) |
| protected void setDeviceProvisioningConfigApplied() { |
| isDeviceProvisioningConfigApplied = true; |
| } |
| |
| @Implementation(minSdk = O) |
| @SystemApi |
| @RequiresPermission(android.Manifest.permission.MANAGE_USERS) |
| protected boolean isDeviceProvisioningConfigApplied() { |
| return isDeviceProvisioningConfigApplied; |
| } |
| |
| /** Sets the password complexity. */ |
| public void setPasswordComplexity(@PasswordComplexity int passwordComplexity) { |
| this.passwordComplexity = passwordComplexity; |
| } |
| |
| @PasswordComplexity |
| @Implementation(minSdk = Q) |
| protected int getPasswordComplexity() { |
| return passwordComplexity; |
| } |
| |
| private boolean passwordMeetsRequirements(String password) { |
| int digit = 0; |
| int alpha = 0; |
| int upper = 0; |
| int lower = 0; |
| int symbol = 0; |
| for (int i = 0; i < password.length(); i++) { |
| char c = password.charAt(i); |
| if (Character.isDigit(c)) { |
| digit++; |
| } |
| if (Character.isLetter(c)) { |
| alpha++; |
| } |
| if (Character.isUpperCase(c)) { |
| upper++; |
| } |
| if (Character.isLowerCase(c)) { |
| lower++; |
| } |
| if (!Character.isLetterOrDigit(c)) { |
| symbol++; |
| } |
| } |
| switch (requiredPasswordQuality) { |
| case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: |
| case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: |
| case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: |
| return true; |
| case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: |
| return password.length() > 0; |
| case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: // complexity not enforced |
| return digit > 0 && password.length() >= passwordMinimumLength; |
| case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: |
| return digit > 0 && alpha > 0 && password.length() >= passwordMinimumLength; |
| case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: |
| return password.length() >= passwordMinimumLength |
| && alpha >= passwordMinimumLetters |
| && lower >= passwordMinimumLowerCase |
| && upper >= passwordMinimumUpperCase |
| && digit + symbol >= passwordMinimumNonLetter |
| && digit >= passwordMinimumNumeric |
| && symbol >= passwordMinimumSymbols; |
| default: |
| return true; |
| } |
| } |
| |
| /** |
| * Retrieves last password set through {@link DevicePolicyManager#resetPassword} or {@link |
| * DevicePolicyManager#resetPasswordWithToken}. |
| */ |
| public String getLastSetPassword() { |
| return lastSetPassword; |
| } |
| |
| /** |
| * Activates reset token for given admin. |
| * |
| * @param admin Which {@link DeviceAdminReceiver} this request is associated with. |
| * @return if the activation state changed. |
| * @throws IllegalArgumentException if there is no token set for this admin. |
| */ |
| public boolean activateResetToken(ComponentName admin) { |
| if (!passwordResetTokens.containsKey(admin)) { |
| throw new IllegalArgumentException("No token set for comopnent: " + admin); |
| } |
| return componentsWithActivatedTokens.add(admin); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void addPersistentPreferredActivity( |
| ComponentName admin, IntentFilter filter, ComponentName activity) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| |
| PackageManager packageManager = context.getPackageManager(); |
| Shadow.<ShadowPackageManager>extract(packageManager) |
| .addPersistentPreferredActivity(filter, activity); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void clearPackagePersistentPreferredActivities( |
| ComponentName admin, String packageName) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| PackageManager packageManager = context.getPackageManager(); |
| Shadow.<ShadowPackageManager>extract(packageManager) |
| .clearPackagePersistentPreferredActivities(packageName); |
| } |
| |
| @Implementation(minSdk = JELLY_BEAN_MR1) |
| protected void setKeyguardDisabledFeatures(ComponentName admin, int which) { |
| enforceActiveAdmin(admin); |
| keyguardDisabledFeatures = which; |
| } |
| |
| @Implementation(minSdk = JELLY_BEAN_MR1) |
| protected int getKeyguardDisabledFeatures(ComponentName admin) { |
| return keyguardDisabledFeatures; |
| } |
| |
| /** |
| * Sets the user provisioning state. |
| * |
| * @param state to store provisioning state |
| */ |
| public void setUserProvisioningState(int state) { |
| setUserProvisioningState(state, Process.myUserHandle()); |
| } |
| |
| @Implementation(minSdk = TIRAMISU) |
| protected void setUserProvisioningState(@UserProvisioningState int state, UserHandle userHandle) { |
| userProvisioningStatesMap.put(userHandle.getIdentifier(), state); |
| } |
| |
| /** |
| * Returns the provisioning state set in {@link #setUserProvisioningState(int)}, or {@link |
| * DevicePolicyManager#STATE_USER_UNMANAGED} if none is set. |
| */ |
| @Implementation(minSdk = N) |
| protected int getUserProvisioningState() { |
| return getUserProvisioningStateForUser(Process.myUserHandle().getIdentifier()); |
| } |
| |
| @Implementation |
| protected boolean hasGrantedPolicy(@NonNull ComponentName admin, int usesPolicy) { |
| enforceActiveAdmin(admin); |
| Set<Integer> policyGrantedSet = adminPolicyGrantedMap.get(admin); |
| return policyGrantedSet != null && policyGrantedSet.contains(usesPolicy); |
| } |
| |
| @Implementation(minSdk = P) |
| protected int getLockTaskFeatures(ComponentName admin) { |
| Objects.requireNonNull(admin, "ComponentName is null"); |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return lockTaskFeatures; |
| } |
| |
| @Implementation(minSdk = P) |
| protected void setLockTaskFeatures(ComponentName admin, int flags) { |
| Objects.requireNonNull(admin, "ComponentName is null"); |
| enforceDeviceOwnerOrProfileOwner(admin); |
| // Throw if Overview is used without Home. |
| boolean hasHome = (flags & LOCK_TASK_FEATURE_HOME) != 0; |
| boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0; |
| Preconditions.checkArgument( |
| hasHome || !hasOverview, |
| "Cannot use LOCK_TASK_FEATURE_OVERVIEW without LOCK_TASK_FEATURE_HOME"); |
| boolean hasNotification = (flags & LOCK_TASK_FEATURE_NOTIFICATIONS) != 0; |
| Preconditions.checkArgument( |
| hasHome || !hasNotification, |
| "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME"); |
| |
| lockTaskFeatures = flags; |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected void setLockTaskPackages(@NonNull ComponentName admin, String[] packages) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| lockTaskPackages.clear(); |
| Collections.addAll(lockTaskPackages, packages); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected String[] getLockTaskPackages(@NonNull ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return lockTaskPackages.toArray(new String[0]); |
| } |
| |
| @Implementation(minSdk = LOLLIPOP) |
| protected boolean isLockTaskPermitted(@NonNull String pkg) { |
| return lockTaskPackages.contains(pkg); |
| } |
| |
| @Implementation(minSdk = O) |
| protected void setAffiliationIds(@NonNull ComponentName admin, @NonNull Set<String> ids) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| affiliationIds.clear(); |
| affiliationIds.addAll(ids); |
| } |
| |
| @Implementation(minSdk = O) |
| protected Set<String> getAffiliationIds(@NonNull ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return affiliationIds; |
| } |
| |
| @Implementation(minSdk = M) |
| protected void setPermissionPolicy(@NonNull ComponentName admin, int policy) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| permissionPolicy = policy; |
| } |
| |
| @Implementation(minSdk = M) |
| protected int getPermissionPolicy(ComponentName admin) { |
| enforceDeviceOwnerOrProfileOwner(admin); |
| return permissionPolicy; |
| } |
| |
| /** |
| * Grants a particular device policy for an active ComponentName. |
| * |
| * @param admin the ComponentName which DeviceAdminReceiver this request is associated with. Must |
| * be an active administrator, or an exception will be thrown. This value must never be null. |
| * @param usesPolicy the uses-policy to check |
| */ |
| public void grantPolicy(@NonNull ComponentName admin, int usesPolicy) { |
| enforceActiveAdmin(admin); |
| Set<Integer> policyGrantedSet = adminPolicyGrantedMap.get(admin); |
| if (policyGrantedSet == null) { |
| policyGrantedSet = new HashSet<>(); |
| policyGrantedSet.add(usesPolicy); |
| adminPolicyGrantedMap.put(admin, policyGrantedSet); |
| } else { |
| policyGrantedSet.add(usesPolicy); |
| } |
| } |
| |
| @Implementation(minSdk = M) |
| protected SystemUpdatePolicy getSystemUpdatePolicy() { |
| return policy; |
| } |
| |
| @Implementation(minSdk = M) |
| protected void setSystemUpdatePolicy(ComponentName admin, SystemUpdatePolicy policy) { |
| this.policy = policy; |
| } |
| |
| /** |
| * Sets the system update policy. |
| * |
| * @see #setSystemUpdatePolicy(ComponentName, SystemUpdatePolicy) |
| */ |
| public void setSystemUpdatePolicy(SystemUpdatePolicy policy) { |
| setSystemUpdatePolicy(null, policy); |
| } |
| |
| /** |
| * Set the list of target users that the calling device or profile owner can use when calling |
| * {@link #bindDeviceAdminServiceAsUser}. |
| * |
| * @see #getBindDeviceAdminTargetUsers(ComponentName) |
| */ |
| public void setBindDeviceAdminTargetUsers(List<UserHandle> bindDeviceAdminTargetUsers) { |
| this.bindDeviceAdminTargetUsers = bindDeviceAdminTargetUsers; |
| } |
| |
| /** |
| * Returns the list of target users that the calling device or profile owner can use when calling |
| * {@link #bindDeviceAdminServiceAsUser}. |
| * |
| * @see #setBindDeviceAdminTargetUsers(List) |
| */ |
| @Implementation(minSdk = O) |
| protected List<UserHandle> getBindDeviceAdminTargetUsers(ComponentName admin) { |
| return bindDeviceAdminTargetUsers; |
| } |
| |
| /** |
| * Bind to the same package in another user. |
| * |
| * <p>This validates that the targetUser is one from {@link |
| * #getBindDeviceAdminTargetUsers(ComponentName)} but does not actually bind to a different user, |
| * instead binding to the same user. |
| * |
| * <p>It also does not validate the service being bound to. |
| */ |
| @Implementation(minSdk = O) |
| protected boolean bindDeviceAdminServiceAsUser( |
| ComponentName admin, |
| Intent serviceIntent, |
| ServiceConnection conn, |
| int flags, |
| UserHandle targetUser) { |
| if (!getBindDeviceAdminTargetUsers(admin).contains(targetUser)) { |
| throw new SecurityException("Not allowed to bind to target user id"); |
| } |
| |
| return context.bindServiceAsUser(serviceIntent, conn, flags, targetUser); |
| } |
| |
| @Implementation(minSdk = N) |
| protected void setShortSupportMessage(ComponentName admin, @Nullable CharSequence message) { |
| enforceActiveAdmin(admin); |
| shortSupportMessageMap.put(admin, message); |
| } |
| |
| @Implementation(minSdk = N) |
| @Nullable |
| protected CharSequence getShortSupportMessage(ComponentName admin) { |
| enforceActiveAdmin(admin); |
| return shortSupportMessageMap.get(admin); |
| } |
| |
| @Implementation(minSdk = N) |
| protected void setLongSupportMessage(ComponentName admin, @Nullable CharSequence message) { |
| enforceActiveAdmin(admin); |
| longSupportMessageMap.put(admin, message); |
| } |
| |
| @Implementation(minSdk = N) |
| @Nullable |
| protected CharSequence getLongSupportMessage(ComponentName admin) { |
| enforceActiveAdmin(admin); |
| return longSupportMessageMap.get(admin); |
| } |
| |
| /** |
| * Sets the return value of the {@link |
| * DevicePolicyManager#isOrganizationOwnedDeviceWithManagedProfile} method (only for Android R+). |
| */ |
| public void setOrganizationOwnedDeviceWithManagedProfile(boolean value) { |
| organizationOwnedDeviceWithManagedProfile = value; |
| } |
| |
| /** |
| * Returns the value stored using in the shadow, while the real method returns the value store on |
| * the device. |
| * |
| * <p>The value can be set by {@link #setOrganizationOwnedDeviceWithManagedProfile} and is {@code |
| * false} by default. |
| */ |
| @Implementation(minSdk = R) |
| protected boolean isOrganizationOwnedDeviceWithManagedProfile() { |
| return organizationOwnedDeviceWithManagedProfile; |
| } |
| |
| @Implementation(minSdk = S) |
| @NearbyStreamingPolicy |
| protected int getNearbyNotificationStreamingPolicy() { |
| return nearbyNotificationStreamingPolicy; |
| } |
| |
| @Implementation(minSdk = S) |
| protected void setNearbyNotificationStreamingPolicy(@NearbyStreamingPolicy int policy) { |
| nearbyNotificationStreamingPolicy = policy; |
| } |
| |
| @Implementation(minSdk = S) |
| @NearbyStreamingPolicy |
| protected int getNearbyAppStreamingPolicy() { |
| return nearbyAppStreamingPolicy; |
| } |
| |
| @Implementation(minSdk = S) |
| protected void setNearbyAppStreamingPolicy(@NearbyStreamingPolicy int policy) { |
| nearbyAppStreamingPolicy = policy; |
| } |
| |
| @Nullable |
| @Implementation(minSdk = TIRAMISU) |
| protected String getDevicePolicyManagementRoleHolderPackage() { |
| return devicePolicyManagementRoleHolderPackage; |
| } |
| |
| /** |
| * Sets the package name of the device policy management role holder. |
| * |
| * @see #getDevicePolicyManagementRoleHolderPackage() |
| */ |
| public void setDevicePolicyManagementRoleHolderPackage(@Nullable String packageName) { |
| devicePolicyManagementRoleHolderPackage = packageName; |
| } |
| |
| @Implementation(minSdk = TIRAMISU) |
| protected void finalizeWorkProfileProvisioning( |
| UserHandle managedProfileUser, @Nullable Account migratedAccount) { |
| finalizedWorkProfileProvisioningMap.put(managedProfileUser, migratedAccount); |
| } |
| |
| /** |
| * Returns if {@link #finalizeWorkProfileProvisioning(UserHandle, Account)} was called with the |
| * provided parameters. |
| */ |
| public boolean isWorkProfileProvisioningFinalized( |
| UserHandle userHandle, @Nullable Account migratedAccount) { |
| return finalizedWorkProfileProvisioningMap.containsKey(userHandle) |
| && Objects.equals(finalizedWorkProfileProvisioningMap.get(userHandle), migratedAccount); |
| } |
| |
| /** |
| * Returns the managed profiles set in {@link #setPolicyManagedProfiles(List)}. This value does |
| * not take the user handle parameter into account. |
| */ |
| @Implementation(minSdk = TIRAMISU) |
| protected List<UserHandle> getPolicyManagedProfiles(UserHandle userHandle) { |
| return policyManagedProfiles; |
| } |
| |
| /** Sets the value returned by {@link #getPolicyManagedProfiles(UserHandle)}. */ |
| public void setPolicyManagedProfiles(List<UserHandle> policyManagedProfiles) { |
| this.policyManagedProfiles = policyManagedProfiles; |
| } |
| |
| /** |
| * Returns the user provisioning state set by {@link #setUserProvisioningState(int, UserHandle)}, |
| * or {@link DevicePolicyManager#STATE_USER_UNMANAGED} if none is set. |
| */ |
| @UserProvisioningState |
| public int getUserProvisioningStateForUser(int userId) { |
| return userProvisioningStatesMap.getOrDefault(userId, DevicePolicyManager.STATE_USER_UNMANAGED); |
| } |
| } |