LockdownVpnTracker.java revision 15e47235c055495ec0ccc24768a6746a960d3a61
136e612a488511940b61f09803b270aa1c61b68e0Jason Sams/*
2dd6c8b34f172ba699954e4d3095dba8c0fd5e930Jason Sams * Copyright (C) 2012 The Android Open Source Project
336e612a488511940b61f09803b270aa1c61b68e0Jason Sams *
436e612a488511940b61f09803b270aa1c61b68e0Jason Sams * Licensed under the Apache License, Version 2.0 (the "License");
536e612a488511940b61f09803b270aa1c61b68e0Jason Sams * you may not use this file except in compliance with the License.
636e612a488511940b61f09803b270aa1c61b68e0Jason Sams * You may obtain a copy of the License at
736e612a488511940b61f09803b270aa1c61b68e0Jason Sams *
836e612a488511940b61f09803b270aa1c61b68e0Jason Sams *      http://www.apache.org/licenses/LICENSE-2.0
936e612a488511940b61f09803b270aa1c61b68e0Jason Sams *
1036e612a488511940b61f09803b270aa1c61b68e0Jason Sams * Unless required by applicable law or agreed to in writing, software
1136e612a488511940b61f09803b270aa1c61b68e0Jason Sams * distributed under the License is distributed on an "AS IS" BASIS,
1236e612a488511940b61f09803b270aa1c61b68e0Jason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1336e612a488511940b61f09803b270aa1c61b68e0Jason Sams * See the License for the specific language governing permissions and
1436e612a488511940b61f09803b270aa1c61b68e0Jason Sams * limitations under the License.
1536e612a488511940b61f09803b270aa1c61b68e0Jason Sams */
1636e612a488511940b61f09803b270aa1c61b68e0Jason Sams
1736e612a488511940b61f09803b270aa1c61b68e0Jason Samspackage com.android.server.net;
1836e612a488511940b61f09803b270aa1c61b68e0Jason Sams
1943ee06857bb7f99446d1d84f8789016c5d105558Jason Samsimport static android.Manifest.permission.CONNECTIVITY_INTERNAL;
20dfac814c18f73dd7289f9927edca3e3b6ec6bc00Alex Sakhartchoukimport static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
2136e612a488511940b61f09803b270aa1c61b68e0Jason Samsimport static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
229c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines
23c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.app.Notification;
24c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.app.NotificationManager;
25c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.app.PendingIntent;
26c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.content.BroadcastReceiver;
27c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.content.Context;
28c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.content.Intent;
29c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.content.IntentFilter;
30c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.ConnectivityManager;
31c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.LinkProperties;
32c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.LinkAddress;
33c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.NetworkInfo;
34c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.NetworkInfo.DetailedState;
35c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.net.NetworkInfo.State;
36a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Samsimport android.net.NetworkPolicyManager;
37c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.os.INetworkManagementService;
38c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.os.RemoteException;
39c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.security.Credentials;
40c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.security.KeyStore;
41c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.system.Os;
42c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.text.TextUtils;
43c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport android.util.Slog;
44c11e25c4e653124def1fb18e203b894f42106cbeTim Murray
45c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport com.android.internal.R;
46c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport com.android.internal.net.VpnConfig;
47c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport com.android.internal.net.VpnProfile;
48c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport com.android.internal.util.Preconditions;
493aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandezimport com.android.server.ConnectivityService;
503aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandezimport com.android.server.EventLogTags;
513aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandezimport com.android.server.connectivity.Vpn;
52c11e25c4e653124def1fb18e203b894f42106cbeTim Murray
53c11e25c4e653124def1fb18e203b894f42106cbeTim Murrayimport java.util.List;
543aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez
5536e612a488511940b61f09803b270aa1c61b68e0Jason Sams/**
5636e612a488511940b61f09803b270aa1c61b68e0Jason Sams * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
57ea84a7c51790f9ba5f2194a66d6cf4ea8d879776Jason Sams * connected and kicks off VPN connection, managing any required {@code netd}
58718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams * firewall rules.
59718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams */
6070d4e5024298f71edb3b04867e05568f5495b4ceJason Samspublic class LockdownVpnTracker {
617d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    private static final String TAG = "LockdownVpnTracker";
62ea84a7c51790f9ba5f2194a66d6cf4ea8d879776Jason Sams
633aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    /** Number of VPN attempts before waiting for user intervention. */
643aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private static final int MAX_ERROR_COUNT = 4;
65718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
66718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams    private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
67718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
68718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams    private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
69718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams    private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
703aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
713aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private static final int ROOT_UID = 0;
723aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
733aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final Context mContext;
743aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final INetworkManagementService mNetService;
753aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final ConnectivityService mConnService;
763aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final Vpn mVpn;
773aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final VpnProfile mProfile;
783aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
793aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final Object mStateLock = new Object();
803aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
813aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final PendingIntent mConfigIntent;
823aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private final PendingIntent mResetIntent;
833aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
843aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private String mAcceptedEgressIface;
853aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private String mAcceptedIface;
863aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private List<LinkAddress> mAcceptedSourceAddr;
873aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
883aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private int mErrorCount;
893aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
903aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    public static boolean isEnabled() {
913aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk        return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
923aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    }
939c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines
947d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    public LockdownVpnTracker(Context context, INetworkManagementService netService,
957d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            ConnectivityService connService, Vpn vpn, VpnProfile profile) {
96918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        mContext = Preconditions.checkNotNull(context);
97718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        mNetService = Preconditions.checkNotNull(netService);
989c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        mConnService = Preconditions.checkNotNull(connService);
99918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        mVpn = Preconditions.checkNotNull(vpn);
100918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        mProfile = Preconditions.checkNotNull(profile);
101fd79e02e0fec8620da7affaadcf275cf0518241aAlex Sakhartchouk
102fd79e02e0fec8620da7affaadcf275cf0518241aAlex Sakhartchouk        final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
103fd79e02e0fec8620da7affaadcf275cf0518241aAlex Sakhartchouk        configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
104fd79e02e0fec8620da7affaadcf275cf0518241aAlex Sakhartchouk        mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
105a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams
1069c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
107a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
108f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk        mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
109f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk    }
110f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk
111f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk    private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
112f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk        @Override
113f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk        public void onReceive(Context context, Intent intent) {
114f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk            reset();
115f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk        }
116a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    };
117a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams
118a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    /**
119a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams     * Watch for state changes to both active egress network, kicking off a VPN
120a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams     * connection when ready, or setting firewall rules once VPN is connected.
121a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams     */
122718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams    private void handleStateChangedLocked() {
1233aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
124718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
125718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final LinkProperties egressProp = mConnService.getActiveLinkProperties();
12602f41705199336f808ece50d81585450e7f8f61fStephen Hines
127718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
128718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
129718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
130ef1dac28d3bf98bd61cd9874fb3ccab42105e9b6Stephen Hines        // Restart VPN when egress network disconnected or changed
131718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final boolean egressDisconnected = egressInfo == null
132718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                || State.DISCONNECTED.equals(egressInfo.getState());
133718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        final boolean egressChanged = egressProp == null
13452d836332f6aae74ed97fda1b53681f36710af64Stephen Hines                || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
135718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
136f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        final String egressTypeName = (egressInfo == null) ?
137f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams                null : ConnectivityManager.getNetworkTypeName(egressInfo.getType());
138f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        final String egressIface = (egressProp == null) ?
139f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams                null : egressProp.getInterfaceName();
140f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        Slog.d(TAG, "handleStateChanged: egress=" + egressTypeName +
141f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams                " " + mAcceptedEgressIface + "->" + egressIface);
1421d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams
1431d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        if (egressDisconnected || egressChanged) {
1441d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams            clearSourceRulesLocked();
1451d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams            mAcceptedEgressIface = null;
1461d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams            mVpn.stopLegacyVpnPrivileged();
1471d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        }
1481d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        if (egressDisconnected) {
1491d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams            hideNotification();
1501d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams            return;
1511d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        }
1521d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams
1531d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        final int egressType = egressInfo.getType();
1541d45c47975ab2a8cef6db5a8976276de31e1e8d0Jason Sams        if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
1553a2914132146f340511425d7f78540098606b512Stephen Hines            EventLogTags.writeLockdownVpnError(egressType);
1563a2914132146f340511425d7f78540098606b512Stephen Hines        }
157718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
158718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        if (mErrorCount > MAX_ERROR_COUNT) {
159718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams            showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
160718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
161718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
162718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams            if (mProfile.isValidLockdownProfile()) {
163718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                Slog.d(TAG, "Active network connected; starting VPN");
164768bc02d815a94ad29146f1ed60c847d1af118ccJason Sams                EventLogTags.writeLockdownVpnConnecting(egressType);
165768bc02d815a94ad29146f1ed60c847d1af118ccJason Sams                showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
1669c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines
167a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                mAcceptedEgressIface = egressProp.getInterfaceName();
168a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                try {
169a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                    // Use the privileged method because Lockdown VPN is initiated by the system, so
170a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                    // no additional permission checks are necessary.
171a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                    mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp);
172718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                } catch (IllegalStateException e) {
173718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                    mAcceptedEgressIface = null;
174718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                    Slog.e(TAG, "Failed to start VPN", e);
175718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                    showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
176718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                }
177718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams            } else {
178718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams                Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
1798e90f2bc1fa35a2dc7bd2aab8b8241b628800218Alex Sakhartchouk                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
1808140d7b0f62a6e5b54e318c959f2d501f7ee6784Jason Sams            }
1818140d7b0f62a6e5b54e318c959f2d501f7ee6784Jason Sams
182718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams        } else if (vpnInfo.isConnected() && vpnConfig != null) {
183718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams            final String iface = vpnConfig.interfaze;
184718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams            final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
185718cd1f322ee5b62b6a49cb36195bcb18a5ab711Jason Sams
186ea84a7c51790f9ba5f2194a66d6cf4ea8d879776Jason Sams            if (TextUtils.equals(iface, mAcceptedIface)
18736e612a488511940b61f09803b270aa1c61b68e0Jason Sams                  && sourceAddrs.equals(mAcceptedSourceAddr)) {
18836e612a488511940b61f09803b270aa1c61b68e0Jason Sams                return;
1899c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines            }
190a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams
191a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            Slog.d(TAG, "VPN connected using iface=" + iface +
192a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams                    ", sourceAddr=" + sourceAddrs.toString());
193a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            EventLogTags.writeLockdownVpnConnected(egressType);
194a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
195c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams
196c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams            try {
197c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                clearSourceRulesLocked();
198c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams
199c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                mNetService.setFirewallInterfaceRule(iface, true);
200c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                for (LinkAddress addr : sourceAddrs) {
201c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                    setFirewallEgressSourceRule(addr, true);
202c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                }
203c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams
204c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                mNetService.setFirewallUidRule(ROOT_UID, FIREWALL_RULE_ALLOW);
205c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams                mNetService.setFirewallUidRule(Os.getuid(), FIREWALL_RULE_ALLOW);
206c1d6210fb5cc558ccea95a59a2b33bb9015fc7deJason Sams
2079c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines                mErrorCount = 0;
208918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk                mAcceptedIface = iface;
209918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk                mAcceptedSourceAddr = sourceAddrs;
210918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk            } catch (RemoteException e) {
211918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk                throw new RuntimeException("Problem setting firewall rules", e);
2127d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            }
2137d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2147d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo));
2153aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk        }
2167d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    }
2177d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2183aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    public void init() {
2197d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        synchronized (mStateLock) {
2207d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            initLocked();
2219c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        }
222918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk    }
223918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk
2247d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    private void initLocked() {
2257d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        Slog.d(TAG, "initLocked()");
2267d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2277d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        mVpn.setEnableTeardown(false);
2283aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
2297d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
2307d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
2313aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk
2327d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        try {
2337d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            // TODO: support non-standard port numbers
2343aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
2357d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
2367d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, true);
2379c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        } catch (RemoteException e) {
238918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk            throw new RuntimeException("Problem setting firewall rules", e);
239918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        }
2407d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2417d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        synchronized (mStateLock) {
2427d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            handleStateChangedLocked();
2437d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        }
2443aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    }
2457d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2467d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    public void shutdown() {
2473aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk        synchronized (mStateLock) {
2487d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            shutdownLocked();
2497d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        }
2503aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    }
2517d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2527d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    private void shutdownLocked() {
2539c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        Slog.d(TAG, "shutdownLocked()");
254918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk
255918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        mAcceptedEgressIface = null;
256918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk        mErrorCount = 0;
2577d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2587d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        mVpn.stopLegacyVpnPrivileged();
2597d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        try {
2607d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
2613aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
2627d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, false);
2637d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        } catch (RemoteException e) {
2643aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk            throw new RuntimeException("Problem setting firewall rules", e);
2657d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        }
2667d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        clearSourceRulesLocked();
2673aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk        hideNotification();
2687d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2697d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        mContext.unregisterReceiver(mResetReceiver);
2709c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        mVpn.setEnableTeardown(true);
271918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk    }
272918e840628a0b40a95fd42618f604ea5a44aebaeAlex Sakhartchouk
2737d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    public void reset() {
2747d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        Slog.d(TAG, "reset()");
2757d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        synchronized (mStateLock) {
2767d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            // cycle tracker, reset error count, and trigger retry
2773aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk            shutdownLocked();
2787d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            initLocked();
2797d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            handleStateChangedLocked();
2803aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk        }
2817d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk    }
2827d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk
2833aac0abe7965ce9e2078c7d5796805d83e39df7cAlex Sakhartchouk    private void clearSourceRulesLocked() {
2847d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk        try {
2857d5f5e7c8943e043a422ad51c85d4e1684c37e28Alex Sakhartchouk            if (mAcceptedIface != null) {
2869c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines                mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
287f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                mAcceptedIface = null;
288f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk            }
289f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk            if (mAcceptedSourceAddr != null) {
290f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                for (LinkAddress addr : mAcceptedSourceAddr) {
291f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                    setFirewallEgressSourceRule(addr, false);
292f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                }
2939c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines
294f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                mNetService.setFirewallUidRule(ROOT_UID, FIREWALL_RULE_DEFAULT);
295f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                mNetService.setFirewallUidRule(Os.getuid(), FIREWALL_RULE_DEFAULT);
296f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk
297f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk                mAcceptedSourceAddr = null;
298f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk            }
299f5d8ac7cc35747ef7285ccc196f616b96229def9Alex Sakhartchouk        } catch (RemoteException e) {
3009c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines            throw new RuntimeException("Problem setting firewall rules", e);
301a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        }
302a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    }
303a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams
304a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    private void setFirewallEgressSourceRule(
305a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            LinkAddress address, boolean allow) throws RemoteException {
306a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        // Our source address based firewall rules must only cover our own source address, not the
307f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        // whole subnet
308f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        final String addrString = address.getAddress().getHostAddress();
309f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        mNetService.setFirewallEgressSourceRule(addrString, allow);
310f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams    }
311f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams
312f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams    public void onNetworkInfoChanged() {
313f110d4b787b91dabe968a812e76e5c1f8d953487Jason Sams        synchronized (mStateLock) {
3149c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines            handleStateChangedLocked();
315a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        }
316a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    }
317a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams
318a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    public void onVpnStateChanged(NetworkInfo info) {
319a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        if (info.getDetailedState() == DetailedState.FAILED) {
320a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            mErrorCount++;
3218cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams        }
3228cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams        synchronized (mStateLock) {
3238cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams            handleStateChangedLocked();
3243c0dfbab807a459622aeade4940daddf482dec66Jason Sams        }
3258cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams    }
3263c0dfbab807a459622aeade4940daddf482dec66Jason Sams
327ea84a7c51790f9ba5f2194a66d6cf4ea8d879776Jason Sams    public NetworkInfo augmentNetworkInfo(NetworkInfo info) {
3289c9ad3f8c218954e46aab81f9af7834cea5675caStephen Hines        if (info.isConnected()) {
329a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
330a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            info = new NetworkInfo(info);
331a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams            info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
332a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        }
333a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams        return info;
334a1b13ed0912a7e08f9848196b4ca64dcb5db9d0bJason Sams    }
3358cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams
3368cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams    private void showNotification(int titleRes, int iconRes) {
3378cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams        final Notification.Builder builder = new Notification.Builder(mContext)
3383c0dfbab807a459622aeade4940daddf482dec66Jason Sams                .setWhen(0)
3398cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams                .setSmallIcon(iconRes)
3403c0dfbab807a459622aeade4940daddf482dec66Jason Sams                .setContentTitle(mContext.getString(titleRes))
3413c0dfbab807a459622aeade4940daddf482dec66Jason Sams                .setContentText(mContext.getString(R.string.vpn_lockdown_config))
342e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                .setContentIntent(mConfigIntent)
343e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                .setPriority(Notification.PRIORITY_LOW)
344e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                .setOngoing(true)
345e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
346e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                        mResetIntent)
347e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                .setColor(mContext.getColor(
348e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams                        com.android.internal.R.color.system_notification_accent_color));
349e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams
350e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams        NotificationManager.from(mContext).notify(TAG, 0, builder.build());
351e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams    }
352e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams
353e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams    private void hideNotification() {
354e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams        NotificationManager.from(mContext).cancel(TAG, 0);
355e29f3e74f71ea730519ff8ae1d8dd4c1630bbaf9Jason Sams    }
3568cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams}
3578cb39de03aef6097a90033600d11a60ae000a6e4Jason Sams