Merge "Refactor password metric computation."

This commit is contained in:
Andrew Scull
2016-10-25 09:04:55 +00:00
committed by Android (Google) Code Review
8 changed files with 557 additions and 327 deletions

View File

@@ -575,6 +575,7 @@ aidl_files := \
frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
frameworks/base/core/java/android/accounts/Account.aidl \
frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
frameworks/base/core/java/android/print/PageRange.aidl \
frameworks/base/core/java/android/print/PrintAttributes.aidl \

View File

@@ -26,6 +26,7 @@ import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.ComponentName;
import android.content.Context;
@@ -3523,12 +3524,10 @@ public class DevicePolicyManager {
/**
* @hide
*/
public void setActivePasswordState(int quality, int length, int letters, int uppercase,
int lowercase, int numbers, int symbols, int nonletter, int userHandle) {
public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
if (mService != null) {
try {
mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
numbers, symbols, nonletter, userHandle);
mService.setActivePasswordState(metrics, userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -18,6 +18,7 @@
package android.app.admin;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.PasswordMetrics;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -29,6 +30,7 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
import java.util.List;
/**
@@ -117,8 +119,7 @@ interface IDevicePolicyManager {
void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
int numbers, int symbols, int nonletter, int userHandle);
void setActivePasswordState(in PasswordMetrics metrics, int userHandle);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
void reportFailedFingerprintAttempt(int userHandle);

View File

@@ -0,0 +1,20 @@
/*
**
** Copyright 2016, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.app.admin;
parcelable PasswordMetrics;

View File

@@ -0,0 +1,238 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.admin;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.os.Parcelable;
import android.os.Parcel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.io.IOException;
/**
* A class that represents the metrics of a password that are used to decide whether or not a
* password meets the requirements.
*
* {@hide}
*/
public class PasswordMetrics implements Parcelable {
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
public int length = 0;
public int letters = 0;
public int upperCase = 0;
public int lowerCase = 0;
public int numeric = 0;
public int symbols = 0;
public int nonLetter = 0;
public PasswordMetrics() {}
public PasswordMetrics(int quality, int length) {
this.quality = quality;
this.length = length;
}
public PasswordMetrics(int quality, int length, int letters, int upperCase, int lowerCase,
int numeric, int symbols, int nonLetter) {
this(quality, length);
this.letters = letters;
this.upperCase = upperCase;
this.lowerCase = lowerCase;
this.numeric = numeric;
this.symbols = symbols;
this.nonLetter = nonLetter;
}
private PasswordMetrics(Parcel in) {
quality = in.readInt();
length = in.readInt();
letters = in.readInt();
upperCase = in.readInt();
lowerCase = in.readInt();
numeric = in.readInt();
symbols = in.readInt();
nonLetter = in.readInt();
}
public boolean isDefault() {
return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
&& length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
&& numeric == 0 && symbols == 0 && nonLetter == 0;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(quality);
dest.writeInt(length);
dest.writeInt(letters);
dest.writeInt(upperCase);
dest.writeInt(lowerCase);
dest.writeInt(numeric);
dest.writeInt(symbols);
dest.writeInt(nonLetter);
}
public static final Parcelable.Creator<PasswordMetrics> CREATOR
= new Parcelable.Creator<PasswordMetrics>() {
public PasswordMetrics createFromParcel(Parcel in) {
return new PasswordMetrics(in);
}
public PasswordMetrics[] newArray(int size) {
return new PasswordMetrics[size];
}
};
public static PasswordMetrics computeForPassword(@NonNull String password) {
// Analyse the characters used
int letters = 0;
int upperCase = 0;
int lowerCase = 0;
int numeric = 0;
int symbols = 0;
int nonLetter = 0;
final int length = password.length();
for (int i = 0; i < length; i++) {
switch (categoryChar(password.charAt(i))) {
case CHAR_LOWER_CASE:
letters++;
lowerCase++;
break;
case CHAR_UPPER_CASE:
letters++;
upperCase++;
break;
case CHAR_DIGIT:
numeric++;
nonLetter++;
break;
case CHAR_SYMBOL:
symbols++;
nonLetter++;
break;
}
}
// Determine the quality of the password
final boolean hasNumeric = numeric > 0;
final boolean hasNonNumeric = (letters + symbols) > 0;
final int quality;
if (hasNonNumeric && hasNumeric) {
quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
} else if (hasNonNumeric) {
quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
} else if (hasNumeric) {
quality = maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
: DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
} else {
quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
return new PasswordMetrics(
quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter);
}
/*
* Returns the maximum length of a sequential characters. A sequence is defined as
* monotonically increasing characters with a constant interval or the same character repeated.
*
* For example:
* maxLengthSequence("1234") == 4
* maxLengthSequence("13579") == 5
* maxLengthSequence("1234abc") == 4
* maxLengthSequence("aabc") == 3
* maxLengthSequence("qwertyuio") == 1
* maxLengthSequence("@ABC") == 3
* maxLengthSequence(";;;;") == 4 (anything that repeats)
* maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
*
* @param string the pass
* @return the number of sequential letters or digits
*/
public static int maxLengthSequence(@NonNull String string) {
if (string.length() == 0) return 0;
char previousChar = string.charAt(0);
@CharacterCatagory int category = categoryChar(previousChar); //current sequence category
int diff = 0; //difference between two consecutive characters
boolean hasDiff = false; //if we are currently targeting a sequence
int maxLength = 0; //maximum length of a sequence already found
int startSequence = 0; //where the current sequence started
for (int current = 1; current < string.length(); current++) {
char currentChar = string.charAt(current);
@CharacterCatagory int categoryCurrent = categoryChar(currentChar);
int currentDiff = (int) currentChar - (int) previousChar;
if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
maxLength = Math.max(maxLength, current - startSequence);
startSequence = current;
hasDiff = false;
category = categoryCurrent;
}
else {
if(hasDiff && currentDiff != diff) {
maxLength = Math.max(maxLength, current - startSequence);
startSequence = current - 1;
}
diff = currentDiff;
hasDiff = true;
}
previousChar = currentChar;
}
maxLength = Math.max(maxLength, string.length() - startSequence);
return maxLength;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({CHAR_UPPER_CASE, CHAR_LOWER_CASE, CHAR_DIGIT, CHAR_SYMBOL})
private @interface CharacterCatagory {}
private static final int CHAR_LOWER_CASE = 0;
private static final int CHAR_UPPER_CASE = 1;
private static final int CHAR_DIGIT = 2;
private static final int CHAR_SYMBOL = 3;
@CharacterCatagory
private static int categoryChar(char c) {
if ('a' <= c && c <= 'z') return CHAR_LOWER_CASE;
if ('A' <= c && c <= 'Z') return CHAR_UPPER_CASE;
if ('0' <= c && c <= '9') return CHAR_DIGIT;
return CHAR_SYMBOL;
}
private static int maxDiffCategory(@CharacterCatagory int category) {
switch (category) {
case CHAR_LOWER_CASE:
case CHAR_UPPER_CASE:
return 1;
case CHAR_DIGIT:
return 10;
default:
return 0;
}
}
}

View File

@@ -19,6 +19,7 @@ package com.android.internal.widget;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.ComponentName;
@@ -137,10 +138,6 @@ public class LockPatternUtils {
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
@@ -593,8 +590,7 @@ public class LockPatternUtils {
setCredentialRequiredToDecrypt(false);
}
getDevicePolicyManager().setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
getDevicePolicyManager().setActivePasswordState(new PasswordMetrics(), userHandle);
onAfterChangingPassword(userHandle);
}
@@ -665,8 +661,8 @@ public class LockPatternUtils {
setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
pattern.size(), 0, 0, 0, 0, 0, 0, userId);
dpm.setActivePasswordState(new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()), userId);
onAfterChangingPassword(userId);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -736,96 +732,6 @@ public class LockPatternUtils {
return getDeviceOwnerInfo() != null;
}
/**
* Compute the password quality from the given password string.
*/
static public int computePasswordQuality(String password) {
boolean hasDigit = false;
boolean hasNonDigit = false;
final int len = password.length();
for (int i = 0; i < len; i++) {
if (Character.isDigit(password.charAt(i))) {
hasDigit = true;
} else {
hasNonDigit = true;
}
}
if (hasNonDigit && hasDigit) {
return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
}
if (hasNonDigit) {
return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
}
if (hasDigit) {
return maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
: DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
}
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
private static int categoryChar(char c) {
if ('a' <= c && c <= 'z') return 0;
if ('A' <= c && c <= 'Z') return 1;
if ('0' <= c && c <= '9') return 2;
return 3;
}
private static int maxDiffCategory(int category) {
if (category == 0 || category == 1) return 1;
else if (category == 2) return 10;
return 0;
}
/*
* Returns the maximum length of a sequential characters. A sequence is defined as
* monotonically increasing characters with a constant interval or the same character repeated.
*
* For example:
* maxLengthSequence("1234") == 4
* maxLengthSequence("1234abc") == 4
* maxLengthSequence("aabc") == 3
* maxLengthSequence("qwertyuio") == 1
* maxLengthSequence("@ABC") == 3
* maxLengthSequence(";;;;") == 4 (anything that repeats)
* maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
*
* @param string the pass
* @return the number of sequential letters or digits
*/
public static int maxLengthSequence(String string) {
if (string.length() == 0) return 0;
char previousChar = string.charAt(0);
int category = categoryChar(previousChar); //current category of the sequence
int diff = 0; //difference between two consecutive characters
boolean hasDiff = false; //if we are currently targeting a sequence
int maxLength = 0; //maximum length of a sequence already found
int startSequence = 0; //where the current sequence started
for (int current = 1; current < string.length(); current++) {
char currentChar = string.charAt(current);
int categoryCurrent = categoryChar(currentChar);
int currentDiff = (int) currentChar - (int) previousChar;
if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
maxLength = Math.max(maxLength, current - startSequence);
startSequence = current;
hasDiff = false;
category = categoryCurrent;
}
else {
if(hasDiff && currentDiff != diff) {
maxLength = Math.max(maxLength, current - startSequence);
startSequence = current - 1;
}
diff = currentDiff;
hasDiff = true;
}
previousChar = currentChar;
}
maxLength = Math.max(maxLength, string.length() - startSequence);
return maxLength;
}
/** Update the encryption password if it is enabled **/
private void updateEncryptionPassword(final int type, final String password) {
if (!isDeviceEncryptionEnabled()) {
@@ -871,7 +777,8 @@ public class LockPatternUtils {
getLockSettings().setLockPassword(password, savedPassword, userHandle);
getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
int computedQuality = computePasswordQuality(password);
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
final int computedQuality = metrics.quality;
// Update the device encryption password.
if (userHandle == UserHandle.USER_SYSTEM
@@ -891,36 +798,11 @@ public class LockPatternUtils {
setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int letters = 0;
int uppercase = 0;
int lowercase = 0;
int numbers = 0;
int symbols = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else {
symbols++;
nonletter++;
}
}
dpm.setActivePasswordState(Math.max(quality, computedQuality),
password.length(), letters, uppercase, lowercase,
numbers, symbols, nonletter, userHandle);
metrics.quality = Math.max(quality, metrics.quality);
dpm.setActivePasswordState(metrics, userHandle);
} else {
// The password is not anything.
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
0, 0, 0, 0, 0, 0, 0, userHandle);
dpm.setActivePasswordState(new PasswordMetrics(), userHandle);
}
// Add the password to the password history. We assume all

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.app.admin;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/** Unit tests for {@link PasswordMetrics}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PasswordMetricsTest {
@Test
public void testIsDefault() {
final PasswordMetrics metrics = new PasswordMetrics();
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, metrics.quality);
assertEquals(0, metrics.length);
assertEquals(0, metrics.letters);
assertEquals(0, metrics.upperCase);
assertEquals(0, metrics.lowerCase);
assertEquals(0, metrics.numeric);
assertEquals(0, metrics.symbols);
assertEquals(0, metrics.nonLetter);
assertTrue("default constructor does not produce default metrics", metrics.isDefault());
}
@Test
public void testIsNotDefault() {
final PasswordMetrics metrics = new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, 12);
assertFalse("non-default metrics are repoted as default", metrics.isDefault());
}
@Test
public void testComputeForEmptyPassword() {
final PasswordMetrics metrics = PasswordMetrics.computeForPassword("");
assertTrue("empty password has default metrics", metrics.isDefault());
}
@Test
public void testParceling() {
final int quality = 0;
final int length = 1;
final int letters = 2;
final int upperCase = 3;
final int lowerCase = 4;
final int numeric = 5;
final int symbols = 6;
final int nonLetter = 7;
final Parcel parcel = Parcel.obtain();
final PasswordMetrics metrics;
try {
new PasswordMetrics(
quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter)
.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
metrics = PasswordMetrics.CREATOR.createFromParcel(parcel);
} finally {
parcel.recycle();
}
assertEquals(quality, metrics.quality);
assertEquals(length, metrics.length);
assertEquals(letters, metrics.letters);
assertEquals(upperCase, metrics.upperCase);
assertEquals(lowerCase, metrics.lowerCase);
assertEquals(numeric, metrics.numeric);
assertEquals(symbols, metrics.symbols);
assertEquals(nonLetter, metrics.nonLetter);
}
@Test
public void testComputeForPassword_metrics() {
final PasswordMetrics metrics = PasswordMetrics.computeForPassword("6B~0z1Z3*8A");
assertEquals(11, metrics.length);
assertEquals(4, metrics.letters);
assertEquals(3, metrics.upperCase);
assertEquals(1, metrics.lowerCase);
assertEquals(5, metrics.numeric);
assertEquals(2, metrics.symbols);
assertEquals(7, metrics.nonLetter);
}
@Test
public void testComputeForPassword_quality() {
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
PasswordMetrics.computeForPassword("a1").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
PasswordMetrics.computeForPassword("a").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
PasswordMetrics.computeForPassword("*~&%$").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
PasswordMetrics.computeForPassword("1").quality);
// contains a long sequence so isn't complex
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
PasswordMetrics.computeForPassword("1234").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
PasswordMetrics.computeForPassword("").quality);
}
@Test
public void testMaxLengthSequence() {
assertEquals(4, PasswordMetrics.maxLengthSequence("1234"));
assertEquals(5, PasswordMetrics.maxLengthSequence("13579"));
assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd"));
assertEquals(3, PasswordMetrics.maxLengthSequence("aabc"));
assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio"));
assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC"));
// anything that repeats
assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;"));
// ordered, but not composed of alphas or digits
assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>"));
}
}

View File

@@ -51,6 +51,7 @@ import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.SystemUpdatePolicy;
@@ -425,14 +426,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
public static class DevicePolicyData {
int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
int mActivePasswordLength = 0;
int mActivePasswordUpperCase = 0;
int mActivePasswordLowerCase = 0;
int mActivePasswordLetters = 0;
int mActivePasswordNumeric = 0;
int mActivePasswordSymbols = 0;
int mActivePasswordNonLetter = 0;
@NonNull PasswordMetrics mActivePasswordMetrics = new PasswordMetrics();
int mFailedPasswordAttempts = 0;
int mUserHandle;
@@ -598,31 +592,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final DeviceAdminInfo info;
int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
int minimumPasswordLength = DEF_MINIMUM_PASSWORD_LENGTH;
static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
int minimumPasswordUpperCase = DEF_MINIMUM_PASSWORD_UPPER_CASE;
static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE;
static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
static final int DEF_MINIMUM_PASSWORD_LETTERS = 1;
int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LETTERS;
static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1;
int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC;
static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1;
int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS;
static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
int minimumPasswordNonLetter = DEF_MINIMUM_PASSWORD_NON_LETTER;
@NonNull
PasswordMetrics minimumPasswordMetrics = new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, DEF_MINIMUM_PASSWORD_LENGTH,
DEF_MINIMUM_PASSWORD_LETTERS, DEF_MINIMUM_PASSWORD_UPPER_CASE,
DEF_MINIMUM_PASSWORD_LOWER_CASE, DEF_MINIMUM_PASSWORD_NUMERIC,
DEF_MINIMUM_PASSWORD_SYMBOLS, DEF_MINIMUM_PASSWORD_NON_LETTER);
static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
@@ -728,13 +714,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.startTag(null, TAG_POLICIES);
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
if (minimumPasswordMetrics.quality
!= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
out.startTag(null, TAG_PASSWORD_QUALITY);
out.attribute(null, ATTR_VALUE, Integer.toString(passwordQuality));
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.quality));
out.endTag(null, TAG_PASSWORD_QUALITY);
if (minimumPasswordLength != DEF_MINIMUM_PASSWORD_LENGTH) {
if (minimumPasswordMetrics.length != DEF_MINIMUM_PASSWORD_LENGTH) {
out.startTag(null, TAG_MIN_PASSWORD_LENGTH);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLength));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.length));
out.endTag(null, TAG_MIN_PASSWORD_LENGTH);
}
if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
@@ -742,34 +730,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, ATTR_VALUE, Integer.toString(passwordHistoryLength));
out.endTag(null, TAG_PASSWORD_HISTORY_LENGTH);
}
if (minimumPasswordUpperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
if (minimumPasswordMetrics.upperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
out.startTag(null, TAG_MIN_PASSWORD_UPPERCASE);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordUpperCase));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.upperCase));
out.endTag(null, TAG_MIN_PASSWORD_UPPERCASE);
}
if (minimumPasswordLowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
if (minimumPasswordMetrics.lowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
out.startTag(null, TAG_MIN_PASSWORD_LOWERCASE);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLowerCase));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.lowerCase));
out.endTag(null, TAG_MIN_PASSWORD_LOWERCASE);
}
if (minimumPasswordLetters != DEF_MINIMUM_PASSWORD_LETTERS) {
if (minimumPasswordMetrics.letters != DEF_MINIMUM_PASSWORD_LETTERS) {
out.startTag(null, TAG_MIN_PASSWORD_LETTERS);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLetters));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.letters));
out.endTag(null, TAG_MIN_PASSWORD_LETTERS);
}
if (minimumPasswordNumeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
if (minimumPasswordMetrics.numeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
out.startTag(null, TAG_MIN_PASSWORD_NUMERIC);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNumeric));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.numeric));
out.endTag(null, TAG_MIN_PASSWORD_NUMERIC);
}
if (minimumPasswordSymbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
if (minimumPasswordMetrics.symbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
out.startTag(null, TAG_MIN_PASSWORD_SYMBOLS);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordSymbols));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.symbols));
out.endTag(null, TAG_MIN_PASSWORD_SYMBOLS);
}
if (minimumPasswordNonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
if (minimumPasswordMetrics.nonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
out.startTag(null, TAG_MIN_PASSWORD_NONLETTER);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNonLetter));
out.attribute(
null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.nonLetter));
out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
}
}
@@ -968,31 +962,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (TAG_POLICIES.equals(tag)) {
info.readPoliciesFromXml(parser);
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
passwordQuality = Integer.parseInt(
minimumPasswordMetrics.quality = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
minimumPasswordLength = Integer.parseInt(
minimumPasswordMetrics.length = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
passwordHistoryLength = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
minimumPasswordUpperCase = Integer.parseInt(
minimumPasswordMetrics.upperCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
minimumPasswordLowerCase = Integer.parseInt(
minimumPasswordMetrics.lowerCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
minimumPasswordLetters = Integer.parseInt(
minimumPasswordMetrics.letters = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
minimumPasswordNumeric = Integer.parseInt(
minimumPasswordMetrics.numeric = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
minimumPasswordSymbols = Integer.parseInt(
minimumPasswordMetrics.symbols = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
minimumPasswordNonLetter = Integer.parseInt(
minimumPasswordMetrics.nonLetter = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
@@ -1232,23 +1226,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
pw.print(prefix); pw.print("passwordQuality=0x");
pw.println(Integer.toHexString(passwordQuality));
pw.println(Integer.toHexString(minimumPasswordMetrics.quality));
pw.print(prefix); pw.print("minimumPasswordLength=");
pw.println(minimumPasswordLength);
pw.println(minimumPasswordMetrics.length);
pw.print(prefix); pw.print("passwordHistoryLength=");
pw.println(passwordHistoryLength);
pw.print(prefix); pw.print("minimumPasswordUpperCase=");
pw.println(minimumPasswordUpperCase);
pw.println(minimumPasswordMetrics.upperCase);
pw.print(prefix); pw.print("minimumPasswordLowerCase=");
pw.println(minimumPasswordLowerCase);
pw.println(minimumPasswordMetrics.lowerCase);
pw.print(prefix); pw.print("minimumPasswordLetters=");
pw.println(minimumPasswordLetters);
pw.println(minimumPasswordMetrics.letters);
pw.print(prefix); pw.print("minimumPasswordNumeric=");
pw.println(minimumPasswordNumeric);
pw.println(minimumPasswordMetrics.numeric);
pw.print(prefix); pw.print("minimumPasswordSymbols=");
pw.println(minimumPasswordSymbols);
pw.println(minimumPasswordMetrics.symbols);
pw.print(prefix); pw.print("minimumPasswordNonLetter=");
pw.println(minimumPasswordNonLetter);
pw.println(minimumPasswordMetrics.nonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
@@ -2281,20 +2275,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endTag(null, "failed-password-attempts");
}
if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
|| policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0
|| policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0
|| policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) {
final PasswordMetrics metrics = policy.mActivePasswordMetrics;
if (!metrics.isDefault()) {
out.startTag(null, "active-password");
out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality));
out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength));
out.attribute(null, "uppercase", Integer.toString(policy.mActivePasswordUpperCase));
out.attribute(null, "lowercase", Integer.toString(policy.mActivePasswordLowerCase));
out.attribute(null, "letters", Integer.toString(policy.mActivePasswordLetters));
out.attribute(null, "numeric", Integer
.toString(policy.mActivePasswordNumeric));
out.attribute(null, "symbols", Integer.toString(policy.mActivePasswordSymbols));
out.attribute(null, "nonletter", Integer.toString(policy.mActivePasswordNonLetter));
out.attribute(null, "quality", Integer.toString(metrics.quality));
out.attribute(null, "length", Integer.toString(metrics.length));
out.attribute(null, "uppercase", Integer.toString(metrics.upperCase));
out.attribute(null, "lowercase", Integer.toString(metrics.lowerCase));
out.attribute(null, "letters", Integer.toString(metrics.letters));
out.attribute(null, "numeric", Integer.toString(metrics.numeric));
out.attribute(null, "symbols", Integer.toString(metrics.symbols));
out.attribute(null, "nonletter", Integer.toString(metrics.nonLetter));
out.endTag(null, "active-password");
}
@@ -2464,22 +2455,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.mPasswordOwner = Integer.parseInt(
parser.getAttributeValue(null, "value"));
} else if ("active-password".equals(tag)) {
policy.mActivePasswordQuality = Integer.parseInt(
parser.getAttributeValue(null, "quality"));
policy.mActivePasswordLength = Integer.parseInt(
parser.getAttributeValue(null, "length"));
policy.mActivePasswordUpperCase = Integer.parseInt(
parser.getAttributeValue(null, "uppercase"));
policy.mActivePasswordLowerCase = Integer.parseInt(
parser.getAttributeValue(null, "lowercase"));
policy.mActivePasswordLetters = Integer.parseInt(
parser.getAttributeValue(null, "letters"));
policy.mActivePasswordNumeric = Integer.parseInt(
parser.getAttributeValue(null, "numeric"));
policy.mActivePasswordSymbols = Integer.parseInt(
parser.getAttributeValue(null, "symbols"));
policy.mActivePasswordNonLetter = Integer.parseInt(
parser.getAttributeValue(null, "nonletter"));
final PasswordMetrics m = policy.mActivePasswordMetrics;
m.quality = Integer.parseInt(parser.getAttributeValue(null, "quality"));
m.length = Integer.parseInt(parser.getAttributeValue(null, "length"));
m.upperCase = Integer.parseInt(parser.getAttributeValue(null, "uppercase"));
m.lowerCase = Integer.parseInt(parser.getAttributeValue(null, "lowercase"));
m.letters = Integer.parseInt(parser.getAttributeValue(null, "letters"));
m.numeric = Integer.parseInt(parser.getAttributeValue(null, "numeric"));
m.symbols = Integer.parseInt(parser.getAttributeValue(null, "symbols"));
m.nonLetter = Integer.parseInt(parser.getAttributeValue(null, "nonletter"));
} else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2525,19 +2509,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final long identity = mInjector.binderClearCallingIdentity();
try {
int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
if (actualPasswordQuality < policy.mActivePasswordQuality) {
if (actualPasswordQuality < policy.mActivePasswordMetrics.quality) {
Slog.w(LOG_TAG, "Active password quality 0x"
+ Integer.toHexString(policy.mActivePasswordQuality)
+ Integer.toHexString(policy.mActivePasswordMetrics.quality)
+ " does not match actual quality 0x"
+ Integer.toHexString(actualPasswordQuality));
policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
policy.mActivePasswordLength = 0;
policy.mActivePasswordUpperCase = 0;
policy.mActivePasswordLowerCase = 0;
policy.mActivePasswordLetters = 0;
policy.mActivePasswordNumeric = 0;
policy.mActivePasswordSymbols = 0;
policy.mActivePasswordNonLetter = 0;
policy.mActivePasswordMetrics = new PasswordMetrics();
}
} finally {
mInjector.binderRestoreCallingIdentity(identity);
@@ -3127,8 +3104,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.passwordQuality != quality) {
ap.passwordQuality = quality;
if (ap.minimumPasswordMetrics.quality != quality) {
ap.minimumPasswordMetrics.quality = quality;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3145,7 +3122,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.passwordQuality : mode;
return admin != null ? admin.minimumPasswordMetrics.quality : mode;
}
// Return the strictest policy across all participating admins.
@@ -3154,8 +3131,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
if (mode < admin.passwordQuality) {
mode = admin.passwordQuality;
if (mode < admin.minimumPasswordMetrics.quality) {
mode = admin.minimumPasswordMetrics.quality;
}
}
return mode;
@@ -3214,8 +3191,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordLength != length) {
ap.minimumPasswordLength = length;
if (ap.minimumPasswordMetrics.length != length) {
ap.minimumPasswordMetrics.length = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3232,7 +3209,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordLength : length;
return admin != null ? admin.minimumPasswordMetrics.length : length;
}
// Return the strictest policy across all participating admins.
@@ -3241,8 +3218,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
if (length < admin.minimumPasswordLength) {
length = admin.minimumPasswordLength;
if (length < admin.minimumPasswordMetrics.length) {
length = admin.minimumPasswordMetrics.length;
}
}
return length;
@@ -3469,8 +3446,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordUpperCase != length) {
ap.minimumPasswordUpperCase = length;
if (ap.minimumPasswordMetrics.upperCase != length) {
ap.minimumPasswordMetrics.upperCase = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3487,7 +3464,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordUpperCase : length;
return admin != null ? admin.minimumPasswordMetrics.upperCase : length;
}
// Return the strictest policy across all participating admins.
@@ -3496,8 +3473,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
if (length < admin.minimumPasswordUpperCase) {
length = admin.minimumPasswordUpperCase;
if (length < admin.minimumPasswordMetrics.upperCase) {
length = admin.minimumPasswordMetrics.upperCase;
}
}
return length;
@@ -3510,8 +3487,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordLowerCase != length) {
ap.minimumPasswordLowerCase = length;
if (ap.minimumPasswordMetrics.lowerCase != length) {
ap.minimumPasswordMetrics.lowerCase = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3528,7 +3505,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordLowerCase : length;
return admin != null ? admin.minimumPasswordMetrics.lowerCase : length;
}
// Return the strictest policy across all participating admins.
@@ -3537,8 +3514,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
if (length < admin.minimumPasswordLowerCase) {
length = admin.minimumPasswordLowerCase;
if (length < admin.minimumPasswordMetrics.lowerCase) {
length = admin.minimumPasswordMetrics.lowerCase;
}
}
return length;
@@ -3554,8 +3531,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordLetters != length) {
ap.minimumPasswordLetters = length;
if (ap.minimumPasswordMetrics.letters != length) {
ap.minimumPasswordMetrics.letters = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3572,7 +3549,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordLetters : length;
return admin != null ? admin.minimumPasswordMetrics.letters : length;
}
// Return the strictest policy across all participating admins.
@@ -3584,8 +3561,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
if (length < admin.minimumPasswordLetters) {
length = admin.minimumPasswordLetters;
if (length < admin.minimumPasswordMetrics.letters) {
length = admin.minimumPasswordMetrics.letters;
}
}
return length;
@@ -3601,8 +3578,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordNumeric != length) {
ap.minimumPasswordNumeric = length;
if (ap.minimumPasswordMetrics.numeric != length) {
ap.minimumPasswordMetrics.numeric = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3619,7 +3596,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordNumeric : length;
return admin != null ? admin.minimumPasswordMetrics.numeric : length;
}
// Return the strictest policy across all participating admins.
@@ -3631,8 +3608,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
if (length < admin.minimumPasswordNumeric) {
length = admin.minimumPasswordNumeric;
if (length < admin.minimumPasswordMetrics.numeric) {
length = admin.minimumPasswordMetrics.numeric;
}
}
return length;
@@ -3648,8 +3625,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordSymbols != length) {
ap.minimumPasswordSymbols = length;
if (ap.minimumPasswordMetrics.symbols != length) {
ap.minimumPasswordMetrics.symbols = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3666,7 +3643,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordSymbols : length;
return admin != null ? admin.minimumPasswordMetrics.symbols : length;
}
// Return the strictest policy across all participating admins.
@@ -3678,8 +3655,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
if (length < admin.minimumPasswordSymbols) {
length = admin.minimumPasswordSymbols;
if (length < admin.minimumPasswordMetrics.symbols) {
length = admin.minimumPasswordMetrics.symbols;
}
}
return length;
@@ -3695,8 +3672,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
if (ap.minimumPasswordNonLetter != length) {
ap.minimumPasswordNonLetter = length;
if (ap.minimumPasswordMetrics.nonLetter != length) {
ap.minimumPasswordMetrics.nonLetter = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3713,7 +3690,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return admin != null ? admin.minimumPasswordNonLetter : length;
return admin != null ? admin.minimumPasswordMetrics.nonLetter : length;
}
// Return the strictest policy across all participating admins.
@@ -3725,8 +3702,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
if (length < admin.minimumPasswordNonLetter) {
length = admin.minimumPasswordNonLetter;
if (length < admin.minimumPasswordMetrics.nonLetter) {
length = admin.minimumPasswordMetrics.nonLetter;
}
}
return length;
@@ -3767,28 +3744,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private boolean isActivePasswordSufficientForUserLocked(
DevicePolicyData policy, int userHandle, boolean parent) {
final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
if (policy.mActivePasswordQuality < requiredPasswordQuality) {
if (policy.mActivePasswordMetrics.quality < requiredPasswordQuality) {
return false;
}
if (requiredPasswordQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
&& policy.mActivePasswordLength < getPasswordMinimumLength(
&& policy.mActivePasswordMetrics.length < getPasswordMinimumLength(
null, userHandle, parent)) {
return false;
}
if (requiredPasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
return true;
}
return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase(
return policy.mActivePasswordMetrics.upperCase >= getPasswordMinimumUpperCase(
null, userHandle, parent)
&& policy.mActivePasswordLowerCase >= getPasswordMinimumLowerCase(
&& policy.mActivePasswordMetrics.lowerCase >= getPasswordMinimumLowerCase(
null, userHandle, parent)
&& policy.mActivePasswordLetters >= getPasswordMinimumLetters(
&& policy.mActivePasswordMetrics.letters >= getPasswordMinimumLetters(
null, userHandle, parent)
&& policy.mActivePasswordNumeric >= getPasswordMinimumNumeric(
&& policy.mActivePasswordMetrics.numeric >= getPasswordMinimumNumeric(
null, userHandle, parent)
&& policy.mActivePasswordSymbols >= getPasswordMinimumSymbols(
&& policy.mActivePasswordMetrics.symbols >= getPasswordMinimumSymbols(
null, userHandle, parent)
&& policy.mActivePasswordNonLetter >= getPasswordMinimumNonLetter(
&& policy.mActivePasswordMetrics.nonLetter >= getPasswordMinimumNonLetter(
null, userHandle, parent);
}
@@ -3982,8 +3959,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int realQuality = LockPatternUtils.computePasswordQuality(password);
final int realQuality = metrics.quality;
if (realQuality < quality
&& quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Slog.w(LOG_TAG, "resetPassword: password quality 0x"
@@ -4001,67 +3979,48 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
int letters = 0;
int uppercase = 0;
int lowercase = 0;
int numbers = 0;
int symbols = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else {
symbols++;
nonletter++;
}
}
int neededLetters = getPasswordMinimumLetters(null, userHandle, /* parent */ false);
if(letters < neededLetters) {
Slog.w(LOG_TAG, "resetPassword: number of letters " + letters
if(metrics.letters < neededLetters) {
Slog.w(LOG_TAG, "resetPassword: number of letters " + metrics.letters
+ " does not meet required number of letters " + neededLetters);
return false;
}
int neededNumbers = getPasswordMinimumNumeric(null, userHandle, /* parent */ false);
if (numbers < neededNumbers) {
Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + numbers
int neededNumeric = getPasswordMinimumNumeric(null, userHandle, /* parent */ false);
if (metrics.numeric < neededNumeric) {
Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + metrics.numeric
+ " does not meet required number of numerical digits "
+ neededNumbers);
+ neededNumeric);
return false;
}
int neededLowerCase = getPasswordMinimumLowerCase(
null, userHandle, /* parent */ false);
if (lowercase < neededLowerCase) {
Slog.w(LOG_TAG, "resetPassword: number of lowercase letters " + lowercase
if (metrics.lowerCase < neededLowerCase) {
Slog.w(LOG_TAG, "resetPassword: number of lowercase letters "
+ metrics.lowerCase
+ " does not meet required number of lowercase letters "
+ neededLowerCase);
return false;
}
int neededUpperCase = getPasswordMinimumUpperCase(
null, userHandle, /* parent */ false);
if (uppercase < neededUpperCase) {
Slog.w(LOG_TAG, "resetPassword: number of uppercase letters " + uppercase
if (metrics.upperCase < neededUpperCase) {
Slog.w(LOG_TAG, "resetPassword: number of uppercase letters "
+ metrics.upperCase
+ " does not meet required number of uppercase letters "
+ neededUpperCase);
return false;
}
int neededSymbols = getPasswordMinimumSymbols(null, userHandle, /* parent */ false);
if (symbols < neededSymbols) {
Slog.w(LOG_TAG, "resetPassword: number of special symbols " + symbols
if (metrics.symbols < neededSymbols) {
Slog.w(LOG_TAG, "resetPassword: number of special symbols " + metrics.symbols
+ " does not meet required number of special symbols " + neededSymbols);
return false;
}
int neededNonLetter = getPasswordMinimumNonLetter(
null, userHandle, /* parent */ false);
if (nonletter < neededNonLetter) {
Slog.w(LOG_TAG, "resetPassword: number of non-letter characters " + nonletter
if (metrics.nonLetter < neededNonLetter) {
Slog.w(LOG_TAG, "resetPassword: number of non-letter characters "
+ metrics.nonLetter
+ " does not meet required number of non-letter characters "
+ neededNonLetter);
return false;
@@ -4850,8 +4809,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
public void setActivePasswordState(int quality, int length, int letters, int uppercase,
int lowercase, int numbers, int symbols, int nonletter, int userHandle) {
public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
if (!mHasFeature) {
return;
}
@@ -4864,21 +4822,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
validateQualityConstant(quality);
validateQualityConstant(metrics.quality);
DevicePolicyData policy = getUserData(userHandle);
long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
policy.mActivePasswordQuality = quality;
policy.mActivePasswordLength = length;
policy.mActivePasswordLetters = letters;
policy.mActivePasswordLowerCase = lowercase;
policy.mActivePasswordUpperCase = uppercase;
policy.mActivePasswordNumeric = numbers;
policy.mActivePasswordSymbols = symbols;
policy.mActivePasswordNonLetter = nonletter;
policy.mActivePasswordMetrics = metrics;
policy.mFailedPasswordAttempts = 0;
saveSettingsLocked(userHandle);
updatePasswordExpirationsLocked(userHandle);
@@ -8437,10 +8388,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Returns true if specified admin is allowed to limit passwords and has a
* {@code passwordQuality} of at least {@code minPasswordQuality}
* {@code minimumPasswordMetrics.quality} of at least {@code minPasswordQuality}
*/
private static boolean isLimitPasswordAllowed(ActiveAdmin admin, int minPasswordQuality) {
if (admin.passwordQuality < minPasswordQuality) {
if (admin.minimumPasswordMetrics.quality < minPasswordQuality) {
return false;
}
return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);