Loading api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -6597,6 +6597,7 @@ package android.app.admin { method public java.lang.CharSequence getOrganizationName(android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName); method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName); method public int getPasswordComplexity(); method public long getPasswordExpiration(android.content.ComponentName); method public long getPasswordExpirationTimeout(android.content.ComponentName); method public int getPasswordHistoryLength(android.content.ComponentName); Loading Loading @@ -6853,6 +6854,10 @@ package android.app.admin { field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000 field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000 field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000 field public static final int PASSWORD_COMPLEXITY_NONE = 0; // 0x0 field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000 field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000 field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000 core/java/android/app/admin/DevicePolicyManager.java +95 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.admin; import android.Manifest.permission; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.IntDef; Loading Loading @@ -1381,6 +1382,73 @@ public class DevicePolicyManager { public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; /** * Constant for {@link #getPasswordComplexity()}: no password. * * <p>Note that these complexity constants are ordered so that higher values are more complex. */ public static final int PASSWORD_COMPLEXITY_NONE = 0; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>pattern * <li>PIN with repeating (4444) or ordered (1234, 4321, 2468) sequences * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_SOMETHING * @see #PASSWORD_QUALITY_NUMERIC */ public static final int PASSWORD_COMPLEXITY_LOW = 0x10000; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at * least 4 * <li>alphabetic, length at least 4 * <li>alphanumeric, length at least 4 * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX * @see #PASSWORD_QUALITY_ALPHABETIC * @see #PASSWORD_QUALITY_ALPHANUMERIC */ public static final int PASSWORD_COMPLEXITY_MEDIUM = 0x30000; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at * least 4 * <li>alphabetic, length at least 6 * <li>alphanumeric, length at least 6 * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX * @see #PASSWORD_QUALITY_ALPHABETIC * @see #PASSWORD_QUALITY_ALPHANUMERIC */ public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"PASSWORD_COMPLEXITY_"}, value = { PASSWORD_COMPLEXITY_NONE, PASSWORD_COMPLEXITY_LOW, PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_COMPLEXITY_HIGH, }) public @interface PasswordComplexity {} /** * Activity action: have the user enter a new password for the parent profile. * If the intent is launched from within a managed profile, this will trigger Loading Loading @@ -3105,6 +3173,33 @@ public class DevicePolicyManager { return false; } /** * Returns how complex the current user's screen lock is. * * <p>Note that when called from a profile which uses an unified challenge with its parent, the * screen lock complexity of the parent will be returned. However, this API does not support * explicitly querying the parent profile screen lock complexity via {@link * #getParentProfileInstance}. * * @throws IllegalStateException if the user is not unlocked. * @throws SecurityException if the calling application does not have the permission * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY} */ @PasswordComplexity @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity() { throwIfParentInstance("getPasswordComplexity"); if (mService == null) { return PASSWORD_COMPLEXITY_NONE; } try { return mService.getPasswordComplexity(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * When called by a profile owner of a managed profile returns true if the profile uses unified * challenge with its parent user. Loading core/java/android/app/admin/IDevicePolicyManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ interface IDevicePolicyManager { boolean isActivePasswordSufficient(int userHandle, boolean parent); boolean isProfileActivePasswordSufficientForParent(int userHandle); int getPasswordComplexity(); boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); Loading core/java/android/app/admin/PasswordMetrics.java +116 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,14 @@ package android.app.admin; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.os.Parcel; import android.os.Parcelable; Loading @@ -35,6 +41,8 @@ public class PasswordMetrics implements Parcelable { // consider it a complex PIN/password. public static final int MAX_ALLOWED_SEQUENCE = 3; // TODO(b/120536847): refactor isActivePasswordSufficient logic so that the actual password // quality is not overwritten public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; public int length = 0; public int letters = 0; Loading @@ -46,6 +54,10 @@ public class PasswordMetrics implements Parcelable { public PasswordMetrics() {} public PasswordMetrics(int quality) { this.quality = quality; } public PasswordMetrics(int quality, int length) { this.quality = quality; this.length = length; Loading Loading @@ -173,6 +185,15 @@ public class PasswordMetrics implements Parcelable { && this.nonLetter == o.nonLetter; } private boolean satisfiesBucket(PasswordMetrics... bucket) { for (PasswordMetrics metrics : bucket) { if (this.quality == metrics.quality) { return this.length >= metrics.length; } } return false; } /* * Returns the maximum length of a sequential characters. A sequence is defined as * monotonically increasing characters with a constant interval or the same character repeated. Loading Loading @@ -254,4 +275,99 @@ public class PasswordMetrics implements Parcelable { return 0; } } /** Determines the {@link PasswordComplexity} of this {@link PasswordMetrics}. */ @PasswordComplexity public int determineComplexity() { for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) { if (satisfiesBucket(bucket.getMetrics())) { return bucket.mComplexityLevel; } } return PASSWORD_COMPLEXITY_NONE; } /** * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}. */ public static class PasswordComplexityBucket { /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket HIGH = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_HIGH, new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */ 8)); /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket MEDIUM = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_MEDIUM, new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */ 4)); /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_LOW} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket LOW = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_LOW, new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)); /** * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}. */ private static final PasswordComplexityBucket NONE = new PasswordComplexityBucket(PASSWORD_COMPLEXITY_NONE, new PasswordMetrics()); /** Array containing all buckets from high to low. */ private static final PasswordComplexityBucket[] BUCKETS = new PasswordComplexityBucket[] {HIGH, MEDIUM, LOW}; @PasswordComplexity private final int mComplexityLevel; private final PasswordMetrics[] mMetrics; private PasswordComplexityBucket(@PasswordComplexity int complexityLevel, PasswordMetrics... metrics) { this.mComplexityLevel = complexityLevel; this.mMetrics = metrics; } /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */ public PasswordMetrics[] getMetrics() { return mMetrics; } /** Returns the bucket that {@code complexityLevel} represents. */ public static PasswordComplexityBucket complexityLevelToBucket( @PasswordComplexity int complexityLevel) { for (PasswordComplexityBucket bucket : BUCKETS) { if (bucket.mComplexityLevel == complexityLevel) { return bucket; } } return NONE; } } } core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +129 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,16 @@ package android.app.admin; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import android.app.admin.PasswordMetrics.PasswordComplexityBucket; import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; Loading Loading @@ -164,4 +171,126 @@ public class PasswordMetricsTest { } @Test public void testConstructQuality() { PasswordMetrics expected = new PasswordMetrics(); expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertEquals(expected, actual); } @Test public void testDetermineComplexity_none() { assertEquals(PASSWORD_COMPLEXITY_NONE, PasswordMetrics.computeForPassword("").determineComplexity()); } @Test public void testDetermineComplexity_lowSomething() { assertEquals(PASSWORD_COMPLEXITY_LOW, new PasswordMetrics(PASSWORD_QUALITY_SOMETHING).determineComplexity()); } @Test public void testDetermineComplexity_lowNumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("1234").determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("124").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("a!").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("a!1").determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("1238").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("ab!c").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("ab!1").determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("12389647!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("alphabetic!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity()); } @Test public void testComplexityLevelToBucket_none() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_NONE).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_low() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_LOW).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_medium() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_MEDIUM).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_high() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_HIGH).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity()); } } } Loading
api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -6597,6 +6597,7 @@ package android.app.admin { method public java.lang.CharSequence getOrganizationName(android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName); method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName); method public int getPasswordComplexity(); method public long getPasswordExpiration(android.content.ComponentName); method public long getPasswordExpirationTimeout(android.content.ComponentName); method public int getPasswordHistoryLength(android.content.ComponentName); Loading Loading @@ -6853,6 +6854,10 @@ package android.app.admin { field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000 field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000 field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000 field public static final int PASSWORD_COMPLEXITY_NONE = 0; // 0x0 field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000 field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000 field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
core/java/android/app/admin/DevicePolicyManager.java +95 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.admin; import android.Manifest.permission; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.IntDef; Loading Loading @@ -1381,6 +1382,73 @@ public class DevicePolicyManager { public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; /** * Constant for {@link #getPasswordComplexity()}: no password. * * <p>Note that these complexity constants are ordered so that higher values are more complex. */ public static final int PASSWORD_COMPLEXITY_NONE = 0; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>pattern * <li>PIN with repeating (4444) or ordered (1234, 4321, 2468) sequences * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_SOMETHING * @see #PASSWORD_QUALITY_NUMERIC */ public static final int PASSWORD_COMPLEXITY_LOW = 0x10000; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at * least 4 * <li>alphabetic, length at least 4 * <li>alphanumeric, length at least 4 * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX * @see #PASSWORD_QUALITY_ALPHABETIC * @see #PASSWORD_QUALITY_ALPHANUMERIC */ public static final int PASSWORD_COMPLEXITY_MEDIUM = 0x30000; /** * Constant for {@link #getPasswordComplexity()}: password satisfies one of the following: * <ul> * <li>PIN with <b>no</b> repeating (4444) or ordered (1234, 4321, 2468) sequences, length at * least 4 * <li>alphabetic, length at least 6 * <li>alphanumeric, length at least 6 * </ul> * * <p>Note that these complexity constants are ordered so that higher values are more complex. * * @see #PASSWORD_QUALITY_NUMERIC_COMPLEX * @see #PASSWORD_QUALITY_ALPHABETIC * @see #PASSWORD_QUALITY_ALPHANUMERIC */ public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"PASSWORD_COMPLEXITY_"}, value = { PASSWORD_COMPLEXITY_NONE, PASSWORD_COMPLEXITY_LOW, PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_COMPLEXITY_HIGH, }) public @interface PasswordComplexity {} /** * Activity action: have the user enter a new password for the parent profile. * If the intent is launched from within a managed profile, this will trigger Loading Loading @@ -3105,6 +3173,33 @@ public class DevicePolicyManager { return false; } /** * Returns how complex the current user's screen lock is. * * <p>Note that when called from a profile which uses an unified challenge with its parent, the * screen lock complexity of the parent will be returned. However, this API does not support * explicitly querying the parent profile screen lock complexity via {@link * #getParentProfileInstance}. * * @throws IllegalStateException if the user is not unlocked. * @throws SecurityException if the calling application does not have the permission * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY} */ @PasswordComplexity @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity() { throwIfParentInstance("getPasswordComplexity"); if (mService == null) { return PASSWORD_COMPLEXITY_NONE; } try { return mService.getPasswordComplexity(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * When called by a profile owner of a managed profile returns true if the profile uses unified * challenge with its parent user. Loading
core/java/android/app/admin/IDevicePolicyManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ interface IDevicePolicyManager { boolean isActivePasswordSufficient(int userHandle, boolean parent); boolean isProfileActivePasswordSufficientForParent(int userHandle); int getPasswordComplexity(); boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); Loading
core/java/android/app/admin/PasswordMetrics.java +116 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,14 @@ package android.app.admin; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.os.Parcel; import android.os.Parcelable; Loading @@ -35,6 +41,8 @@ public class PasswordMetrics implements Parcelable { // consider it a complex PIN/password. public static final int MAX_ALLOWED_SEQUENCE = 3; // TODO(b/120536847): refactor isActivePasswordSufficient logic so that the actual password // quality is not overwritten public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; public int length = 0; public int letters = 0; Loading @@ -46,6 +54,10 @@ public class PasswordMetrics implements Parcelable { public PasswordMetrics() {} public PasswordMetrics(int quality) { this.quality = quality; } public PasswordMetrics(int quality, int length) { this.quality = quality; this.length = length; Loading Loading @@ -173,6 +185,15 @@ public class PasswordMetrics implements Parcelable { && this.nonLetter == o.nonLetter; } private boolean satisfiesBucket(PasswordMetrics... bucket) { for (PasswordMetrics metrics : bucket) { if (this.quality == metrics.quality) { return this.length >= metrics.length; } } return false; } /* * Returns the maximum length of a sequential characters. A sequence is defined as * monotonically increasing characters with a constant interval or the same character repeated. Loading Loading @@ -254,4 +275,99 @@ public class PasswordMetrics implements Parcelable { return 0; } } /** Determines the {@link PasswordComplexity} of this {@link PasswordMetrics}. */ @PasswordComplexity public int determineComplexity() { for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) { if (satisfiesBucket(bucket.getMetrics())) { return bucket.mComplexityLevel; } } return PASSWORD_COMPLEXITY_NONE; } /** * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}. */ public static class PasswordComplexityBucket { /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket HIGH = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_HIGH, new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */ 8)); /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket MEDIUM = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_MEDIUM, new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4), new PasswordMetrics( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */ 4)); /** * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_LOW} in terms of * {@link PasswordMetrics}. */ private static final PasswordComplexityBucket LOW = new PasswordComplexityBucket( PASSWORD_COMPLEXITY_LOW, new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC), new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)); /** * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}. */ private static final PasswordComplexityBucket NONE = new PasswordComplexityBucket(PASSWORD_COMPLEXITY_NONE, new PasswordMetrics()); /** Array containing all buckets from high to low. */ private static final PasswordComplexityBucket[] BUCKETS = new PasswordComplexityBucket[] {HIGH, MEDIUM, LOW}; @PasswordComplexity private final int mComplexityLevel; private final PasswordMetrics[] mMetrics; private PasswordComplexityBucket(@PasswordComplexity int complexityLevel, PasswordMetrics... metrics) { this.mComplexityLevel = complexityLevel; this.mMetrics = metrics; } /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */ public PasswordMetrics[] getMetrics() { return mMetrics; } /** Returns the bucket that {@code complexityLevel} represents. */ public static PasswordComplexityBucket complexityLevelToBucket( @PasswordComplexity int complexityLevel) { for (PasswordComplexityBucket bucket : BUCKETS) { if (bucket.mComplexityLevel == complexityLevel) { return bucket; } } return NONE; } } }
core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +129 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,16 @@ package android.app.admin; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import android.app.admin.PasswordMetrics.PasswordComplexityBucket; import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; Loading Loading @@ -164,4 +171,126 @@ public class PasswordMetricsTest { } @Test public void testConstructQuality() { PasswordMetrics expected = new PasswordMetrics(); expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); assertEquals(expected, actual); } @Test public void testDetermineComplexity_none() { assertEquals(PASSWORD_COMPLEXITY_NONE, PasswordMetrics.computeForPassword("").determineComplexity()); } @Test public void testDetermineComplexity_lowSomething() { assertEquals(PASSWORD_COMPLEXITY_LOW, new PasswordMetrics(PASSWORD_QUALITY_SOMETHING).determineComplexity()); } @Test public void testDetermineComplexity_lowNumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("1234").determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("124").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("a!").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, PasswordMetrics.computeForPassword("a!1").determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("1238").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("ab!c").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, PasswordMetrics.computeForPassword("ab!1").determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("12389647!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("alphabetic!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity()); } @Test public void testComplexityLevelToBucket_none() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_NONE).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_low() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_LOW).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_medium() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_MEDIUM).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity()); } } @Test public void testComplexityLevelToBucket_high() { PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( PASSWORD_COMPLEXITY_HIGH).getMetrics(); for (PasswordMetrics metrics : bucket) { assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity()); } } }