Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e40a062 authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Convert LockdownVpnTracker to NetworkCallbacks.

This will allow moving LockdownVpnTracker from the connectivity
to the VPN code. This requires moderate refactoring since it's
pretty tightly coupled to both.

In this CL:

1. Add an @hide API to tell ConnectivityService that legacy
   lockdown VPN is enabled. I chose not to use the existing
   setVpnRequiredForUids API because that method has specific
   semantics and because it will be required long term since
   it's used by non-legacy VPN types.

2. Instead of updating LockdownVpnTracker inline from the
   ConnectivityService handler thread, have it listen to
   NetworkCallbacks. This introduces an extra thread hop, but
   most of the interactions between the lockdown VPN and CS were
   via NetworkAgent, which is asynchronous anyway.

3. Add code to LegacyTypeTracker to send the extra
   CONNECTIVITY_ACTION broadcast for the underlying network type
   that is sent after the VPN connects. In order to do this, make
   Make LockdownVpnTracker specify its underlying network
   (via setUnderlyingNetworks) when it connects.

4. Reimplement LockdownVpnTracker#augmentNetworkInfo based on
   information that is available in ConnectivityService.

5. Remove the code in LockdownVpnTracker that counted errors.
   I think this code has not worked since lollipop, because
   ConnectivityService never sees NetworkInfo objects in state
   FAILED. This is because ConnectivityService only hears about
   NetworkInfo objects via NetworkAgents, and LegacyVpnRunner
   only registers its NetworkAgent when the connection succeeds.

Bug: 173331190
Test: passes existing tests in ConnectivityServiceTest
Change-Id: I66d18512882efd468ee0ecec61f28786a195b357
parent 5fb746fd
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -1220,6 +1220,45 @@ public class ConnectivityManager {
        }
    }

    /**
     * Informs ConnectivityService of whether the legacy lockdown VPN, as implemented by
     * LockdownVpnTracker, is in use. This is deprecated for new devices starting from Android 12
     * but is still supported for backwards compatibility.
     * <p>
     * This type of VPN is assumed always to use the system default network, and must always declare
     * exactly one underlying network, which is the network that was the default when the VPN
     * connected.
     * <p>
     * Calling this method with {@code true} enables legacy behaviour, specifically:
     * <ul>
     *     <li>Any VPN that applies to userId 0 behaves specially with respect to deprecated
     *     {@link #CONNECTIVITY_ACTION} broadcasts. Any such broadcasts will have the state in the
     *     {@link #EXTRA_NETWORK_INFO} replaced by state of the VPN network. Also, any time the VPN
     *     connects, a {@link #CONNECTIVITY_ACTION} broadcast will be sent for the network
     *     underlying the VPN.</li>
     *     <li>Deprecated APIs that return {@link NetworkInfo} objects will have their state
     *     similarly replaced by the VPN network state.</li>
     *     <li>Information on current network interfaces passed to NetworkStatsService will not
     *     include any VPN interfaces.</li>
     * </ul>
     *
     * @param enabled whether legacy lockdown VPN is enabled or disabled
     *
     * TODO: @SystemApi(client = MODULE_LIBRARIES)
     *
     * @hide
     */
    @RequiresPermission(anyOf = {
            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
            android.Manifest.permission.NETWORK_SETTINGS})
    public void setLegacyLockdownVpnEnabled(boolean enabled) {
        try {
            mService.setLegacyLockdownVpnEnabled(enabled);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns details about the currently active default data network
     * for a given uid.  This is for internal use only to avoid spying
+1 −0
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ interface IConnectivityManager
    boolean isVpnLockdownEnabled(int userId);
    List<String> getVpnLockdownWhitelist(int userId);
    void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
    void setLegacyLockdownVpnEnabled(boolean enabled);

    void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);

+116 −68
Original line number Diff line number Diff line
@@ -318,7 +318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
    // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
    // a direct call to LockdownVpnTracker.isEnabled().
    @GuardedBy("mVpns")
    private boolean mLockdownEnabled;
    private volatile boolean mLockdownEnabled;
    @GuardedBy("mVpns")
    private LockdownVpnTracker mLockdownTracker;

@@ -755,6 +755,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
            }
        }

        // When a lockdown VPN connects, send another CONNECTED broadcast for the underlying
        // network type, to preserve previous behaviour.
        private void maybeSendLegacyLockdownBroadcast(@NonNull NetworkAgentInfo vpnNai) {
            if (vpnNai != mService.getLegacyLockdownNai()) return;

            if (vpnNai.declaredUnderlyingNetworks == null
                    || vpnNai.declaredUnderlyingNetworks.length != 1) {
                Log.wtf(TAG, "Legacy lockdown VPN must have exactly one underlying network: "
                        + Arrays.toString(vpnNai.declaredUnderlyingNetworks));
                return;
            }
            final NetworkAgentInfo underlyingNai =  mService.getNetworkAgentInfoForNetwork(
                    vpnNai.declaredUnderlyingNetworks[0]);
            if (underlyingNai == null) return;

            final int type = underlyingNai.networkInfo.getType();
            final DetailedState state = DetailedState.CONNECTED;
            maybeLogBroadcast(underlyingNai, state, type, true /* isDefaultNetwork */);
            mService.sendLegacyNetworkBroadcast(underlyingNai, state, type);
        }

        /** Adds the given network to the specified legacy type list. */
        public void add(int type, NetworkAgentInfo nai) {
            if (!isTypeSupported(type)) {
@@ -772,9 +793,17 @@ public class ConnectivityService extends IConnectivityManager.Stub

            // Send a broadcast if this is the first network of its type or if it's the default.
            final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);

            // If a legacy lockdown VPN is active, override the NetworkInfo state in all broadcasts
            // to preserve previous behaviour.
            final DetailedState state = mService.getLegacyLockdownState(DetailedState.CONNECTED);
            if ((list.size() == 1) || isDefaultNetwork) {
                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
                mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
                maybeLogBroadcast(nai, state, type, isDefaultNetwork);
                mService.sendLegacyNetworkBroadcast(nai, state, type);
            }

            if (type == TYPE_VPN && state == DetailedState.CONNECTED) {
                maybeSendLegacyLockdownBroadcast(nai);
            }
        }

