Merge "Fix crash on indexing AccountWorkProfileDashboardFragment"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2ee59e9..62803af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2981,6 +2981,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
         <service
             android:name=".development.qstile.DevelopmentTiles$GPUProfiling"
@@ -2991,6 +2993,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
         <service
             android:name=".development.qstile.DevelopmentTiles$ForceRTL"
@@ -3001,6 +3005,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
         <service
             android:name=".development.qstile.DevelopmentTiles$AnimationSpeed"
@@ -3011,6 +3017,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
 
         <service
@@ -3022,6 +3030,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
 
         <service
@@ -3033,6 +3043,8 @@
             <intent-filter>
                 <action android:name="android.service.quicksettings.action.QS_TILE" />
             </intent-filter>
+            <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+                       android:value="true"/>
         </service>
 
         <activity
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2ee24f3..ed5865a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1410,9 +1410,6 @@
         <item quantity="other">Must be fewer than <xliff:g id="number" example="17">%d</xliff:g> digits</item>
     </plurals>
 
-    <!-- Error shown when in PIN mode and user enters a non-digit -->
-    <string name="lockpassword_pin_contains_non_digits">Must contain only digits 0-9</string>
-
     <!-- Error shown when in PIN mode and PIN has been used recently. Please keep this string short! -->
     <string name="lockpassword_pin_recently_used">Device admin doesn\'t allow using a recent PIN</string>
 
@@ -1467,6 +1464,11 @@
         <item quantity="other">Must contain at least <xliff:g id="count" example="3">%d</xliff:g> non-letter characters</item>
     </plurals>
 
