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
699fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    // TODO: migrate to using DialogFragment when editing
709fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
7197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh    private final IConnectivityManager mService = IConnectivityManager.Stub
7297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
73310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private final KeyStore mKeyStore = KeyStore.getInstance();
74310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private boolean mUnlocking = false;
75310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
7688b0140241c646c87d356748621cef28a3105466Robert Greenwalt    private HashMap<String, VpnPreference> mPreferences = new HashMap<String, VpnPreference>();
77310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private VpnDialog mDialog;
78bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh
79bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh    private Handler mUpdater;
8097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh    private LegacyVpnInfo mInfo;
81bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh
82bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh    // The key of the profile for the current ContextMenu.
83310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private String mSelectedKey;
84310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
85310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
86310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onCreate(Bundle savedState) {
87310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onCreate(savedState);
889fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
899fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        setHasOptionsMenu(true);
90310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        addPreferencesFromResource(R.xml.vpn_settings2);
91310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
92310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (savedState != null) {
93310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
94310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    savedState.getByteArray("VpnProfile"));
95310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (profile != null) {
96310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, profile,
97310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        savedState.getBoolean("VpnEditing"));
98310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
99310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
100310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
101310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
102310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
1039fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1049fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        super.onCreateOptionsMenu(menu, inflater);
1059fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        inflater.inflate(R.menu.vpn, menu);
1069fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1079fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1089fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
10907d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    public void onPrepareOptionsMenu(Menu menu) {
11007d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        super.onPrepareOptionsMenu(menu);
11107d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
11207d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        // Hide lockdown VPN on devices that require IMS authentication
11307d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        if (SystemProperties.getBoolean("persist.radio.imsregrequired", false)) {
11407d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey            menu.findItem(R.id.vpn_lockdown).setVisible(false);
11507d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        }
11607d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    }
11707d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
11807d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    @Override
1199fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public boolean onOptionsItemSelected(MenuItem item) {
1209fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        switch (item.getItemId()) {
1219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_create: {
1229fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                // Generate a new key. Here we just use the current time.
1239fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                long millis = System.currentTimeMillis();
1249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                while (mPreferences.containsKey(Long.toHexString(millis))) {
1259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ++millis;
1269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
1279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog = new VpnDialog(
1289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        getActivity(), this, new VpnProfile(Long.toHexString(millis)), true);
1299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.setOnDismissListener(this);
1309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.show();
1319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_lockdown: {
1349fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                LockdownConfigFragment.show(this);
1359fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1379fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
1389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return super.onOptionsItemSelected(item);
1399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
142310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onSaveInstanceState(Bundle savedState) {
143310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // We do not save view hierarchy, as they are just profiles.
144310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
145310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = mDialog.getProfile();
146310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putString("VpnKey", profile.key);
147310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putByteArray("VpnProfile", profile.encode());
148310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putBoolean("VpnEditing", mDialog.isEditing());
149310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
150310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // else?
151310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
152310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
153310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
154310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onResume() {
155310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onResume();
156310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
157310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Check KeyStore here, so others do not need to deal with it.
158310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
159310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (!mUnlocking) {
160310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                // Let us unlock KeyStore. See you later!
161310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                Credentials.getInstance().unlock(getActivity());
162310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            } else {
163310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                // We already tried, but it is still not working!
164d68dbe29bb0a6639bfd95449eb28e7d4b174a3feChia-chi Yeh                finishFragment();
165310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
166310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mUnlocking = !mUnlocking;
167310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return;
168310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
169310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
170310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Now KeyStore is always unlocked. Reset the flag.
171310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mUnlocking = false;
172310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
173310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Currently we are the only user of profiles in KeyStore.
174310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Assuming KeyStore and KeyGuard do the right thing, we can
175310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // safely cache profiles in the memory.
17688b0140241c646c87d356748621cef28a3105466Robert Greenwalt        if (mPreferences.size() == 0) {
177d501b5c963c0cbcc9c666f502229599464eaf3fdChia-chi Yeh            PreferenceGroup group = getPreferenceScreen();
178310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
1799fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final Context context = getActivity();
1809fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final List<VpnProfile> profiles = loadVpnProfiles(mKeyStore);
1819fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (VpnProfile profile : profiles) {
1829fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                final VpnPreference pref = new VpnPreference(context, profile);
1839fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                pref.setOnPreferenceClickListener(this);
1849fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mPreferences.put(profile.key, pref);
1859fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                group.addPreference(pref);
186310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
187310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
188310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
189310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Show the dialog if there is one.
190310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
191310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.setOnDismissListener(this);
192310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.show();
193310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
194310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
195310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Start monitoring.
196bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        if (mUpdater == null) {
197bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            mUpdater = new Handler(this);
198310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
199bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        mUpdater.sendEmptyMessage(0);
200310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
201310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Register for context menu. Hmmm, getListView() is hidden?
202310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        registerForContextMenu(getListView());
203310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
204310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
205310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
206310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onPause() {
207310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onPause();
208310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
209310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Hide the dialog if there is one.
210310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
211310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.setOnDismissListener(null);
212310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog.dismiss();
213310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
214310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
215310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Unregister for context menu.
2166d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh        if (getView() != null) {
2176d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh            unregisterForContextMenu(getListView());
2186d4334be691815cd9615b0fdebb756999a9c06a3Chia-chi Yeh        }
219310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
220310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
221310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
222310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onDismiss(DialogInterface dialog) {
223310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // Here is the exit of a dialog.
224310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog = null;
225310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
226310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
227310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
228310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onClick(DialogInterface dialog, int button) {
229310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (button == DialogInterface.BUTTON_POSITIVE) {
230310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Always save the profile.
231310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = mDialog.getProfile();
232310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mKeyStore.put(Credentials.VPN + profile.key, profile.encode());
233310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
234310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Update the preference.
235310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnPreference preference = mPreferences.get(profile.key);
236310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference != null) {
237310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(profile.key);
238310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference.update(profile);
239310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            } else {
240310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference = new VpnPreference(getActivity(), profile);
2419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                preference.setOnPreferenceClickListener(this);
242310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.put(profile.key, preference);
243310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().addPreference(preference);
244310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
245310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
246310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // If we are not editing, connect!
247310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (!mDialog.isEditing()) {
248413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                try {
249413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    connect(profile);
250413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                } catch (Exception e) {
251413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    Log.e(TAG, "connect", e);
252413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                }
253310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
254310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
255310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
256310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
257310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
258310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
259310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
260310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
261310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return;
262310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
263310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
264310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (info instanceof AdapterContextMenuInfo) {
265310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Preference preference = (Preference) getListView().getItemAtPosition(
266310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    ((AdapterContextMenuInfo) info).position);
267310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
268310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnProfile profile = ((VpnPreference) preference).getProfile();
269310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mSelectedKey = profile.key;
270310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.setHeaderTitle(profile.name);
271310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
272310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
273310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
274310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
275310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
276310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
277310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
278310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onContextItemSelected(MenuItem item) {
279310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
280310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
281310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
282310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
283310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
284310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference preference = mPreferences.get(mSelectedKey);
285310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference == null) {
286310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called but no preference is found");
287310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
288310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
289310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
290310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        switch (item.getItemId()) {
291310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_edit:
292310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
293310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.setOnDismissListener(this);
294310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.show();
295310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
296310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_delete:
297310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(mSelectedKey);
298310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().removePreference(preference);
299310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.remove(mSelectedKey);
300310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mKeyStore.delete(Credentials.VPN + mSelectedKey);
301310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
302310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
303310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return false;
304310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
305310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
306310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
307310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onPreferenceClick(Preference preference) {
308310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
309310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
310310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return true;
311310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
312310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
313310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference instanceof VpnPreference) {
31497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            VpnProfile profile = ((VpnPreference) preference).getProfile();
31597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mInfo != null && profile.key.equals(mInfo.key) &&
31697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.state == LegacyVpnInfo.STATE_CONNECTED) {
31797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                try {
31897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.intent.send();
31997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    return true;
32097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                } catch (Exception e) {
32197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    // ignore
32297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
32397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
32497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mDialog = new VpnDialog(getActivity(), this, profile, false);
325310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        } else {
326310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Generate a new key. Here we just use the current time.
327310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            long millis = System.currentTimeMillis();
328310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            while (mPreferences.containsKey(Long.toHexString(millis))) {
329310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                ++millis;
330310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
331310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog = new VpnDialog(getActivity(), this,
332310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    new VpnProfile(Long.toHexString(millis)), true);
333310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
334310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.setOnDismissListener(this);
335310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.show();
336310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
337310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
338310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
339310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
340310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean handleMessage(Message message) {
341bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        mUpdater.removeMessages(0);
342310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
343310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (isResumed()) {
34497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
34597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                LegacyVpnInfo info = mService.getLegacyVpnInfo();
34697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (mInfo != null) {
34797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(mInfo.key);
34897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
34997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(-1);
35097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
35197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo = null;
35297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
35397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (info != null) {
35497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(info.key);
35597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
35697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(info.state);
35797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        mInfo = info;
35897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
35997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
36097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
36197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
36297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
363bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            mUpdater.sendEmptyMessageDelayed(0, 1000);
364310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
365310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
366310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
367310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
368f35130dd79c100097025585c0b5a5ebbd38a414eChia-chi Yeh    private void connect(VpnProfile profile) throws Exception {
369745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        try {
370745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            mService.startLegacyVpn(profile);
371745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        } catch (IllegalStateException e) {
372745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
373d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yeh        }
374310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
375310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
376310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private void disconnect(String key) {
37797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        if (mInfo != null && key.equals(mInfo.key)) {
37897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
37997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
38097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
38197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
38297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
38397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
384310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
385310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
386b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    @Override
387b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    protected int getHelpResource() {
388b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani        return R.string.help_url_vpn;
389b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    }
390b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani
3919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    private static class VpnPreference extends Preference {
392310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        private VpnProfile mProfile;
39397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        private int mState = -1;
394310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
395310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference(Context context, VpnProfile profile) {
396310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            super(context);
397310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setPersistent(false);
39897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            setOrder(0);
399310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
400310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
401310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
402310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
403310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
404310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnProfile getProfile() {
405310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return mProfile;
406310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
407310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
408310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update(VpnProfile profile) {
409310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
410310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
411310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
412310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
41397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        void update(int state) {
41497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mState = state;
41597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            update();
41697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
41797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh
418310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update() {
41997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mState < 0) {
420310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                String[] types = getContext().getResources()
421310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        .getStringArray(R.array.vpn_types_long);
422310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                setSummary(types[mProfile.type]);
42397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } else {
42497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                String[] states = getContext().getResources()
42597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        .getStringArray(R.array.vpn_states);
42697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                setSummary(states[mState]);
427310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
428310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setTitle(mProfile.name);
42997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            notifyHierarchyChanged();
430310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
431310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
432310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        @Override
433310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        public int compareTo(Preference preference) {
434bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            int result = -1;
435310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
436310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnPreference another = (VpnPreference) preference;
437310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                if ((result = another.mState - mState) == 0 &&
438310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
439310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.type - another.mProfile.type) == 0) {
440310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    result = mProfile.key.compareTo(another.mProfile.key);
441310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                }
442310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
443310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return result;
444310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
445310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
4469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    /**
4489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     * Dialog to configure always-on VPN.
4499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     */
4509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public static class LockdownConfigFragment extends DialogFragment {
4519fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<VpnProfile> mProfiles;
4529fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<CharSequence> mTitles;
4539fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private int mCurrentIndex;
4549fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4559fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static class TitleAdapter extends ArrayAdapter<CharSequence> {
4569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            public TitleAdapter(Context context, List<CharSequence> objects) {
4579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                super(context, com.android.internal.R.layout.select_dialog_singlechoice_holo,
4589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        android.R.id.text1, objects);
4599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4619fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4629fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public static void show(VpnSettings parent) {
4639fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            if (!parent.isAdded()) return;
4649fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4659fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LockdownConfigFragment dialog = new LockdownConfigFragment();
4669fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
4679fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4689fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4699fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static String getStringOrNull(KeyStore keyStore, String key) {
4709fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final byte[] value = keyStore.get(Credentials.LOCKDOWN_VPN);
4719fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return value == null ? null : new String(value);
4729fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4739fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4749fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private void initProfiles(KeyStore keyStore, Resources res) {
4759fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
4769fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
477f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey            mProfiles = loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
4789fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles = Lists.newArrayList();
4799fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles.add(res.getText(R.string.vpn_lockdown_none));
4809fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mCurrentIndex = 0;
4819fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4829fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (VpnProfile profile : mProfiles) {
4839fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                if (TextUtils.equals(profile.key, lockdownKey)) {
4849fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    mCurrentIndex = mTitles.size();
4859fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
4869fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mTitles.add(profile.name);
4879fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4889fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4899fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4909fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        @Override
4919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public Dialog onCreateDialog(Bundle savedInstanceState) {
4929fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final Context context = getActivity();
4939fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final KeyStore keyStore = KeyStore.getInstance();
4949fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4959fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            initProfiles(keyStore, context.getResources());
4969fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4979fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
4989fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
4999fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5009fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setTitle(R.string.vpn_menu_lockdown);
5019fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5029fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
5039fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final ListView listView = (ListView) view.findViewById(android.R.id.list);
5049fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5059fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setAdapter(new TitleAdapter(context, mTitles));
5069fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setItemChecked(mCurrentIndex, true);
5079fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setView(view);
5089fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5099fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5109fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                @Override
5119fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                public void onClick(DialogInterface dialog, int which) {
5129fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    final int newIndex = listView.getCheckedItemPosition();
5139fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (mCurrentIndex == newIndex) return;
5149fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5159fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (newIndex == 0) {
5169fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        keyStore.delete(Credentials.LOCKDOWN_VPN);
5179fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5189fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    } else {
5199fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        final VpnProfile profile = mProfiles.get(newIndex - 1);
5209fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        if (!profile.isValidLockdownProfile()) {
5219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            Toast.makeText(context, R.string.vpn_lockdown_config_error,
5229fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                                    Toast.LENGTH_LONG).show();
5239fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            return;
5249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        }
5259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes());
5269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    }
5279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    // kick profiles since we changed them
5299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ConnectivityManager.from(getActivity()).updateLockdownVpn();
5309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            });
5329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return builder.create();
5349fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5359fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
5369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
537f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey    private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
5389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final ArrayList<VpnProfile> result = Lists.newArrayList();
5399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final String[] keys = keyStore.saw(Credentials.VPN);
5409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        if (keys != null) {
5419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (String key : keys) {
5429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                final VpnProfile profile = VpnProfile.decode(
5439fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        key, keyStore.get(Credentials.VPN + key));
544f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey                if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
5459fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    result.add(profile);
5469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
5489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return result;
5509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
551310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh}
552