@@ -1474,11 +1503,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
        if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
            networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
        }
        synchronized (mVpns) {
            if (mLockdownTracker != null) {
                mLockdownTracker.augmentNetworkInfo(networkInfo);
            }
        }
        networkInfo.setDetailedState(
                getLegacyLockdownState(networkInfo.getDetailedState()),
                "" /* reason */, null /* extraInfo */);
    }

    /**
@@ -1537,14 +1564,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        return nai.network;
    }

    // Public because it's used by mLockdownTracker.
    public NetworkInfo getActiveNetworkInfoUnfiltered() {
        enforceAccessPermission();
        final int uid = mDeps.getCallingUid();
        NetworkState state = getUnfilteredActiveNetworkState(uid);
        return state.networkInfo;
    }

    @Override
    public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
        NetworkStack.checkNetworkStackPermission(mContext);
@@ -2340,13 +2359,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
    }

    private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
        synchronized (mVpns) {
            if (mLockdownTracker != null) {
                info = new NetworkInfo(info);
                mLockdownTracker.augmentNetworkInfo(info);
            }
        }

        Intent intent = new Intent(bcastType);
        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
@@ -2887,7 +2899,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
                        Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
                        break;
                    }

                    final List<Network> underlying = (List<Network>) arg.second;

                    if (isLegacyLockdownNai(nai)
                            && (underlying == null || underlying.size() != 1)) {
                        Log.wtf(TAG, "Legacy lockdown VPN " + nai.toShortString()
                                + " must have exactly one underlying network: " + underlying);
                    }

                    final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
                    nai.declaredUnderlyingNetworks = (underlying != null)
                            ? underlying.toArray(new Network[0]) : null;
@@ -3496,7 +3516,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    //  incorrect) behavior.
                    mNetworkActivityTracker.updateDataActivityTracking(
                            null /* newNetwork */, nai);
                    notifyLockdownVpn(nai);
                    ensureNetworkTransitionWakelock(nai.toShortString());
                }
            }