+    <plurals name="lockpassword_password_requires_nonnumerical">
+        <item quantity="one">Must contain at least 1 non-numerical character</item>
+        <item quantity="other">Must contain at least <xliff:g id="count" example="3">%d</xliff:g> non-numerical characters</item>
+    </plurals>
+
     <!-- Error shown when in PASSWORD mode and password has been used recently. Please keep this string short! -->
     <string name="lockpassword_password_recently_used">Device admin doesn\'t allow using a recent
         password</string>
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java
index 9c9400d..31bca12 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java
@@ -114,16 +114,16 @@
         if (function != previousFunction && !Utils.isMonkeyRunning()) {
             mPreviousFunction = previousFunction;
 
-            if (function == UsbManager.FUNCTION_RNDIS) {
-                //Update the UI in advance to make it looks smooth
-                final RadioButtonPreference prevPref =
-                        (RadioButtonPreference) mProfilesContainer.findPreference(
-                                UsbBackend.usbFunctionsToString(mPreviousFunction));
-                if (prevPref != null) {
-                    prevPref.setChecked(false);
-                    preference.setChecked(true);
-                }
+            //Update the UI in advance to make it looks smooth
+            final RadioButtonPreference prevPref =
+                    (RadioButtonPreference) mProfilesContainer.findPreference(
+                            UsbBackend.usbFunctionsToString(mPreviousFunction));
+            if (prevPref != null) {
+                prevPref.setChecked(false);
+                preference.setChecked(true);
+            }
 
+            if (function == UsbManager.FUNCTION_RNDIS) {
                 // We need to have entitlement check for usb tethering, so use API in
                 // ConnectivityManager.
                 mConnectivityManager.startTethering(TETHERING_USB, true /* showProvisioningUi */,
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 4f1b050..a2885d6 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -760,8 +760,12 @@
             if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 // Clearing of user biometrics when screen lock is cleared is done at
                 // LockSettingsService.removeBiometricsForUser().
-                mChooseLockSettingsHelper.utils().setLockCredential(
-                        LockscreenCredential.createNone(), mUserPassword, mUserId);
+                if (mUserPassword != null) {
+                    // No need to call setLockCredential if the user currently doesn't
+                    // have a password
+                    mChooseLockSettingsHelper.utils().setLockCredential(
+                            LockscreenCredential.createNone(), mUserPassword, mUserId);
+                }
                 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
                 getActivity().setResult(Activity.RESULT_OK);
                 finish();
diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java
index 62a0063..1bfdac9 100644
--- a/src/com/android/settings/password/ChooseLockGenericController.java
+++ b/src/com/android/settings/password/ChooseLockGenericController.java
@@ -92,6 +92,7 @@
      */
     public int upgradeQuality(int quality) {
         // Compare specified quality and dpm quality
+        // TODO(b/142781408): convert from quality to credential type once PIN is supported.
         int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
         return Math.max(dpmUpgradedQuality,
                 PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 6872543..ff94d8b 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -17,12 +17,20 @@
 package com.android.settings.password;
 
 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_NUMERIC_COMPLEX;
 
+import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
+import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LETTERS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LOWER_CASE;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_DIGITS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_LETTER;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_SYMBOLS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_UPPER_CASE;
+import static com.android.internal.widget.PasswordValidationError.RECENTLY_USED;
+import static com.android.internal.widget.PasswordValidationError.TOO_LONG;
+import static com.android.internal.widget.PasswordValidationError.TOO_SHORT;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
 
 import android.app.Activity;
@@ -51,7 +59,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
@@ -64,6 +71,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.PasswordValidationError;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.EncryptionInterstitial;
 import com.android.settings.R;
@@ -79,7 +87,7 @@
 import com.google.android.setupdesign.GlifLayout;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 public class ChooseLockPassword extends SettingsActivity {
@@ -192,17 +200,12 @@
         private long mChallenge;
         private ImeAwareEditText mPasswordEntry;
         private TextViewInputDisabler mPasswordEntryInputDisabler;
-        private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
-        private int mPasswordMaxLength = 16;
-        private int mPasswordMinLetters = 0;
-        private int mPasswordMinUpperCase = 0;
-        private int mPasswordMinLowerCase = 0;
-        private int mPasswordMinSymbols = 0;
-        private int mPasswordMinNumeric = 0;
-        private int mPasswordMinNonLetter = 0;
-        private int mPasswordMinLengthToFulfillAllPolicies = 0;
-        private boolean mPasswordNumSequenceAllowed = true;
-        @PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
+
+        // Minimum password metrics enforced by admins.
+        private PasswordMetrics mMinMetrics;
+        private List<PasswordValidationError> mValidationErrors;
+
+        @PasswordComplexity private int mMinComplexity = PASSWORD_COMPLEXITY_NONE;
         protected int mUserId;
         private byte[] mPasswordHistoryHashFactor;
 
@@ -228,28 +231,6 @@
         private static final int CONFIRM_EXISTING_REQUEST = 58;
         static final int RESULT_FINISHED = RESULT_FIRST_USER;
 
-        private static final int MIN_LETTER_IN_PASSWORD = 0;
-        private static final int MIN_UPPER_LETTERS_IN_PASSWORD = 1;
-        private static final int MIN_LOWER_LETTERS_IN_PASSWORD = 2;
-        private static final int MIN_SYMBOLS_IN_PASSWORD = 3;
-        private static final int MIN_NUMBER_IN_PASSWORD = 4;
-        private static final int MIN_NON_LETTER_IN_PASSWORD = 5;
-
-        // Error code returned from {@link #validatePassword(byte[])}.
-        static final int NO_ERROR = 0;
-        static final int CONTAIN_INVALID_CHARACTERS = 1 << 0;
-        static final int TOO_SHORT = 1 << 1;
-        static final int TOO_LONG = 1 << 2;
-        static final int CONTAIN_NON_DIGITS = 1 << 3;
-        static final int CONTAIN_SEQUENTIAL_DIGITS = 1 << 4;
-        static final int RECENTLY_USED = 1 << 5;
-        static final int NOT_ENOUGH_LETTER = 1 << 6;
-        static final int NOT_ENOUGH_UPPER_CASE = 1 << 7;
-        static final int NOT_ENOUGH_LOWER_CASE = 1 << 8;
-        static final int NOT_ENOUGH_DIGITS = 1 << 9;
-        static final int NOT_ENOUGH_SYMBOLS = 1 << 10;
-        static final int NOT_ENOUGH_NON_LETTER = 1 << 11;
-
         /**
          * Keep track internally of where the user is in choosing a pattern.
          */
@@ -381,13 +362,13 @@
             mForFingerprint = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
-            mRequestedMinComplexity = intent.getIntExtra(
+            mMinComplexity = intent.getIntExtra(
                     EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
-            mRequestedQuality = Math.max(
-                    intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
-                    mLockPatternUtils.getRequestedPasswordQuality(mUserId));
 
-            loadDpmPasswordRequirements();
+            mRequestedQuality = intent.getIntExtra(
+                    LockPatternUtils.PASSWORD_TYPE_KEY, PASSWORD_QUALITY_NUMERIC);
+
+            mMinMetrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId);
             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
 
             if (intent.getBooleanExtra(
@@ -602,209 +583,23 @@
         }
 
         /**
-         * Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
-         */
-        private void loadDpmPasswordRequirements() {
-            final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
-            if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
-                mPasswordNumSequenceAllowed = false;
-            }
-            mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
-                    mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
-            mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
-            mPasswordMinLetters = mLockPatternUtils.getRequestedPasswordMinimumLetters(mUserId);
-            mPasswordMinUpperCase = mLockPatternUtils.getRequestedPasswordMinimumUpperCase(mUserId);
-            mPasswordMinLowerCase = mLockPatternUtils.getRequestedPasswordMinimumLowerCase(mUserId);
-            mPasswordMinNumeric = mLockPatternUtils.getRequestedPasswordMinimumNumeric(mUserId);
-            mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
-            mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
-
-            // Modify the value based on dpm policy
-            switch (dpmPasswordQuality) {
-                case PASSWORD_QUALITY_ALPHABETIC:
-                    if (mPasswordMinLetters == 0) {
-                        mPasswordMinLetters = 1;
-                    }
-                    break;
-                case PASSWORD_QUALITY_ALPHANUMERIC:
-                    if (mPasswordMinLetters == 0) {
-                        mPasswordMinLetters = 1;
-                    }
-                    if (mPasswordMinNumeric == 0) {
-                        mPasswordMinNumeric = 1;
-                    }
-                    break;
-                case PASSWORD_QUALITY_COMPLEX:
-                    // Reserve all the requirements.
-                    break;
-                default:
-                    mPasswordMinNumeric = 0;
-                    mPasswordMinLetters = 0;
-                    mPasswordMinUpperCase = 0;
-                    mPasswordMinLowerCase = 0;
-                    mPasswordMinSymbols = 0;
-                    mPasswordMinNonLetter = 0;
-            }
-
-            mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
-        }
-
-        /**
-         * Merges the dpm requirements and the min complexity requirements.
+         * Validates PIN/Password and returns the validation result and updates mValidationErrors
+         * and mPasswordReused to reflect validation results.
          *
-         * <p>Since there are more than one set of metrics to meet the min complexity requirement,
-         * and we are not hard-coding any one of them to be the requirements the user must fulfil,
-         * we are taking what the user has already entered into account when compiling the list of
-         * requirements from min complexity. Then we merge this list with the DPM requirements, and
-         * present the merged set as validation results to the user on the UI.
-         *
-         * <p>For example, suppose min 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.
-         * This might result in a little confusion for the user but the UI does not support showing
-         * multiple sets of requirements / validation results as options to users, this is the best
-         * we can do now.
-         */
-        private void mergeMinComplexityAndDpmRequirements(int userEnteredPasswordQuality) {
-            if (mRequestedMinComplexity == PASSWORD_COMPLEXITY_NONE) {
-                // dpm requirements are dominant if min complexity is none
-                return;
-            }
-
-            // reset dpm requirements
-            loadDpmPasswordRequirements();
-
-            PasswordMetrics minMetrics = PasswordMetrics.getMinimumMetrics(
-                    mRequestedMinComplexity, userEnteredPasswordQuality, mRequestedQuality,
-                    requiresNumeric(), requiresLettersOrSymbols());
-            mPasswordNumSequenceAllowed = mPasswordNumSequenceAllowed
-                    && minMetrics.quality != PASSWORD_QUALITY_NUMERIC_COMPLEX;
-            mPasswordMinLength = Math.max(mPasswordMinLength, minMetrics.length);
-            mPasswordMinLetters = Math.max(mPasswordMinLetters, minMetrics.letters);
-            mPasswordMinUpperCase = Math.max(mPasswordMinUpperCase, minMetrics.upperCase);
-            mPasswordMinLowerCase = Math.max(mPasswordMinLowerCase, minMetrics.lowerCase);
-            mPasswordMinNumeric = Math.max(mPasswordMinNumeric, minMetrics.numeric);
-            mPasswordMinSymbols = Math.max(mPasswordMinSymbols, minMetrics.symbols);
-            mPasswordMinNonLetter = Math.max(mPasswordMinNonLetter, minMetrics.nonLetter);
-
-            if (minMetrics.quality == PASSWORD_QUALITY_ALPHABETIC) {
-                if (!requiresLettersOrSymbols()) {
-                    mPasswordMinLetters = 1;
-                }
-            }
-            if (minMetrics.quality == PASSWORD_QUALITY_ALPHANUMERIC) {
-                if (!requiresLettersOrSymbols()) {
-                    mPasswordMinLetters = 1;
-                }
-                if (!requiresNumeric()) {
-                    mPasswordMinNumeric = 1;
-                }
-            }
-
-            mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
-        }
-
-        private boolean requiresLettersOrSymbols() {
-            // This is the condition for the password to be considered ALPHABETIC according to
-            // PasswordMetrics.computeForPassword()
-            return mPasswordMinLetters + mPasswordMinUpperCase
-                    + mPasswordMinLowerCase + mPasswordMinSymbols + mPasswordMinNonLetter > 0;
-        }
-
-        private boolean requiresNumeric() {
-            return mPasswordMinNumeric > 0;
-        }
-
-        /**
-         * Validates PIN/Password and returns the validation result.
-         *
-         * @param password the raw password the user typed in
-         * @return the validation result.
+         * @param credential credential the user typed in.
+         * @return whether password satisfies all the requirements.
          */
         @VisibleForTesting
-        int validatePassword(LockscreenCredential credential) {
+        boolean validatePassword(LockscreenCredential credential) {
             final byte[] password = credential.getCredential();
-            int errorCode = NO_ERROR;
-            final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-            mergeMinComplexityAndDpmRequirements(metrics.quality);
-
-            if (password == null || password.length < mPasswordMinLength) {
-                if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
-                    errorCode |= TOO_SHORT;
-                }
-            } else if (password.length > mPasswordMaxLength) {
-                errorCode |= TOO_LONG;
-            } else {
-                // The length requirements are fulfilled.
-                if (!mPasswordNumSequenceAllowed
-                        && !requiresLettersOrSymbols()
-                        && metrics.numeric == password.length) {
-                    // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
-                    // if DevicePolicyManager or min password complexity requires a complex numeric
-                    // password. There can be two cases in the UI: 1. User chooses to enroll a
-                    // PIN, 2. User chooses to enroll a password but enters a numeric-only pin. We
-                    // should carry out the sequence check in both cases.
-                    //
-                    // Conditions for the !requiresLettersOrSymbols() to be necessary:
-                    // - DPM requires NUMERIC_COMPLEX
-                    // - min complexity not NONE, user picks PASSWORD type so ALPHABETIC or
-                    // ALPHANUMERIC is required
-                    // Imagine user has entered "12345678", if we don't skip the sequence check, the
-                    // validation result would show both "requires a letter" and "sequence not
-                    // allowed", while the only requirement the user needs to know is "requires a
-                    // letter" because once the user has fulfilled the alphabetic requirement, the
-                    // password would not be containing only digits so this check would not be
-                    // performed anyway.
-                    final int sequence = PasswordMetrics.maxLengthSequence(password);
-                    if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
-                        errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
-                    }
-                }
-                // Is the password recently used?
-                if (mLockPatternUtils.checkPasswordHistory(password, getPasswordHistoryHashFactor(),
-                        mUserId)) {
-                    errorCode |= RECENTLY_USED;
-                }
+            mValidationErrors = PasswordMetrics.validatePassword(
+                    mMinMetrics, mMinComplexity, !mIsAlphaMode, password);
+            if (mValidationErrors.isEmpty() &&  mLockPatternUtils.checkPasswordHistory(
+                        password, getPasswordHistoryHashFactor(), mUserId)) {
+                mValidationErrors =
+                        Collections.singletonList(new PasswordValidationError(RECENTLY_USED));
             }
-
-            // Allow non-control Latin-1 characters only.
-            for (int i = 0; i < password.length; i++) {
-                char c = (char) password[i];
-                if (c < 32 || c > 127) {
-                    errorCode |= CONTAIN_INVALID_CHARACTERS;
-                    break;
-                }
-            }
-
-            // Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
-            // user finds some way to bring up soft keyboard.
-            if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
-                    || mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
-                if (metrics.letters > 0 || metrics.symbols > 0) {
-                    errorCode |= CONTAIN_NON_DIGITS;
-                }
-            }
-
-            if (metrics.letters < mPasswordMinLetters) {
-                errorCode |= NOT_ENOUGH_LETTER;
-            }
-            if (metrics.upperCase < mPasswordMinUpperCase) {
-                errorCode |= NOT_ENOUGH_UPPER_CASE;
-            }
-            if (metrics.lowerCase < mPasswordMinLowerCase) {
-                errorCode |= NOT_ENOUGH_LOWER_CASE;
-            }
-            if (metrics.symbols < mPasswordMinSymbols) {
-                errorCode |= NOT_ENOUGH_SYMBOLS;
-            }
-            if (metrics.numeric < mPasswordMinNumeric) {
-                errorCode |= NOT_ENOUGH_DIGITS;
-            }
-            if (metrics.nonLetter < mPasswordMinNonLetter) {
-                errorCode |= NOT_ENOUGH_NON_LETTER;
-            }
-            return errorCode;
+            return mValidationErrors.isEmpty();
         }
 
         /**
@@ -830,7 +625,7 @@
             mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
                     : LockscreenCredential.createPin(passwordText);
             if (mUiStage == Stage.Introduction) {
-                if (validatePassword(mChosenPassword) == NO_ERROR) {
+                if (validatePassword(mChosenPassword)) {
                     mFirstPassword = mChosenPassword;
                     mPasswordEntry.setText("");
                     updateStage(Stage.NeedToConfirm);
@@ -879,79 +674,79 @@
         }
 
         /**
-         * @param errorCode error code returned from {@link #validatePassword(String)}.
+         * @param errorCode error code returned from password validation.
          * @return an array of messages describing the error, important messages come first.
          */
-        String[] convertErrorCodeToMessages(int errorCode) {
+        String[] convertErrorCodeToMessages() {
             List<String> messages = new ArrayList<>();
-            if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) {
-                messages.add(getString(R.string.lockpassword_illegal_character));
+            for (PasswordValidationError error : mValidationErrors) {
+                switch (error.errorCode) {
+                    case CONTAINS_INVALID_CHARACTERS:
+                        messages.add(getString(R.string.lockpassword_illegal_character));
+                        break;
+                    case NOT_ENOUGH_UPPER_CASE:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_uppercase,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_LOWER_CASE:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_lowercase,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_LETTERS:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_letters,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_DIGITS:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_numeric,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_SYMBOLS:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_symbols,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_NON_LETTER:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_nonletter,
+                                error.requirement, error.requirement));
+                        break;
+                    case NOT_ENOUGH_NON_DIGITS:
+                        messages.add(getResources().getQuantityString(
+                                R.plurals.lockpassword_password_requires_nonnumerical,
+                                error.requirement, error.requirement));
+                        break;
+                    case TOO_SHORT:
+                        messages.add(getResources().getQuantityString(
+                                mIsAlphaMode
+                                        ? R.plurals.lockpassword_password_too_short
+                                        : R.plurals.lockpassword_pin_too_short,
+                                error.requirement, error.requirement));
+                        break;
+                    case TOO_LONG:
+                        messages.add(getResources().getQuantityString(
+                                mIsAlphaMode
+                                        ? R.plurals.lockpassword_password_too_long
+                                        : R.plurals.lockpassword_pin_too_long,
+                                error.requirement + 1, error.requirement + 1));
+                        break;
+                    case CONTAINS_SEQUENCE:
+                        messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
+                        break;
+                    case RECENTLY_USED:
+                        messages.add(getString(mIsAlphaMode
+                                ? R.string.lockpassword_password_recently_used
+                                : R.string.lockpassword_pin_recently_used));
+                        break;
+                    default:
+                        Log.wtf(TAG, "unknown error validating password: " + error);
+                }
             }
-            if ((errorCode & CONTAIN_NON_DIGITS) > 0) {
-                messages.add(getString(R.string.lockpassword_pin_contains_non_digits));
-            }
-            if ((errorCode & NOT_ENOUGH_UPPER_CASE) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
-                        mPasswordMinUpperCase));
-            }
-            if ((errorCode & NOT_ENOUGH_LOWER_CASE) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
-                        mPasswordMinLowerCase));
-            }
-            if ((errorCode & NOT_ENOUGH_LETTER) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
-                        mPasswordMinLetters));
-            }
-            if ((errorCode & NOT_ENOUGH_DIGITS) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
-                        mPasswordMinNumeric));
-            }
-            if ((errorCode & NOT_ENOUGH_SYMBOLS) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
-                        mPasswordMinSymbols));
-            }
-            if ((errorCode & NOT_ENOUGH_NON_LETTER) > 0) {
-                messages.add(getResources().getQuantityString(
-                        R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
-                        mPasswordMinNonLetter));
-            }
-            if ((errorCode & TOO_SHORT) > 0) {
-                messages.add(getResources().getQuantityString(
-                        mIsAlphaMode
-                                ? R.plurals.lockpassword_password_too_short
-                                : R.plurals.lockpassword_pin_too_short,
-                        mPasswordMinLength,
-                        mPasswordMinLength));
-            }
-            if ((errorCode & TOO_LONG) > 0) {
-                messages.add(getResources().getQuantityString(
-                        mIsAlphaMode
-                                ? R.plurals.lockpassword_password_too_long
-                                : R.plurals.lockpassword_pin_too_long,
-                        mPasswordMaxLength + 1,
-                        mPasswordMaxLength + 1));
-            }
-            if ((errorCode & CONTAIN_SEQUENTIAL_DIGITS) > 0) {
-                messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
-            }
-            if ((errorCode & RECENTLY_USED) > 0) {
-                messages.add(getString((mIsAlphaMode) ? R.string.lockpassword_password_recently_used
-                        : R.string.lockpassword_pin_recently_used));
-            }
-            return messages.toArray(new String[0]);
-        }
 
