Password constraints cleanup.
Unified password validation code between DPMS and Settings. Bug: 138375712 Test: atest tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java Change-Id: I55e4dae47526659594af3fcdbfc1292edceb5807
This commit is contained in:
@@ -1410,9 +1410,6 @@
|
|||||||
<item quantity="other">Must be fewer than <xliff:g id="number" example="17">%d</xliff:g> digits</item>
|
<item quantity="other">Must be fewer than <xliff:g id="number" example="17">%d</xliff:g> digits</item>
|
||||||
</plurals>
|
</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! -->
|
<!-- 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>
|
<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>
|
<item quantity="other">Must contain at least <xliff:g id="count" example="3">%d</xliff:g> non-letter characters</item>
|
||||||
</plurals>
|
</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! -->
|
<!-- 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
|
<string name="lockpassword_password_recently_used">Device admin doesn\'t allow using a recent
|
||||||
password</string>
|
password</string>
|
||||||
|
@@ -92,6 +92,7 @@ public class ChooseLockGenericController {
|
|||||||
*/
|
*/
|
||||||
public int upgradeQuality(int quality) {
|
public int upgradeQuality(int quality) {
|
||||||
// Compare specified quality and dpm 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));
|
int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||||
return Math.max(dpmUpgradedQuality,
|
return Math.max(dpmUpgradedQuality,
|
||||||
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
|
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
|
||||||
|
@@ -17,12 +17,20 @@
|
|||||||
package com.android.settings.password;
|
package com.android.settings.password;
|
||||||
|
|
||||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
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;
|
||||||
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 static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -51,7 +59,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.TextView.OnEditorActionListener;
|
import android.widget.TextView.OnEditorActionListener;
|
||||||
|
|
||||||
@@ -64,6 +71,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||||
import com.android.internal.widget.LockscreenCredential;
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
|
import com.android.internal.widget.PasswordValidationError;
|
||||||
import com.android.internal.widget.TextViewInputDisabler;
|
import com.android.internal.widget.TextViewInputDisabler;
|
||||||
import com.android.settings.EncryptionInterstitial;
|
import com.android.settings.EncryptionInterstitial;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -79,7 +87,7 @@ import com.google.android.setupcompat.template.FooterButton;
|
|||||||
import com.google.android.setupdesign.GlifLayout;
|
import com.google.android.setupdesign.GlifLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChooseLockPassword extends SettingsActivity {
|
public class ChooseLockPassword extends SettingsActivity {
|
||||||
@@ -192,17 +200,12 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
private long mChallenge;
|
private long mChallenge;
|
||||||
private ImeAwareEditText mPasswordEntry;
|
private ImeAwareEditText mPasswordEntry;
|
||||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||||
private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
|
|
||||||
private int mPasswordMaxLength = 16;
|
// Minimum password metrics enforced by admins.
|
||||||
private int mPasswordMinLetters = 0;
|
private PasswordMetrics mMinMetrics;
|
||||||
private int mPasswordMinUpperCase = 0;
|
private List<PasswordValidationError> mValidationErrors;
|
||||||
private int mPasswordMinLowerCase = 0;
|
|
||||||
private int mPasswordMinSymbols = 0;
|
@PasswordComplexity private int mMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||||
private int mPasswordMinNumeric = 0;
|
|
||||||
private int mPasswordMinNonLetter = 0;
|
|
||||||
private int mPasswordMinLengthToFulfillAllPolicies = 0;
|
|
||||||
private boolean mPasswordNumSequenceAllowed = true;
|
|
||||||
@PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
|
||||||
protected int mUserId;
|
protected int mUserId;
|
||||||
private byte[] mPasswordHistoryHashFactor;
|
private byte[] mPasswordHistoryHashFactor;
|
||||||
|
|
||||||
@@ -228,28 +231,6 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
private static final int CONFIRM_EXISTING_REQUEST = 58;
|
private static final int CONFIRM_EXISTING_REQUEST = 58;
|
||||||
static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
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.
|
* Keep track internally of where the user is in choosing a pattern.
|
||||||
*/
|
*/
|
||||||
@@ -381,13 +362,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
mForFingerprint = intent.getBooleanExtra(
|
mForFingerprint = intent.getBooleanExtra(
|
||||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||||
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||||
mRequestedMinComplexity = intent.getIntExtra(
|
mMinComplexity = intent.getIntExtra(
|
||||||
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
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());
|
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||||
|
|
||||||
if (intent.getBooleanExtra(
|
if (intent.getBooleanExtra(
|
||||||
@@ -602,209 +583,23 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
|
* Validates PIN/Password and returns the validation result and updates mValidationErrors
|
||||||
*/
|
* and mPasswordReused to reflect validation results.
|
||||||
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.
|
|
||||||
*
|
*
|
||||||
* <p>Since there are more than one set of metrics to meet the min complexity requirement,
|
* @param credential credential the user typed in.
|
||||||
* and we are not hard-coding any one of them to be the requirements the user must fulfil,
|
* @return whether password satisfies all the requirements.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
int validatePassword(LockscreenCredential credential) {
|
boolean validatePassword(LockscreenCredential credential) {
|
||||||
final byte[] password = credential.getCredential();
|
final byte[] password = credential.getCredential();
|
||||||
int errorCode = NO_ERROR;
|
mValidationErrors = PasswordMetrics.validatePassword(
|
||||||
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
|
mMinMetrics, mMinComplexity, !mIsAlphaMode, password);
|
||||||
mergeMinComplexityAndDpmRequirements(metrics.quality);
|
if (mValidationErrors.isEmpty() && mLockPatternUtils.checkPasswordHistory(
|
||||||
|
password, getPasswordHistoryHashFactor(), mUserId)) {
|
||||||
if (password == null || password.length < mPasswordMinLength) {
|
mValidationErrors =
|
||||||
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
|
Collections.singletonList(new PasswordValidationError(RECENTLY_USED));
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return mValidationErrors.isEmpty();
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -830,7 +625,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
|
mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
|
||||||
: LockscreenCredential.createPin(passwordText);
|
: LockscreenCredential.createPin(passwordText);
|
||||||
if (mUiStage == Stage.Introduction) {
|
if (mUiStage == Stage.Introduction) {
|
||||||
if (validatePassword(mChosenPassword) == NO_ERROR) {
|
if (validatePassword(mChosenPassword)) {
|
||||||
mFirstPassword = mChosenPassword;
|
mFirstPassword = mChosenPassword;
|
||||||
mPasswordEntry.setText("");
|
mPasswordEntry.setText("");
|
||||||
updateStage(Stage.NeedToConfirm);
|
updateStage(Stage.NeedToConfirm);
|
||||||
@@ -879,79 +674,79 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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.
|
* @return an array of messages describing the error, important messages come first.
|
||||||
*/
|
*/
|
||||||
String[] convertErrorCodeToMessages(int errorCode) {
|
String[] convertErrorCodeToMessages() {
|
||||||
List<String> messages = new ArrayList<>();
|
List<String> messages = new ArrayList<>();
|
||||||
if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) {
|
for (PasswordValidationError error : mValidationErrors) {
|
||||||
messages.add(getString(R.string.lockpassword_illegal_character));
|
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() {
|
return messages.toArray(new String[0]);
|
||||||
final int minLengthForLetters = Math.max(mPasswordMinLetters,
|
|
||||||
mPasswordMinUpperCase + mPasswordMinLowerCase);
|
|
||||||
final int minLengthForNonLetters = Math.max(mPasswordMinNonLetter,
|
|
||||||
mPasswordMinSymbols + mPasswordMinNumeric);
|
|
||||||
return minLengthForLetters + minLengthForNonLetters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -966,17 +761,17 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
final int length = password.size();
|
final int length = password.size();
|
||||||
if (mUiStage == Stage.Introduction) {
|
if (mUiStage == Stage.Introduction) {
|
||||||
mPasswordRestrictionView.setVisibility(View.VISIBLE);
|
mPasswordRestrictionView.setVisibility(View.VISIBLE);
|
||||||
final int errorCode = validatePassword(password);
|
final boolean passwordCompliant = validatePassword(password);
|
||||||
String[] messages = convertErrorCodeToMessages(errorCode);
|
String[] messages = convertErrorCodeToMessages();
|
||||||
// Update the fulfillment of requirements.
|
// Update the fulfillment of requirements.
|
||||||
mPasswordRequirementAdapter.setRequirements(messages);
|
mPasswordRequirementAdapter.setRequirements(messages);
|
||||||
// Enable/Disable the next button accordingly.
|
// Enable/Disable the next button accordingly.
|
||||||
setNextEnabled(errorCode == NO_ERROR);
|
setNextEnabled(passwordCompliant);
|
||||||
} else {
|
} else {
|
||||||
// Hide password requirement view when we are just asking user to confirm the pw.
|
// Hide password requirement view when we are just asking user to confirm the pw.
|
||||||
mPasswordRestrictionView.setVisibility(View.GONE);
|
mPasswordRestrictionView.setVisibility(View.GONE);
|
||||||
setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
|
setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
|
||||||
setNextEnabled(canInput && length >= mPasswordMinLength);
|
setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
|
||||||
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
|
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
|
||||||
}
|
}
|
||||||
int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
|
int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
|
||||||
|
@@ -77,7 +77,6 @@ public class ChooseLockPasswordTest {
|
|||||||
SettingsShadowResources.overrideResource(
|
SettingsShadowResources.overrideResource(
|
||||||
com.android.internal.R.string.config_headlineFontFamily, "");
|
com.android.internal.R.string.config_headlineFontFamily, "");
|
||||||
mShadowDpm = ShadowDevicePolicyManager.getShadow();
|
mShadowDpm = ShadowDevicePolicyManager.getShadow();
|
||||||
mShadowDpm.setPasswordMaximumLength(16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -157,7 +156,7 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
||||||
"Must contain at least 1 letter",
|
"Must contain at least 1 non-numerical character",
|
||||||
"Must be at least 10 characters");
|
"Must be at least 10 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +179,7 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
||||||
"Must contain at least 1 letter",
|
"Must contain at least 1 non-numerical character",
|
||||||
"Must be at least 4 characters");
|
"Must be at least 4 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +192,7 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
/* 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 contain at least 1 numerical digit",
|
||||||
"Must be at least 9 characters");
|
"Must be at least 9 characters");
|
||||||
}
|
}
|
||||||
@@ -220,7 +219,9 @@ public class ChooseLockPasswordTest {
|
|||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
||||||
"Must contain at least 2 special symbols",
|
"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
|
@Test
|
||||||
@@ -280,7 +281,7 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createPassword("12345678"),
|
/* userEnteredPassword= */ LockscreenCredential.createPassword("12345678"),
|
||||||
"Must contain at least 1 letter");
|
"Ascending, descending, or repeated sequence of digits isn't allowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -291,8 +292,8 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
/* 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
|
@Test
|
||||||
@@ -303,8 +304,8 @@ public class ChooseLockPasswordTest {
|
|||||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||||
/* userEnteredPassword= */ LockscreenCredential.createPassword("1"),
|
/* 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
|
@Test
|
||||||
@@ -329,6 +330,28 @@ public class ChooseLockPasswordTest {
|
|||||||
"Must be at least 6 characters");
|
"Must be at least 6 characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
|
public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
|
||||||
ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
|
ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
|
||||||
@@ -369,9 +392,8 @@ public class ChooseLockPasswordTest {
|
|||||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
|
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
|
||||||
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
|
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
|
||||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
|
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
|
||||||
int validateResult = fragment.validatePassword(userEnteredPassword);
|
fragment.validatePassword(userEnteredPassword);
|
||||||
String[] messages = fragment.convertErrorCodeToMessages(validateResult);
|
String[] messages = fragment.convertErrorCodeToMessages();
|
||||||
|
|
||||||
assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
|
assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,8 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.app.admin.PasswordMetrics;
|
||||||
|
import android.app.admin.PasswordPolicy;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
@@ -26,7 +28,6 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
|||||||
private ComponentName mDeviceOwnerComponentName;
|
private ComponentName mDeviceOwnerComponentName;
|
||||||
private int mDeviceOwnerUserId = -1;
|
private int mDeviceOwnerUserId = -1;
|
||||||
private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
|
private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||||
private int mPasswordMaxLength = 16;
|
|
||||||
private int mPasswordMinLength = 0;
|
private int mPasswordMinLength = 0;
|
||||||
private int mPasswordMinSymbols = 0;
|
private int mPasswordMinSymbols = 0;
|
||||||
|
|
||||||
@@ -77,41 +78,26 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
public int getPasswordQuality(ComponentName admin, int userHandle) {
|
public PasswordMetrics getPasswordMinimumMetrics(int userHandle) {
|
||||||
return mPasswordMinQuality;
|
PasswordPolicy policy = new PasswordPolicy();
|
||||||
|
policy.quality = mPasswordMinQuality;
|
||||||
|
policy.length = mPasswordMinLength;
|
||||||
|
policy.symbols = mPasswordMinSymbols;
|
||||||
|
return policy.getMinMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPasswordQuality(int quality) {
|
public void setPasswordQuality(int quality) {
|
||||||
mPasswordMinQuality = quality;
|
mPasswordMinQuality = quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Implementation
|
|
||||||
public int getPasswordMinimumLength(ComponentName admin, int userHandle) {
|
|
||||||
return mPasswordMinLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordMinimumLength(int length) {
|
public void setPasswordMinimumLength(int length) {
|
||||||
mPasswordMinLength = length;
|
mPasswordMinLength = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Implementation
|
|
||||||
public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) {
|
|
||||||
return mPasswordMinSymbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordMinimumSymbols(int numOfSymbols) {
|
public void setPasswordMinimumSymbols(int numOfSymbols) {
|
||||||
mPasswordMinSymbols = numOfSymbols;
|
mPasswordMinSymbols = numOfSymbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Implementation
|
|
||||||
public int getPasswordMaximumLength(int quality) {
|
|
||||||
return mPasswordMaxLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordMaximumLength(int length) {
|
|
||||||
mPasswordMaxLength = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShadowDevicePolicyManager getShadow() {
|
public static ShadowDevicePolicyManager getShadow() {
|
||||||
return (ShadowDevicePolicyManager) Shadow.extract(
|
return (ShadowDevicePolicyManager) Shadow.extract(
|
||||||
RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));
|
RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));
|
||||||
|
Reference in New Issue
Block a user