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

Commit 6e79d000 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Android (Google) Code Review
Browse files

Merge changes Iaa78a7ed,I6497b7ef into pi-dev

* changes:
  Support strict mode private DNS on VPNs that provide Internet.
  Add test coverage for strict mode private DNS.
parents 48280338 029d9ea1
Loading
Loading
Loading
Loading
+4 −7
Original line number Original line Diff line number Diff line
@@ -2349,9 +2349,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
        }
    }
    }


    private boolean networkRequiresValidation(NetworkAgentInfo nai) {
    private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
        return NetworkMonitor.isValidationRequired(
        return nai.networkMonitor.isPrivateDnsValidationRequired();
                mDefaultRequest.networkCapabilities, nai.networkCapabilities);
    }
    }


    private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
    private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
@@ -2369,16 +2368,14 @@ public class ConnectivityService extends IConnectivityManager.Stub


        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
            handlePerNetworkPrivateDnsConfig(nai, cfg);
            handlePerNetworkPrivateDnsConfig(nai, cfg);
            if (networkRequiresValidation(nai)) {
            if (networkRequiresPrivateDnsValidation(nai)) {
                handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
                handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
            }
            }
        }
        }
    }
    }


    private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
    private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
        // Private DNS only ever applies to networks that might provide
        if (!networkRequiresPrivateDnsValidation(nai)) return;
        // Internet access and therefore also require validation.
        if (!networkRequiresValidation(nai)) return;


        // Notify the NetworkMonitor thread in case it needs to cancel or
        // Notify the NetworkMonitor thread in case it needs to cancel or
        // schedule DNS resolutions. If a DNS resolution is required the
        // schedule DNS resolutions. If a DNS resolution is required the
+1 −1
Original line number Original line Diff line number Diff line
@@ -45,7 +45,7 @@ public class ConnectivityConstants {
    //
    //
    // This ensures that a) the explicitly selected network is never trumped by anything else, and
    // This ensures that a) the explicitly selected network is never trumped by anything else, and
    // b) the explicitly selected network is never torn down.
    // b) the explicitly selected network is never torn down.
    public static final int MAXIMUM_NETWORK_SCORE = 100;
    public static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
    // VPNs typically have priority over other networks. Give them a score that will
    // VPNs typically have priority over other networks. Give them a score that will
    // let them win every single time.
    // let them win every single time.
    public static final int VPN_DEFAULT_SCORE = 101;
    public static final int VPN_DEFAULT_SCORE = 101;
+2 −2
Original line number Original line Diff line number Diff line
@@ -432,11 +432,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        // down an explicitly selected network before the user gets a chance to prefer it when
        // down an explicitly selected network before the user gets a chance to prefer it when
        // a higher-scoring network (e.g., Ethernet) is available.
        // a higher-scoring network (e.g., Ethernet) is available.
        if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
        if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
            return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
            return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
        }
        }


        int score = currentScore;
        int score = currentScore;
        if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
        if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
            score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
            score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
        }
        }
        if (score < 0) score = 0;
        if (score < 0) score = 0;
