Add credential storage settings.

* Changes
  + Initial implementation of credential storage settings.
  + Use alert icon on delete and reconnect dialogs in VpnSettings.
    (piggy-backed)
  Patch Set 12:
  + Add password length and no-space verification.
  + Simplify dialog view xml files.
This commit is contained in:
Hung-ying Tyan
2009-07-02 00:26:46 +08:00
parent 132b21440b
commit 7031ab0d75
7 changed files with 737 additions and 13 deletions

View File

@@ -30,18 +30,26 @@ import android.database.Cursor;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.security.Keystore;
import android.text.Html;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.widget.LockPatternUtils;
import android.telephony.TelephonyManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
@@ -52,7 +60,7 @@ public class SecuritySettings extends PreferenceActivity implements
DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
// Lock Settings
private static final String KEY_LOCK_ENABLED = "lockenabled";
private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
private static final String KEY_TACTILE_FEEDBACK_ENABLED = "tactilefeedback";
@@ -65,12 +73,31 @@ public class SecuritySettings extends PreferenceActivity implements
private Preference mChoosePattern;
private CheckBoxPreference mShowPassword;
// Location Settings
private static final String LOCATION_CATEGORY = "location_category";
private static final String LOCATION_NETWORK = "location_network";
private static final String LOCATION_GPS = "location_gps";
// Credential storage
private static final String ACTION_ADD_CREDENTIAL =
"android.security.ADD_CREDENTIAL";
private static final String ACTION_UNLOCK_CREDENTIAL_STORAGE =
"android.security.UNLOCK_CREDENTIAL_STORAGE";
private static final String KEY_CSTOR_TYPE_NAME = "typeName";
private static final String KEY_CSTOR_ITEM = "item";
private static final String KEY_CSTOR_NAMESPACE = "namespace";
private static final String KEY_CSTOR_DESCRIPTION = "description";
private static final int CSTOR_MIN_PASSWORD_LENGTH = 8;
private static final int CSTOR_INIT_DIALOG = 1;
private static final int CSTOR_CHANGE_PASSWORD_DIALOG = 2;
private static final int CSTOR_UNLOCK_DIALOG = 3;
private static final int CSTOR_RESET_DIALOG = 4;
private static final int CSTOR_NAME_CREDENTIAL_DIALOG = 5;
private CstorHelper mCstorHelper = new CstorHelper();
// Vendor specific
private static final String GSETTINGS_PROVIDER = "com.google.android.providers.settings";
private static final String USE_LOCATION = "use_location";
@@ -128,6 +155,8 @@ public class SecuritySettings extends PreferenceActivity implements
if (getIntent().getBooleanExtra("SHOW_USE_LOCATION", false) && !doneUseLocation) {
showUseLocationDialog(true);
}
mCstorHelper.handleCstorIntents(getIntent());
}
private PreferenceScreen createPreferenceHierarchy() {
@@ -165,7 +194,7 @@ public class SecuritySettings extends PreferenceActivity implements
mChoosePattern = getPreferenceManager().createPreferenceScreen(this);
mChoosePattern.setIntent(intent);
inlinePrefCat.addPreference(mChoosePattern);
int activePhoneType = TelephonyManager.getDefault().getPhoneType();
// do not display SIM lock for CDMA phone
@@ -178,7 +207,7 @@ public class SecuritySettings extends PreferenceActivity implements
intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.IccLockSettings");
simLockPreferences.setIntent(intent);
PreferenceCategory simLockCat = new PreferenceCategory(this);
simLockCat.setTitle(R.string.sim_lock_settings_title);
root.addPreference(simLockCat);
@@ -189,14 +218,22 @@ public class SecuritySettings extends PreferenceActivity implements
PreferenceCategory passwordsCat = new PreferenceCategory(this);
passwordsCat.setTitle(R.string.security_passwords_title);
root.addPreference(passwordsCat);
CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this);
showPassword.setKey("show_password");
showPassword.setTitle(R.string.show_password);
showPassword.setSummary(R.string.show_password_summary);
showPassword.setPersistent(false);
passwordsCat.addPreference(showPassword);
// Credential storage
PreferenceCategory credStoreCat = new PreferenceCategory(this);
credStoreCat.setTitle(R.string.cstor_settings_category);
root.addPreference(credStoreCat);
credStoreCat.addPreference(mCstorHelper.createAccessCheckBox());
credStoreCat.addPreference(mCstorHelper.createSetPasswordPreference());
credStoreCat.addPreference(mCstorHelper.createResetPreference());
return root;
}
@@ -217,7 +254,7 @@ public class SecuritySettings extends PreferenceActivity implements
R.string.lockpattern_settings_change_lock_pattern :
R.string.lockpattern_settings_choose_lock_pattern;
mChoosePattern.setTitle(chooseStringRes);
mShowPassword
.setChecked(Settings.System.getInt(getContentResolver(),
Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
@@ -383,4 +420,491 @@ public class SecuritySettings extends PreferenceActivity implements
mUseLocation.setChecked(false);
}
}
@Override
protected Dialog onCreateDialog (int id) {
switch (id) {
case CSTOR_INIT_DIALOG:
case CSTOR_CHANGE_PASSWORD_DIALOG:
return mCstorHelper.createSetPasswordDialog(id);
case CSTOR_UNLOCK_DIALOG:
return mCstorHelper.createUnlockDialog();
case CSTOR_RESET_DIALOG:
return mCstorHelper.createResetDialog();
case CSTOR_NAME_CREDENTIAL_DIALOG:
return mCstorHelper.createNameCredentialDialog();
default:
return null;
}
}
private class CstorHelper implements
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private Keystore mKeystore = Keystore.getInstance();
private View mView;
private int mDialogId;
private boolean mConfirm = true;
private CheckBoxPreference mAccessCheckBox;
private Preference mResetButton;
private Intent mSpecialIntent;
private CstorAddCredentialHelper mCstorAddCredentialHelper;
void handleCstorIntents(Intent intent) {
if (intent == null) return;
String action = intent.getAction();
if (ACTION_ADD_CREDENTIAL.equals(action)) {
mCstorAddCredentialHelper = new CstorAddCredentialHelper(intent);
showDialog(CSTOR_NAME_CREDENTIAL_DIALOG);
} else if (ACTION_UNLOCK_CREDENTIAL_STORAGE.equals(action)) {
mSpecialIntent = intent;
showDialog(mCstorHelper.isCstorInitialized()
? CSTOR_UNLOCK_DIALOG
: CSTOR_INIT_DIALOG);
}
}
private boolean isCstorUnlocked() {
return (mKeystore.getState() == Keystore.UNLOCKED);
}
private boolean isCstorInitialized() {
return (mKeystore.getState() != Keystore.UNINITIALIZED);
}
private void lockCstor() {
mKeystore.lock();
mAccessCheckBox.setChecked(false);
}
private int unlockCstor(String passwd) {
int ret = mKeystore.unlock(passwd);
if (ret == -1) resetCstor();
if (ret == 0) {
Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
Toast.LENGTH_SHORT).show();
}
return ret;
}
private int changeCstorPassword(String oldPasswd, String newPasswd) {
int ret = mKeystore.changePassword(oldPasswd, newPasswd);
if (ret == -1) resetCstor();
return ret;
}
private void initCstor(String passwd) {
mKeystore.setPassword(passwd);
enablePreferences(true);
mAccessCheckBox.setChecked(true);
Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
Toast.LENGTH_SHORT).show();
}
private void resetCstor() {
mKeystore.reset();
enablePreferences(false);
mAccessCheckBox.setChecked(false);
}
private void addCredential() {
String message = String.format(getString(R.string.cstor_is_added),
mCstorAddCredentialHelper.getName());
Toast.makeText(SecuritySettings.this, message, Toast.LENGTH_SHORT)
.show();
}
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
if (mCstorAddCredentialHelper != null) finish();
return;
}
switch (mDialogId) {
case CSTOR_INIT_DIALOG:
case CSTOR_CHANGE_PASSWORD_DIALOG:
mConfirm = checkPasswords((Dialog) dialog);
break;
case CSTOR_UNLOCK_DIALOG:
mConfirm = checkUnlockPassword((Dialog) dialog);
break;
case CSTOR_RESET_DIALOG:
resetCstor();
break;
case CSTOR_NAME_CREDENTIAL_DIALOG:
mConfirm = checkAddCredential();
break;
}
}
public void onDismiss(DialogInterface dialog) {
if (!mConfirm) {
mConfirm = true;
showDialog(mDialogId);
} else {
removeDialog(mDialogId);
if (mCstorAddCredentialHelper != null) {
if (!isCstorInitialized()) {
showDialog(CSTOR_INIT_DIALOG);
} else if (!isCstorUnlocked()) {
showDialog(CSTOR_UNLOCK_DIALOG);
} else {
addCredential();
finish();
}
} else if (mSpecialIntent != null) {
finish();
}
}
}
private void showResetWarning(int count) {
TextView v = showError(count <= 3
? R.string.cstor_password_error_reset_warning
: R.string.cstor_password_error);
if (count <= 3) {
if (count == 1) {
v.setText(getString(
R.string.cstor_password_error_reset_warning));
} else {
String format = getString(
R.string.cstor_password_error_reset_warning_plural);
v.setText(String.format(format, count));
}
}
}
private boolean checkAddCredential() {
hideError();
String name = getText(R.id.cstor_credential_name);
if (TextUtils.isEmpty(name)) {
showError(R.string.cstor_name_empty_error);
return false;
}
for (int i = 0, len = name.length(); i < len; i++) {
if (!Character.isLetterOrDigit(name.charAt(i))) {
showError(R.string.cstor_name_char_error);
return false;
}
}
mCstorAddCredentialHelper.setName(name);
return true;
}
// returns true if the password is long enough and does not contain
// characters that we don't like
private boolean verifyPassword(String passwd) {
if (passwd == null) {
showError(R.string.cstor_passwords_empty_error);
return false;
} else if ((passwd.length() < CSTOR_MIN_PASSWORD_LENGTH)
|| passwd.contains(" ")) {
showError(R.string.cstor_password_verification_error);
return false;
} else {
return true;
}
}
// returns true if the password is ok
private boolean checkUnlockPassword(Dialog d) {
hideError();
String passwd = getText(R.id.cstor_password);
if (TextUtils.isEmpty(passwd)) {
showError(R.string.cstor_password_empty_error);
return false;
}
int count = unlockCstor(passwd);
if (count > 0) {
showResetWarning(count);
return false;
} else {
// done or reset
return true;
}
}
// returns true if the passwords are ok
private boolean checkPasswords(Dialog d) {
hideError();
String oldPasswd = getText(R.id.cstor_old_password);
String newPasswd = getText(R.id.cstor_new_password);
String confirmPasswd = getText(R.id.cstor_confirm_password);
if ((mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG)
&& TextUtils.isEmpty(oldPasswd)) {
showError(R.string.cstor_password_empty_error);
return false;
}
if (TextUtils.isEmpty(newPasswd)
&& TextUtils.isEmpty(confirmPasswd)) {
showError(R.string.cstor_passwords_empty_error);
return false;
}
if (!verifyPassword(newPasswd)) {
return false;
} else if (!newPasswd.equals(confirmPasswd)) {
showError(R.string.cstor_passwords_error);
return false;
}
if (mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG) {
int count = changeCstorPassword(oldPasswd, newPasswd);
if (count > 0) {
showResetWarning(count);
return false;
} else {
// done or reset
return true;
}
} else {
initCstor(newPasswd);
return true;
}
}
private TextView showError(int messageId) {
TextView v = (TextView) mView.findViewById(R.id.cstor_error);
v.setText(messageId);
if (v != null) v.setVisibility(View.VISIBLE);
return v;
}
private void hideError() {
View v = mView.findViewById(R.id.cstor_error);
if (v != null) v.setVisibility(View.GONE);
}
private String getText(int viewId) {
return ((TextView) mView.findViewById(viewId)).getText().toString();
}
private void setText(int viewId, String text) {
TextView v = (TextView) mView.findViewById(viewId);
if (v != null) v.setText(text);
}
private void enablePreferences(boolean enabled) {
mAccessCheckBox.setEnabled(enabled);
mResetButton.setEnabled(enabled);
}
private Preference createAccessCheckBox() {
CheckBoxPreference pref = new CheckBoxPreference(
SecuritySettings.this);
pref.setTitle(R.string.cstor_access_title);
pref.setSummary(R.string.cstor_access_summary);
pref.setChecked(isCstorUnlocked());
pref.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(
Preference pref, Object value) {
if (((Boolean) value)) {
showDialog(isCstorInitialized()
? CSTOR_UNLOCK_DIALOG
: CSTOR_INIT_DIALOG);
} else {
lockCstor();
}
return true;
}
});
pref.setEnabled(isCstorInitialized());
mAccessCheckBox = pref;
return pref;
}
private Preference createSetPasswordPreference() {
Preference pref = new Preference(SecuritySettings.this);
pref.setTitle(R.string.cstor_set_passwd_title);
pref.setSummary(R.string.cstor_set_passwd_summary);
pref.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference pref) {
showDialog(isCstorInitialized()
? CSTOR_CHANGE_PASSWORD_DIALOG
: CSTOR_INIT_DIALOG);
return true;
}
});
return pref;
}
private Preference createResetPreference() {
Preference pref = new Preference(SecuritySettings.this);
pref.setTitle(R.string.cstor_reset_title);
pref.setSummary(R.string.cstor_reset_summary);
pref.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference pref) {
showDialog(CSTOR_RESET_DIALOG);
return true;
}
});
pref.setEnabled(isCstorInitialized());
mResetButton = pref;
return pref;
}
private Dialog createUnlockDialog() {
mDialogId = CSTOR_UNLOCK_DIALOG;
mView = View.inflate(SecuritySettings.this,
R.layout.cstor_unlock_dialog_view, null);
hideError();
Dialog d = new AlertDialog.Builder(SecuritySettings.this)
.setView(mView)
.setTitle(R.string.cstor_access_dialog_title)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this)
.setCancelable(false)
.create();
d.setOnDismissListener(this);
return d;
}
private Dialog createSetPasswordDialog(int id) {
mDialogId = id;
mView = View.inflate(SecuritySettings.this,
R.layout.cstor_set_password_dialog_view, null);
hideError();
switch (id) {
case CSTOR_INIT_DIALOG:
mView.findViewById(R.id.cstor_old_password_block)
.setVisibility(View.GONE);
break;
case CSTOR_CHANGE_PASSWORD_DIALOG:
mView.findViewById(R.id.cstor_first_time_hint)
.setVisibility(View.GONE);
break;
default:
throw new RuntimeException(
"Unknown dialog id: " + mDialogId);
}
Dialog d = new AlertDialog.Builder(SecuritySettings.this)
.setView(mView)
.setTitle(R.string.cstor_set_passwd_dialog_title)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this)
.setCancelable(false)
.create();
d.setOnDismissListener(this);
return d;
}
private Dialog createResetDialog() {
mDialogId = CSTOR_RESET_DIALOG;
return new AlertDialog.Builder(SecuritySettings.this)
.setTitle(android.R.string.dialog_alert_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.cstor_reset_hint)
.setPositiveButton(getString(android.R.string.ok), this)
.setNegativeButton(getString(android.R.string.cancel), this)
.create();
}
private Dialog createNameCredentialDialog() {
mDialogId = CSTOR_NAME_CREDENTIAL_DIALOG;
mView = View.inflate(SecuritySettings.this,
R.layout.cstor_name_credential_dialog_view, null);
hideError();
setText(R.id.cstor_credential_name_title,
getString(R.string.cstor_credential_name));
setText(R.id.cstor_credential_info_title,
getString(R.string.cstor_credential_info));
setText(R.id.cstor_credential_info,
mCstorAddCredentialHelper.getDescription().toString());
Dialog d = new AlertDialog.Builder(SecuritySettings.this)
.setView(mView)
.setTitle(R.string.cstor_name_credential_dialog_title)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this)
.setCancelable(false)
.create();
d.setOnDismissListener(this);
return d;
}
}
private class CstorAddCredentialHelper {
private String mTypeName;
private List<byte[]> mItemList;
private List<String> mNamespaceList;
private String mDescription;
private String mName;
CstorAddCredentialHelper(Intent intent) {
parse(intent);
}
String getTypeName() {
return mTypeName;
}
byte[] getItem(int i) {
return mItemList.get(i);
}
String getNamespace(int i) {
return mNamespaceList.get(i);
}
CharSequence getDescription() {
return Html.fromHtml(mDescription);
}
void setName(String name) {
mName = name;
}
String getName() {
return mName;
}
private void parse(Intent intent) {
mTypeName = intent.getStringExtra(KEY_CSTOR_TYPE_NAME);
mItemList = new ArrayList<byte[]>();
mNamespaceList = new ArrayList<String>();
for (int i = 0; ; i++) {
byte[] blob = intent.getByteArrayExtra(KEY_CSTOR_ITEM + i);
if (blob == null) break;
mItemList.add(blob);
mNamespaceList.add(intent.getStringExtra(
KEY_CSTOR_NAMESPACE + i));
}
// build description string
StringBuilder sb = new StringBuilder();
for (int i = 0; ; i++) {
String s = intent.getStringExtra(KEY_CSTOR_DESCRIPTION + i);
if (s == null) break;
sb.append(s).append("<br>");
}
mDescription = sb.toString();
}
}
}