@@ -5071,10 +5090,59 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mVpnBlockedUidRanges = newVpnBlockedUidRanges;
    }

    @Override
    public void setLegacyLockdownVpnEnabled(boolean enabled) {
        enforceSettingsPermission();
        mHandler.post(() -> mLockdownEnabled = enabled);
    }

    // TODO: remove when the VPN code moves out.
    private boolean isLockdownVpnEnabled() {
        return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
    }

    private boolean isLegacyLockdownNai(NetworkAgentInfo nai) {
        return mLockdownEnabled
                && getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
                && nai.networkCapabilities.appliesToUid(Process.FIRST_APPLICATION_UID);
    }

    private NetworkAgentInfo getLegacyLockdownNai() {
        if (!mLockdownEnabled) {
            return null;
        }
        // The legacy lockdown VPN always only applies to UID 0.
        final NetworkAgentInfo nai = getVpnForUid(Process.FIRST_APPLICATION_UID);
        if (nai == null || !isLegacyLockdownNai(nai)) return null;

        // The legacy lockdown VPN must always have exactly one underlying network.
        if (nai.declaredUnderlyingNetworks == null ||  nai.declaredUnderlyingNetworks.length != 1) {
            return null;
        }

        // The legacy lockdown VPN always uses the default network.
        // If the VPN's underlying network is no longer the current default network, it means that
        // the default network has just switched, and the VPN is about to disconnect.
        // Report that the VPN is not connected, so when the state of NetworkInfo objects
        // overwritten by getLegacyLockdownState will be set to CONNECTING and not CONNECTED.
        final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
        if (defaultNetwork == null
                || !defaultNetwork.network.equals(nai.declaredUnderlyingNetworks[0])) {
            return null;
        }

        return nai;
    };

    private DetailedState getLegacyLockdownState(DetailedState origState) {
        if (origState != DetailedState.CONNECTED) {
            return origState;
        }
        return (mLockdownEnabled && getLegacyLockdownNai() == null)
                ? DetailedState.CONNECTING
                : DetailedState.CONNECTED;
    }

    @Override
    public boolean updateLockdownVpn() {
        // Allow the system UID for the system server and for Settings.
@@ -5087,8 +5155,11 @@ public class ConnectivityService extends IConnectivityManager.Stub

        synchronized (mVpns) {
            // Tear down existing lockdown if profile was removed
            mLockdownEnabled = isLockdownVpnEnabled();
            if (mLockdownEnabled) {
            if (!isLockdownVpnEnabled()) {
                setLockdownTracker(null);
                return true;
            }

            byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
            if (profileTag == null) {
                loge("Lockdown VPN configured but cannot be read from keystore");
@@ -5109,10 +5180,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                return false;
            }
            setLockdownTracker(
                        new LockdownVpnTracker(mContext, this, mHandler, mKeyStore, vpn,  profile));
            } else {
                setLockdownTracker(null);
            }
                    new LockdownVpnTracker(mContext, mHandler, mKeyStore, vpn,  profile));
        }

        return true;
@@ -7341,7 +7409,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
        }
        mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
        notifyLockdownVpn(newDefaultNetwork);
        handleApplyDefaultProxy(null != newDefaultNetwork
                ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
        updateTcpBufferSizes(null != newDefaultNetwork
@@ -7799,12 +7866,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
                mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
                mLegacyTypeTracker.add(
                        newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
                // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
                // to reflect the NetworkInfo of this new network. This broadcast has to be sent
                // after the disconnect broadcasts above, but before the broadcasts sent by the
                // legacy type tracker below.
                // TODO : refactor this, it's too complex
                notifyLockdownVpn(newDefaultNetwork);
            }
        }

@@ -7862,18 +7923,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        sendInetConditionBroadcast(nai.networkInfo);
    }

    private void notifyLockdownVpn(NetworkAgentInfo nai) {
        synchronized (mVpns) {
            if (mLockdownTracker != null) {
                if (nai != null && nai.isVPN()) {
                    mLockdownTracker.onVpnStateChanged(nai.networkInfo);
                } else {
                    mLockdownTracker.onNetworkInfoChanged();
                }
            }
        }
    }

    @NonNull
    private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
        final NetworkInfo newInfo = new NetworkInfo(info);
