* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call @@ -353,7 +358,10 @@ public class DevicePolicyManager { /** * Retrieve the current number of upper case letters required in the - * password for all admins or a particular one. + * password for all admins or a particular one. This is the same value as + * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)} + * and only applies when the password quality is + * {@link #PASSWORD_QUALITY_COMPLEX}. * * @param admin The name of the admin component to check, or null to * aggregate all admins. @@ -380,7 +388,8 @@ public class DevicePolicyManager { * not take place immediately. To prompt the user for a new password, use * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This * constraint is only imposed if the administrator has also requested - * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. + * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The + * default value is 0. *
* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call @@ -404,7 +413,10 @@ public class DevicePolicyManager { /** * Retrieve the current number of lower case letters required in the - * password for all admins or a particular one. + * password for all admins or a particular one. This is the same value as + * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)} + * and only applies when the password quality is + * {@link #PASSWORD_QUALITY_COMPLEX}. * * @param admin The name of the admin component to check, or null to * aggregate all admins. @@ -431,7 +443,8 @@ public class DevicePolicyManager { * place immediately. To prompt the user for a new password, use * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This * constraint is only imposed if the administrator has also requested - * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. + * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The + * default value is 1. *
* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call @@ -454,7 +467,10 @@ public class DevicePolicyManager { /** * Retrieve the current number of letters required in the password for all - * admins or a particular one. + * admins or a particular one. This is the same value as + * set by {#link {@link #setPasswordMinimumLetters(ComponentName, int)} + * and only applies when the password quality is + * {@link #PASSWORD_QUALITY_COMPLEX}. * * @param admin The name of the admin component to check, or null to * aggregate all admins. @@ -480,7 +496,8 @@ public class DevicePolicyManager { * not take place immediately. To prompt the user for a new password, use * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This * constraint is only imposed if the administrator has also requested - * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. + * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The + * default value is 1. *
* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call @@ -503,7 +520,10 @@ public class DevicePolicyManager { /** * Retrieve the current number of numerical digits required in the password - * for all admins or a particular one. + * for all admins or a particular one. This is the same value as + * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)} + * and only applies when the password quality is + * {@link #PASSWORD_QUALITY_COMPLEX}. * * @param admin The name of the admin component to check, or null to * aggregate all admins. @@ -529,7 +549,8 @@ public class DevicePolicyManager { * place immediately. To prompt the user for a new password, use * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This * constraint is only imposed if the administrator has also requested - * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. + * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The + * default value is 1. *
* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call @@ -552,7 +573,10 @@ public class DevicePolicyManager { /** * Retrieve the current number of symbols required in the password for all - * admins or a particular one. + * admins or a particular one. This is the same value as + * set by {#link {@link #setPasswordMinimumSymbols(ComponentName, int)} + * and only applies when the password quality is + * {@link #PASSWORD_QUALITY_COMPLEX}. * * @param admin The name of the admin component to check, or null to * aggregate all admins. @@ -569,6 +593,59 @@ public class DevicePolicyManager { return 0; } + /** + * Called by an application that is administering the device to set the + * minimum number of non-letter characters (numerical digits or symbols) + * required in the password. After setting this, the user will not be able + * to enter a new password that is not at least as restrictive as what has + * been set. Note that the current password will remain until the user has + * set a new one, so the change does not take place immediately. To prompt + * the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} after + * setting this value. This constraint is only imposed if the administrator + * has also requested {@link #PASSWORD_QUALITY_COMPLEX} with + * {@link #setPasswordQuality}. The default value is 0. + *
+ * The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+ * this method; if it has not, a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated
+ * with.
+ * @param length The new desired minimum number of letters required in the
+ * password. A value of 0 means there is no restriction.
+ */
+ public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
+ if (mService != null) {
+ try {
+ mService.setPasswordMinimumNonLetter(admin, length);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the current number of non-letter characters required in the
+ * password for all admins or a particular one. This is the same value as
+ * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
+ * and only applies when the password quality is
+ * {@link #PASSWORD_QUALITY_COMPLEX}.
+ *
+ * @param admin The name of the admin component to check, or null to
+ * aggregate all admins.
+ * @return The minimum number of letters required in the password.
+ */
+ public int getPasswordMinimumNonLetter(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getPasswordMinimumNonLetter(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
/**
* Called by an application that is administering the device to set the length
* of the password history. After setting this, the user will not be able to
@@ -894,11 +971,11 @@ public class DevicePolicyManager {
* @hide
*/
public void setActivePasswordState(int quality, int length, int letters, int uppercase,
- int lowercase, int numbers, int symbols) {
+ int lowercase, int numbers, int symbols, int nonletter) {
if (mService != null) {
try {
mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
- numbers, symbols);
+ numbers, symbols, nonletter);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fa31a373d9f23..3ada95cf62644 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -45,6 +45,9 @@ interface IDevicePolicyManager {
void setPasswordMinimumSymbols(in ComponentName who, int length);
int getPasswordMinimumSymbols(in ComponentName who);
+
+ void setPasswordMinimumNonLetter(in ComponentName who, int length);
+ int getPasswordMinimumNonLetter(in ComponentName who);
void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who);
@@ -71,7 +74,8 @@ interface IDevicePolicyManager {
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
- void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, int numbers, int symbols);
+ void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
+ int numbers, int symbols, int nonletter);
void reportFailedPasswordAttempt();
void reportSuccessfulPasswordAttempt();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9983c027f33da..0b62a67e70fec 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -164,6 +164,9 @@ public class LockPatternUtils {
return getDevicePolicyManager().getPasswordMinimumSymbols(null);
}
+ public int getRequestedPasswordMinimumNonLetter() {
+ return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
+ }
/**
* Returns the actual password mode, as set by keyguard after updating the password.
*
@@ -369,10 +372,10 @@ public class LockPatternUtils {
setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
- .size(), 0, 0, 0, 0, 0);
+ .size(), 0, 0, 0, 0, 0, 0);
} else {
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, 0);
}
} catch (FileNotFoundException fnfe) {
// Cant do much, unless we want to fail over to using the settings
@@ -441,6 +444,7 @@ public class LockPatternUtils {
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') {
@@ -451,16 +455,18 @@ public class LockPatternUtils {
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);
+ .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
} else {
// The password is not anything.
dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
}
// Add the password to the password history. We assume all
// password
@@ -483,7 +489,7 @@ public class LockPatternUtils {
setString(PASSWORD_HISTORY_KEY, passwordHistory);
} else {
dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0);
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
}
} catch (FileNotFoundException fnfe) {
// Cant do much, unless we want to fail over to using the settings provider
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 1e1c416937608..c0ea68d731904 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -80,6 +80,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int mActivePasswordLetters = 0;
int mActivePasswordNumeric = 0;
int mActivePasswordSymbols = 0;
+ int mActivePasswordNonLetter = 0;
int mFailedPasswordAttempts = 0;
int mPasswordOwner = -1;
@@ -100,6 +101,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int minimumPasswordLetters = 1;
int minimumPasswordNumeric = 1;
int minimumPasswordSymbols = 1;
+ int minimumPasswordNonLetter = 0;
long maximumTimeToUnlock = 0;
int maximumFailedPasswordsForWipe = 0;
@@ -153,6 +155,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
out.endTag(null, "min-password-symbols");
}
+ if (minimumPasswordNonLetter > 0) {
+ out.startTag(null, "min-password-nonletter");
+ out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
+ out.endTag(null, "min-password-nonletter");
+ }
}
if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
out.startTag(null, "max-time-to-unlock");
@@ -202,6 +209,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if ("min-password-symbols".equals(tag)) {
minimumPasswordSymbols = Integer.parseInt(
parser.getAttributeValue(null, "value"));
+ } else if ("min-password-nonletter".equals(tag)) {
+ minimumPasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
} else if ("max-time-to-unlock".equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
parser.getAttributeValue(null, "value"));
@@ -240,6 +250,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.println(minimumPasswordNumeric);
pw.print(prefix); pw.print("minimumPasswordSymbols=");
pw.println(minimumPasswordSymbols);
+ pw.print(prefix); pw.print("minimumPasswordNonLetter=");
+ pw.println(minimumPasswordNonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
@@ -429,7 +441,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
|| mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
|| mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
- || mActivePasswordSymbols != 0) {
+ || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
out.startTag(null, "active-password");
out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
out.attribute(null, "length", Integer.toString(mActivePasswordLength));
@@ -439,6 +451,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "numeric", Integer
.toString(mActivePasswordNumeric));
out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
+ out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
out.endTag(null, "active-password");
}
@@ -529,6 +542,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
parser.getAttributeValue(null, "numeric"));
mActivePasswordSymbols = Integer.parseInt(
parser.getAttributeValue(null, "symbols"));
+ mActivePasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown tag: " + tag);
@@ -571,6 +586,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mActivePasswordLetters = 0;
mActivePasswordNumeric = 0;
mActivePasswordSymbols = 0;
+ mActivePasswordNonLetter = 0;
}
validatePasswordOwnerLocked();
@@ -974,6 +990,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ public void setPasswordMinimumNonLetter(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNonLetter != length) {
+ ap.minimumPasswordNonLetter = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumNonLetter(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordNonLetter : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i