-        private int getMinLengthToFulfillAllPolicies() {
-            final int minLengthForLetters = Math.max(mPasswordMinLetters,
-                    mPasswordMinUpperCase + mPasswordMinLowerCase);
-            final int minLengthForNonLetters = Math.max(mPasswordMinNonLetter,
-                    mPasswordMinSymbols + mPasswordMinNumeric);
-            return minLengthForLetters + minLengthForNonLetters;
+            return messages.toArray(new String[0]);
         }
 
         /**
@@ -966,17 +761,17 @@
             final int length = password.size();
             if (mUiStage == Stage.Introduction) {
                 mPasswordRestrictionView.setVisibility(View.VISIBLE);
-                final int errorCode = validatePassword(password);
-                String[] messages = convertErrorCodeToMessages(errorCode);
+                final boolean passwordCompliant = validatePassword(password);
+                String[] messages = convertErrorCodeToMessages();
                 // Update the fulfillment of requirements.
                 mPasswordRequirementAdapter.setRequirements(messages);
                 // Enable/Disable the next button accordingly.
-                setNextEnabled(errorCode == NO_ERROR);
+                setNextEnabled(passwordCompliant);
             } else {
                 // Hide password requirement view when we are just asking user to confirm the pw.
                 mPasswordRestrictionView.setVisibility(View.GONE);
                 setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
-                setNextEnabled(canInput && length >= mPasswordMinLength);
+                setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
                 mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
             }
             int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 1da2a63..bf8c571 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -25,6 +25,7 @@
 import android.app.trust.TrustManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
@@ -52,6 +53,12 @@
 public class ConfirmDeviceCredentialActivity extends FragmentActivity {
     public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
 
+    /**
+     * If the intent is sent from {@link com.android.systemui.keyguard.WorkLockActivity} then
+     * check for device policy management flags.
+     */
+    public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY = "from_work_lock_activity";
+
     // The normal flow that apps go through
     private static final int CREDENTIAL_NORMAL = 1;
     // Unlocks the managed profile when the primary profile is unlocked
