Prevent factory reset for short passwords in CryptKeeper

Since patterns and passwords can't be shorter than 4
elements, we shouldn't count them against attempts to unlock
the device.  If we do, we risk resetting the device
due to stray input.

Bug: 18211475
Bug: 18273380

Change-Id: Ib329cd3383471483cda68bfac4278b17d0c09c57

Conflicts:
	src/com/android/settings/CryptKeeper.java
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index eded55b..33fa9fd 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -135,7 +135,18 @@
     // how long we wait to clear a right pattern
     private static final int RIGHT_PATTERN_CLEAR_TIMEOUT_MS = 500;
 
-    private Runnable mClearPatternRunnable = new Runnable() {
+    // When the user enters a short pin/password, run this to show an error,
+    // but don't count it against attempts.
+    private final Runnable mFakeUnlockAttemptRunnable = new Runnable() {
+        public void run() {
+            handleBadAttempt(1 /* failedAttempt */);
+        }
+    };
+
+    // TODO: this should be tuned to match minimum decryption timeout
+    private static final int FAKE_ATTEMPT_DELAY = 1000;
+
+    private final Runnable mClearPatternRunnable = new Runnable() {
         @Override
         public void run() {
             mLockPatternView.clearPattern();
@@ -164,8 +175,7 @@
         @Override
         protected void onPreExecute() {
             super.onPreExecute();
-            final TextView status = (TextView) findViewById(R.id.status);
-            status.setText(R.string.checking_decryption);
+            beginAttempt();
         }
 
         @Override
@@ -207,59 +217,66 @@
                 showFactoryReset(true);
                 return;
             } else {
-                // Wrong entry. Handle pattern case.
-                if (mLockPatternView != null) {
-                    mLockPatternView.setDisplayMode(DisplayMode.Wrong);
-                    mLockPatternView.removeCallbacks(mClearPatternRunnable);
-                    mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
+                handleBadAttempt(failedAttempts);
+            }
+        }
+    }
+
+    private void beginAttempt() {
+        final TextView status = (TextView) findViewById(R.id.status);
+        status.setText(R.string.checking_decryption);
+    }
+
+    private void handleBadAttempt(Integer failedAttempts) {
+        // Wrong entry. Handle pattern case.
+        if (mLockPatternView != null) {
+            mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+            mLockPatternView.removeCallbacks(mClearPatternRunnable);
+            mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
+        }
+        if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
+            mCooldown = COOL_DOWN_INTERVAL;
+            cooldown();
+        } else {
+            final TextView status = (TextView) findViewById(R.id.status);
+
+            int remainingAttempts = MAX_FAILED_ATTEMPTS - failedAttempts;
+            if (remainingAttempts < COOL_DOWN_ATTEMPTS) {
+                CharSequence warningTemplate = getText(R.string.crypt_keeper_warn_wipe);
+                CharSequence warning = TextUtils.expandTemplate(warningTemplate,
+                        Integer.toString(remainingAttempts));
+                status.setText(warning);
+            } else {
+                int passwordType = StorageManager.CRYPT_TYPE_PASSWORD;
+                try {
+                    final IMountService service = getMountService();
+                    passwordType = service.getPasswordType();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error calling mount service " + e);
                 }
-                if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
-                    mCooldown = COOL_DOWN_INTERVAL;
-                    cooldown();
+
+                if (passwordType == StorageManager.CRYPT_TYPE_PIN) {
+                    status.setText(R.string.cryptkeeper_wrong_pin);
+                } else if (passwordType == StorageManager.CRYPT_TYPE_PATTERN) {
+                    status.setText(R.string.cryptkeeper_wrong_pattern);
                 } else {
-                    final TextView status = (TextView) findViewById(R.id.status);
-
-                    int remainingAttempts = MAX_FAILED_ATTEMPTS - failedAttempts;
-                    if (remainingAttempts < COOL_DOWN_ATTEMPTS) {
-                        CharSequence warningTemplate = getText(R.string.crypt_keeper_warn_wipe);
-                        CharSequence warning = TextUtils.expandTemplate(warningTemplate,
-                                                                        Integer.toString(remainingAttempts));
-                        status.setText(warning);
-                    } else {
-                        int passwordType = StorageManager.CRYPT_TYPE_PASSWORD;
-                        try {
-                            final IMountService service = getMountService();
-                            passwordType = service.getPasswordType();
-                        } catch (Exception e) {
-                            Log.e(TAG, "Error calling mount service " + e);
-                        }
-
-                        if (passwordType == StorageManager.CRYPT_TYPE_PIN) {
-                            status.setText(R.string.cryptkeeper_wrong_pin);
-                        } else if (passwordType == StorageManager.CRYPT_TYPE_PATTERN) {
-                            status.setText(R.string.cryptkeeper_wrong_pattern);
-                        } else {
-                            status.setText(R.string.cryptkeeper_wrong_password);
-                        }
-                    }
-
-
-                    if (mLockPatternView != null) {
-                        mLockPatternView.setDisplayMode(DisplayMode.Wrong);
-                    }
-                    // Reenable the password entry
-                    if (mPasswordEntry != null) {
-                        mPasswordEntry.setEnabled(true);
-                        final InputMethodManager imm = (InputMethodManager) getSystemService(
-                                                  Context.INPUT_METHOD_SERVICE);
-                        imm.showSoftInput(mPasswordEntry, 0);
-                        setBackFunctionality(true);
-                    }
-                    if (mLockPatternView != null) {
-                        mLockPatternView.setEnabled(true);
-                    }
+                    status.setText(R.string.cryptkeeper_wrong_password);
                 }
             }
+
+            if (mLockPatternView != null) {
+                mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+                mLockPatternView.setEnabled(true);
+            }
+
+            // Reenable the password entry
+            if (mPasswordEntry != null) {
+                mPasswordEntry.setEnabled(true);
+                final InputMethodManager imm = (InputMethodManager) getSystemService(
+                        Context.INPUT_METHOD_SERVICE);
+                imm.showSoftInput(mPasswordEntry, 0);
+                setBackFunctionality(true);
+            }
         }
     }
 
