Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 559ff8ac authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "New API for getting the screen lock complexity"

parents 987712e0 e9586558
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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
+95 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app.admin;

import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -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
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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);
+116 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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;
@@ -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.
@@ -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;
        }
    }
}
+129 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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