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
197bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.annotation.UiThread;
207bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.annotation.WorkerThread;
212bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.app.AppOpsManager;
22310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.content.Context;
232bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.content.Intent;
242bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.content.pm.PackageInfo;
252bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.content.pm.PackageManager;
262bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.net.ConnectivityManager.NetworkCallback;
277bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.net.ConnectivityManager;
28bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yehimport android.net.IConnectivityManager;
292bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.net.Network;
302bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.net.NetworkCapabilities;
312bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.net.NetworkRequest;
32310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Bundle;
33310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Handler;
34310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.os.Message;
352bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.os.RemoteException;
36bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yehimport android.os.ServiceManager;
372bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport android.os.UserHandle;
38ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynoldsimport android.os.UserManager;
39310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.security.Credentials;
40310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.security.KeyStore;
4139b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.Preference;
4239b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.PreferenceGroup;
4339b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.PreferenceScreen;
447bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.util.ArrayMap;
457bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.util.ArraySet;
467bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport android.util.Log;
47310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.Menu;
489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport android.view.MenuInflater;
49310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yehimport android.view.MenuItem;
50310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
519d1bfd1e8de6e46137a9571507c03526880d6a46Chris Wrenimport com.android.internal.logging.MetricsProto.MetricsEvent;
5297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
53d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yehimport com.android.internal.net.VpnConfig;
54c6e84c09590ec5e4da287fba32dd53775156ae76Jeff Sharkeyimport com.android.internal.net.VpnProfile;
55f5de1db28a61d159e62ef42f64a2cdcb316d0c2cJeff Sharkeyimport com.android.internal.util.ArrayUtils;
5614c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Changimport com.android.settings.GearPreference;
5714c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Changimport com.android.settings.GearPreference.OnGearClickListener;
58745e6212e71f6fe9da863c9c9f7b092542929449Jeff Sharkeyimport com.android.settings.R;
597dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shankaimport com.android.settings.RestrictedSettingsFragment;
607dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shankaimport com.android.settingslib.RestrictedLockUtils;
619fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport com.google.android.collect.Lists;
62310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
639fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport java.util.ArrayList;
647bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport java.util.Collections;
659fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkeyimport java.util.List;
667bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport java.util.Map;
677bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Leeimport java.util.Set;
68310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
692bd92d5d0685144aad566b9d29454fb519ff0371Robin Leeimport static android.app.AppOpsManager.OP_ACTIVATE_VPN;
70310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
712bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee/**
722bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee * Settings screen listing VPNs. Configured VPNs and networks managed by apps
732bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee * are shown in the same list.
742bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee */
757dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shankapublic class VpnSettings extends RestrictedSettingsFragment implements
762bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        Handler.Callback, Preference.OnPreferenceClickListener {
772bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private static final String LOG_TAG = "VpnSettings";
789fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
7901b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    private static final int RESCAN_MESSAGE = 0;
8001b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    private static final int RESCAN_INTERVAL_MS = 1000;
8101b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
822bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
832bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
842bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
852bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
862bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            .build();
878b78299d94ebb09fb9bf2bc7fb0015d2f1950839Jeff Sharkey
882bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private final IConnectivityManager mConnectivityService = IConnectivityManager.Stub
8997fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
902bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private ConnectivityManager mConnectivityManager;
912bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private UserManager mUserManager;
922bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee
93310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    private final KeyStore mKeyStore = KeyStore.getInstance();
94310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
9514c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang    private Map<String, LegacyVpnPreference> mLegacyVpnPreferences = new ArrayMap<>();
967bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>();
97bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh
98bbb5094be6673929bcb808cdeaf748a73be4dbf0Chia-chi Yeh    private Handler mUpdater;
992bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private LegacyVpnInfo mConnectedLegacyVpn;
100310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
101ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds    private boolean mUnavailable;
102ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds
1037dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka    public VpnSettings() {
1047dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka        super(UserManager.DISALLOW_CONFIG_VPN);
1057dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka    }
1067dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka
107310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
1088a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren    protected int getMetricsCategory() {
1099d1bfd1e8de6e46137a9571507c03526880d6a46Chris Wren        return MetricsEvent.VPN;
1108a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren    }
1118a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren
1128a963babe2e36b7a41f77b8d2598c97658196e58Chris Wren    @Override
113eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee    public void onActivityCreated(Bundle savedInstanceState) {
114eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee        super.onActivityCreated(savedInstanceState);
1159fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1162bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
1172bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
1182bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee
119eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee        mUnavailable = isUiRestricted();
120eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee        setHasOptionsMenu(!mUnavailable);
121eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee
122310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        addPreferencesFromResource(R.xml.vpn_settings2);
1232bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    }
124310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
1252bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    @Override
1269fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1279fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        super.onCreateOptionsMenu(menu, inflater);
1289fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        inflater.inflate(R.menu.vpn, menu);
1299fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1309fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1319fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
13207d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    public void onPrepareOptionsMenu(Menu menu) {
13307d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey        super.onPrepareOptionsMenu(menu);
13407d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
1354198607ee274ffc9749ac069f7047d9e3a4da43eRobin Lee        // Disable all actions if VPN configuration has been disallowed
1364198607ee274ffc9749ac069f7047d9e3a4da43eRobin Lee        for (int i = 0; i < menu.size(); i++) {
1377dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            if (isUiRestrictedByOnlyAdmin()) {
1387dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getPrefContext(),
1397dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                        menu.getItem(i), getRestrictionEnforcedAdmin());
1407dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            } else {
1417dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                menu.getItem(i).setEnabled(!mUnavailable);
1427dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            }
1434198607ee274ffc9749ac069f7047d9e3a4da43eRobin Lee        }
14407d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    }
14507d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey
14607d465ddb8629f128dde193d22de28cfd56663caJeff Sharkey    @Override
1479fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    public boolean onOptionsItemSelected(MenuItem item) {
1489fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        switch (item.getItemId()) {
1499fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            case R.id.vpn_create: {
1509fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                // Generate a new key. Here we just use the current time.
1519fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                long millis = System.currentTimeMillis();
15214c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                while (mLegacyVpnPreferences.containsKey(Long.toHexString(millis))) {
1539fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                    ++millis;
1549fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
1552bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                VpnProfile profile = new VpnProfile(Long.toHexString(millis));
1562bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                ConfigDialogFragment.show(this, profile, true /* editing */, false /* exists */);
1579fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                return true;
1589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
1599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
1609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return super.onOptionsItemSelected(item);
1619fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
1629fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
1639fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    @Override
164310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    public void onResume() {
165310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        super.onResume();
166310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
167ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds        if (mUnavailable) {
168dd142295da8912c1f2d972341abb2e5b761075e9Robin Lee            // Show a message to explain that VPN settings have been disabled
1697dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            if (!isUiRestrictedByOnlyAdmin()) {
1707dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka                getEmptyTextView().setText(R.string.vpn_settings_not_available);
171ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds            }
1727dbbe131683c231f6820bca4d67853649a4836f8Sudheer Shanka            getPreferenceScreen().removeAll();
173ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds            return;
174eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee        } else {
175eb034eb65e99e3abee7e9951bfd02456a9fcf7deRobin Lee            getEmptyTextView().setText(R.string.vpn_no_vpns_added);
176ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds        }
177ee27b9de8f2ab17d50b90dd8c13546aebb4e9fc1Julia Reynolds
17801b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        // Start monitoring
17901b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
18001b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
18101b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        // Trigger a refresh
18201b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        if (mUpdater == null) {
18301b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee            mUpdater = new Handler(this);
18401b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        }
18501b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
18601b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    }
18701b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
18801b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    @Override
18901b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    public void onPause() {
190614fc178bf028ef66bea24a70ae476a91833a7f1Robin Lee        if (mUnavailable) {
191dd142295da8912c1f2d972341abb2e5b761075e9Robin Lee            super.onPause();
192dd142295da8912c1f2d972341abb2e5b761075e9Robin Lee            return;
193dd142295da8912c1f2d972341abb2e5b761075e9Robin Lee        }
194dd142295da8912c1f2d972341abb2e5b761075e9Robin Lee
1955f6f4cc572589674196e836904c4b27f84f21934Robin Lee        // Stop monitoring
19601b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
19701b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
19801b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        if (mUpdater != null) {
19901b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee            mUpdater.removeCallbacksAndMessages(null);
20001b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        }
20101b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
20201b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        super.onPause();
2032bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    }
204310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
20501b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    @Override
20601b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee    public boolean handleMessage(Message message) {
20701b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        mUpdater.removeMessages(RESCAN_MESSAGE);
20801b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
20937b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee        // Run heavy RPCs before switching to UI thread
2107bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore);
211003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        final List<AppVpnInfo> vpnApps = getVpnApps(getActivity(), /* includeProfiles */ true);
2122bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee
213b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee        final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
214b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee        final Set<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
215310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
21614c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        final Set<AppVpnInfo> alwaysOnAppVpnInfos = getAlwaysOnAppVpnInfos();
21714c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        final String lockdownVpnKey = VpnUtils.getLockdownVpn();
218cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee
21937b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee        // Refresh list of VPNs
2207bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        getActivity().runOnUiThread(new Runnable() {
2217bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            @Override
2227bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            public void run() {
22337b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee                // Can't do anything useful if the context has gone away
22437b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee                if (!isAdded()) {
22537b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee                    return;
22637b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee                }
22737b179fa8cdde8e00a745cbf030bcb1836e921c4Robin Lee
2287bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                // Find new VPNs by subtracting existing ones from the full set
2297bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                final Set<Preference> updates = new ArraySet<>();
230310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
2317bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                for (VpnProfile profile : vpnProfiles) {
23214c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                    LegacyVpnPreference p = findOrCreatePreference(profile);
233b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    if (connectedLegacyVpns.containsKey(profile.key)) {
234b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                        p.setState(connectedLegacyVpns.get(profile.key).state);
235b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    } else {
236b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                        p.setState(LegacyVpnPreference.STATE_NONE);
237b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    }
23814c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                    p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key));
2397bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    updates.add(p);
2407bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                }
2417bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                for (AppVpnInfo app : vpnApps) {
2427bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    AppPreference p = findOrCreatePreference(app);
243b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    if (connectedAppVpns.contains(app)) {
244b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                        p.setState(AppPreference.STATE_CONNECTED);
245b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    } else {
246b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                        p.setState(AppPreference.STATE_DISCONNECTED);
247b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                    }
24814c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                    p.setAlwaysOn(alwaysOnAppVpnInfos.contains(app));
2497bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    updates.add(p);
25001b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee                }
25101b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
252b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                // Trim out deleted VPN preferences
25314c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                mLegacyVpnPreferences.values().retainAll(updates);
2547bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                mAppPreferences.values().retainAll(updates);
2557bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
2567bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                final PreferenceGroup vpnGroup = getPreferenceScreen();
2577bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                for (int i = vpnGroup.getPreferenceCount() - 1; i >= 0; i--) {
2587bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    Preference p = vpnGroup.getPreference(i);
2597bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    if (updates.contains(p)) {
2607bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                        updates.remove(p);
2617bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    } else {
2627bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                        vpnGroup.removePreference(p);
2637bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    }
2647bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                }
2657bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
2667bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                // Show any new preferences on the screen
2677bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                for (Preference pref : updates) {
2687bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    vpnGroup.addPreference(pref);
2697bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                }
27001b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee            }
2717bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        });
27201b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee
27301b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        mUpdater.sendEmptyMessageDelayed(RESCAN_MESSAGE, RESCAN_INTERVAL_MS);
27401b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee        return true;
275310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
276310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
277310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    @Override
2782bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    public boolean onPreferenceClick(Preference preference) {
27914c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        if (preference instanceof LegacyVpnPreference) {
28014c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            LegacyVpnPreference pref = (LegacyVpnPreference) preference;
28114c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            VpnProfile profile = pref.getProfile();
2822bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) &&
2832bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) {
284413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                try {
2852bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    mConnectedLegacyVpn.intent.send();
2862bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    return true;
287413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                } catch (Exception e) {
2887bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    Log.w(LOG_TAG, "Starting config intent failed", e);
289413b171159cec5ad1e7b3cf4f1f842b5f2debc05Chia-chi Yeh                }
290310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
2912bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            ConfigDialogFragment.show(this, profile, false /* editing */, true /* exists */);
2922bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            return true;
2932bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        } else if (preference instanceof AppPreference) {
2942bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            AppPreference pref = (AppPreference) preference;
2952bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
296310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
2972bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            if (!connected) {
2982bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                try {
2997bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    UserHandle user = UserHandle.of(pref.getUserId());
3002bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    Context userContext = getActivity().createPackageContextAsUser(
3012bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                            getActivity().getPackageName(), 0 /* flags */, user);
3022bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    PackageManager pm = userContext.getPackageManager();
3032bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    Intent appIntent = pm.getLaunchIntentForPackage(pref.getPackageName());
3042bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    if (appIntent != null) {
3052bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                        userContext.startActivityAsUser(appIntent, user);
3062bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                        return true;
3072bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    }
3082bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                } catch (PackageManager.NameNotFoundException nnfe) {
3097bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    Log.w(LOG_TAG, "VPN provider does not exist: " + pref.getPackageName(), nnfe);
3102bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                }
311310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
312310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
3137bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            // Already connected or no launch intent available - show an info dialog
3142bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            PackageInfo pkgInfo = pref.getPackageInfo();
315ab6a65c03b2505ed2cce0226e11ef2d566699cf4Robin Lee            AppDialogFragment.show(this, pkgInfo, pref.getLabel(), false /* editing */, connected);
3162bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            return true;
317310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
318310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        return false;
319310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh    }
320310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
3217bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    @Override
3227bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    protected int getHelpResource() {
3237bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        return R.string.help_url_vpn;
3247bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    }
3257bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
32614c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang    private OnGearClickListener mGearListener = new OnGearClickListener() {
3272bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        @Override
32814c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        public void onGearClick(GearPreference p) {
32914c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            if (p instanceof LegacyVpnPreference) {
33014c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                LegacyVpnPreference pref = (LegacyVpnPreference) p;
3312bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */,
3322bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                        true /* exists */);
33314c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            } else if (p instanceof AppPreference) {
33414c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                AppPreference pref = (AppPreference) p;;
33516da2aa45000dd32213a3d84baacc82cd2b16e5fVictor Chang                AppManagementFragment.show(getPrefContext(), pref);
336310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh            }
337310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh        }
3382bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    };
339310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
3402bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    private NetworkCallback mNetworkCallback = new NetworkCallback() {
3412bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        @Override
3422bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        public void onAvailable(Network network) {
3432bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            if (mUpdater != null) {
34401b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee                mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
3452bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            }
346d95ec871138ebb367cfd8b67b4814438ac30c628Chia-chi Yeh        }
347310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
3482bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        @Override
3492bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        public void onLost(Network network) {
3502bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            if (mUpdater != null) {
35101b35bcae307907bd8aaaa9cf23fa50f70e5f491Robin Lee                mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
35297fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh            }
35397fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
3542bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    };
355310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
3567bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    @UiThread
35714c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang    private LegacyVpnPreference findOrCreatePreference(VpnProfile profile) {
35814c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        LegacyVpnPreference pref = mLegacyVpnPreferences.get(profile.key);
3597bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        if (pref == null) {
36014c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            pref = new LegacyVpnPreference(getPrefContext());
36114c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            pref.setOnGearClickListener(mGearListener);
3627bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            pref.setOnPreferenceClickListener(this);
36314c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            mLegacyVpnPreferences.put(profile.key, pref);
3647bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        }
365b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee        // This may change as the profile can update and keep the same key.
3667bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        pref.setProfile(profile);
3677bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        return pref;
3687bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    }
3697bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
3707bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    @UiThread
3717bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    private AppPreference findOrCreatePreference(AppVpnInfo app) {
3727bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        AppPreference pref = mAppPreferences.get(app);
3737bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        if (pref == null) {
374b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee            pref = new AppPreference(getPrefContext(), app.userId, app.packageName);
37514c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            pref.setOnGearClickListener(mGearListener);
3767bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            pref.setOnPreferenceClickListener(this);
3777bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            mAppPreferences.put(app, pref);
3787bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        }
3797bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        return pref;
380b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani    }
381b0b37ae21c172491bc170659b5f429601858ddc1Amith Yamasani
3827bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    @WorkerThread
383b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee    private Map<String, LegacyVpnInfo> getConnectedLegacyVpns() {
3847bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        try {
3857bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            mConnectedLegacyVpn = mConnectivityService.getLegacyVpnInfo(UserHandle.myUserId());
3867bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            if (mConnectedLegacyVpn != null) {
387b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee                return Collections.singletonMap(mConnectedLegacyVpn.key, mConnectedLegacyVpn);
3887bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            }
3897bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        } catch (RemoteException e) {
3907bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            Log.e(LOG_TAG, "Failure updating VPN list with connected legacy VPNs", e);
3917bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        }
392b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee        return Collections.emptyMap();
3937bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    }
3947bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
3957bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    @WorkerThread
396b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee    private Set<AppVpnInfo> getConnectedAppVpns() {
3977bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        // Mark connected third-party services
398b166ea26687f7b72cbc544438436ae85167ca2f1Robin Lee        Set<AppVpnInfo> connections = new ArraySet<>();
3997bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        try {
4007bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            for (UserHandle profile : mUserManager.getUserProfiles()) {
4017bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                VpnConfig config = mConnectivityService.getVpnConfig(profile.getIdentifier());
4027bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                if (config != null && !config.legacy) {
4037bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    connections.add(new AppVpnInfo(profile.getIdentifier(), config.user));
4047bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                }
4057bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            }
4067bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        } catch (RemoteException e) {
4077bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee            Log.e(LOG_TAG, "Failure updating VPN list with connected app VPNs", e);
4087bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        }
4097bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        return connections;
4107bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee    }
4117bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee
412cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee    @WorkerThread
41314c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang    private Set<AppVpnInfo> getAlwaysOnAppVpnInfos() {
41414c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang        Set<AppVpnInfo> result = new ArraySet<>();
415cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee        for (UserHandle profile : mUserManager.getUserProfiles()) {
416cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee            final int profileId = profile.getIdentifier();
41714c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            final String packageName = mConnectivityManager.getAlwaysOnVpnPackageForUser(profileId);
41814c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang            if (packageName != null) {
41914c2ac4dcb17c27e065384e69c9f2e24dcbf22aaVictor Chang                result.add(new AppVpnInfo(profileId, packageName));
420cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee            }
421cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee        }
422cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee        return result;
423cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee    }
424cac0dddd2ad4ca44c4dd5c0f57cca9439edbf773Robin Lee
425003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee    static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles) {
4267bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee        List<AppVpnInfo> result = Lists.newArrayList();
427310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh
428003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        final Set<Integer> profileIds;
429003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        if (includeProfiles) {
430003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee            profileIds = new ArraySet<>();
431003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee            for (UserHandle profile : UserManager.get(context).getUserProfiles()) {
432003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee                profileIds.add(profile.getIdentifier());
433003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee            }
434003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        } else {
435003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee            profileIds = Collections.singleton(UserHandle.myUserId());
43697fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh        }
43797fd85fd9776f3fb5c35217e82bcbcb5aea8416dChia-chi Yeh
4382bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        // Fetch VPN-enabled apps from AppOps.
439003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        AppOpsManager aom = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
4402bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        List<AppOpsManager.PackageOps> apps = aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN});
4412bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        if (apps != null) {
4422bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            for (AppOpsManager.PackageOps pkg : apps) {
4432bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                int userId = UserHandle.getUserId(pkg.getUid());
444003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee                if (!profileIds.contains(userId)) {
4452bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    // Skip packages for users outside of our profile group.
4462bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    continue;
447310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh                }
4482bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                // Look for a MODE_ALLOWED permission to activate VPN.
4492bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                boolean allowed = false;
4502bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                for (AppOpsManager.OpEntry op : pkg.getOps()) {
4512bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    if (op.getOp() == OP_ACTIVATE_VPN &&
4522bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                            op.getMode() == AppOpsManager.MODE_ALLOWED) {
4532bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                        allowed = true;
4542bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                    }
4552bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                }
4562bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                if (allowed) {
4577bf8654a5c1dfca8552aa635e552ded374e6fd46Robin Lee                    result.add(new AppVpnInfo(userId, pkg.getPackageName()));
4589fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey                }
4599fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4609fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
461003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee
462003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee        Collections.sort(result);
4632bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        return result;
4642bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee    }
4659fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
466003a4b563a61144513688f6508101c7d2ee48f6fRobin Lee    static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
4672bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee        final ArrayList<VpnProfile> result = Lists.newArrayList();
4689fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey
4693848407d0c2fad920419d6b925e0c8e374502680Alex Klyubin        for (String key : keyStore.list(Credentials.VPN)) {
4702bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key));
4712bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee            if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
4722bd92d5d0685144aad566b9d29454fb519ff0371Robin Lee                result.add(profile);
4739fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey            }
4749fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        }
4759fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey        return result;
4769fd7ac1ec7c0c3984f225a237bd6e3dbafc7f831Jeff Sharkey    }
477310d619acba5cd1f7c8a55aa7906ed4f1c011bd8Chia-chi Yeh}
478