@@ -7912,7 +7961,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            oldInfo = networkAgent.networkInfo;
            networkAgent.networkInfo = newInfo;
        }
        notifyLockdownVpn(networkAgent);

        if (DBG) {
            log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+114 −59
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.net;

import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.provider.Settings.ACTION_VPN_SETTINGS;

import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
@@ -28,22 +29,22 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.os.Handler;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.server.ConnectivityService;
import com.android.server.EventLogTags;
import com.android.server.connectivity.Vpn;

@@ -51,9 +52,8 @@ import java.util.List;
import java.util.Objects;

/**
 * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
 * connected and kicks off VPN connection, managing any required {@code netd}
 * firewall rules.
 * State tracker for legacy lockdown VPN. Watches for physical networks to be
 * connected and kicks off VPN connection.
 */
public class LockdownVpnTracker {
    private static final String TAG = "LockdownVpnTracker";
@@ -64,7 +64,7 @@ public class LockdownVpnTracker {
    public static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";

    @NonNull private final Context mContext;
    @NonNull private final ConnectivityService mConnService;
    @NonNull private final ConnectivityManager mCm;
    @NonNull private final NotificationManager mNotificationManager;
    @NonNull private final Handler mHandler;
    @NonNull private final Vpn mVpn;
@@ -76,19 +76,73 @@ public class LockdownVpnTracker {
    @NonNull private final PendingIntent mConfigIntent;
    @NonNull private final PendingIntent mResetIntent;

    @NonNull private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback();
    @NonNull private final VpnNetworkCallback mVpnNetworkCallback = new VpnNetworkCallback();

    private class NetworkCallback extends ConnectivityManager.NetworkCallback {
        private Network mNetwork = null;
        private LinkProperties mLinkProperties = null;

        public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
            boolean networkChanged = false;
            if (!network.equals(mNetwork)) {
                // The default network just changed.
                mNetwork = network;
                networkChanged = true;
            }
            mLinkProperties = lp;
            // Backwards compatibility: previously, LockdownVpnTracker only responded to connects
            // and disconnects, not LinkProperties changes on existing networks.
            if (networkChanged) {
                synchronized (mStateLock) {
                    handleStateChangedLocked();
                }
            }
        }

        public void onLost(Network network) {
            // The default network has gone down.
            mNetwork = null;
            mLinkProperties = null;
            synchronized (mStateLock) {
                handleStateChangedLocked();
            }
        }

        public Network getNetwork() {
            return mNetwork;
        }

        public LinkProperties getLinkProperties() {
            return mLinkProperties;
        }
    }

    private class VpnNetworkCallback extends NetworkCallback {
        @Override
        public void onAvailable(Network network) {
            synchronized (mStateLock) {
                handleStateChangedLocked();
            }
        }
        @Override
        public void onLost(Network network) {
            onAvailable(network);
        }
    }

    @Nullable
    private String mAcceptedEgressIface;

    private int mErrorCount;

    public LockdownVpnTracker(@NonNull Context context,
            @NonNull ConnectivityService connService,
            @NonNull Handler handler,
            @NonNull KeyStore keyStore,
            @NonNull Vpn vpn,
            @NonNull VpnProfile profile) {
        mContext = Objects.requireNonNull(context);
        mConnService = Objects.requireNonNull(connService);
        mCm = mContext.getSystemService(ConnectivityManager.class);
        mHandler = Objects.requireNonNull(handler);
        mVpn = Objects.requireNonNull(vpn);
        mProfile = Objects.requireNonNull(profile);
@@ -110,16 +164,15 @@ public class LockdownVpnTracker {
     * connection when ready, or setting firewall rules once VPN is connected.
     */
    private void handleStateChangedLocked() {

        final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
        final LinkProperties egressProp = mConnService.getActiveLinkProperties();
        final Network network = mDefaultNetworkCallback.getNetwork();
        final NetworkInfo egressInfo = mCm.getNetworkInfo(network);  // Only for logging
        final LinkProperties egressProp = mDefaultNetworkCallback.getLinkProperties();

        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
        final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();

        // Restart VPN when egress network disconnected or changed
        final boolean egressDisconnected = egressInfo == null
                || State.DISCONNECTED.equals(egressInfo.getState());
        final boolean egressDisconnected = (network == null);
        final boolean egressChanged = egressProp == null
                || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());

@@ -137,35 +190,53 @@ public class LockdownVpnTracker {
            hideNotification();
            return;
        }

        if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
            EventLogTags.writeLockdownVpnError(egressType);
        }

        if (mErrorCount > MAX_ERROR_COUNT) {
            // Cannot happen because ConnectivityService never sees a NetworkInfo in state FAILED.
            showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
            return;
        }

        // At this point, |network| is known to be non-null.
        if (!vpnInfo.isConnectedOrConnecting()) {
            if (!mProfile.isValidLockdownProfile()) {
                Log.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
                return;
            }

        } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
            if (mProfile.isValidLockdownProfile()) {
            Log.d(TAG, "Active network connected; starting VPN");
            EventLogTags.writeLockdownVpnConnecting(egressType);
            showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);

                mAcceptedEgressIface = egressProp.getInterfaceName();
            mAcceptedEgressIface = egressIface;
            try {
                // Use the privileged method because Lockdown VPN is initiated by the system, so
                // no additional permission checks are necessary.
                    mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
                //
                // Pass in the underlying network here because the legacy VPN is, in fact, tightly
                // coupled to a given underlying network and cannot provide mobility. This makes
                // things marginally more correct in two ways:
                //
                // 1. When the legacy lockdown VPN connects, LegacyTypeTracker broadcasts an extra
                //    CONNECTED broadcast for the underlying network type. The underlying type comes
                //    from here. LTT *could* assume that the underlying network is the default
                //    network, but that might introduce a race condition if, say, the VPN starts
                //    connecting on cell, but when the connection succeeds and the agent is
                //    registered, the default network is now wifi.
                // 2. If no underlying network is passed in, then CS will assume the underlying
                //    network is the system default. So, if the VPN  is up and underlying network
                //    (e.g., wifi) disconnects, CS will inform apps that the VPN's capabilities have
                //    changed to match the new default network (e.g., cell).
                mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, network, egressProp);
            } catch (IllegalStateException e) {
                mAcceptedEgressIface = null;
                Log.e(TAG, "Failed to start VPN", e);
                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
            }
            } else {
                Log.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
            }

        } else if (vpnInfo.isConnected() && vpnConfig != null) {
            final String iface = vpnConfig.interfaze;
            final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
@@ -174,10 +245,6 @@ public class LockdownVpnTracker {
                    + ", sourceAddr=" + sourceAddrs.toString());
            EventLogTags.writeLockdownVpnConnected(egressType);
            showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);

            final NetworkInfo clone = new NetworkInfo(egressInfo);
            augmentNetworkInfo(clone);
            mConnService.sendConnectedBroadcast(clone);
        }
    }

