diff --git a/res/values/strings.xml b/res/values/strings.xml index f63a0a6e513..b96f3ee1479 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1829,7 +1829,7 @@ found in the list of installed applications. Yes No Back - No, it's a mistake + No Save Cancel @@ -1935,6 +1935,10 @@ found in the list of installed applications. VPN settings Set up & manage Virtual Private Networks (VPNs) + + (unchanged) + + (not set) Credential storage diff --git a/src/com/android/settings/vpn/L2tpEditor.java b/src/com/android/settings/vpn/L2tpEditor.java index 643ba3b61f2..29036f23b88 100644 --- a/src/com/android/settings/vpn/L2tpEditor.java +++ b/src/com/android/settings/vpn/L2tpEditor.java @@ -30,9 +30,7 @@ import android.preference.PreferenceGroup; */ class L2tpEditor extends VpnProfileEditor { private CheckBoxPreference mSecret; - private EditTextPreference mSecretString; - private String mOriginalSecret; - private boolean mOriginalSecretEnabled; + private SecretHandler mSecretHandler; public L2tpEditor(L2tpProfile p) { super(p); @@ -43,11 +41,8 @@ class L2tpEditor extends VpnProfileEditor { Context c = subpanel.getContext(); subpanel.addPreference(createSecretPreference(c)); subpanel.addPreference(createSecretStringPreference(c)); - mSecretString.setEnabled(mSecret.isChecked()); L2tpProfile profile = (L2tpProfile) getProfile(); - mOriginalSecret = profile.getSecretString(); - mOriginalSecretEnabled = profile.isSecretEnabled(); } @Override @@ -55,9 +50,7 @@ class L2tpEditor extends VpnProfileEditor { String result = super.validate(); if (!mSecret.isChecked()) return result; - return ((result != null) - ? result - : validate(mSecretString, R.string.vpn_a_l2tp_secret)); + return ((result != null) ? result : mSecretHandler.validate()); } private Preference createSecretPreference(Context c) { @@ -73,7 +66,7 @@ class L2tpEditor extends VpnProfileEditor { Preference pref, Object newValue) { boolean enabled = (Boolean) newValue; profile.setSecretEnabled(enabled); - mSecretString.setEnabled(enabled); + mSecretHandler.getPreference().setEnabled(enabled); setSecretTitle(mSecret, R.string.vpn_l2tp_secret, enabled); setSecretSummary(mSecret, enabled); @@ -84,22 +77,22 @@ class L2tpEditor extends VpnProfileEditor { } private Preference createSecretStringPreference(Context c) { - final L2tpProfile profile = (L2tpProfile) getProfile(); - mSecretString = createSecretPreference(c, + SecretHandler sHandler = mSecretHandler = new SecretHandler(c, R.string.vpn_l2tp_secret_string_title, - R.string.vpn_l2tp_secret, - profile.getSecretString(), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - profile.setSecretString((String) newValue); - setSecretSummary(mSecretString, - R.string.vpn_l2tp_secret, - (String) newValue); - return true; - } - }); - return mSecretString; + R.string.vpn_l2tp_secret) { + @Override + protected String getSecretFromProfile() { + return ((L2tpProfile) getProfile()).getSecretString(); + } + + @Override + protected void saveSecretToProfile(String secret) { + ((L2tpProfile) getProfile()).setSecretString(secret); + } + }; + Preference pref = sHandler.getPreference(); + pref.setEnabled(mSecret.isChecked()); + return pref; } private void setSecretSummary(CheckBoxPreference secret, boolean enabled) { diff --git a/src/com/android/settings/vpn/L2tpIpsecPskEditor.java b/src/com/android/settings/vpn/L2tpIpsecPskEditor.java index 11590daaa5b..1277c282b6e 100644 --- a/src/com/android/settings/vpn/L2tpIpsecPskEditor.java +++ b/src/com/android/settings/vpn/L2tpIpsecPskEditor.java @@ -29,6 +29,7 @@ import android.preference.PreferenceGroup; */ class L2tpIpsecPskEditor extends L2tpEditor { private EditTextPreference mPresharedKey; + private SecretHandler mPskHandler; public L2tpIpsecPskEditor(L2tpIpsecPskProfile p) { super(p); @@ -45,27 +46,23 @@ class L2tpIpsecPskEditor extends L2tpEditor { public String validate() { String result = super.validate(); - return ((result != null) - ? result - : validate(mPresharedKey, R.string.vpn_a_ipsec_presharedkey)); + return ((result != null) ? result : mPskHandler.validate()); } private Preference createPresharedKeyPreference(Context c) { - final L2tpIpsecPskProfile profile = (L2tpIpsecPskProfile) getProfile(); - mPresharedKey = createSecretPreference(c, + SecretHandler pskHandler = mPskHandler = new SecretHandler(c, R.string.vpn_ipsec_presharedkey_title, - R.string.vpn_ipsec_presharedkey, - profile.getPresharedKey(), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - profile.setPresharedKey((String) newValue); - setSecretSummary(mPresharedKey, - R.string.vpn_ipsec_presharedkey, - (String) newValue); - return true; - } - }); - return mPresharedKey; + R.string.vpn_ipsec_presharedkey) { + @Override + protected String getSecretFromProfile() { + return ((L2tpIpsecPskProfile) getProfile()).getPresharedKey(); + } + + @Override + protected void saveSecretToProfile(String secret) { + ((L2tpIpsecPskProfile) getProfile()).setPresharedKey(secret); + } + }; + return pskHandler.getPreference(); } } diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java index b4dc2b688a8..162c129956e 100644 --- a/src/com/android/settings/vpn/VpnEditor.java +++ b/src/com/android/settings/vpn/VpnEditor.java @@ -27,6 +27,7 @@ import android.net.vpn.L2tpProfile; import android.net.vpn.VpnProfile; import android.net.vpn.VpnType; import android.os.Bundle; +import android.os.Parcel; import android.os.Parcelable; import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; @@ -47,6 +48,7 @@ public class VpnEditor extends PreferenceActivity { private VpnProfileEditor mProfileEditor; private boolean mAddingProfile; + private byte[] mOriginalProfileData; @Override public void onCreate(Bundle savedInstanceState) { @@ -61,6 +63,10 @@ public class VpnEditor extends PreferenceActivity { addPreferencesFromResource(R.xml.vpn_edit); initViewFor(p); + + Parcel parcel = Parcel.obtain(); + p.writeToParcel(parcel, 0); + mOriginalProfileData = parcel.marshall(); } @Override @@ -90,7 +96,11 @@ public class VpnEditor extends PreferenceActivity { return true; case MENU_CANCEL: - showCancellationConfirmDialog(); + if (profileChanged()) { + showCancellationConfirmDialog(); + } else { + finish(); + } return true; } return super.onOptionsItemSelected(item); @@ -131,7 +141,7 @@ public class VpnEditor extends PreferenceActivity { return false; } - setResult(getProfile()); + if (profileChanged()) setResult(getProfile()); return true; } @@ -177,4 +187,17 @@ public class VpnEditor extends PreferenceActivity { private VpnProfile getProfile() { return mProfileEditor.getProfile(); } + + private boolean profileChanged() { + Parcel newParcel = Parcel.obtain(); + getProfile().writeToParcel(newParcel, 0); + byte[] newData = newParcel.marshall(); + if (mOriginalProfileData.length == newData.length) { + for (int i = 0, n = mOriginalProfileData.length; i < n; i++) { + if (mOriginalProfileData[i] != newData[i]) return true; + } + return false; + } + return true; + } } diff --git a/src/com/android/settings/vpn/VpnProfileEditor.java b/src/com/android/settings/vpn/VpnProfileEditor.java index bf2e57dba8a..bf51749d89c 100644 --- a/src/com/android/settings/vpn/VpnProfileEditor.java +++ b/src/com/android/settings/vpn/VpnProfileEditor.java @@ -33,9 +33,6 @@ import android.text.method.PasswordTransformationMethod; * The common class for editing {@link VpnProfile}. */ class VpnProfileEditor { - static final String SECRET_SET_INDICATOR = - new String(new byte[] {(byte) 1, (byte) 0}); - private static final String KEY_VPN_NAME = "vpn_name"; private EditTextPreference mName; @@ -147,22 +144,6 @@ class VpnProfileEditor { return pref; } - protected EditTextPreference createSecretPreference(Context c, int titleId, - int fieldNameId, String value, - Preference.OnPreferenceChangeListener listener) { - EditTextPreference pref = new EditTextPreference(c); - pref.setTitle(titleId); - pref.setDialogTitle(titleId); - pref.getEditText().setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); - pref.getEditText().setTransformationMethod( - new PasswordTransformationMethod()); - pref.setText(TextUtils.isEmpty(value) ? "" : SECRET_SET_INDICATOR); - setSecretSummary(pref, fieldNameId, value); - pref.setPersistent(true); - pref.setOnPreferenceChangeListener(listener); - return pref; - } - protected String validate(Preference pref, int fieldNameId) { Context c = pref.getContext(); String value = (pref instanceof EditTextPreference) @@ -191,15 +172,6 @@ class VpnProfileEditor { : v); } - protected void setSecretSummary(Preference pref, int fieldNameId, - String value) { - Context c = pref.getContext(); - String formatString = TextUtils.isEmpty(value) - ? c.getString(R.string.vpn_field_not_set) - : c.getString(R.string.vpn_field_is_set); - pref.setSummary(String.format(formatString, c.getString(fieldNameId))); - } - protected void setSecretTitle( CheckBoxPreference pref, int fieldNameId, boolean enabled) { Context c = pref.getContext(); @@ -215,4 +187,69 @@ class VpnProfileEditor { getProfile().setName(newName); setSummary(mName, R.string.vpn_name, newName); } + + // Secret is tricky to handle because empty field may mean "not set" or + // "unchanged". This class hides that logic from callers. + protected static abstract class SecretHandler { + private EditTextPreference mPref; + private int mFieldNameId; + private boolean mHadSecret; + + protected SecretHandler(Context c, int titleId, int fieldNameId) { + String value = getSecretFromProfile(); + mHadSecret = !TextUtils.isEmpty(value); + mFieldNameId = fieldNameId; + + EditTextPreference pref = mPref = new EditTextPreference(c); + pref.setTitle(titleId); + pref.setDialogTitle(titleId); + pref.getEditText().setInputType( + InputType.TYPE_TEXT_VARIATION_PASSWORD); + pref.getEditText().setTransformationMethod( + new PasswordTransformationMethod()); + pref.setText(""); + pref.getEditText().setHint(mHadSecret + ? R.string.vpn_secret_unchanged + : R.string.vpn_secret_not_set); + setSecretSummary(value); + pref.setPersistent(true); + saveSecretToProfile(""); + pref.setOnPreferenceChangeListener( + new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange( + Preference pref, Object newValue) { + saveSecretToProfile((String) newValue); + setSecretSummary((String) newValue); + return true; + } + }); + } + + protected EditTextPreference getPreference() { + return mPref; + } + + protected String validate() { + Context c = mPref.getContext(); + String value = mPref.getText(); + return ((TextUtils.isEmpty(value) && !mHadSecret) + ? String.format( + c.getString(R.string.vpn_error_miss_entering), + c.getString(mFieldNameId)) + : null); + } + + private void setSecretSummary(String value) { + EditTextPreference pref = mPref; + Context c = pref.getContext(); + String formatString = (TextUtils.isEmpty(value) && !mHadSecret) + ? c.getString(R.string.vpn_field_not_set) + : c.getString(R.string.vpn_field_is_set); + pref.setSummary( + String.format(formatString, c.getString(mFieldNameId))); + } + + protected abstract String getSecretFromProfile(); + protected abstract void saveSecretToProfile(String secret); + } } diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java index f0e71f782eb..137e42049ca 100644 --- a/src/com/android/settings/vpn/VpnSettings.java +++ b/src/com/android/settings/vpn/VpnSettings.java @@ -18,7 +18,6 @@ package com.android.settings.vpn; import com.android.settings.R; import com.android.settings.SecuritySettings; -import static com.android.settings.vpn.VpnProfileEditor.SECRET_SET_INDICATOR; import android.app.AlertDialog; import android.app.Dialog; @@ -197,7 +196,7 @@ public class VpnSettings extends PreferenceActivity implements return createConnectDialog(); case DIALOG_RECONNECT: - return createReconnectDialogBuilder().create(); + return createReconnectDialog(); case DIALOG_AUTH_ERROR: return createAuthErrorDialog(); @@ -219,14 +218,44 @@ public class VpnSettings extends PreferenceActivity implements this) .setNegativeButton(getString(android.R.string.cancel), this) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + removeDialog(DIALOG_CONNECT); + onIdle(); + } + }) .create(); } - private AlertDialog.Builder createReconnectDialogBuilder() { + private Dialog createReconnectDialog() { + return createCommonDialogBuilder() + .setMessage(R.string.vpn_confirm_reconnect) + .create(); + } + + private Dialog createAuthErrorDialog() { + return createCommonDialogBuilder() + .setMessage(R.string.vpn_auth_error_dialog_msg) + .create(); + } + private Dialog createUnknownServerDialog() { + return createCommonDialogBuilder() + .setMessage(R.string.vpn_unknown_server_dialog_msg) + .setPositiveButton(R.string.vpn_yes_button, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int w) { + VpnProfile p = mConnectingActor.getProfile(); + onIdle(); + startVpnEditor(p); + } + }) + .create(); + } + + private AlertDialog.Builder createCommonDialogBuilder() { return new AlertDialog.Builder(this) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(R.string.vpn_confirm_reconnect) .setPositiveButton(R.string.vpn_yes_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int w) { @@ -246,26 +275,6 @@ public class VpnSettings extends PreferenceActivity implements }); } - private Dialog createAuthErrorDialog() { - return createReconnectDialogBuilder() - .setMessage(R.string.vpn_auth_error_dialog_msg) - .create(); - } - - private Dialog createUnknownServerDialog() { - return createReconnectDialogBuilder() - .setMessage(R.string.vpn_unknown_server_dialog_msg) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - VpnProfile p = mConnectingActor.getProfile(); - onIdle(); - startVpnEditor(p); - } - }) - .create(); - } - @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { @@ -413,6 +422,7 @@ public class VpnSettings extends PreferenceActivity implements } } else { removeDialog(DIALOG_CONNECT); + onIdle(); } } @@ -707,6 +717,7 @@ public class VpnSettings extends PreferenceActivity implements } private void onIdle() { + Log.d(TAG, " onIdle()"); mActiveProfile = null; mConnectingActor = null; enableProfilePreferences(); @@ -853,28 +864,28 @@ public class VpnSettings extends PreferenceActivity implements case L2TP_IPSEC_PSK: L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; String presharedKey = pskProfile.getPresharedKey(); - if (!presharedKey.equals(SECRET_SET_INDICATOR)) { - String keyName = KEY_PREFIX_IPSEC_PSK + p.getId(); + String keyName = KEY_PREFIX_IPSEC_PSK + p.getId(); + if (!TextUtils.isEmpty(presharedKey)) { int ret = ks.put(NAMESPACE_VPN, keyName, presharedKey); - if (ret < 0) { + if (ret != 0) { Log.e(TAG, "keystore write failed: key=" + keyName); } - pskProfile.setPresharedKey(keyNameForDaemon(keyName)); } + pskProfile.setPresharedKey(keyNameForDaemon(keyName)); // pass through case L2TP: L2tpProfile l2tpProfile = (L2tpProfile) p; - String keyName = KEY_PREFIX_L2TP_SECRET + p.getId(); + keyName = KEY_PREFIX_L2TP_SECRET + p.getId(); if (l2tpProfile.isSecretEnabled()) { String secret = l2tpProfile.getSecretString(); - if (!secret.equals(SECRET_SET_INDICATOR)) { + if (!TextUtils.isEmpty(secret)) { int ret = ks.put(NAMESPACE_VPN, keyName, secret); - if (ret < 0) { + if (ret != 0) { Log.e(TAG, "keystore write failed: key=" + keyName); } - l2tpProfile.setSecretString(keyNameForDaemon(keyName)); } + l2tpProfile.setSecretString(keyNameForDaemon(keyName)); } else { ks.remove(NAMESPACE_VPN, keyName); }