+40 −15
Original line number Original line Diff line number Diff line
@@ -243,12 +243,6 @@ public class NetworkMonitor extends StateMachine {


    private String mPrivateDnsProviderHostname = "";
    private String mPrivateDnsProviderHostname = "";


    public static boolean isValidationRequired(
            NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
        // TODO: Consider requiring validation for DUN networks.
        return dfltNetCap.satisfiedByNetworkCapabilities(nc);
    }

    private final Context mContext;
    private final Context mContext;
    private final Handler mConnectivityServiceHandler;
    private final Handler mConnectivityServiceHandler;
    private final NetworkAgentInfo mNetworkAgentInfo;
    private final NetworkAgentInfo mNetworkAgentInfo;
@@ -381,11 +375,19 @@ public class NetworkMonitor extends StateMachine {
        return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
        return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
    }
    }


    private boolean isValidationRequired() {
    @VisibleForTesting
        return isValidationRequired(
    public boolean isValidationRequired() {
                mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
        // TODO: Consider requiring validation for DUN networks.
        return mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
                mNetworkAgentInfo.networkCapabilities);
    }
    }


    public boolean isPrivateDnsValidationRequired() {
        // VPNs become the default network for applications even if they do not provide the INTERNET
        // capability (e.g., split tunnels; See b/119216095).
        // Ensure private DNS works on such VPNs as well.
        return isValidationRequired() || mNetworkAgentInfo.isVPN();
    }


    private void notifyNetworkTestResultInvalid(Object obj) {
    private void notifyNetworkTestResultInvalid(Object obj) {
        mConnectivityServiceHandler.sendMessage(obtainMessage(
        mConnectivityServiceHandler.sendMessage(obtainMessage(
@@ -455,7 +457,7 @@ public class NetworkMonitor extends StateMachine {
                    return HANDLED;
                    return HANDLED;
                case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
                case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
                    final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
                    final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
                    if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) {
                    if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) {
                        // No DNS resolution required.
                        // No DNS resolution required.
                        //
                        //
                        // We don't force any validation in opportunistic mode
                        // We don't force any validation in opportunistic mode
@@ -621,9 +623,20 @@ public class NetworkMonitor extends StateMachine {
                    //    the network so don't bother validating here.  Furthermore sending HTTP
                    //    the network so don't bother validating here.  Furthermore sending HTTP
                    //    packets over the network may be undesirable, for example an extremely
                    //    packets over the network may be undesirable, for example an extremely
                    //    expensive metered network, or unwanted leaking of the User Agent string.
                    //    expensive metered network, or unwanted leaking of the User Agent string.
                    //
                    // On networks that need to support private DNS in strict mode (e.g., VPNs, but
                    // not networks that don't provide Internet access), we still need to perform
                    // private DNS server resolution.
                    if (!isValidationRequired()) {
                    if (!isValidationRequired()) {
                        validationLog("Network would not satisfy default request, not validating");
                        if (isPrivateDnsValidationRequired()) {
                            validationLog("Network would not satisfy default request, "
                                    + "resolving private DNS");
                            transitionTo(mEvaluatingPrivateDnsState);
                        } else {
                            validationLog("Network would not satisfy default request, "
                                    + "not validating");
                            transitionTo(mValidatedState);
                            transitionTo(mValidatedState);
                        }
                        return HANDLED;
                        return HANDLED;
                    }
                    }
                    mAttempts++;
                    mAttempts++;
@@ -796,7 +809,7 @@ public class NetworkMonitor extends StateMachine {
            try {
            try {
                // Do a blocking DNS resolution using the network-assigned nameservers.
                // Do a blocking DNS resolution using the network-assigned nameservers.
                // Do not set AI_ADDRCONFIG in ai_flags so we get all address families in advance.
                // Do not set AI_ADDRCONFIG in ai_flags so we get all address families in advance.
                final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
                final InetAddress[] ips = resolveAllLocally(
                        mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */);
                        mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */);
                mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
                mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
            } catch (UnknownHostException uhe) {
            } catch (UnknownHostException uhe) {
@@ -830,7 +843,7 @@ public class NetworkMonitor extends StateMachine {
            final String host = UUID.randomUUID().toString().substring(0, 8) +
            final String host = UUID.randomUUID().toString().substring(0, 8) +
                    ONE_TIME_HOSTNAME_SUFFIX;
                    ONE_TIME_HOSTNAME_SUFFIX;
            try {
            try {
                final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
                final InetAddress[] ips = getAllByName(mNetworkAgentInfo.network(), host);
                return (ips != null && ips.length > 0);
                return (ips != null && ips.length > 0);
            } catch (UnknownHostException uhe) {}
            } catch (UnknownHostException uhe) {}
            return false;
            return false;
@@ -1046,7 +1059,7 @@ public class NetworkMonitor extends StateMachine {
        int result;
        int result;
        String connectInfo;
        String connectInfo;
        try {
        try {
            InetAddress[] addresses = mNetwork.getAllByName(host);
            InetAddress[] addresses = getAllByName(mNetwork, host);
            StringBuffer buffer = new StringBuffer();
            StringBuffer buffer = new StringBuffer();
            for (InetAddress address : addresses) {
            for (InetAddress address : addresses) {
                buffer.append(',').append(address.getHostAddress());
                buffer.append(',').append(address.getHostAddress());
@@ -1233,6 +1246,18 @@ public class NetworkMonitor extends StateMachine {
        }
        }
    }
    }


    @VisibleForTesting
    protected InetAddress[] getAllByName(Network network, String host) throws UnknownHostException {
        return network.getAllByName(host);
    }

    @VisibleForTesting
    protected InetAddress[] resolveAllLocally(Network network, String hostname, int flags)
            throws UnknownHostException {
        // We cannot use this in OneAddressPerFamilyNetwork#getAllByName because that's static.
        return ResolvUtil.blockingResolveAllLocally(network, hostname, flags);
    }

    private URL makeURL(String url) {
    private URL makeURL(String url) {
        if (url != null) {
        if (url != null) {
            try {
            try {
+130 −7
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
@@ -165,6 +166,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.Spy;


import java.net.InetAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collection;
@@ -387,7 +389,7 @@ public class ConnectivityServiceTest {


        MockNetworkAgent(int transport, LinkProperties linkProperties) {
        MockNetworkAgent(int transport, LinkProperties linkProperties) {
            final int type = transportToLegacyType(transport);
            final int type = transportToLegacyType(transport);
            final String typeName = ConnectivityManager.getNetworkTypeName(transport);
            final String typeName = ConnectivityManager.getNetworkTypeName(type);
            mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
            mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
            mNetworkCapabilities = new NetworkCapabilities();
            mNetworkCapabilities = new NetworkCapabilities();
            mNetworkCapabilities.addTransportType(transport);
            mNetworkCapabilities.addTransportType(transport);
@@ -455,6 +457,10 @@ public class ConnectivityServiceTest {
            mNetworkAgent.sendNetworkScore(mScore);
            mNetworkAgent.sendNetworkScore(mScore);
        }
        }


        public int getScore() {
            return mScore;
        }

        public void explicitlySelected(boolean acceptUnvalidated) {
        public void explicitlySelected(boolean acceptUnvalidated) {
            mNetworkAgent.explicitlySelected(acceptUnvalidated);
            mNetworkAgent.explicitlySelected(acceptUnvalidated);
        }
        }
@@ -869,6 +875,7 @@ public class ConnectivityServiceTest {
        // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
        // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
        public int gen204ProbeResult = 500;
        public int gen204ProbeResult = 500;
        public String gen204ProbeRedirectUrl = null;
        public String gen204ProbeRedirectUrl = null;
        public volatile InetAddress[] dnsLookupResults = null;


        public WrappedNetworkMonitor(Context context, Handler handler,
        public WrappedNetworkMonitor(Context context, Handler handler,
                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
@@ -883,6 +890,25 @@ public class ConnectivityServiceTest {
            if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
            if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
            return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
            return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
        }
        }

        private InetAddress[] fakeDnsLookup() throws UnknownHostException {
            if (dnsLookupResults == null) {
                throw new UnknownHostException();
            }
            return dnsLookupResults;
        }

        @Override
        protected InetAddress[] getAllByName(Network network, String hostname)
                throws UnknownHostException {
            return fakeDnsLookup();
        }

        @Override
        protected InetAddress[] resolveAllLocally(Network network, String hostname, int flags)
                throws UnknownHostException {
            return fakeDnsLookup();
        }
    }
    }


    private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
    private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
@@ -1086,6 +1112,8 @@ public class ConnectivityServiceTest {
                return TYPE_WIFI;
                return TYPE_WIFI;
            case TRANSPORT_CELLULAR:
            case TRANSPORT_CELLULAR:
                return TYPE_MOBILE;
                return TYPE_MOBILE;
            case TRANSPORT_VPN:
                return TYPE_VPN;
            default:
            default:
                return TYPE_NONE;
                return TYPE_NONE;
        }
        }
@@ -4021,7 +4049,7 @@ public class ConnectivityServiceTest {
        cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
        cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));


        mCellNetworkAgent.sendLinkProperties(cellLp);
        mCellNetworkAgent.sendLinkProperties(cellLp);
        mCellNetworkAgent.connect(false);
        mCellNetworkAgent.connect(true);
        waitForIdle();
        waitForIdle();
        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
                anyInt(), mStringArrayCaptor.capture(), any(), any(),
                anyInt(), mStringArrayCaptor.capture(), any(), any(),
@@ -4039,9 +4067,10 @@ public class ConnectivityServiceTest {
                mCellNetworkAgent);
                mCellNetworkAgent);
        CallbackInfo cbi = cellNetworkCallback.expectCallback(
        CallbackInfo cbi = cellNetworkCallback.expectCallback(
                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();


        setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
        setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
@@ -4066,14 +4095,45 @@ public class ConnectivityServiceTest {
        reset(mNetworkManagementService);
        reset(mNetworkManagementService);
        cellNetworkCallback.assertNoCallback();
        cellNetworkCallback.assertNoCallback();


        // Strict mode.
        mCellNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults = new InetAddress[] {
                InetAddress.getByName("2001:db8::66"),
                InetAddress.getByName("192.0.2.44")
        };
        setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
        setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
        // Can't test dns configuration for strict mode without properly mocking

        // out the DNS lookups, but can test that LinkProperties is updated.
        // Expect a callback saying that private DNS is now in strict mode.
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
                mCellNetworkAgent);
        LinkProperties lp = (LinkProperties) cbi.arg;
        assertTrue(lp.isPrivateDnsActive());
        assertEquals("strict.example.com", lp.getPrivateDnsServerName());
        cellNetworkCallback.assertNoCallback();
        cellNetworkCallback.assertNoCallback();
        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());

        assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
        // When the validation callback arrives, LinkProperties are updated.
        // We need to wait for this callback because the test thread races with the NetworkMonitor
        // thread, and if the test thread wins the race, then the times(2) verify call below will
        // fail.
        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
                mCellNetworkAgent.getNetwork().netId, "2001:db8::66", "strict.example.com", true);
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        lp = (LinkProperties) cbi.arg;
        assertTrue(lp.isPrivateDnsActive());
        assertEquals(1, lp.getValidatedPrivateDnsServers().size());

        // setDnsConfigurationForNetwork is called twice: once when private DNS is set to strict
        // mode and once when the hostname resolves.
        verify(mNetworkManagementService, times(2)).setDnsConfigurationForNetwork(
                anyInt(), mStringArrayCaptor.capture(), any(), any(),
                eq("strict.example.com"), tlsServers.capture());
        assertEquals(2, mStringArrayCaptor.getValue().length);
        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                new String[]{"2001:db8::1", "192.0.2.1"}));
        assertEquals(2, tlsServers.getValue().length);
        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                new String[]{"2001:db8::66", "192.0.2.44"}));
        reset(mNetworkManagementService);


        // Send the same LinkProperties and expect getting the same result including private dns.
        // Send the same LinkProperties and expect getting the same result including private dns.
        // b/118518971
        // b/118518971
@@ -4395,6 +4455,69 @@ public class ConnectivityServiceTest {
        mCm.unregisterNetworkCallback(defaultCallback);
        mCm.unregisterNetworkCallback(defaultCallback);
    }
    }


    @Test
    public void testVpnUnvalidated() throws Exception {
        final TestNetworkCallback callback = new TestNetworkCallback();
        mCm.registerDefaultNetworkCallback(callback);

        // Enable private DNS.
        setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");

        // Bring up Ethernet.
        mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
        mEthernetNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults =
                new InetAddress[]{ InetAddress.getByName("2001:db8::1") };
        mEthernetNetworkAgent.connect(true);
        callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
        callback.assertNoCallback();

        // Bring up a VPN that has the INTERNET capability but does not validate.
        final int uid = Process.myUid();
        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
        vpnNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
        vpnNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults = null;

        final ArraySet<UidRange> ranges = new ArraySet<>();
        ranges.add(new UidRange(uid, uid));
        mMockVpn.setNetworkAgent(vpnNetworkAgent);
        mMockVpn.setUids(ranges);
        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
        mMockVpn.connect();

        // Even though the VPN is unvalidated, it becomes the default network for our app.
        callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
        // TODO: this looks like a spurious callback.
        callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
        callback.assertNoCallback();

        assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
        assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());

        NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
        assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
        assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));

        assertFalse(vpnNetworkAgent.getWrappedNetworkMonitor().isValidationRequired());
        assertTrue(vpnNetworkAgent.getWrappedNetworkMonitor().isPrivateDnsValidationRequired());

        // Pretend that the strict mode private DNS hostname now resolves. Even though the
        // connectivity probe still returns 500, the network validates because the connectivity
        // probe is not used on VPNs.
        vpnNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults =
                new InetAddress[]{ InetAddress.getByName("2001:db8::1") };
        mCm.reportNetworkConnectivity(vpnNetworkAgent.getNetwork(), true);

        // Expect to see the validated capability, but no other changes, because the VPN is already
        // the default network for the app.
        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
        callback.assertNoCallback();

        vpnNetworkAgent.disconnect();
        callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
        callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
    }

    @Test
    @Test
    public void testVpnSetUnderlyingNetworks() {
    public void testVpnSetUnderlyingNetworks() {
        final int uid = Process.myUid();
        final int uid = Process.myUid();