@@ -90,6 +97,8 @@
     private TrustManager mTrustManager;
     private ChooseLockSettingsHelper mChooseLockSettingsHelper;
     private Handler mHandler = new Handler(Looper.getMainLooper());
+    private Context mContext;
+    private boolean mFromWorkLockActivity;
 
     private String mTitle;
     private String mDetails;
@@ -149,6 +158,8 @@
         mLockPatternUtils = new LockPatternUtils(this);
 
         Intent intent = getIntent();
+        mContext = this;
+        mFromWorkLockActivity = intent.getBooleanExtra(EXTRA_FROM_WORK_LOCK_ACTIVITY, false);
         mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
         mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
         String alternateButton = intent.getStringExtra(
@@ -190,7 +201,7 @@
         } else if (isManagedProfile && isInternalActivity()
                 && !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
             mCredentialMode = CREDENTIAL_MANAGED;
-            if (isBiometricAllowed(effectiveUserId, mUserId)) {
+            if (mFromWorkLockActivity && isBiometricAllowed(effectiveUserId, mUserId)) {
                 showBiometricPrompt(bpBundle);
                 launchedBiometric = true;
             } else {
@@ -256,10 +267,36 @@
                 || !mUserManager.isUserUnlocked(mUserId);
     }
 
+    /**
+     * TODO: Pass a list of disabled features to an internal BiometricPrompt API, so we can
+     * potentially show different modalities on multi-auth devices.
+     *
+     * @param effectiveUserId
+     * @return false if their exists one biometric on the device which is not disabled by the
+     * policy manager.
+     */
     private boolean isBiometricDisabledByAdmin(int effectiveUserId) {
         final int disabledFeatures =
-                mDevicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId);
-        return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS) != 0;
+            mDevicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId);
+
+        final PackageManager pm = mContext.getPackageManager();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
+            && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0) {
+            Log.d(TAG,"Fingerprint enabled & allowed by device policy manager");
+            return false;
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_IRIS)
+            && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_IRIS) == 0) {
+            Log.d(TAG,"Iris enabled & allowed by device policy manager");
+            return false;
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)
+            && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FACE) == 0) {
+            Log.d(TAG,"Face enabled & allowed by device policy manager");
+            return false;
+        }
+
+        return true;
     }
 
     private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 084d710..4bbf51d 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -77,7 +77,6 @@
         SettingsShadowResources.overrideResource(
                 com.android.internal.R.string.config_headlineFontFamily, "");
         mShadowDpm = ShadowDevicePolicyManager.getShadow();
