VpnSettings.java revision 8b78299d94ebb09fb9bf2bc7fb0015d2f1950839
1310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh/*
2310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * Copyright (C) 2011 The Android Open Source Project
3310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh *
4310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
5310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * you may not use this file except in compliance with the License.
6310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * You may obtain a copy of the License at
7310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh *
8310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
9310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh *
10310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
11310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
12310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * See the License for the specific language governing permissions and
14310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh * limitations under the License.
15310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh */
16310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
17310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehpackage com.android.settings.vpn2;
18310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
199fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.app.AlertDialog;
209fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.app.Dialog;
219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.app.DialogFragment;
22310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.content.Context;
23310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.content.DialogInterface;
249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.content.res.Resources;
259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.net.ConnectivityManager;
26bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yehimport android.net.IConnectivityManager;
27310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Bundle;
28310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Handler;
29310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Message;
30bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yehimport android.os.ServiceManager;
3107d465ddb8629f128dde193d22de28cfd56663caJeff Sharkeyimport android.os.SystemProperties;
32310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.preference.Preference;
33310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.preference.PreferenceGroup;
34310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.security.Credentials;
35310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.security.KeyStore;
369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.text.TextUtils;
37310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.util.Log;
38310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.ContextMenu;
39310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.ContextMenu.ContextMenuInfo;
409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.view.LayoutInflater;
41310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.Menu;
429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.view.MenuInflater;
43310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.MenuItem;
44310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.View;
45310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.widget.AdapterView.AdapterContextMenuInfo;
469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.widget.ArrayAdapter;
479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.widget.ListView;
4894cc1bbf3218a020548a90032ec34f608f4cefdbChia-chi Yehimport android.widget.Toast;
49310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
5097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
51d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yehimport com.android.internal.net.VpnConfig;
52c6e84c09590ec5e4da287fba32dd53775156ae76Jeff Sharkeyimport com.android.internal.net.VpnProfile;
53f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkeyimport com.android.internal.util.ArrayUtils;
54745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkeyimport com.android.settings.R;
55310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport com.android.settings.SettingsPreferenceFragment;
569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport com.google.android.collect.Lists;
57310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport java.util.ArrayList;
59310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport java.util.HashMap;
609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport java.util.List;
61310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
62310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehpublic class VpnSettings extends SettingsPreferenceFragment implements
63310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        Handler.Callback, Preference.OnPreferenceClickListener,
64310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
65310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private static final String TAG = "VpnSettings";
66310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
679fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    private static final String TAG_LOCKDOWN = "lockdown";
689fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
698b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey    private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
708b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey
719fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    // TODO: migrate to using DialogFragment when editing
729fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
7397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh    private final IConnectivityManager mService = IConnectivityManager.Stub
7497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
75310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private final KeyStore mKeyStore = KeyStore.getInstance();
76310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private boolean mUnlocking = false;
77310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
7888b0140241c646c87d356748621cef28a3105466Robert Greenwalt    private HashMap<String, VpnPreference> mPreferences = new HashMap<String, VpnPreference>();
79310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private VpnDialog mDialog;
80bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh
81bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh    private Handler mUpdater;
8297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh    private LegacyVpnInfo mInfo;
83bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh
84bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh    // The key of the profile for the current ContextMenu.
85310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private String mSelectedKey;
86310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
87310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
88310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onCreate(Bundle savedState) {
89310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onCreate(savedState);
909fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        setHasOptionsMenu(true);
92310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        addPreferencesFromResource(R.xml.vpn_settings2);
93310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
948b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey        final boolean pickLockdown = getActivity()
958b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey                .getIntent().getBooleanExtra(EXTRA_PICK_LOCKDOWN, false);
968b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey        if (pickLockdown) {
978b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey            LockdownConfigFragment.show(this);
988b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey        }
998b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey
100310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (savedState != null) {
101310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
102310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    savedState.getByteArray("VpnProfile"));
103310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (profile != null) {
104310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, profile,
105310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        savedState.getBoolean("VpnEditing"));
106310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
107310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
108310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
109310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
110310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
1119fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1129fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        super.onCreateOptionsMenu(menu, inflater);
1139fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        inflater.inflate(R.menu.vpn, menu);
1149fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1159fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1169fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
11707d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    public void onPrepareOptionsMenu(Menu menu) {
11807d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        super.onPrepareOptionsMenu(menu);
11907d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
12007d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        // Hide lockdown VPN on devices that require IMS authentication
12107d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        if (SystemProperties.getBoolean("persist.radio.imsregrequired", false)) {
12207d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey            menu.findItem(R.id.vpn_lockdown).setVisible(false);
12307d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        }
12407d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    }
12507d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
12607d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    @Override
1279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public boolean onOptionsItemSelected(MenuItem item) {
1289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        switch (item.getItemId()) {
1299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_create: {
1309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                // Generate a new key. Here we just use the current time.
1319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                long millis = System.currentTimeMillis();
1329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                while (mPreferences.containsKey(Long.toHexString(millis))) {
1339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ++millis;
1349fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
1359fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog = new VpnDialog(
1369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        getActivity(), this, new VpnProfile(Long.toHexString(millis)), true);
1379fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.setOnDismissListener(this);
1389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.show();
1399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_lockdown: {
1429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                LockdownConfigFragment.show(this);
1439fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1449fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1459fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
1469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return super.onOptionsItemSelected(item);
1479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
150310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onSaveInstanceState(Bundle savedState) {
151310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // We do not save view hierarchy, as they are just profiles.
152310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
153310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = mDialog.getProfile();
154310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putString("VpnKey", profile.key);
155310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putByteArray("VpnProfile", profile.encode());
156310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putBoolean("VpnEditing", mDialog.isEditing());
157310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
158310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // else?
159310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
160310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
161310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
162310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onResume() {
163310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onResume();
164310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
165310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Check KeyStore here, so others do not need to deal with it.
166ca714d8d0c11c904b25bc20a0a9b2f2cc8d78ad5Kenny Root        if (!mKeyStore.isUnlocked()) {
167310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (!mUnlocking) {
168310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                // Let us unlock KeyStore. See you later!
169310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                Credentials.getInstance().unlock(getActivity());
170310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            } else {
171310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                // We already tried, but it is still not working!
172d68dbe29bb0a6639bfd95449eb28e7d4b174a3feChia-chi Yeh                finishFragment();
173310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
174310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mUnlocking = !mUnlocking;
175310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return;
176310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
177310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
178310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Now KeyStore is always unlocked. Reset the flag.
179310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mUnlocking = false;
180310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
181310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Currently we are the only user of profiles in KeyStore.
182310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Assuming KeyStore and KeyGuard do the right thing, we can
183310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // safely cache profiles in the memory.
18488b0140241c646c87d356748621cef28a3105466Robert Greenwalt        if (mPreferences.size() == 0) {
185d501b5c963c0cbcc9c666f502229599464eaf3fdChia-chi Yeh            PreferenceGroup group = getPreferenceScreen();
186310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
1879fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final Context context = getActivity();
1889fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final List<VpnProfile> profiles = loadVpnProfiles(mKeyStore);
1899fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (VpnProfile profile : profiles) {
1909fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                final VpnPreference pref = new VpnPreference(context, profile);
1919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                pref.setOnPreferenceClickListener(this);
1929fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mPreferences.put(profile.key, pref);
1939fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                group.addPreference(pref);
194310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
195310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
196310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
197310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Show the dialog if there is one.
198310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
199310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.setOnDismissListener(this);
200310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.show();
201310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
202310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
203310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Start monitoring.
204bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        if (mUpdater == null) {
205bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            mUpdater = new Handler(this);
206310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
207bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        mUpdater.sendEmptyMessage(0);
208310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
209310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Register for context menu. Hmmm, getListView() is hidden?
210310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        registerForContextMenu(getListView());
211310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
212310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
213310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
214310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onPause() {
215310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onPause();
216310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
217310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Hide the dialog if there is one.
218310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
219310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.setOnDismissListener(null);
220310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.dismiss();
221310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
222310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
223310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Unregister for context menu.
2246d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh        if (getView() != null) {
2256d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh            unregisterForContextMenu(getListView());
2266d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh        }
227310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
228310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
229310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
230310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onDismiss(DialogInterface dialog) {
231310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Here is the exit of a dialog.
232310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog = null;
233310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
234310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
235310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
236310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onClick(DialogInterface dialog, int button) {
237310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (button == DialogInterface.BUTTON_POSITIVE) {
238310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Always save the profile.
239310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = mDialog.getProfile();
240310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mKeyStore.put(Credentials.VPN + profile.key, profile.encode());
241310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
242310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Update the preference.
243310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnPreference preference = mPreferences.get(profile.key);
244310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference != null) {
245310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(profile.key);
246310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference.update(profile);
247310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            } else {
248310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference = new VpnPreference(getActivity(), profile);
2499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                preference.setOnPreferenceClickListener(this);
250310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.put(profile.key, preference);
251310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().addPreference(preference);
252310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
253310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
254310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // If we are not editing, connect!
255310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (!mDialog.isEditing()) {
256413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                try {
257413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    connect(profile);
258413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                } catch (Exception e) {
259413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    Log.e(TAG, "connect", e);
260413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                }
261310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
262310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
263310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
264310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
265310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
266310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
267310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
268310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
269310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return;
270310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
271310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
272310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (info instanceof AdapterContextMenuInfo) {
273310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Preference preference = (Preference) getListView().getItemAtPosition(
274310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    ((AdapterContextMenuInfo) info).position);
275310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
276310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnProfile profile = ((VpnPreference) preference).getProfile();
277310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mSelectedKey = profile.key;
278310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.setHeaderTitle(profile.name);
279310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
280310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
281310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
282310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
283310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
284310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
285310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
286310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onContextItemSelected(MenuItem item) {
287310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
288310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
289310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
290310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
291310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
292310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference preference = mPreferences.get(mSelectedKey);
293310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference == null) {
294310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called but no preference is found");
295310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
296310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
297310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
298310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        switch (item.getItemId()) {
299310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_edit:
300310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
301310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.setOnDismissListener(this);
302310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.show();
303310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
304310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_delete:
305310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(mSelectedKey);
306310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().removePreference(preference);
307310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.remove(mSelectedKey);
308310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mKeyStore.delete(Credentials.VPN + mSelectedKey);
309310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
310310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
311310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return false;
312310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
313310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
314310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
315310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onPreferenceClick(Preference preference) {
316310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
317310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
318310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return true;
319310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
320310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
321310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference instanceof VpnPreference) {
32297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            VpnProfile profile = ((VpnPreference) preference).getProfile();
32397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mInfo != null && profile.key.equals(mInfo.key) &&
32497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.state == LegacyVpnInfo.STATE_CONNECTED) {
32597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                try {
32697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.intent.send();
32797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    return true;
32897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                } catch (Exception e) {
32997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    // ignore
33097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
33197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
33297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mDialog = new VpnDialog(getActivity(), this, profile, false);
333310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        } else {
334310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Generate a new key. Here we just use the current time.
335310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            long millis = System.currentTimeMillis();
336310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            while (mPreferences.containsKey(Long.toHexString(millis))) {
337310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                ++millis;
338310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
339310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog = new VpnDialog(getActivity(), this,
340310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    new VpnProfile(Long.toHexString(millis)), true);
341310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
342310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.setOnDismissListener(this);
343310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.show();
344310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
345310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
346310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
347310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
348310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean handleMessage(Message message) {
349bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        mUpdater.removeMessages(0);
350310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
351310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (isResumed()) {
35297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
35397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                LegacyVpnInfo info = mService.getLegacyVpnInfo();
35497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (mInfo != null) {
35597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(mInfo.key);
35697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
35797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(-1);
35897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
35997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo = null;
36097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
36197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (info != null) {
36297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(info.key);
36397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
36497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(info.state);
36597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        mInfo = info;
36697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
36797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
36897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
36997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
37097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
371bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            mUpdater.sendEmptyMessageDelayed(0, 1000);
372310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
373310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
374310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
375310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
376f35130dd79c100097025585c0b5a5ebbd38a414eChia-chi Yeh    private void connect(VpnProfile profile) throws Exception {
377745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        try {
378745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            mService.startLegacyVpn(profile);
379745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        } catch (IllegalStateException e) {
380745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
381d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yeh        }
382310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
383310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
384310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private void disconnect(String key) {
38597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        if (mInfo != null && key.equals(mInfo.key)) {
38697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
38797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
38897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
38997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
39097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
39197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
392310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
393310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
394b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    @Override
395b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    protected int getHelpResource() {
396b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani        return R.string.help_url_vpn;
397b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    }
398b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani
3999fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    private static class VpnPreference extends Preference {
400310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        private VpnProfile mProfile;
40197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        private int mState = -1;
402310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
403310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference(Context context, VpnProfile profile) {
404310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            super(context);
405310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setPersistent(false);
40697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            setOrder(0);
407310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
408310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
409310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
410310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
411310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
412310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnProfile getProfile() {
413310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return mProfile;
414310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
415310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
416310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update(VpnProfile profile) {
417310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
418310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
419310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
420310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
42197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        void update(int state) {
42297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mState = state;
42397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            update();
42497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
42597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh
426310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update() {
42797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mState < 0) {
428310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                String[] types = getContext().getResources()
429310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        .getStringArray(R.array.vpn_types_long);
430310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                setSummary(types[mProfile.type]);
43197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } else {
43297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                String[] states = getContext().getResources()
43397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        .getStringArray(R.array.vpn_states);
43497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                setSummary(states[mState]);
435310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
436310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setTitle(mProfile.name);
43797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            notifyHierarchyChanged();
438310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
439310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
440310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        @Override
441310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        public int compareTo(Preference preference) {
442bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            int result = -1;
443310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
444310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnPreference another = (VpnPreference) preference;
445310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                if ((result = another.mState - mState) == 0 &&
446310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
447310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.type - another.mProfile.type) == 0) {
448310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    result = mProfile.key.compareTo(another.mProfile.key);
449310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                }
450310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
451310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return result;
452310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
453310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
4549fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4559fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    /**
4569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     * Dialog to configure always-on VPN.
4579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     */
4589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public static class LockdownConfigFragment extends DialogFragment {
4599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<VpnProfile> mProfiles;
4609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<CharSequence> mTitles;
4619fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private int mCurrentIndex;
4629fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4639fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static class TitleAdapter extends ArrayAdapter<CharSequence> {
4649fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            public TitleAdapter(Context context, List<CharSequence> objects) {
4659fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                super(context, com.android.internal.R.layout.select_dialog_singlechoice_holo,
4669fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        android.R.id.text1, objects);
4679fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4689fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4699fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4709fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public static void show(VpnSettings parent) {
4719fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            if (!parent.isAdded()) return;
4729fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4739fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LockdownConfigFragment dialog = new LockdownConfigFragment();
4749fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
4759fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4769fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4779fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static String getStringOrNull(KeyStore keyStore, String key) {
4789fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final byte[] value = keyStore.get(Credentials.LOCKDOWN_VPN);
4799fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return value == null ? null : new String(value);
4809fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4819fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4829fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private void initProfiles(KeyStore keyStore, Resources res) {
4839fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
4849fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
485f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey            mProfiles = loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
4869fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles = Lists.newArrayList();
4879fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles.add(res.getText(R.string.vpn_lockdown_none));
4889fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mCurrentIndex = 0;
4899fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4909fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (VpnProfile profile : mProfiles) {
4919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                if (TextUtils.equals(profile.key, lockdownKey)) {
4929fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    mCurrentIndex = mTitles.size();
4939fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
4949fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mTitles.add(profile.name);
4959fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4969fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4979fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4989fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        @Override
4999fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public Dialog onCreateDialog(Bundle savedInstanceState) {
5009fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final Context context = getActivity();
5019fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final KeyStore keyStore = KeyStore.getInstance();
5029fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5039fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            initProfiles(keyStore, context.getResources());
5049fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5059fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
5069fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
5079fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5089fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setTitle(R.string.vpn_menu_lockdown);
5099fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5109fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
5119fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final ListView listView = (ListView) view.findViewById(android.R.id.list);
5129fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5139fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setAdapter(new TitleAdapter(context, mTitles));
5149fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setItemChecked(mCurrentIndex, true);
5159fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setView(view);
5169fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5179fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5189fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                @Override
5199fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                public void onClick(DialogInterface dialog, int which) {
5209fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    final int newIndex = listView.getCheckedItemPosition();
5219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (mCurrentIndex == newIndex) return;
5229fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5239fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (newIndex == 0) {
5249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        keyStore.delete(Credentials.LOCKDOWN_VPN);
5259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    } else {
5279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        final VpnProfile profile = mProfiles.get(newIndex - 1);
5289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        if (!profile.isValidLockdownProfile()) {
5299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            Toast.makeText(context, R.string.vpn_lockdown_config_error,
5309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                                    Toast.LENGTH_LONG).show();
5319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            return;
5329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        }
5339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes());
5349fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    }
5359fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    // kick profiles since we changed them
5379fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ConnectivityManager.from(getActivity()).updateLockdownVpn();
5389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            });
5409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return builder.create();
5429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5439fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
5449fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
545f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey    private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
5469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final ArrayList<VpnProfile> result = Lists.newArrayList();
5479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final String[] keys = keyStore.saw(Credentials.VPN);
5489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        if (keys != null) {
5499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (String key : keys) {
5509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                final VpnProfile profile = VpnProfile.decode(
5519fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        key, keyStore.get(Credentials.VPN + key));
552f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey                if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
5539fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    result.add(profile);
5549fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5559fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
5569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return result;
5589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
559310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh}
560