pipe auth token through confirm and set password

ConfirmDeviceCredentialsActivity and ChooseLockGeneric now understand
CLSH.EXTRA_KEY_HAS_CHALLENGE and CLSH.EXTRA_KEY_CHALLENGE in their
launching intents. If present, they return a hw_auth_token_t verifying
the challenge passed in as a field in keyed by
CLSH.EXTRA_KEY_CHALLENGE_TOKEN in their result intents.

Change-Id: I0b4e02b6a798a9e57d02522880a180dffadfcde1
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 51614a6..a53eca0 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -89,6 +89,8 @@
         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
         private DevicePolicyManager mDPM;
         private KeyStore mKeyStore;
+        private boolean mHasChallenge = false;
+        private long mChallenge;
         private boolean mPasswordConfirmed = false;
         private boolean mWaitingForConfirmation = false;
         private int mEncryptionRequestQuality;
@@ -133,6 +135,11 @@
                 mPasswordConfirmed = !confirmCredentials;
             }
 
+            mHasChallenge = getActivity().getIntent().getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+            mChallenge = getActivity().getIntent().getLongExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
+
             if (savedInstanceState != null) {
                 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
                 mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
@@ -389,8 +396,13 @@
                     maxLength, requirePasswordToDecrypt, confirmCredentials);
         }
 
-        // SetupWizard version will not need this as they will never be changing a password
-        // TODO: confirm
+        protected Intent getLockPasswordIntent(Context context, int quality,
+                int minLength, final int maxLength,
+                boolean requirePasswordToDecrypt, long challenge) {
+            return ChooseLockPassword.createIntent(context, quality, minLength,
+                    maxLength, requirePasswordToDecrypt, challenge);
+        }
+
         private Intent getLockPasswordIntent(Context context, int quality, int minLength,
                 final int maxLength, boolean requirePasswordToDecrypt, String password) {
             return ChooseLockPassword.createIntent(context, quality, minLength, maxLength,
@@ -403,8 +415,11 @@
                     confirmCredentials);
         }
 
-        // SetupWizard version will not need this as they will never be changing a password
-        // TODO: confirm
+        protected Intent getLockPatternIntent(Context context, final boolean requirePassword,
+               long challenge) {
+            return ChooseLockPattern.createIntent(context, requirePassword, challenge);
+        }
+
         private Intent getLockPatternIntent(Context context, final boolean requirePassword,
                 final String pattern) {
             return ChooseLockPattern.createIntent(context, requirePassword, pattern);
@@ -439,12 +454,24 @@
                     minLength = MIN_PASSWORD_LENGTH;
                 }
                 final int maxLength = mDPM.getPasswordMaximumLength(quality);
