diff options
author | Konstantin Lopyrev <klopyrev@google.com> | 2010-05-24 17:10:56 -0700 |
---|---|---|
committer | Konstantin Lopyrev <klopyrev@google.com> | 2010-05-27 15:27:06 -0700 |
commit | a15dcfaf2bc7cbd13b30db6766afe3bbaa01db97 (patch) | |
tree | f58aeeaf40b2c9eec459fca00ee4fa2126629652 | |
parent | afd52a0b40e7986d0993217b3fe0cf44fea21274 (diff) | |
download | frameworks_base-a15dcfaf2bc7cbd13b30db6766afe3bbaa01db97.zip frameworks_base-a15dcfaf2bc7cbd13b30db6766afe3bbaa01db97.tar.gz frameworks_base-a15dcfaf2bc7cbd13b30db6766afe3bbaa01db97.tar.bz2 |
Fix 2677197: Adding minimum complex character support.
Change-Id: I520bc5f9aa924bf9b5585b2235a91cc96cb99c25
7 files changed, 846 insertions, 30 deletions
diff --git a/api/current.xml b/api/current.xml index f416a18..f98a369 100644 --- a/api/current.xml +++ b/api/current.xml @@ -31769,6 +31769,71 @@ <parameter name="admin" type="android.content.ComponentName"> </parameter> </method> +<method name="getPasswordMinimumLetters" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +</method> +<method name="getPasswordMinimumLowerCase" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +</method> +<method name="getPasswordMinimumNumeric" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +</method> +<method name="getPasswordMinimumSymbols" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +</method> +<method name="getPasswordMinimumUpperCase" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +</method> <method name="getPasswordQuality" return="int" abstract="false" @@ -31905,6 +31970,81 @@ <parameter name="length" type="int"> </parameter> </method> +<method name="setPasswordMinimumLetters" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> +<method name="setPasswordMinimumLowerCase" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> +<method name="setPasswordMinimumNumeric" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> +<method name="setPasswordMinimumSymbols" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> +<method name="setPasswordMinimumUpperCase" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> <method name="setPasswordQuality" return="void" abstract="false" @@ -31999,6 +32139,17 @@ visibility="public" > </field> +<field name="PASSWORD_QUALITY_COMPLEX" + type="int" + transient="false" + volatile="false" + value="393216" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="PASSWORD_QUALITY_NUMERIC" type="int" transient="false" diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 67992a4..634adb0 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -205,6 +205,14 @@ public class DevicePolicyManager { public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000; /** + * Constant for {@link #setPasswordQuality}: the user must have entered a + * password containing numeric <em>and</em> alphabetic characters, + * <em>and</em> special symbols. Note that quality constants are ordered so + * that higher values are more restrictive. + */ + public static final int PASSWORD_QUALITY_COMPLEX = 0x60000; + + /** * Called by an application that is administering the device to set the * password restrictions it is imposing. After setting this, the user * will not be able to enter a new password that is not at least as @@ -226,7 +234,7 @@ public class DevicePolicyManager { * @param quality The new desired quality. One of * {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING}, * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC}, - * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}. + * {@link #PASSWORD_QUALITY_ALPHANUMERIC} or {@link #PASSWORD_QUALITY_COMPLEX}. */ public void setPasswordQuality(ComponentName admin, int quality) { if (mService != null) { @@ -264,8 +272,8 @@ public class DevicePolicyManager { * 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 either - * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC}, - * or {@link #PASSWORD_QUALITY_ALPHANUMERIC} + * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC} + * {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX} * with {@link #setPasswordQuality}. * * <p>The calling device admin must have requested @@ -303,6 +311,255 @@ public class DevicePolicyManager { return 0; } + /** + * Called by an application that is administering the device to set the + * minimum number of upper case letters 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}. + * <p> + * 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 upper case letters + * required in the password. A value of 0 means there is no + * restriction. + */ + public void setPasswordMinimumUpperCase(ComponentName admin, int length) { + if (mService != null) { + try { + mService.setPasswordMinimumUpperCase(admin, length); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Retrieve the current number of upper case letters required in the + * password for all admins or a particular one. + * + * @param admin The name of the admin component to check, or null to + * aggregate all admins. + * @return The minimum number of upper case letters required in the + * password. + */ + public int getPasswordMinimumUpperCase(ComponentName admin) { + if (mService != null) { + try { + return mService.getPasswordMinimumUpperCase(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 + * minimum number of lower case letters 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}. + * <p> + * 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 lower case letters + * required in the password. A value of 0 means there is no + * restriction. + */ + public void setPasswordMinimumLowerCase(ComponentName admin, int length) { + if (mService != null) { + try { + mService.setPasswordMinimumLowerCase(admin, length); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Retrieve the current number of lower case letters required in the + * password for all admins or a particular one. + * + * @param admin The name of the admin component to check, or null to + * aggregate all admins. + * @return The minimum number of lower case letters required in the + * password. + */ + public int getPasswordMinimumLowerCase(ComponentName admin) { + if (mService != null) { + try { + return mService.getPasswordMinimumLowerCase(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 + * minimum number of letters 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}. + * <p> + * 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 setPasswordMinimumLetters(ComponentName admin, int length) { + if (mService != null) { + try { + mService.setPasswordMinimumLetters(admin, length); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Retrieve the current number of letters required in the password for all + * admins or a particular one. + * + * @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 getPasswordMinimumLetters(ComponentName admin) { + if (mService != null) { + try { + return mService.getPasswordMinimumLetters(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 + * minimum number of numerical digits 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}. + * <p> + * 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 numerical digits required + * in the password. A value of 0 means there is no restriction. + */ + public void setPasswordMinimumNumeric(ComponentName admin, int length) { + if (mService != null) { + try { + mService.setPasswordMinimumNumeric(admin, length); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Retrieve the current number of numerical digits required in the password + * for all admins or a particular one. + * + * @param admin The name of the admin component to check, or null to + * aggregate all admins. + * @return The minimum number of numerical digits required in the password. + */ + public int getPasswordMinimumNumeric(ComponentName admin) { + if (mService != null) { + try { + return mService.getPasswordMinimumNumeric(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 + * minimum number of 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}. + * <p> + * 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 symbols required in the + * password. A value of 0 means there is no restriction. + */ + public void setPasswordMinimumSymbols(ComponentName admin, int length) { + if (mService != null) { + try { + mService.setPasswordMinimumSymbols(admin, length); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Retrieve the current number of symbols required in the password for all + * admins or a particular one. + * + * @param admin The name of the admin component to check, or null to + * aggregate all admins. + * @return The minimum number of symbols required in the password. + */ + public int getPasswordMinimumSymbols(ComponentName admin) { + if (mService != null) { + try { + return mService.getPasswordMinimumSymbols(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 @@ -627,10 +884,12 @@ public class DevicePolicyManager { /** * @hide */ - public void setActivePasswordState(int quality, int length) { + public void setActivePasswordState(int quality, int length, int letters, int uppercase, + int lowercase, int numbers, int symbols) { if (mService != null) { try { - mService.setActivePasswordState(quality, length); + mService.setActivePasswordState(quality, length, letters, uppercase, lowercase, + numbers, symbols); } 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 98fc162..fa31a37 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -27,9 +27,24 @@ import android.os.RemoteCallback; interface IDevicePolicyManager { void setPasswordQuality(in ComponentName who, int quality); int getPasswordQuality(in ComponentName who); - + void setPasswordMinimumLength(in ComponentName who, int length); int getPasswordMinimumLength(in ComponentName who); + + void setPasswordMinimumUpperCase(in ComponentName who, int length); + int getPasswordMinimumUpperCase(in ComponentName who); + + void setPasswordMinimumLowerCase(in ComponentName who, int length); + int getPasswordMinimumLowerCase(in ComponentName who); + + void setPasswordMinimumLetters(in ComponentName who, int length); + int getPasswordMinimumLetters(in ComponentName who); + + void setPasswordMinimumNumeric(in ComponentName who, int length); + int getPasswordMinimumNumeric(in ComponentName who); + + void setPasswordMinimumSymbols(in ComponentName who, int length); + int getPasswordMinimumSymbols(in ComponentName who); void setPasswordHistoryLength(in ComponentName who, int length); int getPasswordHistoryLength(in ComponentName who); @@ -56,7 +71,7 @@ interface IDevicePolicyManager { void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result); void removeActiveAdmin(in ComponentName policyReceiver); - void setActivePasswordState(int quality, int length); + void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, int numbers, int symbols); 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 28684a4..9983c02 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -144,6 +144,26 @@ public class LockPatternUtils { return getDevicePolicyManager().getPasswordHistoryLength(null); } + public int getRequestedPasswordMinimumLetters() { + return getDevicePolicyManager().getPasswordMinimumLetters(null); + } + + public int getRequestedPasswordMinimumUpperCase() { + return getDevicePolicyManager().getPasswordMinimumUpperCase(null); + } + + public int getRequestedPasswordMinimumLowerCase() { + return getDevicePolicyManager().getPasswordMinimumLowerCase(null); + } + + public int getRequestedPasswordMinimumNumeric() { + return getDevicePolicyManager().getPasswordMinimumNumeric(null); + } + + public int getRequestedPasswordMinimumSymbols() { + return getDevicePolicyManager().getPasswordMinimumSymbols(null); + } + /** * Returns the actual password mode, as set by keyguard after updating the password. * @@ -308,6 +328,11 @@ public class LockPatternUtils { activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; } break; + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: + if (isLockPasswordEnabled()) { + activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; + } + break; } return activePasswordQuality; } @@ -316,8 +341,6 @@ public class LockPatternUtils { * Clear any lock pattern or password. */ public void clearLock() { - getDevicePolicyManager().setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); setLockPatternEnabled(false); saveLockPattern(null); @@ -330,7 +353,7 @@ public class LockPatternUtils { */ public void saveLockPattern(List<LockPatternView.Cell> pattern) { // Compute the hash - final byte[] hash = LockPatternUtils.patternToHash(pattern); + final byte[] hash = LockPatternUtils.patternToHash(pattern); try { // Write the hash to file RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw"); @@ -345,14 +368,15 @@ public class LockPatternUtils { if (pattern != null) { setBoolean(PATTERN_EVER_CHOSEN_KEY, true); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()); + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern + .size(), 0, 0, 0, 0, 0); } else { - dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, + 0, 0, 0, 0); } } catch (FileNotFoundException fnfe) { - // Cant do much, unless we want to fail over to using the settings provider + // Cant do much, unless we want to fail over to using the settings + // provider Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename); } catch (IOException ioe) { // Cant do much @@ -410,13 +434,33 @@ public class LockPatternUtils { DevicePolicyManager dpm = getDevicePolicyManager(); if (password != null) { int computedQuality = computePasswordQuality(password); - setLong(PASSWORD_TYPE_KEY, computedQuality); + setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality)); if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - dpm.setActivePasswordState(computedQuality, password.length()); + int letters = 0; + int uppercase = 0; + int lowercase = 0; + int numbers = 0; + int symbols = 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++; + } else { + symbols++; + } + } + dpm.setActivePasswordState(Math.max(quality, computedQuality), password + .length(), letters, uppercase, lowercase, numbers, symbols); } else { // The password is not anything. dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0); } // Add the password to the password history. We assume all // password @@ -439,7 +483,7 @@ public class LockPatternUtils { setString(PASSWORD_HISTORY_KEY, passwordHistory); } else { dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0); } } catch (FileNotFoundException fnfe) { // Cant do much, unless we want to fail over to using the settings provider @@ -579,7 +623,8 @@ public class LockPatternUtils { return savedPasswordExists() && (mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC); + || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC + || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); } /** @@ -716,7 +761,8 @@ public class LockPatternUtils { final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; + || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC + || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() || isPassword && savedPasswordExists(); return secure; diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java index 587f9c1..8693294 100644 --- a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -667,6 +667,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: currentMode = UnlockMode.Password; break; case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: diff --git a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java index c519d82..8fdff92 100644 --- a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -88,7 +88,8 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality(); final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality - || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality; + || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality + || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality; mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); @@ -200,7 +201,8 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen mPasswordEntry.setEnabled(true); final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(); final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality - || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality; + || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality + || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality; if(isAlpha) { mTitle.setText(R.string.keyguard_password_enter_password_code); } else { diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 7de510e..d2add10 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -75,6 +75,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 mFailedPasswordAttempts = 0; int mPasswordOwner = -1; @@ -90,6 +95,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; int minimumPasswordLength = 0; int passwordHistoryLength = 0; + int minimumPasswordUpperCase = 0; + int minimumPasswordLowerCase = 0; + int minimumPasswordLetters = 1; + int minimumPasswordNumeric = 1; + int minimumPasswordSymbols = 1; long maximumTimeToUnlock = 0; int maximumFailedPasswordsForWipe = 0; @@ -118,6 +128,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, "value", Integer.toString(passwordHistoryLength)); out.endTag(null, "password-history-length"); } + if (minimumPasswordUpperCase > 0) { + out.startTag(null, "min-password-uppercase"); + out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase)); + out.endTag(null, "min-password-uppercase"); + } + if (minimumPasswordLowerCase > 0) { + out.startTag(null, "min-password-lowercase"); + out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase)); + out.endTag(null, "min-password-lowercase"); + } + if (minimumPasswordLetters > 0) { + out.startTag(null, "min-password-letters"); + out.attribute(null, "value", Integer.toString(minimumPasswordLetters)); + out.endTag(null, "min-password-letters"); + } + if (minimumPasswordNumeric > 0) { + out.startTag(null, "min-password-numeric"); + out.attribute(null, "value", Integer.toString(minimumPasswordNumeric)); + out.endTag(null, "min-password-numeric"); + } + if (minimumPasswordSymbols > 0) { + out.startTag(null, "min-password-symbols"); + out.attribute(null, "value", Integer.toString(minimumPasswordSymbols)); + out.endTag(null, "min-password-symbols"); + } } if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { out.startTag(null, "max-time-to-unlock"); @@ -152,6 +187,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("password-history-length".equals(tag)) { passwordHistoryLength = Integer.parseInt( parser.getAttributeValue(null, "value")); + } else if ("min-password-uppercase".equals(tag)) { + minimumPasswordUpperCase = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("min-password-lowercase".equals(tag)) { + minimumPasswordLowerCase = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("min-password-letters".equals(tag)) { + minimumPasswordLetters = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("min-password-numeric".equals(tag)) { + minimumPasswordNumeric = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } else if ("min-password-symbols".equals(tag)) { + minimumPasswordSymbols = Integer.parseInt( + parser.getAttributeValue(null, "value")); } else if ("max-time-to-unlock".equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, "value")); @@ -180,6 +230,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(minimumPasswordLength); pw.print(prefix); pw.print("passwordHistoryLength="); pw.println(passwordHistoryLength); + pw.print(prefix); pw.print("minimumPasswordUpperCase="); + pw.println(minimumPasswordUpperCase); + pw.print(prefix); pw.print("minimumPasswordLowerCase="); + pw.println(minimumPasswordLowerCase); + pw.print(prefix); pw.print("minimumPasswordLetters="); + pw.println(minimumPasswordLetters); + pw.print(prefix); pw.print("minimumPasswordNumeric="); + pw.println(minimumPasswordNumeric); + pw.print(prefix); pw.print("minimumPasswordSymbols="); + pw.println(minimumPasswordSymbols); pw.print(prefix); pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); pw.print(prefix); pw.print("maximumFailedPasswordsForWipe="); @@ -366,10 +426,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "failed-password-attempts"); } - if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) { + if (mActivePasswordQuality != 0 || mActivePasswordLength != 0 + || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0 + || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0 + || mActivePasswordSymbols != 0) { out.startTag(null, "active-password"); out.attribute(null, "quality", Integer.toString(mActivePasswordQuality)); out.attribute(null, "length", Integer.toString(mActivePasswordLength)); + out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase)); + out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase)); + out.attribute(null, "letters", Integer.toString(mActivePasswordLetters)); + out.attribute(null, "numeric", Integer + .toString(mActivePasswordNumeric)); + out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols)); out.endTag(null, "active-password"); } @@ -443,6 +512,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { parser.getAttributeValue(null, "quality")); mActivePasswordLength = Integer.parseInt( parser.getAttributeValue(null, "length")); + mActivePasswordUpperCase = Integer.parseInt( + parser.getAttributeValue(null, "uppercase")); + mActivePasswordLowerCase = Integer.parseInt( + parser.getAttributeValue(null, "lowercase")); + mActivePasswordLetters = Integer.parseInt( + parser.getAttributeValue(null, "letters")); + mActivePasswordNumeric = Integer.parseInt( + parser.getAttributeValue(null, "numeric")); + mActivePasswordSymbols = Integer.parseInt( + parser.getAttributeValue(null, "symbols")); XmlUtils.skipCurrentTag(parser); } else { Slog.w(TAG, "Unknown tag: " + tag); @@ -480,6 +559,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + Integer.toHexString(utils.getActivePasswordQuality())); mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; mActivePasswordLength = 0; + mActivePasswordUpperCase = 0; + mActivePasswordLowerCase = 0; + mActivePasswordLetters = 0; + mActivePasswordNumeric = 0; + mActivePasswordSymbols = 0; } validatePasswordOwnerLocked(); @@ -502,6 +586,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: return; } throw new IllegalArgumentException("Invalid quality constant: 0x" @@ -712,14 +797,194 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public void setPasswordMinimumUpperCase(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.minimumPasswordUpperCase != length) { + ap.minimumPasswordUpperCase = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumUpperCase(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordUpperCase : length; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordUpperCase) { + length = admin.minimumPasswordUpperCase; + } + } + return length; + } + } + + public void setPasswordMinimumLowerCase(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.minimumPasswordLowerCase != length) { + ap.minimumPasswordLowerCase = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumLowerCase(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordLowerCase : length; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordLowerCase) { + length = admin.minimumPasswordLowerCase; + } + } + return length; + } + } + + public void setPasswordMinimumLetters(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.minimumPasswordLetters != length) { + ap.minimumPasswordLetters = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumLetters(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordLetters : length; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordLetters) { + length = admin.minimumPasswordLetters; + } + } + return length; + } + } + + public void setPasswordMinimumNumeric(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.minimumPasswordNumeric != length) { + ap.minimumPasswordNumeric = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumNumeric(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordNumeric : length; + } + + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordNumeric) { + length = admin.minimumPasswordNumeric; + } + } + return length; + } + } + + public void setPasswordMinimumSymbols(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.minimumPasswordSymbols != length) { + ap.minimumPasswordSymbols = length; + saveSettingsLocked(); + } + } + } + + public int getPasswordMinimumSymbols(ComponentName who) { + synchronized (this) { + int length = 0; + + if (who != null) { + ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + return admin != null ? admin.minimumPasswordSymbols : length; + } + + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin admin = mAdminList.get(i); + if (length < admin.minimumPasswordSymbols) { + length = admin.minimumPasswordSymbols; + } + } + return length; + } + } + public boolean isActivePasswordSufficient() { synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); - return mActivePasswordQuality >= getPasswordQuality(null) - && mActivePasswordLength >= getPasswordMinimumLength(null); + if (mActivePasswordQuality < getPasswordQuality(null) + || mActivePasswordLength < getPasswordMinimumLength(null)) { + return false; + } + if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { + return true; + } + return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null) + && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null) + && mActivePasswordLetters >= getPasswordMinimumLetters(null) + && mActivePasswordNumeric >= getPasswordMinimumNumeric(null) + && mActivePasswordSymbols >= getPasswordMinimumSymbols(null); } } @@ -781,14 +1046,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { quality = getPasswordQuality(null); if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int realQuality = LockPatternUtils.computePasswordQuality(password); - if (realQuality < quality) { + if (realQuality < quality + && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { Slog.w(TAG, "resetPassword: password quality 0x" + Integer.toHexString(quality) + " does not meet required quality 0x" + Integer.toHexString(quality)); return false; } - quality = realQuality; + quality = Math.max(realQuality, quality); } int length = getPasswordMinimumLength(null); if (password.length() < length) { @@ -796,6 +1062,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " does not meet required length " + length); return false; } + if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { + int letters = 0; + int uppercase = 0; + int lowercase = 0; + int numbers = 0; + int symbols = 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++; + } else { + symbols++; + } + } + int neededLetters = getPasswordMinimumLetters(null); + if(letters < neededLetters) { + Slog.w(TAG, "resetPassword: number of letters " + letters + + " does not meet required number of letters " + neededLetters); + return false; + } + int neededNumbers = getPasswordMinimumNumeric(null); + if (numbers < neededNumbers) { + Slog + .w(TAG, "resetPassword: number of numerical digits " + numbers + + " does not meet required number of numerical digits " + + neededNumbers); + return false; + } + int neededLowerCase = getPasswordMinimumLowerCase(null); + if (lowercase < neededLowerCase) { + Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase + + " does not meet required number of lowercase letters " + + neededLowerCase); + return false; + } + int neededUpperCase = getPasswordMinimumUpperCase(null); + if (uppercase < neededUpperCase) { + Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase + + " does not meet required number of uppercase letters " + + neededUpperCase); + return false; + } + int neededSymbols = getPasswordMinimumSymbols(null); + if (symbols < neededSymbols) { + Slog.w(TAG, "resetPassword: number of special symbols " + symbols + + " does not meet required number of special symbols " + neededSymbols); + return false; + } + } + + LockPatternUtils utils = new LockPatternUtils(mContext); + if(utils.checkPasswordHistory(password)) { + Slog.w(TAG, "resetPassword: password is the same as one of the last " + + getPasswordHistoryLength(null) + " passwords"); + return false; + } } int callingUid = Binder.getCallingUid(); @@ -946,7 +1274,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setActivePasswordState(int quality, int length) { + public void setActivePasswordState(int quality, int length, int letters, int uppercase, + int lowercase, int numbers, int symbols) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); @@ -954,11 +1283,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { if (mActivePasswordQuality != quality || mActivePasswordLength != length - || mFailedPasswordAttempts != 0) { + || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters + || mActivePasswordUpperCase != uppercase + || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers + || mActivePasswordSymbols != symbols) { long ident = Binder.clearCallingIdentity(); try { mActivePasswordQuality = quality; mActivePasswordLength = length; + mActivePasswordLetters = letters; + mActivePasswordLowerCase = lowercase; + mActivePasswordUpperCase = uppercase; + mActivePasswordNumeric = numbers; + mActivePasswordSymbols = symbols; mFailedPasswordAttempts = 0; saveSettingsLocked(); sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, @@ -1042,6 +1379,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.print(" mActivePasswordQuality=0x"); pw.println(Integer.toHexString(mActivePasswordQuality)); pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength); + pw.print(" mActivePasswordUpperCase="); pw.println(mActivePasswordUpperCase); + pw.print(" mActivePasswordLowerCase="); pw.println(mActivePasswordLowerCase); + pw.print(" mActivePasswordLetters="); pw.println(mActivePasswordLetters); + pw.print(" mActivePasswordNumeric="); pw.println(mActivePasswordNumeric); + pw.print(" mActivePasswordSymbols="); pw.println(mActivePasswordSymbols); pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts); pw.print(" mPasswordOwner="); pw.println(mPasswordOwner); } |