Loading services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +12 −0 Original line number Diff line number Diff line Loading @@ -98,6 +98,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT = "password-quality-applies-parent"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = "cross-profile-widget-providers"; Loading Loading @@ -143,6 +145,7 @@ class ActiveAdmin { @NonNull PasswordPolicy mPasswordPolicy = new PasswordPolicy(); boolean mPasswordPolicyAppliesToParent = true; @DevicePolicyManager.PasswordComplexity int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; Loading Loading @@ -333,6 +336,9 @@ class ActiveAdmin { writeAttributeValueToXml( out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter); } writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT, mPasswordPolicyAppliesToParent); } if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { writeAttributeValueToXml( Loading Loading @@ -626,6 +632,9 @@ class ActiveAdmin { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { mPasswordPolicy.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { mPasswordPolicyAppliesToParent = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); Loading Loading @@ -995,6 +1004,9 @@ class ActiveAdmin { pw.print("minimumPasswordNonLetter="); pw.println(mPasswordPolicy.nonLetter); pw.print("passwordPolicyAppliesToParent="); pw.println(mPasswordPolicyAppliesToParent); pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +55 −5 Original line number Diff line number Diff line Loading @@ -155,10 +155,12 @@ import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.UnsafeStateException; import android.app.backup.IBackupManager; import android.app.compat.CompatChanges; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -527,6 +529,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; /** * Admin apps targeting Android S+ may not use * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality * on the {@code DevicePolicyManager} instance obtained by calling * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}. * Instead, they should use * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set * coarse-grained password requirements device-wide. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L; final Context mContext; final Injector mInjector; final IPackageManager mIPackageManager; Loading Loading @@ -1396,6 +1411,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } public boolean isChangeEnabled(long changeId, String packageName, int userId) { return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); } } /** Loading Loading @@ -3315,6 +3334,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M; } private boolean canSetPasswordQualityOnParent(String packageName, int userId) { return !mInjector.isChangeEnabled( PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, userId); } @Override public void setPasswordQuality(ComponentName who, int quality, boolean parent) { if (!mHasFeature) { Loading @@ -3323,7 +3347,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); validateQualityConstant(quality); final int userId = mInjector.userHandleGetCallingUserId(); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)); final boolean qualityMayApplyToParent = canSetPasswordQualityOnParent(who.getPackageName(), caller.getUserId()); if (!qualityMayApplyToParent) { Preconditions.checkArgument(!parent, "Profile Owner may not apply password quality requirements device-wide"); } final int userId = caller.getUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); Loading @@ -3332,6 +3367,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (passwordPolicy.quality != quality) { passwordPolicy.quality = quality; ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent; resetInactivePasswordRequirementsIfRPlus(userId, ap); updatePasswordValidityCheckpointLocked(userId, parent); updatePasswordQualityCacheForUserGroup(userId); Loading Loading @@ -4063,9 +4099,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId); for (ActiveAdmin admin : admins) { final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier(); // Use the password metrics from the admin in one of three cases: // (1) The admin is of the user we're getting the minimum metrics for. The admin // always affects the user it's managing. This applies also to the parent // ActiveAdmin instance: It'd have the same user handle. // (2) The mPasswordPolicyAppliesToParent field is true: That indicates the // call to setPasswordQuality was made by an admin that may affect the parent. if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); } } } return PasswordMetrics.merge(adminMetrics); } Loading Loading @@ -4155,13 +4200,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)); ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size()); int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); if (isDeviceOwner(admin) || isProfileOwnerUncheckedLocked(admin.info.getComponent(), admin.getUserHandle().getIdentifier())) { maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } } //TODO: Take complexity into account, would need to take complexity from all admins //in the admins list. return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty(); maxRequiredComplexity, false, metrics).isEmpty(); } } Loading Loading @@ -4255,6 +4304,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. admin.mPasswordPolicy = new PasswordPolicy(); admin.mPasswordPolicyAppliesToParent = true; updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent); updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +30 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,8 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; private final Map<Long, Pair<String, Integer>> mEnabledChanges = new ArrayMap<>(); public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; Loading Loading @@ -487,5 +489,33 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } public void setChangeEnabledForPackage( long changeId, boolean enabled, String packageName, int userId) { if (enabled) { mEnabledChanges.put(changeId, Pair.create(packageName, userId)); } else { mEnabledChanges.remove(changeId); } } public void clearEnabledChanges() { mEnabledChanges.clear(); } @Override public boolean isChangeEnabled(long changeId, String packageName, int userId) { Pair<String, Integer> packageAndUser = mEnabledChanges.get(changeId); if (packageAndUser == null) { return false; } if (!packageAndUser.first.equals(packageName) || !packageAndUser.second.equals(userId)) { return false; } return true; } } } services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +79 −5 Original line number Diff line number Diff line Loading @@ -5090,11 +5090,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { doReturn(true).when(getServices().lockPatternUtils) .isSeparateProfileChallengeEnabled(managedProfileUserId); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC); parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) .thenReturn(computeForPassword("1234".getBytes())); .thenReturn(computeForPassword("184342".getBytes())); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) Loading @@ -5107,6 +5107,68 @@ public class DevicePolicyManagerTest extends DpmTestBase { managedProfileUserId)).isFalse(); } @Test public void testCanSetPasswordRequirementOnParentPreS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); dpms.mMockInjector.setChangeEnabledForPackage(165573442L, false, admin1.getPackageName(), managedProfileUserId); parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(admin1)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); } @Test public void testCannotSetPasswordRequirementOnParent() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1); dpms.mMockInjector.setChangeEnabledForPackage(165573442L, true, admin1.getPackageName(), managedProfileUserId); try { assertExpectException(IllegalArgumentException.class, null, () -> parentDpm.setPasswordQuality( admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); } finally { dpms.mMockInjector.clearEnabledChanges(); } } @Test public void testPasswordQualityAppliesToParentPreS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE)) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(null)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); } @Test public void testPasswordQualityDoesNotApplyToParentPostS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(admin1)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } private void setActivePasswordState(PasswordMetrics passwordMetrics) throws Exception { final int userHandle = UserHandle.getUserId(mContext.binder.callingUid); Loading Loading @@ -7014,19 +7076,31 @@ public class DevicePolicyManagerTest extends DpmTestBase { * @param adminUid uid of the admin package. * @param copyFromAdmin package information for {@code admin} will be built based on this * component's information. * @param appTargetSdk admin's target SDK level */ private void addManagedProfile( ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { ComponentName admin, int adminUid, ComponentName copyFromAdmin, int appTargetSdk) throws Exception { final int userId = UserHandle.getUserId(adminUid); getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null, appTargetSdk, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } /** * Same as {@code addManagedProfile} above, except using development API level as the API * level of the admin. */ private void addManagedProfile( ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { addManagedProfile(admin, adminUid, copyFromAdmin, VERSION_CODES.CUR_DEVELOPMENT); } /** * Convert String[] to StringParceledListSlice. */ Loading Loading
services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +12 −0 Original line number Diff line number Diff line Loading @@ -98,6 +98,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT = "password-quality-applies-parent"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = "cross-profile-widget-providers"; Loading Loading @@ -143,6 +145,7 @@ class ActiveAdmin { @NonNull PasswordPolicy mPasswordPolicy = new PasswordPolicy(); boolean mPasswordPolicyAppliesToParent = true; @DevicePolicyManager.PasswordComplexity int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; Loading Loading @@ -333,6 +336,9 @@ class ActiveAdmin { writeAttributeValueToXml( out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter); } writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT, mPasswordPolicyAppliesToParent); } if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { writeAttributeValueToXml( Loading Loading @@ -626,6 +632,9 @@ class ActiveAdmin { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { mPasswordPolicy.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { mPasswordPolicyAppliesToParent = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); Loading Loading @@ -995,6 +1004,9 @@ class ActiveAdmin { pw.print("minimumPasswordNonLetter="); pw.println(mPasswordPolicy.nonLetter); pw.print("passwordPolicyAppliesToParent="); pw.println(mPasswordPolicyAppliesToParent); pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +55 −5 Original line number Diff line number Diff line Loading @@ -155,10 +155,12 @@ import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.UnsafeStateException; import android.app.backup.IBackupManager; import android.app.compat.CompatChanges; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading Loading @@ -527,6 +529,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; /** * Admin apps targeting Android S+ may not use * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality * on the {@code DevicePolicyManager} instance obtained by calling * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}. * Instead, they should use * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set * coarse-grained password requirements device-wide. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L; final Context mContext; final Injector mInjector; final IPackageManager mIPackageManager; Loading Loading @@ -1396,6 +1411,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } public boolean isChangeEnabled(long changeId, String packageName, int userId) { return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); } } /** Loading Loading @@ -3315,6 +3334,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M; } private boolean canSetPasswordQualityOnParent(String packageName, int userId) { return !mInjector.isChangeEnabled( PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, userId); } @Override public void setPasswordQuality(ComponentName who, int quality, boolean parent) { if (!mHasFeature) { Loading @@ -3323,7 +3347,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); validateQualityConstant(quality); final int userId = mInjector.userHandleGetCallingUserId(); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)); final boolean qualityMayApplyToParent = canSetPasswordQualityOnParent(who.getPackageName(), caller.getUserId()); if (!qualityMayApplyToParent) { Preconditions.checkArgument(!parent, "Profile Owner may not apply password quality requirements device-wide"); } final int userId = caller.getUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); Loading @@ -3332,6 +3367,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (passwordPolicy.quality != quality) { passwordPolicy.quality = quality; ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent; resetInactivePasswordRequirementsIfRPlus(userId, ap); updatePasswordValidityCheckpointLocked(userId, parent); updatePasswordQualityCacheForUserGroup(userId); Loading Loading @@ -4063,9 +4099,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId); for (ActiveAdmin admin : admins) { final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier(); // Use the password metrics from the admin in one of three cases: // (1) The admin is of the user we're getting the minimum metrics for. The admin // always affects the user it's managing. This applies also to the parent // ActiveAdmin instance: It'd have the same user handle. // (2) The mPasswordPolicyAppliesToParent field is true: That indicates the // call to setPasswordQuality was made by an admin that may affect the parent. if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); } } } return PasswordMetrics.merge(adminMetrics); } Loading Loading @@ -4155,13 +4200,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)); ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size()); int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); if (isDeviceOwner(admin) || isProfileOwnerUncheckedLocked(admin.info.getComponent(), admin.getUserHandle().getIdentifier())) { maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } } //TODO: Take complexity into account, would need to take complexity from all admins //in the admins list. return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty(); maxRequiredComplexity, false, metrics).isEmpty(); } } Loading Loading @@ -4255,6 +4304,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. admin.mPasswordPolicy = new PasswordPolicy(); admin.mPasswordPolicyAppliesToParent = true; updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent); updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +30 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,8 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; private final Map<Long, Pair<String, Integer>> mEnabledChanges = new ArrayMap<>(); public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; Loading Loading @@ -487,5 +489,33 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } public void setChangeEnabledForPackage( long changeId, boolean enabled, String packageName, int userId) { if (enabled) { mEnabledChanges.put(changeId, Pair.create(packageName, userId)); } else { mEnabledChanges.remove(changeId); } } public void clearEnabledChanges() { mEnabledChanges.clear(); } @Override public boolean isChangeEnabled(long changeId, String packageName, int userId) { Pair<String, Integer> packageAndUser = mEnabledChanges.get(changeId); if (packageAndUser == null) { return false; } if (!packageAndUser.first.equals(packageName) || !packageAndUser.second.equals(userId)) { return false; } return true; } } }
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +79 −5 Original line number Diff line number Diff line Loading @@ -5090,11 +5090,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { doReturn(true).when(getServices().lockPatternUtils) .isSeparateProfileChallengeEnabled(managedProfileUserId); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC); parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) .thenReturn(computeForPassword("1234".getBytes())); .thenReturn(computeForPassword("184342".getBytes())); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) Loading @@ -5107,6 +5107,68 @@ public class DevicePolicyManagerTest extends DpmTestBase { managedProfileUserId)).isFalse(); } @Test public void testCanSetPasswordRequirementOnParentPreS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); dpms.mMockInjector.setChangeEnabledForPackage(165573442L, false, admin1.getPackageName(), managedProfileUserId); parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(admin1)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); } @Test public void testCannotSetPasswordRequirementOnParent() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1); dpms.mMockInjector.setChangeEnabledForPackage(165573442L, true, admin1.getPackageName(), managedProfileUserId); try { assertExpectException(IllegalArgumentException.class, null, () -> parentDpm.setPasswordQuality( admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); } finally { dpms.mMockInjector.clearEnabledChanges(); } } @Test public void testPasswordQualityAppliesToParentPreS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE)) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(null)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); } @Test public void testPasswordQualityDoesNotApplyToParentPostS() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); mContext.binder.callingUid = managedProfileAdminUid; addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertThat(parentDpm.getPasswordQuality(admin1)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } private void setActivePasswordState(PasswordMetrics passwordMetrics) throws Exception { final int userHandle = UserHandle.getUserId(mContext.binder.callingUid); Loading Loading @@ -7014,19 +7076,31 @@ public class DevicePolicyManagerTest extends DpmTestBase { * @param adminUid uid of the admin package. * @param copyFromAdmin package information for {@code admin} will be built based on this * component's information. * @param appTargetSdk admin's target SDK level */ private void addManagedProfile( ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { ComponentName admin, int adminUid, ComponentName copyFromAdmin, int appTargetSdk) throws Exception { final int userId = UserHandle.getUserId(adminUid); getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null, appTargetSdk, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } /** * Same as {@code addManagedProfile} above, except using development API level as the API * level of the admin. */ private void addManagedProfile( ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { addManagedProfile(admin, adminUid, copyFromAdmin, VERSION_CODES.CUR_DEVELOPMENT); } /** * Convert String[] to StringParceledListSlice. */ Loading