-                Intent intent = getLockPasswordIntent(context, quality, minLength,
+                Intent intent;
+                if (mHasChallenge) {
+                    intent = getLockPasswordIntent(context, quality, minLength,
+                            maxLength, mRequirePassword, mChallenge);
+                } else {
+                    intent = getLockPasswordIntent(context, quality, minLength,
                         maxLength, mRequirePassword, mUserPassword);
+                }
                 startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
-                Intent intent = getLockPatternIntent(context, mRequirePassword,
+                Intent intent;
+                if (mHasChallenge) {
+                    intent = getLockPatternIntent(context, mRequirePassword,
+                        mChallenge);
+                } else {
+                    intent = getLockPatternIntent(context, mRequirePassword,
                         mUserPassword);
+                }
                 startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 mChooseLockSettingsHelper.utils().clearLock();
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index 7ef044a..4e28eab 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -84,6 +84,15 @@
         return intent;
     }
 
+    public static Intent createIntent(Context context, int quality,
+            int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge) {
+        Intent intent = createIntent(context, quality, minLength, maxLength, requirePasswordToDecrypt,
+                false);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+        return intent;
+    }
+
     @Override
     protected boolean isValidFragment(String fragmentName) {
         if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
@@ -112,6 +121,8 @@
         private static final String KEY_CURRENT_PASSWORD = "current_password";
 
         private String mCurrentPassword;
+        private boolean mHasChallenge;
+        private long mChallenge;
         private TextView mPasswordEntry;
         private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
         private int mPasswordMaxLength = 16;
@@ -249,8 +260,12 @@
                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
 
             Intent intent = getActivity().getIntent();
-            final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
+            final boolean confirmCredentials = intent.getBooleanExtra(
+                    ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
             mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            mHasChallenge = intent.getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+            mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
             if (savedInstanceState == null) {
                 updateStage(Stage.Introduction);
                 if (confirmCredentials) {
@@ -463,7 +478,15 @@
                             EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
                     mLockPatternUtils.setCredentialRequiredToDecrypt(required);
                     mLockPatternUtils.saveLockPassword(pin, mCurrentPassword, mRequestedQuality);
-                    getActivity().setResult(RESULT_FINISHED);
+
+                    if (mHasChallenge) {
+                        Intent intent = new Intent();
+                        byte[] token = mLockPatternUtils.verifyPassword(pin, mChallenge);
+                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+                        getActivity().setResult(RESULT_FINISHED, intent);
+                    } else {
+                        getActivity().setResult(RESULT_FINISHED);
+                    }
                     getActivity().finish();
                     mDone = true;
                     if (!wasSecureBefore) {
diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java
index c516ad0..e3b7d4b 100644
--- a/src/com/android/settings/ChooseLockPattern.java
+++ b/src/com/android/settings/ChooseLockPattern.java
@@ -84,6 +84,15 @@
         return intent;
     }
 
+
+    public static Intent createIntent(Context context,
+            boolean requirePassword, long challenge) {
+        Intent intent = createIntent(context, requirePassword, false);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+        return intent;
+    }
+
     @Override
     protected boolean isValidFragment(String fragmentName) {
         if (ChooseLockPatternFragment.class.getName().equals(fragmentName)) return true;
@@ -123,6 +132,8 @@
         private static final int ID_EMPTY_MESSAGE = -1;
 
         private String mCurrentPattern;
+        private boolean mHasChallenge;
+        private long mChallenge;
         protected TextView mHeaderText;
         protected LockPatternView mLockPatternView;
         protected TextView mFooterText;
@@ -384,8 +395,11 @@
 
             final boolean confirmCredentials = getActivity().getIntent()
                     .getBooleanExtra("confirm_credentials", true);
-            mCurrentPattern = getActivity().getIntent()
-                    .getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            Intent intent = getActivity().getIntent();
+            mCurrentPattern = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            mHasChallenge = intent.getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+            mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
 
             if (savedInstanceState == null) {
                 if (confirmCredentials) {
@@ -606,7 +620,16 @@
             if (!wasSecureBefore) {
                 startActivity(getRedactionInterstitialIntent(getActivity()));
             }
-            getActivity().setResult(RESULT_FINISHED);
+
+            if (mHasChallenge) {
+                Intent intent = new Intent();
+                byte[] token = utils.verifyPattern(mChosenPattern, mChallenge);
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+                getActivity().setResult(RESULT_FINISHED, intent);
+            } else {
+                getActivity().setResult(RESULT_FINISHED);
+            }
+
             getActivity().finish();
             mDone = true;
         }
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index 65697f6..c113b1d 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -28,6 +28,10 @@
 
     static final String EXTRA_KEY_TYPE = "type";
     static final String EXTRA_KEY_PASSWORD = "password";
+    static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
+    static final String EXTRA_KEY_CHALLENGE = "challenge";
+    static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
+
 
     private LockPatternUtils mLockPatternUtils;
     private Activity mActivity;
@@ -87,13 +91,38 @@
     boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
             @Nullable CharSequence header, @Nullable CharSequence description,
             boolean returnCredentials, boolean external) {
+        return launchConfirmationActivity(request, title, header, description,
+                returnCredentials, external, false, 0);
+    }
+
+    /**
+     * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
+     * @param message optional message to display about the action about to be done
+     * @param details optional detail message to display
+     * @param challenge a challenge to be verified against the device credential.
+     *                  This method can only be called internally.
+     * @return true if one exists and we launched an activity to confirm it
+     * @see #onActivityResult(int, int, android.content.Intent)
+     */
+    boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
+            @Nullable CharSequence header, @Nullable CharSequence description,
+            long challenge) {
+        return launchConfirmationActivity(request, title, header, description,
+                false, false, true, challenge);
+    }
+
+    private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
+            @Nullable CharSequence header, @Nullable CharSequence description,
+            boolean returnCredentials, boolean external, boolean hasChallenge,
+            long challenge) {
         boolean launched = false;
         switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
                 launched = launchConfirmationActivity(request, title, header, description,
-                        returnCredentials
+                        returnCredentials || hasChallenge
                                 ? ConfirmLockPattern.InternalActivity.class
-                                : ConfirmLockPattern.class, external);
+                                : ConfirmLockPattern.class, external,
+                                hasChallenge, challenge);
                 break;
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
@@ -101,16 +130,18 @@
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
                 launched = launchConfirmationActivity(request, title, header, description,
-                        returnCredentials
+                        returnCredentials || hasChallenge
                                 ? ConfirmLockPassword.InternalActivity.class
-                                : ConfirmLockPassword.class, external);
+                                : ConfirmLockPassword.class, external,
+                                hasChallenge, challenge);
                 break;
         }
         return launched;
     }
 
     private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
-            CharSequence message, Class<?> activityClass, boolean external) {
+            CharSequence message, Class<?> activityClass, boolean external, boolean hasChallenge,
+            long challenge) {
         final Intent intent = new Intent();
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
@@ -119,6 +150,8 @@
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, external);
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, external);
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
         intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
         if (mFragment != null) {
             mFragment.startActivityForResult(intent, request);
diff --git a/src/com/android/settings/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
index 9a7f843..7653603 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
@@ -39,6 +39,17 @@
         return intent;
     }
 
