From 4f36995777a4136e8e63ea51cef2ff61df48790d Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Fri, 19 Aug 2011 18:29:22 -0700 Subject: [PATCH] Fix 5026428: Rework unlock attempt logic with active DPM to show better messages Prior to this change, we didn't have a mechanism to warn the user when they were approaching the wipe data threshold dictated by active DPMs. Here's the new flow: - If a device policy manager is installed and sets the max password attempts, we start warning the user when they are within a grace period of hitting the max (currently hard-wired to 5). - We continue to show a dialog after each continued attempt until the user reaches 0 remaining attempts. - We now show a message when they hit 0 so they know why their device is being reset. The device will reboot and wipe data shortly after this final dialog is shown. Also increased the criteria for a pattern attempt from 3 dots to 4 dots since the user can never set a pattern less than 4 in length. This will greatly reduce the likelihood of a false wipe on the pattern unlock screens without compromising security. Change-Id: I28825ef21dfa2e2b6540e743252c6d50c41e5ad7 --- .../internal/widget/LockPatternUtils.java | 10 +- core/res/res/values/strings.xml | 39 +++++- .../policy/impl/LockPatternKeyguardView.java | 129 +++++++++++------- .../policy/impl/PasswordUnlockScreen.java | 1 - 4 files changed, 122 insertions(+), 57 deletions(-) diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e4322c6327392..804f28a386074 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -83,6 +83,13 @@ public class LockPatternUtils { */ public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L; + + /** + * This dictates when we start telling the user that continued failed attempts will wipe + * their device. + */ + public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5; + /** * The minimum number of dots in a valid pattern. */ @@ -93,7 +100,7 @@ public class LockPatternUtils { * attempt for it to be counted against the counts that affect * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET} */ - public static final int MIN_PATTERN_REGISTER_FAIL = 3; + public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE; private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; @@ -112,6 +119,7 @@ public class LockPatternUtils { private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false); private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false); + private static FileObserver sPasswordObserver; private static class PasswordFileObserver extends FileObserver { diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6aff54e5128f7..1f6ab6f5ff9de 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1300,8 +1300,8 @@ change/intercept network settings and traffic - Allows an application to change network settings and to intercept and inspect all network traffic, - for example to change the proxy and port of any APN. Malicious applications could monitor, redirect, or modify network + Allows an application to change network settings and to intercept and inspect all network traffic, + for example to change the proxy and port of any APN. Malicious applications could monitor, redirect, or modify network packets without your knowledge. @@ -1857,7 +1857,7 @@ \n\nPlease try again in %d seconds. - You have incorrectly drawn your unlock pattern %d times. @@ -1865,7 +1865,8 @@ you will be asked to unlock your tablet using your Google sign-in.\n\n Please try again in %d seconds. - You have incorrectly drawn your unlock pattern %d times. @@ -1874,6 +1875,36 @@ Please try again in %d seconds. + + + You have incorrectly attempted to unlock the tablet %d times. + After %d more unsuccessful attempts, + the tablet will be reset to factory default and all user data will be lost. + + + + + You have incorrectly attempted to unlock the phone %d times. + After %d more unsuccessful attempts, + the phone will be reset to factory default and all user data will be lost. + + + + + You have incorrectly attempted to unlock the tablet %d times. + The tablet will now be reset to factory default. + + + + + You have incorrectly attempted to unlock the phone %d times. + The phone will now be reset to factory default. + + Try again in %d seconds. diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index b60bae7d49eb9..adcc9c0f0e8e0 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -42,6 +42,7 @@ import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; @@ -294,22 +295,47 @@ public class LockPatternKeyguardView extends KeyguardViewBase { public void reportFailedUnlockAttempt() { mUpdateMonitor.reportFailedAttempt(); final int failedAttempts = mUpdateMonitor.getFailedAttempts(); - if (DEBUG) Log.d(TAG, - "reportFailedPatternAttempt: #" + failedAttempts + + if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts + " (enableFallback=" + mEnableFallback + ")"); - final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() + + final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - if (usingLockPattern && mEnableFallback && failedAttempts == - (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { - showAlmostAtAccountLoginDialog(); - } else if (usingLockPattern && mEnableFallback - && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { - mLockPatternUtils.setPermanentlyLocked(true); - updateScreen(mMode); - } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) - == 0) { - showTimeoutDialog(); + + final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() + .getMaximumFailedPasswordsForWipe(null); + + final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + + final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? + (failedAttemptsBeforeWipe - failedAttempts) + : Integer.MAX_VALUE; // because DPM returns 0 if no restriction + + if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { + // If we reach this code, it means the user has installed a DevicePolicyManager + // that requests device wipe after N attempts. Once we get below the grace + // period, we'll post this dialog every time as a clear warning until the + // bombshell hits and the device is wiped. + if (remainingBeforeWipe > 0) { + showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); + } else { + // Too many attempts. The device will be wiped shortly. + Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); + showWipeDialog(failedAttempts); + } + } else if (usingPattern && mEnableFallback) { + if (failedAttempts == failedAttemptWarning) { + showAlmostAtAccountLoginDialog(); + } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { + mLockPatternUtils.setPermanentlyLocked(true); + updateScreen(mMode); + } + } else { + final boolean showTimeout = + (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; + if (showTimeout) { + showTimeoutDialog(); + } } mLockPatternUtils.reportFailedPasswordAttempt(); } @@ -727,26 +753,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { return currentMode; } - private void showTimeoutDialog() { - int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; - int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; - if(getUnlockMode() == UnlockMode.Password) { - if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == - DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { - messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; - } else { - messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; - } - } - String message = mContext.getString( - messageId, - mUpdateMonitor.getFailedAttempts(), - timeoutInSeconds); + private void showDialog(String title, String message) { final AlertDialog dialog = new AlertDialog.Builder(mContext) - .setTitle(null) - .setMessage(message) - .setNeutralButton(R.string.ok, null) - .create(); + .setTitle(title) + .setMessage(message) + .setNeutralButton(R.string.ok, null) + .create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); if (!mContext.getResources().getBoolean( com.android.internal.R.bool.config_sf_slowBlur)) { @@ -757,27 +769,42 @@ public class LockPatternKeyguardView extends KeyguardViewBase { dialog.show(); } + private void showTimeoutDialog() { + int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; + if (getUnlockMode() == UnlockMode.Password) { + if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == + DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { + messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; + } else { + messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; + } + } + String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(), + timeoutInSeconds); + showDialog(null, message); + } + private void showAlmostAtAccountLoginDialog() { + final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin, + count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); + showDialog(null, message); + } + + private void showAlmostAtWipeDialog(int attempts, int remaining) { int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; String message = mContext.getString( - R.string.lockscreen_failed_attempts_almost_glogin, - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, - timeoutInSeconds); - final AlertDialog dialog = new AlertDialog.Builder(mContext) - .setTitle(null) - .setMessage(message) - .setNeutralButton(R.string.ok, null) - .create(); - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - dialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } - dialog.show(); + R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining); + showDialog(null, message); + } + + private void showWipeDialog(int attempts) { + String message = mContext.getString( + R.string.lockscreen_failed_attempts_now_wiping, attempts); + showDialog(null, message); } /** diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index d70b3bb2ba901..ee0a6e9e3b20b 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -41,7 +41,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView;