diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index c95a74e0f333c..e93138bd1bc83 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -691,6 +691,19 @@ public final class PasswordMetrics implements Parcelable { return minMetrics; } + /** + * Returns true if password is non-empty and contains digits only. + * @param password + * @return + */ + public static boolean isNumericOnly(@NonNull String password) { + if (password.length() == 0) return false; + for (int i = 0; i < password.length(); i++) { + if (categoryChar(password.charAt(i)) != CHAR_DIGIT) return false; + } + return true; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) return true; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b87ccfb338391..20a88af98300c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4954,6 +4954,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { int flags, CallerIdentity caller) { final int callingUid = caller.getUid(); final int userHandle = UserHandle.getUserId(callingUid); + final boolean isPin = PasswordMetrics.isNumericOnly(password); synchronized (getLockObject()) { final PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userHandle); final List validationErrors; @@ -4961,12 +4962,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO: Consider changing validation API to take LockscreenCredential. if (password.isEmpty()) { validationErrors = PasswordMetrics.validatePasswordMetrics( - minMetrics, complexity, false /* isPin */, + minMetrics, complexity, isPin, new PasswordMetrics(CREDENTIAL_TYPE_NONE)); } else { // TODO(b/120484642): remove getBytes() below validationErrors = PasswordMetrics.validatePassword( - minMetrics, complexity, false, password.getBytes()); + minMetrics, complexity, isPin, password.getBytes()); } if (!validationErrors.isEmpty()) { @@ -4992,8 +4993,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Don't do this with the lock held, because it is going to call // back in to the service. final long ident = mInjector.binderClearCallingIdentity(); - final LockscreenCredential newCredential = - LockscreenCredential.createPasswordOrNone(password); + final LockscreenCredential newCredential; + if (isPin) { + newCredential = LockscreenCredential.createPin(password); + } else { + newCredential = LockscreenCredential.createPasswordOrNone(password); + } try { if (tokenHandle == 0 || token == null) { if (!mLockPatternUtils.setLockCredential(newCredential, diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 4e9367a7f8de2..486d2b35f9f3d 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5089,6 +5089,46 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.clearResetPasswordToken(admin1)).isTrue(); } + @Test + public void resetPasswordWithToken_NumericPin() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + // adding a token + final byte[] token = new byte[32]; + final long handle = 123456; + when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM), + nullable(EscrowTokenStateChangeCallback.class))) + .thenReturn(handle); + assertThat(dpm.setResetPasswordToken(admin1, token)).isTrue(); + + // Test resetting with a numeric pin + final String pin = "123456"; + when(getServices().lockPatternUtils.setLockCredentialWithToken( + LockscreenCredential.createPin(pin), handle, token, + UserHandle.USER_SYSTEM)).thenReturn(true); + assertThat(dpm.resetPasswordWithToken(admin1, pin, token, 0)).isTrue(); + } + + @Test + public void resetPasswordWithToken_EmptyPassword() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + // adding a token + final byte[] token = new byte[32]; + final long handle = 123456; + when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM), + nullable(EscrowTokenStateChangeCallback.class))) + .thenReturn(handle); + assertThat(dpm.setResetPasswordToken(admin1, token)).isTrue(); + + // Test resetting with an empty password + final String password = ""; + when(getServices().lockPatternUtils.setLockCredentialWithToken( + LockscreenCredential.createNone(), handle, token, + UserHandle.USER_SYSTEM)).thenReturn(true); + assertThat(dpm.resetPasswordWithToken(admin1, password, token, 0)).isTrue(); + } + @Test public void testIsActivePasswordSufficient() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;