+    public static Intent createIntent(CharSequence title, CharSequence details, long challenge) {
+        Intent intent = new Intent();
+        intent.setClassName("com.android.settings",
+                ConfirmDeviceCredentialActivity.class.getName());
+        intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
+        intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
+        return intent;
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index 44c599b..d9622c0 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -183,8 +183,34 @@
 
         private void handleNext() {
             final String pin = mPasswordEntry.getText().toString();
-            if (mLockPatternUtils.checkPassword(pin)) {
+            final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+            boolean matched = false;
+            Intent intent = new Intent();
+            if (verifyChallenge)  {
+                if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
+                    long challenge = getActivity().getIntent().getLongExtra(
+                            ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
+                    byte[] token = mLockPatternUtils.verifyPassword(pin, challenge);
+                    if (token != null) {
+                        matched = true;
+                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+                    }
+                }
+            } else if (mLockPatternUtils.checkPassword(pin)) {
+                matched = true;
+                if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
+                    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
+                                    mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
+                                             : StorageManager.CRYPT_TYPE_PIN);
+                    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
+                }
+            }
+
+            if (matched) {
                 authenticationSucceeded(pin);
+                getActivity().setResult(RESULT_OK, intent);
+                getActivity().finish();
             } else {
                 if (++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index 8c4fabc..9db1719 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -273,8 +273,35 @@
             }
 
             public void onPatternDetected(List<LockPatternView.Cell> pattern) {
-                if (mLockPatternUtils.checkPattern(pattern)) {
+                final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
+                        ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+                boolean matched = false;
+                Intent intent = new Intent();
+                if (verifyChallenge) {
+                    if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
+                        long challenge = getActivity().getIntent().getLongExtra(
+                            ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
+                        byte[] token = mLockPatternUtils.verifyPattern(pattern, challenge);
+                        if (token != null) {
+                            matched = true;
+                            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+                        }
+                    }
+                } else if (mLockPatternUtils.checkPattern(pattern)) {
+                    matched = true;
+                    if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
+                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
+                                        StorageManager.CRYPT_TYPE_PATTERN);
+                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
+                                        LockPatternUtils.patternToString(pattern));
+                    }
+
+                }
+
+                if (matched) {
                     authenticationSucceeded(LockPatternUtils.patternToString(pattern));
+                    getActivity().setResult(Activity.RESULT_OK, intent);
+                    getActivity().finish();
                 } else {
                     if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
                             ++mNumWrongConfirmAttempts