diff --git a/api/current.xml b/api/current.xml index 10742dbe979e9..58ce3eeb24188 100644 --- a/api/current.xml +++ b/api/current.xml @@ -26873,11 +26873,22 @@ visibility="public" > + + both> numeric and - * alphabeter (or other symbol) characters. Note that quality constants are + * alphabetic (or other symbol) characters. Note that quality constants are * ordered so that higher values are more restrictive. */ - public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x30000; + public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000; /** * Called by an application that is administering the device to set the @@ -219,7 +226,8 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param quality The new desired quality. One of * {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING}, - * {@link #PASSWORD_QUALITY_NUMERIC}, or {@link #PASSWORD_QUALITY_ALPHANUMERIC}. + * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC}, + * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}. */ public void setPasswordQuality(ComponentName admin, int quality) { if (mService != null) { @@ -257,7 +265,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} or {@link #PASSWORD_QUALITY_ALPHANUMERIC} + * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC}, + * or {@link #PASSWORD_QUALITY_ALPHANUMERIC} * with {@link #setPasswordQuality}. * *

The calling device admin must have requested diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 9b7f4872c1547..37cd4124b8eee 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -89,7 +89,7 @@ public class LockPatternUtils { public static final int MODE_UNSPECIFIED = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; public static final int MODE_PATTERN = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; public static final int MODE_PIN = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; + public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; /** * The minimum number of dots the user must include in a wrong pattern @@ -144,12 +144,11 @@ public class LockPatternUtils { /** * Gets the device policy password mode. If the mode is non-specific, returns * MODE_PATTERN which allows the user to choose anything. - * - * @return */ public int getRequestedPasswordMode() { int policyMode = getDevicePolicyManager().getPasswordQuality(null); switch (policyMode) { + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: return MODE_PASSWORD; case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: @@ -269,10 +268,42 @@ public class LockPatternUtils { return getBoolean(PATTERN_EVER_CHOSEN_KEY); } + /** + * Used by device policy manager to validate the current password + * information it has. + */ + public int getActivePasswordQuality() { + switch (getPasswordMode()) { + case MODE_UNSPECIFIED: + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + case MODE_PATTERN: + if (isLockPatternEnabled()) { + return DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; + } else { + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + } + case MODE_PIN: + if (isLockPasswordEnabled()) { + return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; + } else { + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + } + case MODE_PASSWORD: + if (isLockPasswordEnabled()) { + return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; + } else { + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + } + } + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + } + /** * Clear any lock pattern or password. */ public void clearLock() { + getDevicePolicyManager().setActivePasswordState( + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); saveLockPassword(null, LockPatternUtils.MODE_PATTERN); setLockPatternEnabled(false); saveLockPattern(null); @@ -316,15 +347,9 @@ public class LockPatternUtils { } /** - * Compare the given password and mode, ensuring that the password meets - * the mode and returning the minimum mode needed for the given password. - * @param password The password to be used. - * @param reqMode The desired password mode. - * @return Returns {@link #MODE_UNSPECIFIED} if the password is not - * good enough for the given mode. Otherwise, returns either the original - * reqMode or something better if that is needed for the given password. + * Compute the password quality from the given password string. */ - static public int adjustPasswordMode(String password, int reqMode) { + static public int computePasswordQuality(String password) { boolean hasDigit = false; boolean hasNonDigit = false; final int len = password.length(); @@ -336,39 +361,16 @@ public class LockPatternUtils { } } - // First check if it is sufficient. - switch (reqMode) { - case MODE_PASSWORD: { - if (!hasDigit || !hasNonDigit) { - return MODE_UNSPECIFIED; - } - } break; - - case MODE_PIN: - case MODE_PATTERN: { - // Whatever we have is acceptable; we may need to promote the - // mode later. - } break; - - default: - // If it isn't a mode we specifically know, then fail fast. - Log.w(TAG, "adjustPasswordMode: unknown mode " + reqMode); - return MODE_UNSPECIFIED; + if (hasNonDigit && hasDigit) { + return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; } - - // Do we need to promote? if (hasNonDigit) { - if (reqMode < MODE_PASSWORD) { - reqMode = MODE_PASSWORD; - } + return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; } if (hasDigit) { - if (reqMode < MODE_PIN) { - reqMode = MODE_PIN; - } + return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; } - - return reqMode; + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; } /** @@ -392,12 +394,15 @@ public class LockPatternUtils { raf.close(); DevicePolicyManager dpm = getDevicePolicyManager(); if (password != null) { - int finalMode = adjustPasswordMode(password, mode); - if (mode < finalMode) { - mode = finalMode; - } setLong(PASSWORD_TYPE_KEY, mode); - dpm.setActivePasswordState(mode, password.length()); + int quality = computePasswordQuality(password); + if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + dpm.setActivePasswordState(quality, password.length()); + } else { + // The password is not anything. + dpm.setActivePasswordState( + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); + } } else { dpm.setActivePasswordState( DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 1d715778b2e11..7fb7db09637f6 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -46,7 +46,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.util.Log; +import android.util.Slog; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Xml; @@ -150,7 +150,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { maximumFailedPasswordsForWipe = Integer.parseInt( parser.getAttributeValue(null, "value")); } else { - Log.w(TAG, "Unknown admin tag: " + tag); + Slog.w(TAG, "Unknown admin tag: " + tag); } XmlUtils.skipCurrentTag(parser); } @@ -165,8 +165,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag); } } - pw.print(prefix); pw.print("passwordQuality="); - pw.print(passwordQuality); + pw.print(prefix); pw.print("passwordQuality=0x"); + pw.print(Integer.toHexString(passwordQuality)); pw.print(" minimumPasswordLength="); pw.println(minimumPasswordLength); pw.print(prefix); pw.print("maximumTimeToUnlock="); @@ -185,7 +185,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int change = isPackageDisappearing(aa.info.getPackageName()); if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) { - Log.w(TAG, "Admin unexpectedly uninstalled: " + Slog.w(TAG, "Admin unexpectedly uninstalled: " + aa.info.getComponent()); removed = true; mAdminList.remove(i); @@ -194,7 +194,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.getPackageManager().getReceiverInfo( aa.info.getComponent(), 0); } catch (NameNotFoundException e) { - Log.w(TAG, "Admin package change removed component: " + Slog.w(TAG, "Admin package change removed component: " + aa.info.getComponent()); removed = true; mAdminList.remove(i); @@ -308,10 +308,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { return new DeviceAdminInfo(mContext, infos.get(0)); } catch (XmlPullParserException e) { - Log.w(TAG, "Bad device admin requested: " + adminName, e); + Slog.w(TAG, "Bad device admin requested: " + adminName, e); return null; } catch (IOException e) { - Log.w(TAG, "Bad device admin requested: " + adminName, e); + Slog.w(TAG, "Bad device admin requested: " + adminName, e); return null; } } @@ -343,8 +343,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - out.endTag(null, "policies"); - if (mPasswordOwner >= 0) { out.startTag(null, "password-owner"); out.attribute(null, "value", Integer.toString(mPasswordOwner)); @@ -357,6 +355,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "failed-password-attempts"); } + if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) { + out.startTag(null, "active-password"); + out.attribute(null, "quality", Integer.toString(mActivePasswordQuality)); + out.attribute(null, "length", Integer.toString(mActivePasswordLength)); + out.endTag(null, "active-password"); + } + + out.endTag(null, "policies"); + out.endDocument(); stream.close(); journal.commit(); @@ -410,7 +417,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mAdminList.add(ap); } } catch (RuntimeException e) { - Log.w(TAG, "Failed loading admin " + name, e); + Slog.w(TAG, "Failed loading admin " + name, e); } } else if ("failed-password-attempts".equals(tag)) { mFailedPasswordAttempts = Integer.parseInt( @@ -420,21 +427,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mPasswordOwner = Integer.parseInt( parser.getAttributeValue(null, "value")); XmlUtils.skipCurrentTag(parser); + } else if ("active-password".equals(tag)) { + mActivePasswordQuality = Integer.parseInt( + parser.getAttributeValue(null, "quality")); + mActivePasswordLength = Integer.parseInt( + parser.getAttributeValue(null, "length")); + XmlUtils.skipCurrentTag(parser); } else { - Log.w(TAG, "Unknown tag: " + tag); + Slog.w(TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); } } } catch (NullPointerException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (NumberFormatException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (XmlPullParserException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (IOException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } catch (IndexOutOfBoundsException e) { - Log.w(TAG, "failed parsing " + file + " " + e); + Slog.w(TAG, "failed parsing " + file + " " + e); } try { if (stream != null) { @@ -444,6 +457,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Ignore } + // Validate that what we stored for the password quality matches + // sufficiently what is currently set. Note that this is only + // a sanity check in case the two get out of sync; this should + // never normally happen. + LockPatternUtils utils = new LockPatternUtils(mContext); + if (utils.getActivePasswordQuality() < mActivePasswordQuality) { + Slog.w(TAG, "Active password quality 0x" + + Integer.toHexString(mActivePasswordQuality) + + " does not match actual quality 0x" + + Integer.toHexString(utils.getActivePasswordQuality())); + mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + mActivePasswordLength = 0; + } + validatePasswordOwnerLocked(); long timeMs = getMaximumTimeToLock(null); @@ -453,10 +480,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); } catch (RemoteException e) { - Log.w(TAG, "Failure talking with power manager", e); + Slog.w(TAG, "Failure talking with power manager", e); } } + static void validateQualityConstant(int quality) { + switch (quality) { + case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + return; + } + throw new IllegalArgumentException("Invalid quality constant: 0x" + + Integer.toHexString(quality)); + } + void validatePasswordOwnerLocked() { if (mPasswordOwner >= 0) { boolean haveOwner = false; @@ -467,7 +507,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } if (!haveOwner) { - Log.w(TAG, "Previous password owner " + mPasswordOwner + Slog.w(TAG, "Previous password owner " + mPasswordOwner + " no longer active; disabling"); mPasswordOwner = -1; } @@ -557,15 +597,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordQuality(ComponentName who, int mode) { + public void setPasswordQuality(ComponentName who, int quality) { + validateQualityConstant(quality); + synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); - if (ap.passwordQuality != mode) { - ap.passwordQuality = mode; + if (ap.passwordQuality != quality) { + ap.passwordQuality = quality; saveSettingsLocked(); } } @@ -693,23 +735,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); quality = getPasswordQuality(null); if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - int adjQuality = LockPatternUtils.adjustPasswordMode(password, quality); - if (adjQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - Log.w(TAG, "resetPassword: password does not meet quality " + quality); + int realQuality = LockPatternUtils.computePasswordQuality(password); + if (realQuality < quality) { + Slog.w(TAG, "resetPassword: password quality 0x" + + Integer.toHexString(quality) + + " does not meet required quality 0x" + + Integer.toHexString(quality)); return false; } - quality = adjQuality; + quality = realQuality; } int length = getPasswordMinimumLength(null); if (password.length() < length) { - Log.w(TAG, "resetPassword: password does not meet length " + length); + Slog.w(TAG, "resetPassword: password length " + password.length() + + " does not meet required length " + length); return false; } } int callingUid = Binder.getCallingUid(); if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) { - Log.w(TAG, "resetPassword: already set by another uid and not entered by user"); + Slog.w(TAG, "resetPassword: already set by another uid and not entered by user"); return false; } @@ -719,11 +765,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { LockPatternUtils utils = new LockPatternUtils(mContext); utils.saveLockPassword(password, quality); - int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) - != 0 ? callingUid : -1; - if (mPasswordOwner != newOwner) { - mPasswordOwner = newOwner; - saveSettingsLocked(); + synchronized (this) { + int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) + != 0 ? callingUid : -1; + if (mPasswordOwner != newOwner) { + mPasswordOwner = newOwner; + saveSettingsLocked(); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -754,7 +802,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); } catch (RemoteException e) { - Log.w(TAG, "Failure talking with power manager", e); + Slog.w(TAG, "Failure talking with power manager", e); } } finally { Binder.restoreCallingIdentity(ident); @@ -807,7 +855,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { RecoverySystem.rebootWipeUserData(mContext); } catch (IOException e) { - Log.w(TAG, "Failed requesting data wipe", e); + Slog.w(TAG, "Failed requesting data wipe", e); } } @@ -857,6 +905,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); + validateQualityConstant(quality); + synchronized (this) { if (mActivePasswordQuality != quality || mActivePasswordLength != length || mFailedPasswordAttempts != 0) { @@ -864,10 +914,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { mActivePasswordQuality = quality; mActivePasswordLength = length; - if (mFailedPasswordAttempts != 0) { - mFailedPasswordAttempts = 0; - saveSettingsLocked(); - } + mFailedPasswordAttempts = 0; + saveSettingsLocked(); sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); } finally { @@ -946,7 +994,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } pw.println(" "); - pw.print(" mActivePasswordQuality="); pw.println(mActivePasswordQuality); + pw.print(" mActivePasswordQuality=0x"); + pw.println(Integer.toHexString(mActivePasswordQuality)); pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength); pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts); pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);