summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2011-06-02 16:47:15 -0700
committerBrian Carlstrom <bdc@google.com>2011-06-02 21:06:21 -0700
commit0e88f4dd19a80e9e4d759595439773fb3e1f0c50 (patch)
treeec6c29c3b6bfd1ca79e9815f0ecdb3e71c5912a5 /src/com/android/settings
parent79e309a9b80096cc02bb80abd6408374a6193418 (diff)
downloadpackages_apps_settings-0e88f4dd19a80e9e4d759595439773fb3e1f0c50.zip
packages_apps_settings-0e88f4dd19a80e9e4d759595439773fb3e1f0c50.tar.gz
packages_apps_settings-0e88f4dd19a80e9e4d759595439773fb3e1f0c50.tar.bz2
Bulletproof CredentialStorage state handling
As noted by the class javadoc, CredentialStorage has seen the number of cases to cope with grow. This change tries to address those cases. src/com/android/settings/CredentialStorage.java Added ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD to coordinate additional producer and consumer. constant declaration here, since its used by callers of ChooseLockSettingsHelper.launchConfirmationActivity src/com/android/settings/ChooseLockSettingsHelper.java old producer src/com/android/settings/ConfirmLockPassword.java new producer (CredentialStorage wants passwords and patterns) src/com/android/settings/ConfirmLockPattern.java new consumer src/com/android/settings/CredentialStorage.java old consumer src/com/android/settings/CryptKeeperSettings.java Made class final and removed protected from method to make it clear ChooseLockSettingsHelper is not to be used by subclassing. src/com/android/settings/ChooseLockSettingsHelper.java Change-Id: Ib2d65398fe44573168a6267a0376c3b0388b16c8
Diffstat (limited to 'src/com/android/settings')
-rw-r--r--src/com/android/settings/ChooseLockSettingsHelper.java8
-rw-r--r--src/com/android/settings/ConfirmLockPassword.java2
-rw-r--r--src/com/android/settings/ConfirmLockPattern.java7
-rw-r--r--src/com/android/settings/CredentialStorage.java234
-rw-r--r--src/com/android/settings/CryptKeeperSettings.java2
5 files changed, 207 insertions, 46 deletions
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index d31fe3b..a069712 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -23,7 +23,10 @@ import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
-public class ChooseLockSettingsHelper {
+public final class ChooseLockSettingsHelper {
+
+ static final String EXTRA_KEY_PASSWORD = "password";
+
private LockPatternUtils mLockPatternUtils;
private Activity mActivity;
private Fragment mFragment;
@@ -49,8 +52,7 @@ public class ChooseLockSettingsHelper {
* @return true if one exists and we launched an activity to confirm it
* @see #onActivityResult(int, int, android.content.Intent)
*/
- protected boolean launchConfirmationActivity(int request,
- CharSequence message, CharSequence details) {
+ boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details) {
boolean launched = false;
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index ffda3d3..1229046 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -149,7 +149,7 @@ public class ConfirmLockPassword extends PreferenceActivity {
if (mLockPatternUtils.checkPassword(pin)) {
Intent intent = new Intent();
- intent.putExtra("password", pin);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index 0653d3f..2892930 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -256,7 +256,12 @@ public class ConfirmLockPattern extends PreferenceActivity {
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
- getActivity().setResult(Activity.RESULT_OK);
+
+ Intent intent = new Intent();
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
+ LockPatternUtils.patternToString(pattern));
+
+ getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index d926d47..e246fce 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -21,6 +21,7 @@ import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
@@ -28,6 +29,7 @@ import android.security.KeyChain.KeyChainConnection;
import android.security.KeyChain;
import android.security.KeyStore;
import android.text.Editable;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
@@ -36,7 +38,43 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.widget.LockPatternUtils;
-public class CredentialStorage extends Activity {
+/**
+ * CredentialStorage handles KeyStore reset, unlock, and install.
+ *
+ * CredentialStorage has a pretty convoluted state machine to migrate
+ * from the old style separate keystore password to a new key guard
+ * based password, as well as to deal with setting up the key guard if
+ * necessary.
+ *
+ * KeyStore: UNINITALIZED
+ * KeyGuard: OFF
+ * Action: set up key guard
+ * Notes: factory state
+ *
+ * KeyStore: UNINITALIZED
+ * KeyGuard: ON
+ * Action: confirm key guard
+ * Notes: user had key guard but no keystore and upgraded from pre-ICS
+ * OR user had key guard and pre-ICS keystore password which was then reset
+ *
+ * KeyStore: LOCKED
+ * KeyGuard: OFF/ON
+ * Action: old unlock dialog
+ * Notes: assume old password, need to use it to unlock.
+ * if unlock, ensure key guard before install.
+ * if reset, treat as UNINITALIZED/OFF
+ *
+ * KeyStore: UNLOCKED
+ * KeyGuard: OFF
+ * Action: set up key guard
+ * Notes: ensure key guard, then proceed
+ *
+ * KeyStore: UNLOCKED
+ * keyguard: ON
+ * Action: normal unlock/install
+ * Notes: this is the common case
+ */
+public final class CredentialStorage extends Activity {
private static final String TAG = "CredentialStorage";
@@ -48,8 +86,22 @@ public class CredentialStorage extends Activity {
// lower than this, keystore should not be activated.
static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+ private static final int CONFIRM_KEY_GUARD_REQUEST = 1;
+
private final KeyStore mKeyStore = KeyStore.getInstance();
- private Bundle mBundle;
+
+ /**
+ * When non-null, the bundle containing credentials to install.
+ */
+ private Bundle mInstallBundle;
+
+ /**
+ * After unsuccessful KeyStore.unlock, the number of unlock
+ * attempts remaining before the KeyStore will reset itself.
+ *
+ * Reset to -1 on successful unlock or reset.
+ */
+ private int mRetriesRemaining = -1;
@Override protected void onResume() {
super.onResume();
@@ -60,41 +112,83 @@ public class CredentialStorage extends Activity {
if (ACTION_RESET.equals(action)) {
new ResetDialog();
} else {
- if (!checkKeyguardQuality()) {
- new ConfigureLockScreenDialog();
- return;
- }
if (ACTION_INSTALL.equals(action) &&
"com.android.certinstaller".equals(getCallingPackage())) {
- mBundle = intent.getExtras();
+ mInstallBundle = intent.getExtras();
+ }
+ // ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
+ handleUnlockOrInstall();
+ }
+ }
+
+ /**
+ * Based on the current state of the KeyStore and key guard, try to
+ * make progress on unlocking or installing to the keystore.
+ */
+ private void handleUnlockOrInstall() {
+ // something already decided we are done, do not proceed
+ if (isFinishing()) {
+ return;
+ }
+ switch (mKeyStore.state()) {
+ case UNINITIALIZED: {
+ ensureKeyGuard();
+ return;
}
- // ACTION_UNLOCK also handled here
- switch (mKeyStore.state()) {
- case UNINITIALIZED:
- // if we had a keyguard set, we should be initialized
- throw new AssertionError();
- case LOCKED:
- // if we have a keyguard, why didn't we unlock?
- // possibly old style password, display prompt
- new UnlockDialog();
- break;
- case UNLOCKED:
- install();
- finish();
- break;
+ case LOCKED: {
+ new UnlockDialog();
+ return;
}
+ case UNLOCKED: {
+ if (!checkKeyGuardQuality()) {
+ new ConfigureKeyGuardDialog();
+ return;
+ }
+ installIfAvailable();
+ finish();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Make sure the user enters the key guard to set or change the
+ * keystore password. This can be used in UNINITIALIZED to set the
+ * keystore password or UNLOCKED to change the password (as is the
+ * case after unlocking with an old-style password).
+ */
+ private void ensureKeyGuard() {
+ if (!checkKeyGuardQuality()) {
+ // key guard not setup, doing so will initialize keystore
+ new ConfigureKeyGuardDialog();
+ // will return to onResume after Activity
+ return;
}
+ // force key guard confirmation
+ if (confirmKeyGuard()) {
+ // will return password value via onActivityResult
+ return;
+ }
+ finish();
}
- private boolean checkKeyguardQuality() {
+ /**
+ * Returns true if the currently set key guard matches our minimum quality requirements.
+ */
+ private boolean checkKeyGuardQuality() {
int quality = new LockPatternUtils(this).getActivePasswordQuality();
return (quality >= MIN_PASSWORD_QUALITY);
}
- private void install() {
- if (mBundle != null && !mBundle.isEmpty()) {
- for (String key : mBundle.keySet()) {
- byte[] value = mBundle.getByteArray(key);
+ /**
+ * Install credentials if available, otherwise do nothing.
+ */
+ private void installIfAvailable() {
+ if (mInstallBundle != null && !mInstallBundle.isEmpty()) {
+ Bundle bundle = mInstallBundle;
+ mInstallBundle = null;
+ for (String key : bundle.keySet()) {
+ byte[] value = bundle.getByteArray(key);
if (value != null && !mKeyStore.put(key, value)) {
Log.e(TAG, "Failed to install " + key);
return;
@@ -104,6 +198,9 @@ public class CredentialStorage extends Activity {
}
}
+ /**
+ * Prompt for reset confirmation, resetting on confirmation, finishing otherwise.
+ */
private class ResetDialog
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener
{
@@ -135,6 +232,9 @@ public class CredentialStorage extends Activity {
}
}
+ /**
+ * Background task to handle reset of both keystore and user installed CAs.
+ */
private class ResetKeyStoreAndKeyChain extends AsyncTask<Void, Void, Boolean> {
@Override protected Boolean doInBackground(Void... unused) {
@@ -168,12 +268,15 @@ public class CredentialStorage extends Activity {
}
}
- private class ConfigureLockScreenDialog
+ /**
+ * Prompt for key guard configuration confirmation.
+ */
+ private class ConfigureKeyGuardDialog
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener
{
private boolean mConfigureConfirmed;
- private ConfigureLockScreenDialog() {
+ private ConfigureKeyGuardDialog() {
AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
.setTitle(android.R.string.dialog_alert_title)
.setIcon(android.R.drawable.ic_dialog_alert)
@@ -202,6 +305,46 @@ public class CredentialStorage extends Activity {
}
}
+ /**
+ * Confirm existing key guard, returning password via onActivityResult.
+ */
+ private boolean confirmKeyGuard() {
+ Resources res = getResources();
+ boolean launched = new ChooseLockSettingsHelper(this)
+ .launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST,
+ res.getText(R.string.master_clear_gesture_prompt),
+ res.getText(R.string.master_clear_gesture_explanation));
+ return launched;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ /**
+ * Receive key guard password initiated by confirmKeyGuard.
+ */
+ if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
+ if (resultCode == Activity.RESULT_OK) {
+ String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+ if (!TextUtils.isEmpty(password)) {
+ // success
+ mKeyStore.password(password);
+ // return to onResume
+ return;
+ }
+ }
+ // failed confirmation, bail
+ finish();
+ }
+ }
+
+ /**
+ * Prompt for unlock with old-style password.
+ *
+ * On successful unlock, ensure migration to key guard before continuing.
+ * On unsuccessful unlock, retry by calling handleUnlockOrInstall.
+ */
private class UnlockDialog implements TextWatcher,
DialogInterface.OnClickListener, DialogInterface.OnDismissListener
{
@@ -214,7 +357,18 @@ public class CredentialStorage extends Activity {
private UnlockDialog() {
View view = View.inflate(CredentialStorage.this, R.layout.credentials_dialog, null);
- ((TextView) view.findViewById(R.id.hint)).setText(R.string.credentials_unlock_hint);
+ CharSequence text;
+ if (mRetriesRemaining == -1) {
+ text = getResources().getText(R.string.credentials_unlock_hint);
+ } else if (mRetriesRemaining > 3) {
+ text = getResources().getText(R.string.credentials_wrong_password);
+ } else if (mRetriesRemaining == 1) {
+ text = getResources().getText(R.string.credentials_reset_warning);
+ } else {
+ text = getString(R.string.credentials_reset_warning_plural, mRetriesRemaining);
+ }
+
+ ((TextView) view.findViewById(R.id.hint)).setText(text);
mOldPassword = (TextView) view.findViewById(R.id.old_password);
mOldPassword.setVisibility(View.VISIBLE);
mOldPassword.addTextChangedListener(this);
@@ -253,26 +407,26 @@ public class CredentialStorage extends Activity {
mKeyStore.unlock(mOldPassword.getText().toString());
int error = mKeyStore.getLastError();
if (error == KeyStore.NO_ERROR) {
+ mRetriesRemaining = -1;
Toast.makeText(CredentialStorage.this,
R.string.credentials_enabled,
Toast.LENGTH_SHORT).show();
- install();
+ // aha, now we are unlocked, switch to key guard.
+ // we'll end up back in onResume to install
+ ensureKeyGuard();
} else if (error == KeyStore.UNINITIALIZED) {
+ mRetriesRemaining = -1;
Toast.makeText(CredentialStorage.this,
R.string.credentials_erased,
Toast.LENGTH_SHORT).show();
+ // we are reset, we can now set new password with key guard
+ handleUnlockOrInstall();
} else if (error >= KeyStore.WRONG_PASSWORD) {
- int count = error - KeyStore.WRONG_PASSWORD + 1;
- if (count > 3) {
- mError.setText(R.string.credentials_wrong_password);
- } else if (count == 1) {
- mError.setText(R.string.credentials_reset_warning);
- } else {
- mError.setText(getString(R.string.credentials_reset_warning_plural, count));
- }
- ((AlertDialog) dialog).show();
- return;
+ // we need to try again
+ mRetriesRemaining = error - KeyStore.WRONG_PASSWORD + 1;
+ handleUnlockOrInstall();
}
+ return;
}
finish();
}
diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java
index 20fd652..a9002fa 100644
--- a/src/com/android/settings/CryptKeeperSettings.java
+++ b/src/com/android/settings/CryptKeeperSettings.java
@@ -176,7 +176,7 @@ public class CryptKeeperSettings extends Fragment {
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK && data != null) {
- String password = data.getStringExtra("password");
+ String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
if (!TextUtils.isEmpty(password)) {
showFinalConfirmation(password);
}