@@ -192,7 +259,15 @@ public class LockdownVpnTracker {

        mVpn.setEnableTeardown(false);
        mVpn.setLockdown(true);
        mCm.setLegacyLockdownVpnEnabled(true);
        handleStateChangedLocked();

        mCm.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
        final NetworkRequest vpnRequest = new NetworkRequest.Builder()
                .clearCapabilities()
                .addTransportType(TRANSPORT_VPN)
                .build();
        mCm.registerNetworkCallback(vpnRequest, mVpnNetworkCallback, mHandler);
    }

    public void shutdown() {
@@ -209,16 +284,18 @@ public class LockdownVpnTracker {

        mVpn.stopVpnRunnerPrivileged();
        mVpn.setLockdown(false);
        mCm.setLegacyLockdownVpnEnabled(false);
        hideNotification();

        mVpn.setEnableTeardown(true);
        mCm.unregisterNetworkCallback(mDefaultNetworkCallback);
        mCm.unregisterNetworkCallback(mVpnNetworkCallback);
    }

    /**
     * Reset VPN lockdown tracker. Called by ConnectivityService when receiving
     * {@link #ACTION_LOCKDOWN_RESET} pending intent.
     */
    @GuardedBy("mConnService.mVpns")
    public void reset() {
        Log.d(TAG, "reset()");
        synchronized (mStateLock) {
@@ -229,28 +306,6 @@ public class LockdownVpnTracker {
        }
    }

    public void onNetworkInfoChanged() {
        synchronized (mStateLock) {
            handleStateChangedLocked();
        }
    }

    public void onVpnStateChanged(NetworkInfo info) {
        if (info.getDetailedState() == DetailedState.FAILED) {
            mErrorCount++;
        }
        synchronized (mStateLock) {
            handleStateChangedLocked();
        }
    }

    public void augmentNetworkInfo(NetworkInfo info) {
        if (info.isConnected()) {
            final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
            info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
        }
    }

    private void showNotification(int titleRes, int iconRes) {
        final Notification.Builder builder =
                new Notification.Builder(mContext, NOTIFICATION_CHANNEL_VPN)
+24 −13

File changed.

Preview size limit exceeded, changes collapsed.