@@ -329,6 +346,8 @@
             | StatusBarManager.DISABLE_SEARCH
             | StatusBarManager.DISABLE_RECENT;
 
+    protected static final int MIN_LENGTH_BEFORE_REPORT = LockPatternUtils.MIN_LOCK_PATTERN_SIZE;
+
     /** @return whether or not this Activity was started for debugging the UI only. */
     private boolean isDebugView() {
         return getIntent().hasExtra(EXTRA_FORCE_VIEW);
@@ -701,6 +720,11 @@
         }
     }
 
+    private void fakeUnlockAttempt(View postingView) {
+        beginAttempt();
+        postingView.postDelayed(mFakeUnlockAttemptRunnable, FAKE_ATTEMPT_DELAY);
+    }
+
     protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
         new LockPatternView.OnPatternListener() {
 
@@ -716,7 +740,12 @@
         @Override
         public void onPatternDetected(List<LockPatternView.Cell> pattern) {
             mLockPatternView.setEnabled(false);
-            new DecryptTask().execute(LockPatternUtils.patternToString(pattern));
+            if (pattern.size() >= MIN_LENGTH_BEFORE_REPORT) {
+                new DecryptTask().execute(LockPatternUtils.patternToString(pattern));
+            } else {
+                // Allow user to make as many of these as they want.
+                fakeUnlockAttempt(mLockPatternView);
+            }
         }
 
         @Override
@@ -873,7 +902,12 @@
             mPasswordEntry.setEnabled(false);
             setBackFunctionality(false);
 
-            new DecryptTask().execute(password);
+            if (password.length() >= LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+                new DecryptTask().execute(password);
+            } else {
+                // Allow user to make as many of these as they want.
+                fakeUnlockAttempt(mPasswordEntry);
+            }
 
             return true;
         }