-        mShadowDpm.setPasswordMaximumLength(16);
     }
 
     @After
@@ -157,7 +156,7 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createNone(),
-                "Must contain at least 1 letter",
+                "Must contain at least 1 non-numerical character",
                 "Must be at least 10 characters");
     }
 
@@ -180,7 +179,7 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createNone(),
-                "Must contain at least 1 letter",
+                "Must contain at least 1 non-numerical character",
                 "Must be at least 4 characters");
     }
 
@@ -193,7 +192,7 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createNone(),
-                "Must contain at least 1 letter",
+                "Must contain at least 1 non-numerical character",
                 "Must contain at least 1 numerical digit",
                 "Must be at least 9 characters");
     }
@@ -220,7 +219,9 @@
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createNone(),
                 "Must contain at least 2 special symbols",
-                "Must be at least 6 characters");
+                "Must be at least 6 characters",
+                "Must contain at least 1 letter",
+                "Must contain at least 1 numerical digit");
     }
 
     @Test
@@ -280,7 +281,7 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createPassword("12345678"),
-                "Must contain at least 1 letter");
+                "Ascending, descending, or repeated sequence of digits isn't allowed");
     }
 
     @Test
@@ -291,8 +292,8 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createNone(),
-                "Must contain at least 1 letter",
-                "Must be at least 6 characters");
+                "Must be at least 6 characters",
+                "Must contain at least 1 non-numerical character");
     }
 
     @Test
