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;
2069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
2169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.Notification;
2269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.NotificationManager;
2369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.app.PendingIntent;
2469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.BroadcastReceiver;
2569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.Context;
2669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.Intent;
2769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.content.IntentFilter;
2869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.LinkProperties;
294ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.net.LinkAddress;
3069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo;
3169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo.DetailedState;
3269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.net.NetworkInfo.State;
3369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.os.INetworkManagementService;
3469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.os.RemoteException;
3569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.security.Credentials;
3669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.security.KeyStore;
3769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.text.TextUtils;
3869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport android.util.Slog;
3969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
4069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.R;
4169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.net.VpnConfig;
4269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
4369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.internal.util.Preconditions;
4469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.server.ConnectivityService;
4591c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkeyimport com.android.server.EventLogTags;
4669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeyimport com.android.server.connectivity.Vpn;
4769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
484ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport java.util.List;
494ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey/**
5169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
5269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * connected and kicks off VPN connection, managing any required {@code netd}
5369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey * firewall rules.
5469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey */
5569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkeypublic class LockdownVpnTracker {
5669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final String TAG = "LockdownVpnTracker";
5769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
5869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    /** Number of VPN attempts before waiting for user intervention. */
5969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final int MAX_ERROR_COUNT = 4;
6069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
6169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
624fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
63580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
644fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey    private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
6569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
6669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Context mContext;
6769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final INetworkManagementService mNetService;
6869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final ConnectivityService mConnService;
6969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Vpn mVpn;
7069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final VpnProfile mProfile;
7169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
7269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private final Object mStateLock = new Object();
7369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
744fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey    private final PendingIntent mConfigIntent;
754fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey    private final PendingIntent mResetIntent;
7669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
7769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private String mAcceptedEgressIface;
7869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private String mAcceptedIface;
794ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker    private List<LinkAddress> mAcceptedSourceAddr;
8069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private int mErrorCount;
8269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public static boolean isEnabled() {
8469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
8569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
8669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
8769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public LockdownVpnTracker(Context context, INetworkManagementService netService,
8869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            ConnectivityService connService, Vpn vpn, VpnProfile profile) {
8969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext = Preconditions.checkNotNull(context);
9069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mNetService = Preconditions.checkNotNull(netService);
9169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mConnService = Preconditions.checkNotNull(connService);
9269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mVpn = Preconditions.checkNotNull(vpn);
9369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mProfile = Preconditions.checkNotNull(profile);
9469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
954fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
964fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
974fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
984fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
99580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
100580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
101580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
10269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
10369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
10469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
10569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        @Override
10669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        public void onReceive(Context context, Intent intent) {
10769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            reset();
10869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
10969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    };
11069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
11169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    /**
11269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     * Watch for state changes to both active egress network, kicking off a VPN
11369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     * connection when ready, or setting firewall rules once VPN is connected.
11469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey     */
11569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void handleStateChangedLocked() {
11669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        Slog.d(TAG, "handleStateChanged()");
11769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
11869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
11969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final LinkProperties egressProp = mConnService.getActiveLinkProperties();
12069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
12169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
12269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
12369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
12469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        // Restart VPN when egress network disconnected or changed
12569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final boolean egressDisconnected = egressInfo == null
12669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                || State.DISCONNECTED.equals(egressInfo.getState());
12769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final boolean egressChanged = egressProp == null
12869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
12969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (egressDisconnected || egressChanged) {
130580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            clearSourceRulesLocked();
13169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mAcceptedEgressIface = null;
13269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mVpn.stopLegacyVpn();
13369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
13457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        if (egressDisconnected) {
13557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey            hideNotification();
13657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey            return;
13757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        }
13869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
13991c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        final int egressType = egressInfo.getType();
14091c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
14191c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey            EventLogTags.writeLockdownVpnError(egressType);
14291c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey        }
14391c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey
14469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mErrorCount > MAX_ERROR_COUNT) {
14569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
14669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
14769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
14869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (mProfile.isValidLockdownProfile()) {
14969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                Slog.d(TAG, "Active network connected; starting VPN");
15091c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey                EventLogTags.writeLockdownVpnConnecting(egressType);
15169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
15269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
15369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mAcceptedEgressIface = egressProp.getInterfaceName();
154421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                try {
155421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
156421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                } catch (IllegalStateException e) {
157421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    mAcceptedEgressIface = null;
158421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    Slog.e(TAG, "Failed to start VPN", e);
159421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                    showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
160421fab8a7b429073512967e54a469ce440772e15Jeff Sharkey                }
16169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            } else {
16269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
16369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
16469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
16569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
16669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else if (vpnInfo.isConnected() && vpnConfig != null) {
16769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            final String iface = vpnConfig.interfaze;
1684ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
16969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
17069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (TextUtils.equals(iface, mAcceptedIface)
1714ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                  && sourceAddrs.equals(mAcceptedSourceAddr)) {
17269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                return;
17369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
17469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
1754ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Slog.d(TAG, "VPN connected using iface=" + iface +
1764ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    ", sourceAddr=" + sourceAddrs.toString());
17791c6a64a04c2d8b27b886d96a56800ae24efb7a9Jeff Sharkey            EventLogTags.writeLockdownVpnConnected(egressType);
17869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
17969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            try {
181580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey                clearSourceRulesLocked();
18269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mNetService.setFirewallInterfaceRule(iface, true);
1844ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                for (LinkAddress addr : sourceAddrs) {
1854ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mNetService.setFirewallEgressSourceRule(addr.toString(), true);
1864ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                }
18769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mErrorCount = 0;
18969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mAcceptedIface = iface;
1904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mAcceptedSourceAddr = sourceAddrs;
19169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            } catch (RemoteException e) {
19269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                throw new RuntimeException("Problem setting firewall rules", e);
19369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
19469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
19569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo));
19669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
19769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
19869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
19969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void init() {
200580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        synchronized (mStateLock) {
201580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            initLocked();
202580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        }
203580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    }
204580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey
205580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private void initLocked() {
206580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        Slog.d(TAG, "initLocked()");
20769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
20869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mVpn.setEnableNotifications(false);
20957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mVpn.setEnableTeardown(false);
21069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
21169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
21269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
21369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
21469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        try {
21569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            // TODO: support non-standard port numbers
21669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
21769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
21842c0c9f35a8efa71703f810ce2bff6d86bb5b30eJeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, true);
21969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } catch (RemoteException e) {
22069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            throw new RuntimeException("Problem setting firewall rules", e);
22169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
22269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
22369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
22469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
22569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
22669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
22769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
22869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void shutdown() {
229580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        synchronized (mStateLock) {
230580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            shutdownLocked();
231580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        }
232580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    }
233580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey
234580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private void shutdownLocked() {
235580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        Slog.d(TAG, "shutdownLocked()");
23669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
23769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mAcceptedEgressIface = null;
23869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mErrorCount = 0;
23969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
24069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mVpn.stopLegacyVpn();
24169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        try {
24269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
24369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
24442c0c9f35a8efa71703f810ce2bff6d86bb5b30eJeff Sharkey            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, false);
24569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } catch (RemoteException e) {
24669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            throw new RuntimeException("Problem setting firewall rules", e);
24769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
248580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey        clearSourceRulesLocked();
24969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        hideNotification();
25069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
25169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mContext.unregisterReceiver(mResetReceiver);
25269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mVpn.setEnableNotifications(true);
25357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mVpn.setEnableTeardown(true);
25469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
25569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
25669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void reset() {
25769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
258580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            // cycle tracker, reset error count, and trigger retry
259580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            shutdownLocked();
260580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey            initLocked();
26169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
26269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
26369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
26469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
265580dd31a68c65b4af68147d52d57f60e0bd52dbeJeff Sharkey    private void clearSourceRulesLocked() {
26669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        try {
26769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (mAcceptedIface != null) {
26869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
26969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mAcceptedIface = null;
27069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
27169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            if (mAcceptedSourceAddr != null) {
2724ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                for (LinkAddress addr : mAcceptedSourceAddr) {
2734ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mNetService.setFirewallEgressSourceRule(addr.toString(), false);
2744ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                }
27569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey                mAcceptedSourceAddr = null;
27669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            }
27769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } catch (RemoteException e) {
27869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            throw new RuntimeException("Problem setting firewall rules", e);
27969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
28069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
28169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
28269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void onNetworkInfoChanged(NetworkInfo info) {
28369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
28469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
28569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
28669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
28769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
28869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void onVpnStateChanged(NetworkInfo info) {
28969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (info.getDetailedState() == DetailedState.FAILED) {
29069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            mErrorCount++;
29169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
29269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        synchronized (mStateLock) {
29369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            handleStateChangedLocked();
29469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
29569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
29669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
29769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public NetworkInfo augmentNetworkInfo(NetworkInfo info) {
2980b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey        if (info.isConnected()) {
2990b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey            final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
3000b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey            info = new NetworkInfo(info);
3010b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey            info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
3020b81be6f79ec3d1b9441c21a3cefc629be1450c8Jeff Sharkey        }
30369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        return info;
30469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
30569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
30669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void showNotification(int titleRes, int iconRes) {
30769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        final Notification.Builder builder = new Notification.Builder(mContext);
30869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        builder.setWhen(0);
30969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        builder.setSmallIcon(iconRes);
31069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        builder.setContentTitle(mContext.getString(titleRes));
3114fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        builder.setContentText(mContext.getString(R.string.vpn_lockdown_config));
3124fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        builder.setContentIntent(mConfigIntent);
31369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        builder.setPriority(Notification.PRIORITY_LOW);
31469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        builder.setOngoing(true);
3154fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey        builder.addAction(
3164fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey                R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), mResetIntent);
3174fa63b2d5e3b2e2a85bf17a9bf056cbdfb7046f0Jeff Sharkey
31869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        NotificationManager.from(mContext).notify(TAG, 0, builder.build());
31969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
32069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
32169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    private void hideNotification() {
32269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        NotificationManager.from(mContext).cancel(TAG, 0);
32369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
32469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey}
325