diff options
Diffstat (limited to 'src/com/android/settings/vpn/VpnSettings.java')
-rw-r--r-- | src/com/android/settings/vpn/VpnSettings.java | 1108 |
1 files changed, 0 insertions, 1108 deletions
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java deleted file mode 100644 index 5d75b6a..0000000 --- a/src/com/android/settings/vpn/VpnSettings.java +++ /dev/null @@ -1,1108 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.vpn; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.vpn.L2tpIpsecProfile; -import android.net.vpn.L2tpIpsecPskProfile; -import android.net.vpn.L2tpProfile; -import android.net.vpn.VpnManager; -import android.net.vpn.VpnProfile; -import android.net.vpn.VpnState; -import android.net.vpn.VpnType; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.preference.PreferenceScreen; -import android.preference.Preference.OnPreferenceClickListener; -import android.security.Credentials; -import android.security.KeyStore; -import android.text.TextUtils; -import android.util.Log; -import android.view.ContextMenu; -import android.view.MenuItem; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.AdapterView.AdapterContextMenuInfo; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * The preference activity for configuring VPN settings. - */ -public class VpnSettings extends SettingsPreferenceFragment - implements DialogInterface.OnClickListener { - - private static final boolean DEBUG = false; - - // Key to the field exchanged for profile editing. - static final String KEY_VPN_PROFILE = "vpn_profile"; - - // Key to the field exchanged for VPN type selection. - static final String KEY_VPN_TYPE = "vpn_type"; - - private static final String TAG = VpnSettings.class.getSimpleName(); - - private static final String PREF_ADD_VPN = "add_new_vpn"; - private static final String PREF_VPN_LIST = "vpn_list"; - - private static final String PROFILES_ROOT = VpnManager.getProfilePath() + "/"; - private static final String PROFILE_OBJ_FILE = ".pobj"; - - private static final String KEY_ACTIVE_PROFILE = "ActiveProfile"; - private static final String KEY_PROFILE_CONNECTING = "ProfileConnecting"; - private static final String KEY_CONNECT_DIALOG_SHOWING = "ConnectDialogShowing"; - - private static final int REQUEST_ADD_OR_EDIT_PROFILE = 1; - static final int REQUEST_SELECT_VPN_TYPE = 2; - - private static final int CONTEXT_MENU_CONNECT_ID = ContextMenu.FIRST + 0; - private static final int CONTEXT_MENU_DISCONNECT_ID = ContextMenu.FIRST + 1; - private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2; - private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3; - - private static final int CONNECT_BUTTON = DialogInterface.BUTTON_POSITIVE; - private static final int OK_BUTTON = DialogInterface.BUTTON_POSITIVE; - - private static final int DIALOG_CONNECT = VpnManager.VPN_ERROR_LARGEST + 1; - private static final int DIALOG_SECRET_NOT_SET = DIALOG_CONNECT + 1; - - private static final int NO_ERROR = VpnManager.VPN_ERROR_NO_ERROR; - - private static final String KEY_PREFIX_IPSEC_PSK = Credentials.VPN + 'i'; - private static final String KEY_PREFIX_L2TP_SECRET = Credentials.VPN + 'l'; - - private static List<VpnProfile> sVpnProfileList = new ArrayList<VpnProfile>(); - - private PreferenceScreen mAddVpn; - private PreferenceCategory mVpnListContainer; - - // profile name --> VpnPreference - private Map<String, VpnPreference> mVpnPreferenceMap; - - // profile engaged in a connection - private VpnProfile mActiveProfile; - - // actor engaged in connecting - private VpnProfileActor mConnectingActor; - - // states saved for unlocking keystore - private Runnable mUnlockAction; - - private KeyStore mKeyStore = KeyStore.getInstance(); - - private VpnManager mVpnManager; - - private ConnectivityReceiver mConnectivityReceiver = - new ConnectivityReceiver(); - - private int mConnectingErrorCode = NO_ERROR; - - private Dialog mShowingDialog; - - private boolean mConnectDialogShowing = false; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.vpn_settings); - - mVpnManager = new VpnManager(getActivity()); - // restore VpnProfile list and construct VpnPreference map - mVpnListContainer = (PreferenceCategory) findPreference(PREF_VPN_LIST); - - // set up the "add vpn" preference - mAddVpn = (PreferenceScreen) findPreference(PREF_ADD_VPN); - mAddVpn.setOnPreferenceClickListener( - new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - startVpnTypeSelection(); - return true; - } - }); - - retrieveVpnListFromStorage(); - restoreInstanceState(savedInstanceState); - } - - @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - if (mActiveProfile != null) { - savedInstanceState.putString(KEY_ACTIVE_PROFILE, - mActiveProfile.getId()); - savedInstanceState.putBoolean(KEY_PROFILE_CONNECTING, - (mConnectingActor != null)); - savedInstanceState.putBoolean(KEY_CONNECT_DIALOG_SHOWING, - mConnectDialogShowing); - } - super.onSaveInstanceState(savedInstanceState); - } - - private void restoreInstanceState(Bundle savedInstanceState) { - if (savedInstanceState == null) return; - String profileId = savedInstanceState.getString(KEY_ACTIVE_PROFILE); - if (profileId != null) { - mActiveProfile = getProfile(getProfileIndexFromId(profileId)); - if (savedInstanceState.getBoolean(KEY_PROFILE_CONNECTING)) { - mConnectingActor = getActor(mActiveProfile); - } - mConnectDialogShowing = savedInstanceState.getBoolean(KEY_CONNECT_DIALOG_SHOWING); - } - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // for long-press gesture on a profile preference - registerForContextMenu(getListView()); - } - - @Override - public void onPause() { - // ignore vpn connectivity event - mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver); - if ((mShowingDialog != null) && mShowingDialog.isShowing()) { - mShowingDialog.dismiss(); - mShowingDialog = null; - } - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - updatePreferenceMap(); - - if (DEBUG) Log.d(TAG, "onResume"); - - // listen to vpn connectivity event - mVpnManager.registerConnectivityReceiver(mConnectivityReceiver); - - if ((mUnlockAction != null) && isKeyStoreUnlocked()) { - Runnable action = mUnlockAction; - mUnlockAction = null; - getActivity().runOnUiThread(action); - } - - if (!mConnectDialogShowing) { - // If mActiveProfile is not null but it's in IDLE state, then a - // retry dialog must be showing now as the previous connection - // attempt failed. In this case, don't call checkVpnConnectionStatus() - // as it will clean up mActiveProfile due to the IDLE state. - if ((mActiveProfile == null) - || (mActiveProfile.getState() != VpnState.IDLE)) { - checkVpnConnectionStatus(); - } - } else { - // Dismiss the connect dialog in case there is another instance - // trying to operate a vpn connection. - if (!mVpnManager.isIdle() || (mActiveProfile == null)) { - removeDialog(DIALOG_CONNECT); - checkVpnConnectionStatus(); - } - } - } - - @Override - public void onDestroyView() { - unregisterForContextMenu(getListView()); - // This should be called after the procedure above as ListView inside this Fragment - // will be deleted here. - super.onDestroyView(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - // Remove any onClick listeners - if (mVpnListContainer != null) { - for (int i = 0; i < mVpnListContainer.getPreferenceCount(); i++) { - mVpnListContainer.getPreference(i).setOnPreferenceClickListener(null); - } - } - } - - @Override - public Dialog onCreateDialog (int id) { - setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - if (mActiveProfile != null) { - changeState(mActiveProfile, VpnState.IDLE); - } - // Make sure onIdle() is called as the above changeState() - // may not be effective if the state is already IDLE. - // XXX: VpnService should broadcast non-IDLE state, say UNUSABLE, - // when an error occurs. - onIdle(); - } - }); - - switch (id) { - case DIALOG_CONNECT: - mConnectDialogShowing = true; - setOnDismissListener(new DialogInterface.OnDismissListener() { - public void onDismiss(DialogInterface dialog) { - mConnectDialogShowing = false; - } - }); - return createConnectDialog(); - - case DIALOG_SECRET_NOT_SET: - return createSecretNotSetDialog(); - - case VpnManager.VPN_ERROR_CHALLENGE: - case VpnManager.VPN_ERROR_UNKNOWN_SERVER: - case VpnManager.VPN_ERROR_PPP_NEGOTIATION_FAILED: - return createEditDialog(id); - - default: - Log.d(TAG, "create reconnect dialog for event " + id); - return createReconnectDialog(id); - } - } - - private Dialog createConnectDialog() { - final Activity activity = getActivity(); - return new AlertDialog.Builder(activity) - .setView(mConnectingActor.createConnectView()) - .setTitle(String.format(activity.getString(R.string.vpn_connect_to), - mConnectingActor.getProfile().getName())) - .setPositiveButton(activity.getString(R.string.vpn_connect_button), - this) - .setNegativeButton(activity.getString(android.R.string.cancel), - this) - .create(); - } - - private Dialog createReconnectDialog(int id) { - int msgId; - switch (id) { - case VpnManager.VPN_ERROR_AUTH: - msgId = R.string.vpn_auth_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_REMOTE_HUNG_UP: - msgId = R.string.vpn_remote_hung_up_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_CONNECTION_LOST: - msgId = R.string.vpn_reconnect_from_lost; - break; - - case VpnManager.VPN_ERROR_REMOTE_PPP_HUNG_UP: - msgId = R.string.vpn_remote_ppp_hung_up_error_dialog_msg; - break; - - default: - msgId = R.string.vpn_confirm_reconnect; - } - return createCommonDialogBuilder().setMessage(msgId).create(); - } - - private Dialog createEditDialog(int id) { - int msgId; - switch (id) { - case VpnManager.VPN_ERROR_CHALLENGE: - msgId = R.string.vpn_challenge_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_UNKNOWN_SERVER: - msgId = R.string.vpn_unknown_server_dialog_msg; - break; - - case VpnManager.VPN_ERROR_PPP_NEGOTIATION_FAILED: - msgId = R.string.vpn_ppp_negotiation_failed_dialog_msg; - break; - - default: - return null; - } - return createCommonEditDialogBuilder().setMessage(msgId).create(); - } - - private Dialog createSecretNotSetDialog() { - return createCommonDialogBuilder() - .setMessage(R.string.vpn_secret_not_set_dialog_msg) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(mActiveProfile, false); - } - }) - .create(); - } - - private AlertDialog.Builder createCommonEditDialogBuilder() { - return createCommonDialogBuilder() - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - VpnProfile p = mActiveProfile; - onIdle(); - startVpnEditor(p, false); - } - }); - } - - private AlertDialog.Builder createCommonDialogBuilder() { - return new AlertDialog.Builder(getActivity()) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - connectOrDisconnect(mActiveProfile); - } - }) - .setNegativeButton(R.string.vpn_no_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - onIdle(); - } - }); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - VpnProfile p = getProfile(getProfilePositionFrom( - (AdapterContextMenuInfo) menuInfo)); - if (p != null) { - VpnState state = p.getState(); - menu.setHeaderTitle(p.getName()); - - boolean isIdle = (state == VpnState.IDLE); - boolean isNotConnect = (isIdle || (state == VpnState.DISCONNECTING) - || (state == VpnState.CANCELLED)); - menu.add(0, CONTEXT_MENU_CONNECT_ID, 0, R.string.vpn_menu_connect) - .setEnabled(isIdle && (mActiveProfile == null)); - menu.add(0, CONTEXT_MENU_DISCONNECT_ID, 0, - R.string.vpn_menu_disconnect) - .setEnabled(state == VpnState.CONNECTED); - menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.vpn_menu_edit) - .setEnabled(isNotConnect); - menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.vpn_menu_delete) - .setEnabled(isNotConnect); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - int position = getProfilePositionFrom( - (AdapterContextMenuInfo) item.getMenuInfo()); - VpnProfile p = getProfile(position); - - switch(item.getItemId()) { - case CONTEXT_MENU_CONNECT_ID: - case CONTEXT_MENU_DISCONNECT_ID: - connectOrDisconnect(p); - return true; - - case CONTEXT_MENU_EDIT_ID: - startVpnEditor(p, false); - return true; - - case CONTEXT_MENU_DELETE_ID: - deleteProfile(position); - return true; - } - - return super.onContextItemSelected(item); - } - - @Override - public void onActivityResult(final int requestCode, final int resultCode, - final Intent data) { - - if (DEBUG) Log.d(TAG, "onActivityResult , result = " + resultCode + ", data = " + data); - if ((resultCode == Activity.RESULT_CANCELED) || (data == null)) { - Log.d(TAG, "no result returned by editor"); - return; - } - - if (requestCode == REQUEST_SELECT_VPN_TYPE) { - final String typeName = data.getStringExtra(KEY_VPN_TYPE); - startVpnEditor(createVpnProfile(typeName), true); - } else if (requestCode == REQUEST_ADD_OR_EDIT_PROFILE) { - VpnProfile p = data.getParcelableExtra(KEY_VPN_PROFILE); - if (p == null) { - Log.e(TAG, "null object returned by editor"); - return; - } - - final Activity activity = getActivity(); - int index = getProfileIndexFromId(p.getId()); - if (checkDuplicateName(p, index)) { - final VpnProfile profile = p; - Util.showErrorMessage(activity, String.format( - activity.getString(R.string.vpn_error_duplicate_name), - p.getName()), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(profile, false); - } - }); - return; - } - - if (needKeyStoreToSave(p)) { - Runnable action = new Runnable() { - public void run() { - onActivityResult(requestCode, resultCode, data); - } - }; - if (!unlockKeyStore(p, action)) return; - } - - try { - if (index < 0) { - addProfile(p); - Util.showShortToastMessage(activity, String.format( - activity.getString(R.string.vpn_profile_added), p.getName())); - } else { - replaceProfile(index, p); - Util.showShortToastMessage(activity, String.format( - activity.getString(R.string.vpn_profile_replaced), - p.getName())); - } - } catch (IOException e) { - final VpnProfile profile = p; - Util.showErrorMessage(activity, e + ": " + e.getMessage(), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(profile, false); - } - }); - } - - // Remove cached VpnEditor as it is needless anymore. - } else { - throw new RuntimeException("unknown request code: " + requestCode); - } - } - - // Called when the buttons on the connect dialog are clicked. - @Override - public synchronized void onClick(DialogInterface dialog, int which) { - if (which == CONNECT_BUTTON) { - Dialog d = (Dialog) dialog; - String error = mConnectingActor.validateInputs(d); - if (error == null) { - mConnectingActor.connect(d); - } else { - // show error dialog - final Activity activity = getActivity(); - mShowingDialog = new AlertDialog.Builder(activity) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(String.format(activity.getString( - R.string.vpn_error_miss_entering), error)) - .setPositiveButton(R.string.vpn_back_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - showDialog(DIALOG_CONNECT); - } - }) - .create(); - // The profile state is "connecting". If we allow the dialog to - // be cancelable, then we need to clear the state in the - // onCancel handler. - mShowingDialog.setCancelable(false); - mShowingDialog.show(); - } - } else { - changeState(mActiveProfile, VpnState.IDLE); - } - } - - private int getProfileIndexFromId(String id) { - int index = 0; - for (VpnProfile p : sVpnProfileList) { - if (p.getId().equals(id)) { - return index; - } else { - index++; - } - } - return -1; - } - - // Replaces the profile at index in sVpnProfileList with p. - // Returns true if p's name is a duplicate. - private boolean checkDuplicateName(VpnProfile p, int index) { - List<VpnProfile> list = sVpnProfileList; - VpnPreference pref = mVpnPreferenceMap.get(p.getName()); - if ((pref != null) && (index >= 0) && (index < list.size())) { - // not a duplicate if p is to replace the profile at index - if (pref.mProfile == list.get(index)) pref = null; - } - return (pref != null); - } - - private int getProfilePositionFrom(AdapterContextMenuInfo menuInfo) { - // excludes mVpnListContainer and the preferences above it - return menuInfo.position - mVpnListContainer.getOrder() - 1; - } - - // position: position in sVpnProfileList - private VpnProfile getProfile(int position) { - return ((position >= 0) ? sVpnProfileList.get(position) : null); - } - - // position: position in sVpnProfileList - private void deleteProfile(final int position) { - if ((position < 0) || (position >= sVpnProfileList.size())) return; - final VpnProfile target = sVpnProfileList.get(position); - DialogInterface.OnClickListener onClickListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Double check if the target is still the one we want - // to remove. - VpnProfile p = sVpnProfileList.get(position); - if (p != target) return; - if (which == OK_BUTTON) { - sVpnProfileList.remove(position); - VpnPreference pref = - mVpnPreferenceMap.remove(p.getName()); - mVpnListContainer.removePreference(pref); - removeProfileFromStorage(p); - } - } - }; - mShowingDialog = new AlertDialog.Builder(getActivity()) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(R.string.vpn_confirm_profile_deletion) - .setPositiveButton(android.R.string.ok, onClickListener) - .setNegativeButton(R.string.vpn_no_button, onClickListener) - .create(); - mShowingDialog.show(); - } - - // Randomly generates an ID for the profile. - // The ID is unique and only set once when the profile is created. - private void setProfileId(VpnProfile profile) { - String id; - - while (true) { - id = String.valueOf(Math.abs( - Double.doubleToLongBits(Math.random()))); - if (id.length() >= 8) break; - } - for (VpnProfile p : sVpnProfileList) { - if (p.getId().equals(id)) { - setProfileId(profile); - return; - } - } - profile.setId(id); - } - - private void addProfile(VpnProfile p) throws IOException { - setProfileId(p); - processSecrets(p); - saveProfileToStorage(p); - - sVpnProfileList.add(p); - addPreferenceFor(p, true); - disableProfilePreferencesIfOneActive(); - } - - // Adds a preference in mVpnListContainer - private VpnPreference addPreferenceFor( - VpnProfile p, boolean addToContainer) { - VpnPreference pref = new VpnPreference(getActivity(), p); - mVpnPreferenceMap.put(p.getName(), pref); - if (addToContainer) mVpnListContainer.addPreference(pref); - - pref.setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference pref) { - connectOrDisconnect(((VpnPreference) pref).mProfile); - return true; - } - }); - return pref; - } - - // index: index to sVpnProfileList - private void replaceProfile(int index, VpnProfile p) throws IOException { - Map<String, VpnPreference> map = mVpnPreferenceMap; - VpnProfile oldProfile = sVpnProfileList.set(index, p); - VpnPreference pref = map.remove(oldProfile.getName()); - if (pref.mProfile != oldProfile) { - throw new RuntimeException("inconsistent state!"); - } - - p.setId(oldProfile.getId()); - - processSecrets(p); - - // TODO: remove copyFiles once the setId() code propagates. - // Copy config files and remove the old ones if they are in different - // directories. - if (Util.copyFiles(getProfileDir(oldProfile), getProfileDir(p))) { - removeProfileFromStorage(oldProfile); - } - saveProfileToStorage(p); - - pref.setProfile(p); - map.put(p.getName(), pref); - } - - private void startVpnTypeSelection() { - if ((getActivity() == null) || isRemoving()) return; - - ((PreferenceActivity) getActivity()).startPreferencePanel( - VpnTypeSelection.class.getCanonicalName(), null, R.string.vpn_type_title, null, - this, REQUEST_SELECT_VPN_TYPE); - } - - private boolean isKeyStoreUnlocked() { - return mKeyStore.test() == KeyStore.NO_ERROR; - } - - // Returns true if the profile needs to access keystore - private boolean needKeyStoreToSave(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC_PSK: - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - if (!TextUtils.isEmpty(presharedKey)) return true; - // $FALL-THROUGH$ - case L2TP: - L2tpProfile l2tpProfile = (L2tpProfile) p; - if (l2tpProfile.isSecretEnabled() && - !TextUtils.isEmpty(l2tpProfile.getSecretString())) { - return true; - } - // $FALL-THROUGH$ - default: - return false; - } - } - - // Returns true if the profile needs to access keystore - private boolean needKeyStoreToConnect(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC: - case L2TP_IPSEC_PSK: - return true; - - case L2TP: - return ((L2tpProfile) p).isSecretEnabled(); - - default: - return false; - } - } - - // Returns true if keystore is unlocked or keystore is not a concern - private boolean unlockKeyStore(VpnProfile p, Runnable action) { - if (isKeyStoreUnlocked()) return true; - mUnlockAction = action; - Credentials.getInstance().unlock(getActivity()); - return false; - } - - private void startVpnEditor(final VpnProfile profile, boolean add) { - if ((getActivity() == null) || isRemoving()) return; - - Bundle args = new Bundle(); - args.putParcelable(KEY_VPN_PROFILE, profile); - // TODO: Show different titles for add and edit. - ((PreferenceActivity)getActivity()).startPreferencePanel( - VpnEditor.class.getCanonicalName(), args, - 0, VpnEditor.getTitle(getActivity(), profile, add), - this, REQUEST_ADD_OR_EDIT_PROFILE); - } - - private synchronized void connect(final VpnProfile p) { - if (needKeyStoreToConnect(p)) { - Runnable action = new Runnable() { - public void run() { - connect(p); - } - }; - if (!unlockKeyStore(p, action)) return; - } - - if (!checkSecrets(p)) return; - changeState(p, VpnState.CONNECTING); - if (mConnectingActor.isConnectDialogNeeded()) { - showDialog(DIALOG_CONNECT); - } else { - mConnectingActor.connect(null); - } - } - - // Do connect or disconnect based on the current state. - private synchronized void connectOrDisconnect(VpnProfile p) { - VpnPreference pref = mVpnPreferenceMap.get(p.getName()); - switch (p.getState()) { - case IDLE: - connect(p); - break; - - case CONNECTING: - // do nothing - break; - - case CONNECTED: - case DISCONNECTING: - changeState(p, VpnState.DISCONNECTING); - getActor(p).disconnect(); - break; - } - } - - private void changeState(VpnProfile p, VpnState state) { - VpnState oldState = p.getState(); - p.setState(state); - mVpnPreferenceMap.get(p.getName()).setSummary( - getProfileSummaryString(p)); - - switch (state) { - case CONNECTED: - mConnectingActor = null; - mActiveProfile = p; - disableProfilePreferencesIfOneActive(); - break; - - case CONNECTING: - if (mConnectingActor == null) { - mConnectingActor = getActor(p); - } - // $FALL-THROUGH$ - case DISCONNECTING: - mActiveProfile = p; - disableProfilePreferencesIfOneActive(); - break; - - case CANCELLED: - changeState(p, VpnState.IDLE); - break; - - case IDLE: - assert(mActiveProfile == p); - - if (mConnectingErrorCode == NO_ERROR) { - onIdle(); - } else { - showDialog(mConnectingErrorCode); - mConnectingErrorCode = NO_ERROR; - } - break; - } - } - - private void onIdle() { - if (DEBUG) Log.d(TAG, " onIdle()"); - mActiveProfile = null; - mConnectingActor = null; - enableProfilePreferences(); - } - - private void disableProfilePreferencesIfOneActive() { - if (mActiveProfile == null) return; - - for (VpnProfile p : sVpnProfileList) { - switch (p.getState()) { - case CONNECTING: - case DISCONNECTING: - case IDLE: - mVpnPreferenceMap.get(p.getName()).setEnabled(false); - break; - - default: - mVpnPreferenceMap.get(p.getName()).setEnabled(true); - } - } - } - - private void enableProfilePreferences() { - for (VpnProfile p : sVpnProfileList) { - mVpnPreferenceMap.get(p.getName()).setEnabled(true); - } - } - - static String getProfileDir(VpnProfile p) { - return PROFILES_ROOT + p.getId(); - } - - static void saveProfileToStorage(VpnProfile p) throws IOException { - File f = new File(getProfileDir(p)); - if (!f.exists()) f.mkdirs(); - ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( - new File(f, PROFILE_OBJ_FILE))); - oos.writeObject(p); - oos.close(); - } - - private void removeProfileFromStorage(VpnProfile p) { - Util.deleteFile(getProfileDir(p)); - } - - private void updatePreferenceMap() { - mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>(); - mVpnListContainer.removeAll(); - for (VpnProfile p : sVpnProfileList) { - addPreferenceFor(p, true); - } - // reset the mActiveProfile if the profile has been removed from the - // other instance. - if ((mActiveProfile != null) - && !mVpnPreferenceMap.containsKey(mActiveProfile.getName())) { - onIdle(); - } - } - - private void retrieveVpnListFromStorage() { - // skip the loop if the profile is loaded already. - if (sVpnProfileList.size() > 0) return; - File root = new File(PROFILES_ROOT); - String[] dirs = root.list(); - if (dirs == null) return; - for (String dir : dirs) { - File f = new File(new File(root, dir), PROFILE_OBJ_FILE); - if (!f.exists()) continue; - try { - VpnProfile p = deserialize(f); - if (p == null) continue; - if (!checkIdConsistency(dir, p)) continue; - - sVpnProfileList.add(p); - } catch (IOException e) { - Log.e(TAG, "retrieveVpnListFromStorage()", e); - } - } - Collections.sort(sVpnProfileList, new Comparator<VpnProfile>() { - public int compare(VpnProfile p1, VpnProfile p2) { - return p1.getName().compareTo(p2.getName()); - } - }); - disableProfilePreferencesIfOneActive(); - } - - private void checkVpnConnectionStatus() { - for (VpnProfile p : sVpnProfileList) { - changeState(p, mVpnManager.getState(p)); - } - } - - // A sanity check. Returns true if the profile directory name and profile ID - // are consistent. - private boolean checkIdConsistency(String dirName, VpnProfile p) { - if (!dirName.equals(p.getId())) { - Log.d(TAG, "ID inconsistent: " + dirName + " vs " + p.getId()); - return false; - } else { - return true; - } - } - - private VpnProfile deserialize(File profileObjectFile) throws IOException { - try { - ObjectInputStream ois = new ObjectInputStream(new FileInputStream( - profileObjectFile)); - VpnProfile p = (VpnProfile) ois.readObject(); - ois.close(); - return p; - } catch (ClassNotFoundException e) { - Log.d(TAG, "deserialize a profile", e); - return null; - } - } - - private String getProfileSummaryString(VpnProfile p) { - final Activity activity = getActivity(); - switch (p.getState()) { - case CONNECTING: - return activity.getString(R.string.vpn_connecting); - case DISCONNECTING: - return activity.getString(R.string.vpn_disconnecting); - case CONNECTED: - return activity.getString(R.string.vpn_connected); - default: - return activity.getString(R.string.vpn_connect_hint); - } - } - - private VpnProfileActor getActor(VpnProfile p) { - return new AuthenticationActor(getActivity(), p); - } - - private VpnProfile createVpnProfile(String type) { - return mVpnManager.createVpnProfile(Enum.valueOf(VpnType.class, type)); - } - - private boolean checkSecrets(VpnProfile p) { - boolean secretMissing = false; - - if (p instanceof L2tpIpsecProfile) { - L2tpIpsecProfile certProfile = (L2tpIpsecProfile) p; - - String cert = certProfile.getCaCertificate(); - if (TextUtils.isEmpty(cert) || - !mKeyStore.contains(Credentials.CA_CERTIFICATE + cert)) { - certProfile.setCaCertificate(null); - secretMissing = true; - } - - cert = certProfile.getUserCertificate(); - if (TextUtils.isEmpty(cert) || - !mKeyStore.contains(Credentials.USER_CERTIFICATE + cert)) { - certProfile.setUserCertificate(null); - secretMissing = true; - } - } - - if (p instanceof L2tpIpsecPskProfile) { - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - String key = KEY_PREFIX_IPSEC_PSK + p.getId(); - if (TextUtils.isEmpty(presharedKey) || !mKeyStore.contains(key)) { - pskProfile.setPresharedKey(null); - secretMissing = true; - } - } - - if (p instanceof L2tpProfile) { - L2tpProfile l2tpProfile = (L2tpProfile) p; - if (l2tpProfile.isSecretEnabled()) { - String secret = l2tpProfile.getSecretString(); - String key = KEY_PREFIX_L2TP_SECRET + p.getId(); - if (TextUtils.isEmpty(secret) || !mKeyStore.contains(key)) { - l2tpProfile.setSecretString(null); - secretMissing = true; - } - } - } - - if (secretMissing) { - mActiveProfile = p; - showDialog(DIALOG_SECRET_NOT_SET); - return false; - } else { - return true; - } - } - - private void processSecrets(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC_PSK: - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - String key = KEY_PREFIX_IPSEC_PSK + p.getId(); - if (!TextUtils.isEmpty(presharedKey) && - !mKeyStore.put(key, presharedKey)) { - Log.e(TAG, "keystore write failed: key=" + key); - } - pskProfile.setPresharedKey(key); - // $FALL-THROUGH$ - case L2TP_IPSEC: - case L2TP: - L2tpProfile l2tpProfile = (L2tpProfile) p; - key = KEY_PREFIX_L2TP_SECRET + p.getId(); - if (l2tpProfile.isSecretEnabled()) { - String secret = l2tpProfile.getSecretString(); - if (!TextUtils.isEmpty(secret) && - !mKeyStore.put(key, secret)) { - Log.e(TAG, "keystore write failed: key=" + key); - } - l2tpProfile.setSecretString(key); - } else { - mKeyStore.delete(key); - } - break; - } - } - - private class VpnPreference extends Preference { - VpnProfile mProfile; - VpnPreference(Context c, VpnProfile p) { - super(c); - setProfile(p); - } - - void setProfile(VpnProfile p) { - mProfile = p; - setTitle(p.getName()); - setSummary(getProfileSummaryString(p)); - } - } - - // to receive vpn connectivity events broadcast by VpnService - private class ConnectivityReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - String profileName = intent.getStringExtra( - VpnManager.BROADCAST_PROFILE_NAME); - if (profileName == null) return; - - VpnState s = (VpnState) intent.getSerializableExtra( - VpnManager.BROADCAST_CONNECTION_STATE); - - if (s == null) { - Log.e(TAG, "received null connectivity state"); - return; - } - - mConnectingErrorCode = intent.getIntExtra( - VpnManager.BROADCAST_ERROR_CODE, NO_ERROR); - - VpnPreference pref = mVpnPreferenceMap.get(profileName); - if (pref != null) { - Log.d(TAG, "received connectivity: " + profileName - + ": connected? " + s - + " err=" + mConnectingErrorCode); - // XXX: VpnService should broadcast non-IDLE state, say UNUSABLE, - // when an error occurs. - changeState(pref.mProfile, s); - } else { - Log.e(TAG, "received connectivity: " + profileName - + ": connected? " + s + ", but profile does not exist;" - + " just ignore it"); - } - } - } -} |