@@ -303,8 +304,8 @@
                 /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
                 /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
                 /* userEnteredPassword= */ LockscreenCredential.createPassword("1"),
-                "Must contain at least 1 letter",
-                "Must be at least 6 characters");
+                "Must be at least 6 characters",
+                "Must contain at least 1 non-numerical character");
     }
 
     @Test
@@ -330,6 +331,28 @@
     }
 
     @Test
+    public void processAndValidatePasswordRequirements_defaultPinMinimumLength() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ LockscreenCredential.createPassword("11"),
+                "PIN must be at least 4 digits");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_maximumLength() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                LockscreenCredential.createPassword("01234567890123456789"),
+                "Must be fewer than 17 characters");
+    }
+
+    @Test
     public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
         ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
         assertThat(drawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_fingerprint_header);
@@ -369,9 +392,8 @@
         intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
         ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
         ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
-        int validateResult = fragment.validatePassword(userEnteredPassword);
-        String[] messages = fragment.convertErrorCodeToMessages(validateResult);
-
+        fragment.validatePassword(userEnteredPassword);
+        String[] messages = fragment.convertErrorCodeToMessages();
         assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
index ca75916..b8fdf89 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
@@ -6,6 +6,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.PasswordMetrics;
+import android.app.admin.PasswordPolicy;
 import android.content.ComponentName;
 
 import org.robolectric.RuntimeEnvironment;
