blob: a604905c13d6dbd9cfbbcb7491986f7a0b216d62 [file] [log] [blame]
package org.robolectric.shadows;
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.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 org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ApplicationPackageManager;
import android.app.KeyguardManager;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
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.Process;
import android.text.TextUtils;
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.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;
/** Shadow for {@link DevicePolicyManager} */
@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 int keyguardDisabledFeatures;
private String lastSetPassword;
private int requiredPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
private int userProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
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 wipeCalled;
private int storageEncryptionStatus;
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 Map<PackageAndPermission, Boolean> appPermissionGrantedMap = new HashMap<>();
private final Map<PackageAndPermission, Integer> appPermissionGrantStateMap = new HashMap<>();
private final Map<ComponentName, byte[]> passwordResetTokens = new HashMap<>();
private final Set<ComponentName> componentsWithActivatedTokens = new HashSet<>();
private Collection<String> packagesToFailForSetApplicationHidden = Collections.emptySet();
private Context context;
private ApplicationPackageManager applicationPackageManager;
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(ComponentName admin, String packageName) {
enforceActiveAdmin(admin);
return uninstallBlockedPackages.contains(packageName);
}
/** @see #setDeviceOwner(ComponentName) */
@Implementation(minSdk = JELLY_BEAN_MR2)
protected String getDeviceOwner() {
return deviceOwner != null ? deviceOwner.getPackageName() : 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);
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(context.getSystemService(Context.USER_SERVICE));
}
/**
* Sets the admin as active admin and device owner.
*
* @see DevicePolicyManager#getDeviceOwner()
*/
public void setDeviceOwner(ComponentName admin) {
setActiveAdmin(admin);
deviceOwner = admin;
}
/**
* 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;
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 enforceDeviceOwner(ComponentName admin) {
if (!admin.equals(deviceOwner)) {
throw new SecurityException("[" + admin + "] is not a device 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;
}
/**
* 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(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 boolean resetPassword(String password, int flags) {
if (!passwordMeetsRequirements(password)) {
return false;
}
lastSetPassword = password;
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);
return true;
}
@Implementation
protected void setPasswordMinimumLength(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumLength = length;
}
@Implementation
protected void setPasswordMinimumLetters(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumLetters = length;
}
@Implementation
protected void setPasswordMinimumLowerCase(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumLowerCase = length;
}
@Implementation
protected void setPasswordMinimumUpperCase(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumUpperCase = length;
}
@Implementation
protected void setPasswordMinimumNonLetter(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumNonLetter = length;
}
@Implementation
protected void setPasswordMinimumNumeric(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumNumeric = length;
}
@Implementation
protected void setPasswordMinimumSymbols(ComponentName admin, int length) {
enforceActiveAdmin(admin);
passwordMinimumSymbols = length;
}
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();
packageManager.addPreferredActivity(filter, 0, null, activity);
}
@Implementation(minSdk = LOLLIPOP)
protected void clearPackagePersistentPreferredActivities(
ComponentName admin, String packageName) {
enforceDeviceOwnerOrProfileOwner(admin);
PackageManager packageManager = context.getPackageManager();
packageManager.clearPackagePreferredActivities(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) {
userProvisioningState = state;
}
/** @return Returns the provisioning state for the current user. */
@Implementation(minSdk = N)
protected int getUserProvisioningState() {
return userProvisioningState;
}
}