Merge "Fix 2385283,2379269: report unlock attempt success/fail to DevicePolicyManager Reports success/fail for password, pattern and account unlock. Tweak pattern timeout a bit to avoid timeouts."
diff --git a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
index 7992dd8..26419dd 100644
--- a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
+++ b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
@@ -177,12 +177,14 @@
             intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             mContext.startActivity(intent);
+            mCallback.reportSuccessfulUnlockAttempt();
 
             // close the keyguard
             mCallback.keyguardDone(true);
         } else {
             mInstructions.setText(R.string.lockscreen_glogin_invalid_input);
             mPassword.setText("");
+            mCallback.reportFailedUnlockAttempt();
         }
     }
 
diff --git a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
index 6bb6a45..06a5f19 100644
--- a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
+++ b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
@@ -63,13 +63,18 @@
     void takeEmergencyCallAction();
 
     /**
-     * Report that the user had a failed attempt unlocking via the pattern.
+     * Report that the user had a failed attempt to unlock with password or pattern.
      */
-    void reportFailedPatternAttempt();
+    void reportFailedUnlockAttempt();
+
+    /**
+     * Report that the user successfully entered their password or pattern.
+     */
+    void reportSuccessfulUnlockAttempt();
 
     /**
      * Report whether we there's another way to unlock the device.
-     * @return true 
+     * @return true
      */
     boolean doesFallbackUnlockScreenExist();
 }
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
index ccb7902..8f6561a 100644
--- a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -293,6 +293,14 @@
             public boolean doesFallbackUnlockScreenExist() {
                 return mEnableFallback;
             }
+
+            public void reportFailedUnlockAttempt() {
+                mLockPatternUtils.reportFailedPasswordAttempt();
+            }
+
+            public void reportSuccessfulUnlockAttempt() {
+                mLockPatternUtils.reportSuccessfulPasswordAttempt();
+            }
         };
 
         /**
diff --git a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
index bb1950a..1a250b8 100644
--- a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -51,11 +51,13 @@
     private TextView mCarrier;
     private LockPatternUtils mLockPatternUtils;
     private Button mCancelButton;
-    private int mPasswordAttempts = 0;
-    private int mMinimumPasswordLength = 4; // TODO: get from policy store
 
     private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
 
+    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // any passwords with length less than or equal to this length.
+    private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
     public PasswordUnlockScreen(Context context, LockPatternUtils lockPatternUtils,
             KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback) {
         super(context);
@@ -142,12 +144,12 @@
     private void verifyPasswordAndUnlock() {
         String entry = mPasswordTextView.getText().toString();
         if (mLockPatternUtils.checkPassword(entry)) {
-            mPasswordAttempts = 0;
             mCallback.keyguardDone(true);
-        } else if (entry.length() >= mMinimumPasswordLength ) {
+            mCallback.reportSuccessfulUnlockAttempt();
+        } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
             // to avoid accidental lockout, only count attempts that are long enough to be a
             // real password. This may require some tweaking.
-            mPasswordAttempts++;
+            mCallback.reportFailedUnlockAttempt();
         }
         mPasswordTextView.setText("");
     }
diff --git a/policy/com/android/internal/policy/impl/UnlockScreen.java b/policy/com/android/internal/policy/impl/UnlockScreen.java
index 30ab879..5e6e54d 100644
--- a/policy/com/android/internal/policy/impl/UnlockScreen.java
+++ b/policy/com/android/internal/policy/impl/UnlockScreen.java
@@ -52,12 +52,20 @@
     // how long before we clear the wrong pattern
     private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
 
-    // how long we stay awake once the user is ready to enter a pattern
+    // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
     private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
 
+    // how long we stay awake after the user hits the first dot.
+    private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
+
     // how many cells the user has to cross before we poke the wakelock
     private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
 
+    // This dictates how long a pattern should be before we count it as an attempt.
+    // This should be long enough to avoid false triggers while the device is in a pocket,
+    // as this can lead to a wiped device if a {@link DeviceAdmin} is active and has it enabled.
+    private static final int MIN_PATTERN_BEFORE_REPORT = 3;
+
     private int mFailedPatternAttemptsSinceLastTimeout = 0;
     private int mTotalFailedPatternAttempts = 0;
     private CountDownTimer mCountdownTimer = null;
@@ -466,6 +474,9 @@
             // the user actually trying to draw a pattern of some minimal length.
             if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
                 mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+            } else {
+                // Give just a little extra time if they hit one of the first few dots
+                mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
             }
         }
 
@@ -476,6 +487,7 @@
                 mInstructions = "";
                 updateStatusLines();
                 mCallback.keyguardDone(true);
+                mCallback.reportSuccessfulUnlockAttempt();
             } else {
                 if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
                     mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
@@ -484,19 +496,21 @@
                 if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                     mTotalFailedPatternAttempts++;
                     mFailedPatternAttemptsSinceLastTimeout++;
-                    mCallback.reportFailedPatternAttempt();
                 }
                 if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                     handleAttemptLockout(deadline);
-                    return;
+                } else {
+                    // TODO mUnlockIcon.setVisibility(View.VISIBLE);
+                    mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
+                    updateStatusLines();
+                    mLockPatternView.postDelayed(
+                            mCancelPatternRunnable,
+                            PATTERN_CLEAR_TIMEOUT_MS);
                 }
-                // TODO mUnlockIcon.setVisibility(View.VISIBLE);
-                mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
-                updateStatusLines();
-                mLockPatternView.postDelayed(
-                        mCancelPatternRunnable,
-                        PATTERN_CLEAR_TIMEOUT_MS);
+                if (pattern.size() > MIN_PATTERN_BEFORE_REPORT) {
+                    mCallback.reportFailedUnlockAttempt();
+                }
             }
         }
     }