@@ -26,7 +28,6 @@
     private ComponentName mDeviceOwnerComponentName;
     private int mDeviceOwnerUserId = -1;
     private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
-    private int mPasswordMaxLength = 16;
     private int mPasswordMinLength = 0;
     private int mPasswordMinSymbols = 0;
 
@@ -77,41 +78,26 @@
     }
 
     @Implementation
-    public int getPasswordQuality(ComponentName admin, int userHandle) {
-        return mPasswordMinQuality;
+    public PasswordMetrics getPasswordMinimumMetrics(int userHandle) {
+        PasswordPolicy policy = new PasswordPolicy();
+        policy.quality = mPasswordMinQuality;
+        policy.length = mPasswordMinLength;
+        policy.symbols = mPasswordMinSymbols;
+        return policy.getMinMetrics();
     }
 
     public void setPasswordQuality(int quality) {
         mPasswordMinQuality = quality;
     }
 
-    @Implementation
-    public int getPasswordMinimumLength(ComponentName admin, int userHandle) {
-        return mPasswordMinLength;
-    }
-
     public void setPasswordMinimumLength(int length) {
         mPasswordMinLength = length;
     }
 
-    @Implementation
-    public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) {
-        return mPasswordMinSymbols;
-    }
-
     public void setPasswordMinimumSymbols(int numOfSymbols) {
         mPasswordMinSymbols = numOfSymbols;
     }
 
-    @Implementation
-    public int getPasswordMaximumLength(int quality) {
-        return mPasswordMaxLength;
-    }
-
-    public void setPasswordMaximumLength(int length) {
-        mPasswordMaxLength = length;
-    }
-
     public static ShadowDevicePolicyManager getShadow() {
         return (ShadowDevicePolicyManager) Shadow.extract(
                 RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));