summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/vpn/VpnSettings.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/settings/vpn/VpnSettings.java')
-rw-r--r--src/com/android/settings/vpn/VpnSettings.java1108
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");
- }
- }
- }
-}