169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey/*
269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project
369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey *
469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * you may not use this file except in compliance with the License.
669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * You may obtain a copy of the License at
769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey *
869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey *
1069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
1169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * See the License for the specific language governing permissions and
1469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * limitations under the License.
1569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey */
1669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
1769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeypackage com.android.server.net;
1869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
1969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport static android.Manifest.permission.CONNECTIVITY_INTERNAL;
20b41c9f7f39939cee8d226eb5e506c3f0573f44f5Xiaohui Chenimport static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
2115e47235c055495ec0ccc24768a6746a960d3a61Amith Yamasaniimport static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
2215e47235c055495ec0ccc24768a6746a960d3a61Amith Yamasaniimport static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
23a249aee10b621a94c986f4823d840e33c2a7d480Robin Leeimport static android.provider.Settings.ACTION_VPN_SETTINGS;
2469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
2569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.Notification;
2669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.NotificationManager;
2769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.PendingIntent;
2869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.BroadcastReceiver;
2969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.Context;
3069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.Intent;
3169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.IntentFilter;
320cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colittiimport android.net.ConnectivityManager;
3369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.LinkProperties;
344ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.net.LinkAddress;
3569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo;
3669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo.DetailedState;
3769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo.State;
3815e47235c055495ec0ccc24768a6746a960d3a61Amith Yamasaniimport android.net.NetworkPolicyManager;
3969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.os.INetworkManagementService;
4069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.os.RemoteException;
4169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.security.Credentials;
4269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.security.KeyStore;
43ad4cd0c01966017e2f51ec3d23d06de3874f100cLorenzo Colittiimport android.system.Os;
4469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.text.TextUtils;
4569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.util.Slog;
4669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
4769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.R;
48282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wrenimport com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
4969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.net.VpnConfig;
5069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
51af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitschimport com.android.internal.notification.SystemNotificationChannels;
5269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.util.Preconditions;
5369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.server.ConnectivityService;
5491c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkeyimport com.android.server.EventLogTags;
5569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.server.connectivity.Vpn;
5669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
574ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport java.util.List;
584ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey/**
6069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
6169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * connected and kicks off VPN connection, managing any required {@code netd}
6269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * firewall rules.
6369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey */
6469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeypublic class LockdownVpnTracker {
6569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final String TAG = "LockdownVpnTracker";
6669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
6769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    /** Number of VPN attempts before waiting for user intervention. */
6869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final int MAX_ERROR_COUNT = 4;
6969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
7069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
714fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
72ad4cd0c01966017e2f51ec3d23d06de3874f100cLorenzo Colitti    private static final int ROOT_UID = 0;
73ad4cd0c01966017e2f51ec3d23d06de3874f100cLorenzo Colitti
7469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Context mContext;
7569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final INetworkManagementService mNetService;
7669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final ConnectivityService mConnService;
7769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Vpn mVpn;
7869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final VpnProfile mProfile;
7969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Object mStateLock = new Object();
8169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
824fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey    private final PendingIntent mConfigIntent;
834fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey    private final PendingIntent mResetIntent;
8469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private String mAcceptedEgressIface;
8669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private String mAcceptedIface;
874ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker    private List<LinkAddress> mAcceptedSourceAddr;
8869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private int mErrorCount;
9069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
9169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public static boolean isEnabled() {
9269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
9369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
9469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
9569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public LockdownVpnTracker(Context context, INetworkManagementService netService,
9669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            ConnectivityService connService, Vpn vpn, VpnProfile profile) {
9769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext = Preconditions.checkNotNull(context);
9869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mNetService = Preconditions.checkNotNull(netService);
9969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mConnService = Preconditions.checkNotNull(connService);
10069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mVpn = Preconditions.checkNotNull(vpn);
10169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mProfile = Preconditions.checkNotNull(profile);
10269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
1034fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
1044fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
1054fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
106580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
107580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
108580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
10969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
11069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
11169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
11269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        @Override
11369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        public void onReceive(Context context, Intent intent) {
11469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            reset();
11569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
11669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    };
11769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
11869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    /**
11969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     * Watch for state changes to both active egress network, kicking off a VPN
12069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     * connection when ready, or setting firewall rules once VPN is connected.
12169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     */
12269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void handleStateChangedLocked() {
12369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
12469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
12569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final LinkProperties egressProp = mConnService.getActiveLinkProperties();
12669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
12769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
12869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
12969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
13069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        // Restart VPN when egress network disconnected or changed
13169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final boolean egressDisconnected = egressInfo == null
13269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                || State.DISCONNECTED.equals(egressInfo.getState());
13369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final boolean egressChanged = egressProp == null
13469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
1350cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti
1360cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti        final String egressTypeName = (egressInfo == null) ?
1370cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti                null : ConnectivityManager.getNetworkTypeName(egressInfo.getType());
1380cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti        final String egressIface = (egressProp == null) ?
1390cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti                null : egressProp.getInterfaceName();
1400cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti        Slog.d(TAG, "handleStateChanged: egress=" + egressTypeName +
1410cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti                " " + mAcceptedEgressIface + "->" + egressIface);
1420cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti
14369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (egressDisconnected || egressChanged) {
14469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mAcceptedEgressIface = null;
145b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            mVpn.stopLegacyVpnPrivileged();
14669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
14757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        if (egressDisconnected) {
14857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey            hideNotification();
14957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey            return;
15057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        }
15169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
15291c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        final int egressType = egressInfo.getType();
15391c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
15491c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey            EventLogTags.writeLockdownVpnError(egressType);
15591c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        }
15691c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey
15769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mErrorCount > MAX_ERROR_COUNT) {
15869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
15969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
16069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
16169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (mProfile.isValidLockdownProfile()) {
16269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                Slog.d(TAG, "Active network connected; starting VPN");
16391c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey                EventLogTags.writeLockdownVpnConnecting(egressType);
16469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
16569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
16669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mAcceptedEgressIface = egressProp.getInterfaceName();
167421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                try {
168b21298a686b04d55ff97223dd317497845713f4bJeff Davidson                    // Use the privileged method because Lockdown VPN is initiated by the system, so
169b21298a686b04d55ff97223dd317497845713f4bJeff Davidson                    // no additional permission checks are necessary.
170b21298a686b04d55ff97223dd317497845713f4bJeff Davidson                    mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp);
171421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                } catch (IllegalStateException e) {
172421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    mAcceptedEgressIface = null;
173421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    Slog.e(TAG, "Failed to start VPN", e);
174421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
175421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                }
17669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            } else {
17769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
17869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
17969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
18069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else if (vpnInfo.isConnected() && vpnConfig != null) {
18269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            final String iface = vpnConfig.interfaze;
1834ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
18469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (TextUtils.equals(iface, mAcceptedIface)
1864ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                  && sourceAddrs.equals(mAcceptedSourceAddr)) {
18769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                return;
18869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
18969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
1904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Slog.d(TAG, "VPN connected using iface=" + iface +
1914ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    ", sourceAddr=" + sourceAddrs.toString());
19291c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey            EventLogTags.writeLockdownVpnConnected(egressType);
19369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
19469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
195f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            final NetworkInfo clone = new NetworkInfo(egressInfo);
196f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            augmentNetworkInfo(clone);
197f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            mConnService.sendConnectedBroadcast(clone);
19869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
19969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
20069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
20169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void init() {
202580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        synchronized (mStateLock) {
203580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            initLocked();
204580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        }
205580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    }
206580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey
207580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private void initLocked() {
208580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        Slog.d(TAG, "initLocked()");
20969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
21057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mVpn.setEnableTeardown(false);
211c3736bc10da63d6a351d3f8e7781ff1d67ecc9a6Robin Lee        mVpn.setLockdown(true);
21269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
21369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
21469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
21569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
216aca5e7e3dc369923a8655f1de84bba903b3b1143Robin Lee        handleStateChangedLocked();
21769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
21869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
21969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void shutdown() {
220580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        synchronized (mStateLock) {
221580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            shutdownLocked();
222580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        }
223580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    }
224580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey
225580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private void shutdownLocked() {
226580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        Slog.d(TAG, "shutdownLocked()");
22769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
22869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mAcceptedEgressIface = null;
22969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mErrorCount = 0;
23069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
231b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        mVpn.stopLegacyVpnPrivileged();
232c3736bc10da63d6a351d3f8e7781ff1d67ecc9a6Robin Lee        mVpn.setLockdown(false);
23369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        hideNotification();
23469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
23569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext.unregisterReceiver(mResetReceiver);
23657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mVpn.setEnableTeardown(true);
23769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
23869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
23969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void reset() {
2400cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti        Slog.d(TAG, "reset()");
24169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
242580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            // cycle tracker, reset error count, and trigger retry
243580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            shutdownLocked();
244580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            initLocked();
24569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
24669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
24769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
24869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
2490cb7903ddedbbb8a8171926e4460b74af589369dLorenzo Colitti    public void onNetworkInfoChanged() {
25069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
25169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
25269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
25369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
25469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
25569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void onVpnStateChanged(NetworkInfo info) {
25669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (info.getDetailedState() == DetailedState.FAILED) {
25769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mErrorCount++;
25869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
25969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
26069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
26169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
26269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
26369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
264f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    public void augmentNetworkInfo(NetworkInfo info) {
2650b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey        if (info.isConnected()) {
2660b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey            final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
2670b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey            info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
2680b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey        }
26969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
27069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
27169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void showNotification(int titleRes, int iconRes) {
272af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch        final Notification.Builder builder =
273af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                new Notification.Builder(mContext, SystemNotificationChannels.VPN)
274af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setWhen(0)
275af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setSmallIcon(iconRes)
276af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setContentTitle(mContext.getString(titleRes))
277af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setContentText(mContext.getString(R.string.vpn_lockdown_config))
278af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setContentIntent(mConfigIntent)
279af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setOngoing(true)
280af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
281af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                                mResetIntent)
282af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        .setColor(mContext.getColor(
283af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                                com.android.internal.R.color.system_notification_accent_color));
2844fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
285282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wren        NotificationManager.from(mContext).notify(null, SystemMessage.NOTE_VPN_STATUS,
286282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wren                builder.build());
28769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
28869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
28969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void hideNotification() {
290282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wren        NotificationManager.from(mContext).cancel(null, SystemMessage.NOTE_VPN_STATUS);
29169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
29269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey}
293