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

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

Merge "New extra and helper methods to set screenlock to a specific complexity level"

parents 448fb122 60e0f7f6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6823,6 +6823,7 @@ package android.app.admin {
    field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
    field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
    field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
    field @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY";
    field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
    field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
    field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
+33 −8
Original line number Diff line number Diff line
@@ -1362,16 +1362,23 @@ public class DevicePolicyManager {
    public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";

    /**
     * Activity action: have the user enter a new password. This activity should
     * be launched after using {@link #setPasswordQuality(ComponentName, int)},
     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
     * enter a new password that meets the current requirements. You can use
     * {@link #isActivePasswordSufficient()} to determine whether you need to
     * have the user select a new password in order to meet the current
     * constraints. Upon being resumed from this activity, you can check the new
     * Activity action: have the user enter a new password.
     *
     * <p>For admin apps, this activity should be launched after using {@link
     * #setPasswordQuality(ComponentName, int)}, or {@link
     * #setPasswordMinimumLength(ComponentName, int)} to have the user enter a new password that
     * meets the current requirements. You can use {@link #isActivePasswordSufficient()} to
     * determine whether you need to have the user select a new password in order to meet the
     * current constraints. Upon being resumed from this activity, you can check the new
     * password characteristics to see if they are sufficient.
     *
     * If the intent is launched from within a managed profile with a profile
     * <p>Non-admin apps can use {@link #getPasswordComplexity()} to check the current screen lock
     * complexity, and use this activity with extra {@link #EXTRA_PASSWORD_COMPLEXITY} to suggest
     * to users how complex the app wants the new screen lock to be. Note that both {@link
     * #getPasswordComplexity()} and the extra {@link #EXTRA_PASSWORD_COMPLEXITY} require the
     * calling app to have the permission {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}.
     *
     * <p>If the intent is launched from within a managed profile with a profile
     * owner built against {@link android.os.Build.VERSION_CODES#M} or before,
     * this will trigger entering a new password for the parent of the profile.
     * For all other cases it will trigger entering a new password for the user
@@ -1383,6 +1390,24 @@ public class DevicePolicyManager {
    public static final String ACTION_SET_NEW_PASSWORD
            = "android.app.action.SET_NEW_PASSWORD";

    /**
     * An integer indicating the complexity level of the new password an app would like the user to
     * set when launching the action {@link #ACTION_SET_NEW_PASSWORD}.
     *
     * <p>Must be one of
     * <ul>
     *     <li>{@link #PASSWORD_COMPLEXITY_HIGH}
     *     <li>{@link #PASSWORD_COMPLEXITY_MEDIUM}
     *     <li>{@link #PASSWORD_COMPLEXITY_LOW}
     *     <li>{@link #PASSWORD_COMPLEXITY_NONE}
     * </ul>
     *
     * <p>If an invalid value is used, it will be treated as {@link #PASSWORD_COMPLEXITY_NONE}.
     */
    @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY)
    public static final String EXTRA_PASSWORD_COMPLEXITY =
            "android.app.extra.PASSWORD_COMPLEXITY";

    /**
     * Constant for {@link #getPasswordComplexity()}: no password.
     *
+130 −18
Original line number Diff line number Diff line
@@ -20,6 +20,11 @@ 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_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;

import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +32,8 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@@ -85,6 +92,101 @@ public class PasswordMetrics implements Parcelable {
        nonLetter = in.readInt();
    }

    /** Returns the min quality allowed by {@code complexityLevel}. */
    public static int complexityLevelToMinQuality(@PasswordComplexity int complexityLevel) {
        // this would be the quality of the first metrics since mMetrics is sorted in ascending
        // order of quality
        return PasswordComplexityBucket
                .complexityLevelToBucket(complexityLevel).mMetrics[0].quality;
    }

    /**
     * Returns a merged minimum {@link PasswordMetrics} requirements that a new password must meet
     * to fulfil {@code requestedQuality}, {@code requiresNumeric} and {@code
     * requiresLettersOrSymbols}, which are derived from {@link DevicePolicyManager} requirements,
     * and {@code complexityLevel}.
     *
     * <p>Note that we are taking {@code userEnteredPasswordQuality} into account because there are
     * more than one set of metrics to meet the minimum complexity requirement and inspecting what
     * the user has entered can help determine whether the alphabetic or alphanumeric set of metrics
     * should be used. For example, suppose minimum complexity requires either ALPHABETIC(8+), or
     * ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
     * would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
     * an alphanumeric password so we would update the min complexity required min length to 6.
     */
    public static PasswordMetrics getMinimumMetrics(@PasswordComplexity int complexityLevel,
            int userEnteredPasswordQuality, int requestedQuality, boolean requiresNumeric,
            boolean requiresLettersOrSymbols) {
        int targetQuality = Math.max(
                userEnteredPasswordQuality,
                getActualRequiredQuality(
                        requestedQuality, requiresNumeric, requiresLettersOrSymbols));
        return getTargetQualityMetrics(complexityLevel, targetQuality);
    }

    /**
     * Returns the {@link PasswordMetrics} at {@code complexityLevel} which the metrics quality
     * is the same as {@code targetQuality}.
     *
     * <p>If {@code complexityLevel} does not allow {@code targetQuality}, returns the metrics
     * with the min quality at {@code complexityLevel}.
     */
    // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
    @VisibleForTesting
    public static PasswordMetrics getTargetQualityMetrics(
            @PasswordComplexity int complexityLevel, int targetQuality) {
        PasswordComplexityBucket targetBucket =
                PasswordComplexityBucket.complexityLevelToBucket(complexityLevel);
        for (PasswordMetrics metrics : targetBucket.mMetrics) {
            if (targetQuality == metrics.quality) {
                return metrics;
            }
        }
        // none of the metrics at complexityLevel has targetQuality, return metrics with min quality
        // see test case testGetMinimumMetrics_actualRequiredQualityStricter for an example, where
        // min complexity allows at least NUMERIC_COMPLEX, user has not entered anything yet, and
        // requested quality is NUMERIC
        return targetBucket.mMetrics[0];
    }

    /**
     * Finds out the actual quality requirement based on whether quality is {@link
     * DevicePolicyManager#PASSWORD_QUALITY_COMPLEX} and whether digits, letters or symbols are
     * required.
     */
    @VisibleForTesting
    // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
    public static int getActualRequiredQuality(
            int requestedQuality, boolean requiresNumeric, boolean requiresLettersOrSymbols) {
        if (requestedQuality != PASSWORD_QUALITY_COMPLEX) {
            return requestedQuality;
        }

        // find out actual password quality from complex requirements
        if (requiresNumeric && requiresLettersOrSymbols) {
            return PASSWORD_QUALITY_ALPHANUMERIC;
        }
        if (requiresLettersOrSymbols) {
            return PASSWORD_QUALITY_ALPHABETIC;
        }
        if (requiresNumeric) {
            // cannot specify numeric complex using complex quality so this must be numeric
            return PASSWORD_QUALITY_NUMERIC;
        }

        // reaching here means dpm sets quality to complex without specifying any requirements
        return PASSWORD_QUALITY_UNSPECIFIED;
    }

    /**
     * Returns {@code complexityLevel} or {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}
     * if {@code complexityLevel} is not valid.
     */
    @PasswordComplexity
    public static int sanitizeComplexityLevel(@PasswordComplexity int complexityLevel) {
        return PasswordComplexityBucket.complexityLevelToBucket(complexityLevel).mComplexityLevel;
    }

    public boolean isDefault() {
        return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
                && length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
@@ -280,7 +382,7 @@ public class PasswordMetrics implements Parcelable {
    @PasswordComplexity
    public int determineComplexity() {
        for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
            if (satisfiesBucket(bucket.getMetrics())) {
            if (satisfiesBucket(bucket.mMetrics)) {
                return bucket.mComplexityLevel;
            }
        }
@@ -290,7 +392,7 @@ public class PasswordMetrics implements Parcelable {
    /**
     * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
     */
    public static class PasswordComplexityBucket {
    private static class PasswordComplexityBucket {
        /**
         * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
         * {@link PasswordMetrics}.
@@ -299,12 +401,13 @@ public class PasswordMetrics implements Parcelable {
                new PasswordComplexityBucket(
                        PASSWORD_COMPLEXITY_HIGH,
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6),
                                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
                                8),
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
                                8));
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
                                6));

        /**
         * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
@@ -314,11 +417,12 @@ public class PasswordMetrics implements Parcelable {
                new PasswordComplexityBucket(
                        PASSWORD_COMPLEXITY_MEDIUM,
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4),
                                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
                                4),
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
                        new PasswordMetrics(
                                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
                                DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
                                4));

        /**
@@ -328,11 +432,11 @@ public class PasswordMetrics implements Parcelable {
        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_SOMETHING),
                        new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
                        new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING));
                        new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
                        new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
                        new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC));

        /**
         * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
@@ -348,19 +452,27 @@ public class PasswordMetrics implements Parcelable {
        private final int mComplexityLevel;
        private final PasswordMetrics[] mMetrics;

        /**
         * @param metricsArray must be sorted in ascending order of {@link #quality}.
         */
        private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
                PasswordMetrics... metrics) {
            this.mComplexityLevel = complexityLevel;
            this.mMetrics = metrics;
                PasswordMetrics... metricsArray) {
            int previousQuality = PASSWORD_QUALITY_UNSPECIFIED;
            for (PasswordMetrics metrics : metricsArray) {
                if (metrics.quality < previousQuality) {
                    throw new IllegalArgumentException("metricsArray must be sorted in ascending"
                            + " order of quality");
                }
                previousQuality = metrics.quality;
            }

        /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */
        public PasswordMetrics[] getMetrics() {
            return mMetrics;
            this.mMetrics = metricsArray;
            this.mComplexityLevel = complexityLevel;

        }

        /** Returns the bucket that {@code complexityLevel} represents. */
        public static PasswordComplexityBucket complexityLevelToBucket(
        private static PasswordComplexityBucket complexityLevelToBucket(
                @PasswordComplexity int complexityLevel) {
            for (PasswordComplexityBucket bucket : BUCKETS) {
                if (bucket.mComplexityLevel == complexityLevel) {
+176 −29

File changed.

Preview size limit exceeded, changes collapsed.