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
94310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (savedState != null) {
95310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
96310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    savedState.getByteArray("VpnProfile"));
97310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (profile != null) {
98310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, profile,
99310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        savedState.getBoolean("VpnEditing"));
100310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
101310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
102310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
103310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
104310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
1059fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1069fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        super.onCreateOptionsMenu(menu, inflater);
1079fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        inflater.inflate(R.menu.vpn, menu);
1089fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1099fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1109fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
11107d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    public void onPrepareOptionsMenu(Menu menu) {
11207d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        super.onPrepareOptionsMenu(menu);
11307d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
11407d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        // Hide lockdown VPN on devices that require IMS authentication
11507d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        if (SystemProperties.getBoolean("persist.radio.imsregrequired", false)) {
11607d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey            menu.findItem(R.id.vpn_lockdown).setVisible(false);
11707d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        }
11807d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    }
11907d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
12007d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    @Override
1219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public boolean onOptionsItemSelected(MenuItem item) {
1229fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        switch (item.getItemId()) {
1239fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_create: {
1249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                // Generate a new key. Here we just use the current time.
1259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                long millis = System.currentTimeMillis();
1269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                while (mPreferences.containsKey(Long.toHexString(millis))) {
1279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ++millis;
1289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
1299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog = new VpnDialog(
1309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        getActivity(), this, new VpnProfile(Long.toHexString(millis)), true);
1319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.setOnDismissListener(this);
1329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mDialog.show();
1339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1349fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1359fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_lockdown: {
1369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                LockdownConfigFragment.show(this);
1379fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
1409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return super.onOptionsItemSelected(item);
1419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1439fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
144310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onSaveInstanceState(Bundle savedState) {
145310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // We do not save view hierarchy, as they are just profiles.
146310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
147310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnProfile profile = mDialog.getProfile();
148310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putString("VpnKey", profile.key);
149310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putByteArray("VpnProfile", profile.encode());
150310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            savedState.putBoolean("VpnEditing", mDialog.isEditing());
151310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
152310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        // else?
153310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
154310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
155310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
156310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onResume() {
157310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onResume();
158310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
1591a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey        final boolean pickLockdown = getActivity()
1601a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey                .getIntent().getBooleanExtra(EXTRA_PICK_LOCKDOWN, false);
1611a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey        if (pickLockdown) {
1621a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey            LockdownConfigFragment.show(this);
1631a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey        }
1641a55f95d45d2be39f3206cf1a4c3a1affd98cd92Jeff Sharkey
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();
24014415168b170bd20ec158a2c40feb70a9f888c3cKenny Root            mKeyStore.put(Credentials.VPN + profile.key, profile.encode(), KeyStore.UID_SELF,
24114415168b170bd20ec158a2c40feb70a9f888c3cKenny Root                    KeyStore.FLAG_ENCRYPTED);
242310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
243310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Update the preference.
244310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            VpnPreference preference = mPreferences.get(profile.key);
245310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference != null) {
246310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(profile.key);
247310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference.update(profile);
248310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            } else {
249310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                preference = new VpnPreference(getActivity(), profile);
2509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                preference.setOnPreferenceClickListener(this);
251310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.put(profile.key, preference);
252310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().addPreference(preference);
253310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
254310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
255310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // If we are not editing, connect!
256310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (!mDialog.isEditing()) {
257413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                try {
258413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    connect(profile);
259413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                } catch (Exception e) {
260413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                    Log.e(TAG, "connect", e);
261413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                }
262310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
263310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
264310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
265310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
266310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
267310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
268310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
269310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
270310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return;
271310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
272310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
273310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (info instanceof AdapterContextMenuInfo) {
274310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Preference preference = (Preference) getListView().getItemAtPosition(
275310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    ((AdapterContextMenuInfo) info).position);
276310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
277310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnProfile profile = ((VpnPreference) preference).getProfile();
278310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mSelectedKey = profile.key;
279310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.setHeaderTitle(profile.name);
280310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
281310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
282310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
283310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
284310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
285310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
286310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
287310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onContextItemSelected(MenuItem item) {
288310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
289310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
290310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
291310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
292310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
293310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference preference = mPreferences.get(mSelectedKey);
294310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference == null) {
295310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onContextItemSelected() is called but no preference is found");
296310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return false;
297310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
298310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
299310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        switch (item.getItemId()) {
300310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_edit:
301310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
302310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.setOnDismissListener(this);
303310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mDialog.show();
304310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
305310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            case R.string.vpn_menu_delete:
306310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                disconnect(mSelectedKey);
307310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                getPreferenceScreen().removePreference(preference);
308310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mPreferences.remove(mSelectedKey);
309310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                mKeyStore.delete(Credentials.VPN + mSelectedKey);
310310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                return true;
311310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
312310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return false;
313310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
314310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
315310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
316310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean onPreferenceClick(Preference preference) {
317310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (mDialog != null) {
318310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
319310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return true;
320310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
321310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
322310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (preference instanceof VpnPreference) {
32397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            VpnProfile profile = ((VpnPreference) preference).getProfile();
32497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mInfo != null && profile.key.equals(mInfo.key) &&
32597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.state == LegacyVpnInfo.STATE_CONNECTED) {
32697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                try {
32797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo.intent.send();
32897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    return true;
32997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                } catch (Exception e) {
33097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    // ignore
33197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
33297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
33397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mDialog = new VpnDialog(getActivity(), this, profile, false);
334310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        } else {
335310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            // Generate a new key. Here we just use the current time.
336310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            long millis = System.currentTimeMillis();
337310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            while (mPreferences.containsKey(Long.toHexString(millis))) {
338310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                ++millis;
339310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
340310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mDialog = new VpnDialog(getActivity(), this,
341310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    new VpnProfile(Long.toHexString(millis)), true);
342310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
343310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.setOnDismissListener(this);
344310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        mDialog.show();
345310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
346310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
347310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
348310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
349310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public boolean handleMessage(Message message) {
350bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh        mUpdater.removeMessages(0);
351310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
352310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        if (isResumed()) {
35397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
35497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                LegacyVpnInfo info = mService.getLegacyVpnInfo();
35597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (mInfo != null) {
35697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(mInfo.key);
35797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
35897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(-1);
35997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
36097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    mInfo = null;
36197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
36297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                if (info != null) {
36397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    VpnPreference preference = mPreferences.get(info.key);
36497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    if (preference != null) {
36597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        preference.update(info.state);
36697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        mInfo = info;
36797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                    }
36897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                }
36997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
37097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
37197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
372bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            mUpdater.sendEmptyMessageDelayed(0, 1000);
373310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
374310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return true;
375310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
376310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
377f35130dd79c100097025585c0b5a5ebbd38a414eChia-chi Yeh    private void connect(VpnProfile profile) throws Exception {
378745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        try {
379745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            mService.startLegacyVpn(profile);
380745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey        } catch (IllegalStateException e) {
381745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkey            Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
382d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yeh        }
383310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
384310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
385310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private void disconnect(String key) {
38697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        if (mInfo != null && key.equals(mInfo.key)) {
38797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            try {
38897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
38997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } catch (Exception e) {
39097fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                // ignore
39197fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
39297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
393310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
394310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
395b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    @Override
396b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    protected int getHelpResource() {
397b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani        return R.string.help_url_vpn;
398b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    }
399b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani
4009fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    private static class VpnPreference extends Preference {
401310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        private VpnProfile mProfile;
40297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        private int mState = -1;
403310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
404310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnPreference(Context context, VpnProfile profile) {
405310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            super(context);
406310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setPersistent(false);
40797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            setOrder(0);
408310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
409310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
410310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
411310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
412310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
413310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        VpnProfile getProfile() {
414310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return mProfile;
415310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
416310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
417310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update(VpnProfile profile) {
418310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            mProfile = profile;
419310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            update();
420310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
421310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
42297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        void update(int state) {
42397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            mState = state;
42497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            update();
42597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
42697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh
427310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        void update() {
42897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            if (mState < 0) {
429310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                String[] types = getContext().getResources()
430310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        .getStringArray(R.array.vpn_types_long);
431310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                setSummary(types[mProfile.type]);
43297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            } else {
43397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                String[] states = getContext().getResources()
43497fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                        .getStringArray(R.array.vpn_states);
43597fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh                setSummary(states[mState]);
436310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
437310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            setTitle(mProfile.name);
43897fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            notifyHierarchyChanged();
439310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
440310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
441310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        @Override
442310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        public int compareTo(Preference preference) {
443bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh            int result = -1;
444310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            if (preference instanceof VpnPreference) {
445310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                VpnPreference another = (VpnPreference) preference;
446310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                if ((result = another.mState - mState) == 0 &&
447310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
448310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                        (result = mProfile.type - another.mProfile.type) == 0) {
449310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                    result = mProfile.key.compareTo(another.mProfile.key);
450310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                }
451310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
452310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            return result;
453310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
454310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
4559fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    /**
4579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     * Dialog to configure always-on VPN.
4589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey     */
4599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public static class LockdownConfigFragment extends DialogFragment {
4609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<VpnProfile> mProfiles;
4619fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private List<CharSequence> mTitles;
4629fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private int mCurrentIndex;
4639fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4649fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static class TitleAdapter extends ArrayAdapter<CharSequence> {
4659fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            public TitleAdapter(Context context, List<CharSequence> objects) {
4669fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                super(context, com.android.internal.R.layout.select_dialog_singlechoice_holo,
4679fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        android.R.id.text1, objects);
4689fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4699fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4709fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4719fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public static void show(VpnSettings parent) {
4729fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            if (!parent.isAdded()) return;
4739fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4749fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LockdownConfigFragment dialog = new LockdownConfigFragment();
4759fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
4769fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4779fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4789fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private static String getStringOrNull(KeyStore keyStore, String key) {
4799fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final byte[] value = keyStore.get(Credentials.LOCKDOWN_VPN);
4809fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return value == null ? null : new String(value);
4819fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4829fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4839fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        private void initProfiles(KeyStore keyStore, Resources res) {
4849fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
4859fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
486f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey            mProfiles = loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
4879fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles = Lists.newArrayList();
4889fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mTitles.add(res.getText(R.string.vpn_lockdown_none));
4899fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            mCurrentIndex = 0;
4909fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4919fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (VpnProfile profile : mProfiles) {
4929fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                if (TextUtils.equals(profile.key, lockdownKey)) {
4939fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    mCurrentIndex = mTitles.size();
4949fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
4959fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                mTitles.add(profile.name);
4969fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4979fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4989fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4999fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        @Override
5009fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        public Dialog onCreateDialog(Bundle savedInstanceState) {
5019fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final Context context = getActivity();
5029fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final KeyStore keyStore = KeyStore.getInstance();
5039fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5049fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            initProfiles(keyStore, context.getResources());
5059fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5069fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
5079fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
5089fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5099fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setTitle(R.string.vpn_menu_lockdown);
5109fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5119fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
5129fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            final ListView listView = (ListView) view.findViewById(android.R.id.list);
5139fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5149fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setAdapter(new TitleAdapter(context, mTitles));
5159fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            listView.setItemChecked(mCurrentIndex, true);
5169fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setView(view);
5179fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5189fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5199fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                @Override
5209fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                public void onClick(DialogInterface dialog, int which) {
5219fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    final int newIndex = listView.getCheckedItemPosition();
5229fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (mCurrentIndex == newIndex) return;
5239fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5249fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    if (newIndex == 0) {
5259fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        keyStore.delete(Credentials.LOCKDOWN_VPN);
5269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    } else {
5289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        final VpnProfile profile = mProfiles.get(newIndex - 1);
5299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        if (!profile.isValidLockdownProfile()) {
5309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            Toast.makeText(context, R.string.vpn_lockdown_config_error,
5319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                                    Toast.LENGTH_LONG).show();
5329fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                            return;
5339fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        }
53414415168b170bd20ec158a2c40feb70a9f888c3cKenny Root                        keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(),
53514415168b170bd20ec158a2c40feb70a9f888c3cKenny Root                                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
5369fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    }
5379fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5389fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    // kick profiles since we changed them
5399fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ConnectivityManager.from(getActivity()).updateLockdownVpn();
5409fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5419fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            });
5429fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
5439fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            return builder.create();
5449fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5459fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
5469fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
547f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey    private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
5489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final ArrayList<VpnProfile> result = Lists.newArrayList();
5499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        final String[] keys = keyStore.saw(Credentials.VPN);
5509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        if (keys != null) {
5519fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            for (String key : keys) {
5529fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                final VpnProfile profile = VpnProfile.decode(
5539fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                        key, keyStore.get(Credentials.VPN + key));
554f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkey                if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
5559fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    result.add(profile);
5569fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
5579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
5589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
5599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return result;
5609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
561310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh}
562