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

Commit b8c95299 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Trigger NetworkCallback events when private DNS usage has changed." into pi-dev

parents 1f596e0c 1fcb7398
Loading
Loading
Loading
Loading
+65 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,8 @@ import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkPolicyManager;
@@ -140,6 +142,7 @@ import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.LingerMonitor;
@@ -155,6 +158,7 @@ import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -256,6 +260,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
    private INetworkStatsService mStatsService;
    private INetworkStatsService mStatsService;
    private INetworkPolicyManager mPolicyManager;
    private INetworkPolicyManager mPolicyManager;
    private NetworkPolicyManagerInternal mPolicyManagerInternal;
    private NetworkPolicyManagerInternal mPolicyManagerInternal;
    private IIpConnectivityMetrics mIpConnectivityMetrics;


    private String mCurrentTcpBufferSizes;
    private String mCurrentTcpBufferSizes;


@@ -414,6 +419,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
    // Handle changes in Private DNS settings.
    // Handle changes in Private DNS settings.
    private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
    private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;


    // Handle private DNS validation status updates.
    private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;

    private static String eventName(int what) {
    private static String eventName(int what) {
        return sMagicDecoderRing.get(what, Integer.toString(what));
        return sMagicDecoderRing.get(what, Integer.toString(what));
    }
    }
@@ -1553,6 +1561,41 @@ public class ConnectivityService extends IConnectivityManager.Stub
        return true;
        return true;
    }
    }


    @VisibleForTesting
    protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
        @Override
        public void onPrivateDnsValidationEvent(int netId, String ipAddress,
                String hostname, boolean validated) {
            try {
                mHandler.sendMessage(mHandler.obtainMessage(
                        EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
                        new PrivateDnsValidationUpdate(netId,
                                InetAddress.parseNumericAddress(ipAddress),
                                hostname, validated)));
            } catch (IllegalArgumentException e) {
                loge("Error parsing ip address in validation event");
            }
        }
    };

    @VisibleForTesting
    protected void registerNetdEventCallback() {
        mIpConnectivityMetrics =
                (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
        if (mIpConnectivityMetrics == null) {
            Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
        }

        try {
            mIpConnectivityMetrics.addNetdEventCallback(
                    INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
                    mNetdEventCallback);
        } catch (Exception e) {
            loge("Error registering netd callback: " + e);
        }
    }

    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
        @Override
        @Override
        public void onUidRulesChanged(int uid, int uidRules) {
        public void onUidRulesChanged(int uid, int uidRules) {
@@ -1738,6 +1781,7 @@ public class ConnectivityService extends IConnectivityManager.Stub


    void systemReady() {
    void systemReady() {
        loadGlobalProxy();
        loadGlobalProxy();
        registerNetdEventCallback();


        synchronized (this) {
        synchronized (this) {
            mSystemReady = true;
            mSystemReady = true;
@@ -2288,6 +2332,9 @@ 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)) {
                handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
            }
        }
        }
    }
    }


@@ -2312,6 +2359,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
        updateDnses(nai.linkProperties, null, nai.network.netId);
        updateDnses(nai.linkProperties, null, nai.network.netId);
    }
    }


    private void handlePrivateDnsValidationUpdate(PrivateDnsValidationUpdate update) {
        NetworkAgentInfo nai = getNetworkAgentInfoForNetId(update.netId);
        if (nai == null) {
            return;
        }
        mDnsManager.updatePrivateDnsValidation(update);
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
    }

    private void updateLingerState(NetworkAgentInfo nai, long now) {
    private void updateLingerState(NetworkAgentInfo nai, long now) {
        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
        // 2. If the network was lingering and there are now requests, unlinger it.
        // 2. If the network was lingering and there are now requests, unlinger it.
@@ -3002,6 +3058,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
                case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
                case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
                    handlePrivateDnsSettingsChanged();
                    handlePrivateDnsSettingsChanged();
                    break;
                    break;
                case EVENT_PRIVATE_DNS_VALIDATION_UPDATE:
                    handlePrivateDnsValidationUpdate(
                            (PrivateDnsValidationUpdate) msg.obj);
                    break;
            }
            }
        }
        }
    }
    }
@@ -4575,6 +4635,11 @@ public class ConnectivityService extends IConnectivityManager.Stub


        updateRoutes(newLp, oldLp, netId);
        updateRoutes(newLp, oldLp, netId);
        updateDnses(newLp, oldLp, netId);
        updateDnses(newLp, oldLp, netId);
        // Make sure LinkProperties represents the latest private DNS status.
        // This does not need to be done before updateDnses because the
        // LinkProperties are not the source of the private DNS configuration.
        // updateDnses will fetch the private DNS configuration from DnsManager.
        mDnsManager.updatePrivateDnsStatus(netId, newLp);


        // Start or stop clat accordingly to network state.
        // Start or stop clat accordingly to network state.
        networkAgent.updateClat(mNetd);
        networkAgent.updateClat(mNetd);
+135 −4
Original line number Original line Diff line number Diff line
@@ -37,10 +37,10 @@ import android.net.Uri;
import android.net.dns.ResolvUtil;
import android.net.dns.ResolvUtil;
import android.os.Binder;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.INetworkManagementService;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.Slog;


import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.MockableSystemProperties;
@@ -50,8 +50,12 @@ import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Collectors;
import java.util.Set;
import java.util.StringJoiner;
import java.util.StringJoiner;




@@ -110,6 +114,7 @@ import java.util.StringJoiner;
 */
 */
public class DnsManager {
public class DnsManager {
    private static final String TAG = DnsManager.class.getSimpleName();
    private static final String TAG = DnsManager.class.getSimpleName();
    private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig();


    /* Defaults for resolver parameters. */
    /* Defaults for resolver parameters. */
    private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
    private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
@@ -183,11 +188,89 @@ public class DnsManager {
        };
        };
    }
    }


    public static class PrivateDnsValidationUpdate {
        final public int netId;
        final public InetAddress ipAddress;
        final public String hostname;
        final public boolean validated;

        public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
                String hostname, boolean validated) {
            this.netId = netId;
            this.ipAddress = ipAddress;
            this.hostname = hostname;
            this.validated = validated;
        }
    }

    private static class PrivateDnsValidationStatuses {
        enum ValidationStatus {
            IN_PROGRESS,
            FAILED,
            SUCCEEDED
        }

        // Validation statuses of <hostname, ipAddress> pairs for a single netId
        private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap;

        private PrivateDnsValidationStatuses() {
            mValidationMap = new HashMap<>();
        }

        private boolean hasValidatedServer() {
            for (ValidationStatus status : mValidationMap.values()) {
                if (status == ValidationStatus.SUCCEEDED) {
                    return true;
                }
            }
            return false;
        }

        private void updateTrackedDnses(String[] ipAddresses, String hostname) {
            Set<Pair<String, InetAddress>> latestDnses = new HashSet<>();
            for (String ipAddress : ipAddresses) {
                try {
                    latestDnses.add(new Pair(hostname,
                            InetAddress.parseNumericAddress(ipAddress)));
                } catch (IllegalArgumentException e) {}
            }
            // Remove <hostname, ipAddress> pairs that should not be tracked.
            for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it =
                    mValidationMap.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next();
                if (!latestDnses.contains(entry.getKey())) {
                    it.remove();
                }
            }
            // Add new <hostname, ipAddress> pairs that should be tracked.
            for (Pair<String, InetAddress> p : latestDnses) {
                if (!mValidationMap.containsKey(p)) {
                    mValidationMap.put(p, ValidationStatus.IN_PROGRESS);
                }
            }
        }

        private void updateStatus(PrivateDnsValidationUpdate update) {
            Pair<String, InetAddress> p = new Pair(update.hostname,
                    update.ipAddress);
            if (!mValidationMap.containsKey(p)) {
                return;
            }
            if (update.validated) {
                mValidationMap.put(p, ValidationStatus.SUCCEEDED);
            } else {
                mValidationMap.put(p, ValidationStatus.FAILED);
            }
        }
    }

    private final Context mContext;
    private final Context mContext;
    private final ContentResolver mContentResolver;
    private final ContentResolver mContentResolver;
    private final INetworkManagementService mNMS;
    private final INetworkManagementService mNMS;
    private final MockableSystemProperties mSystemProperties;
    private final MockableSystemProperties mSystemProperties;
    // TODO: Replace these Maps with SparseArrays.
    private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
    private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
    private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;


    private int mNumDnsEntries;
    private int mNumDnsEntries;
    private int mSampleValidity;
    private int mSampleValidity;
@@ -203,6 +286,7 @@ public class DnsManager {
        mNMS = nms;
        mNMS = nms;
        mSystemProperties = sp;
        mSystemProperties = sp;
        mPrivateDnsMap = new HashMap<>();
        mPrivateDnsMap = new HashMap<>();
        mPrivateDnsValidationMap = new HashMap<>();


        // TODO: Create and register ContentObservers to track every setting
        // TODO: Create and register ContentObservers to track every setting
        // used herein, posting messages to respond to changes.
        // used herein, posting messages to respond to changes.
@@ -214,6 +298,7 @@ public class DnsManager {


    public void removeNetwork(Network network) {
    public void removeNetwork(Network network) {
        mPrivateDnsMap.remove(network.netId);
        mPrivateDnsMap.remove(network.netId);
        mPrivateDnsValidationMap.remove(network.netId);
    }
    }


    public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
    public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -223,6 +308,40 @@ public class DnsManager {
                : mPrivateDnsMap.remove(network.netId);
                : mPrivateDnsMap.remove(network.netId);
    }
    }


    public void updatePrivateDnsStatus(int netId, LinkProperties lp) {
        // Use the PrivateDnsConfig data pushed to this class instance
        // from ConnectivityService.
        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                PRIVATE_DNS_OFF);

        final boolean useTls = privateDnsCfg.useTls;
        final boolean strictMode = privateDnsCfg.inStrictMode();
        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";

        if (strictMode) {
            lp.setUsePrivateDns(true);
            lp.setPrivateDnsServerName(tlsHostname);
        } else if (useTls) {
            // We are in opportunistic mode. Private DNS should be used if there
            // is a known DNS-over-TLS validated server.
            boolean validated = mPrivateDnsValidationMap.containsKey(netId) &&
                    mPrivateDnsValidationMap.get(netId).hasValidatedServer();
            lp.setUsePrivateDns(validated);
            lp.setPrivateDnsServerName(null);
        } else {
            // Private DNS is disabled.
            lp.setUsePrivateDns(false);
            lp.setPrivateDnsServerName(null);
        }
    }

    public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) {
        final PrivateDnsValidationStatuses statuses =
                mPrivateDnsValidationMap.get(update.netId);
        if (statuses == null) return;
        statuses.updateStatus(update);
    }

    public void setDnsConfigurationForNetwork(
    public void setDnsConfigurationForNetwork(
            int netId, LinkProperties lp, boolean isDefaultNetwork) {
            int netId, LinkProperties lp, boolean isDefaultNetwork) {
        final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
        final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
@@ -238,10 +357,11 @@ public class DnsManager {
        //
        //
        // At this time we do not attempt to enable Private DNS on non-Internet
        // At this time we do not attempt to enable Private DNS on non-Internet
        // networks like IMS.
        // networks like IMS.
        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                PRIVATE_DNS_OFF);


        final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
        final boolean useTls = privateDnsCfg.useTls;
        final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
        final boolean strictMode = privateDnsCfg.inStrictMode();
        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
        final String[] tlsServers =
        final String[] tlsServers =
                strictMode ? NetworkUtils.makeStrings(
                strictMode ? NetworkUtils.makeStrings(
@@ -251,6 +371,17 @@ public class DnsManager {
                : useTls ? assignedServers  // Opportunistic
                : useTls ? assignedServers  // Opportunistic
                : new String[0];            // Off
                : new String[0];            // Off


        // Prepare to track the validation status of the DNS servers in the
        // resolver config when private DNS is in opportunistic or strict mode.
        if (useTls) {
            if (!mPrivateDnsValidationMap.containsKey(netId)) {
                mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
            }
            mPrivateDnsValidationMap.get(netId).updateTrackedDnses(tlsServers, tlsHostname);
        } else {
            mPrivateDnsValidationMap.remove(netId);
        }

        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
                netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
                netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
                Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
                Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
+131 −1
Original line number Original line Diff line number Diff line
@@ -161,6 +161,7 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;
import java.util.Set;
import java.util.Set;
@@ -879,6 +880,10 @@ public class ConnectivityServiceTest {
            return mMetricsService;
            return mMetricsService;
        }
        }


        @Override
        protected void registerNetdEventCallback() {
        }

        public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
        public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
            return mLastCreatedNetworkMonitor;
            return mLastCreatedNetworkMonitor;
        }
        }
@@ -3777,6 +3782,11 @@ public class ConnectivityServiceTest {
        // The default on Android is opportunistic mode ("Automatic").
        // The default on Android is opportunistic mode ("Automatic").
        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");


        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
        final NetworkRequest cellRequest = new NetworkRequest.Builder()
                .addTransportType(TRANSPORT_CELLULAR).build();
        mCm.requestNetwork(cellRequest, cellNetworkCallback);

        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
        waitForIdle();
        waitForIdle();
        // CS tells netd about the empty DNS config for this network.
        // CS tells netd about the empty DNS config for this network.
@@ -3812,6 +3822,14 @@ public class ConnectivityServiceTest {
        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                new String[]{"2001:db8::1", "192.0.2.1"}));
                new String[]{"2001:db8::1", "192.0.2.1"}));
        reset(mNetworkManagementService);
        reset(mNetworkManagementService);
        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
                mCellNetworkAgent);
        CallbackInfo cbi = cellNetworkCallback.expectCallback(
                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());


        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(
@@ -3821,6 +3839,7 @@ public class ConnectivityServiceTest {
        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                new String[]{"2001:db8::1", "192.0.2.1"}));
                new String[]{"2001:db8::1", "192.0.2.1"}));
        reset(mNetworkManagementService);
        reset(mNetworkManagementService);
        cellNetworkCallback.assertNoCallback();


        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
@@ -3833,8 +3852,112 @@ public class ConnectivityServiceTest {
        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                new String[]{"2001:db8::1", "192.0.2.1"}));
                new String[]{"2001:db8::1", "192.0.2.1"}));
        reset(mNetworkManagementService);
        reset(mNetworkManagementService);
        cellNetworkCallback.assertNoCallback();


        // Can't test strict mode without properly mocking out the DNS lookups.
        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.
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
    }

    @Test
    public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception {
        // The default on Android is opportunistic mode ("Automatic").
        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");

        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
        final NetworkRequest cellRequest = new NetworkRequest.Builder()
                .addTransportType(TRANSPORT_CELLULAR).build();
        mCm.requestNetwork(cellRequest, cellNetworkCallback);

        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
        waitForIdle();
        LinkProperties lp = new LinkProperties();
        mCellNetworkAgent.sendLinkProperties(lp);
        mCellNetworkAgent.connect(false);
        waitForIdle();
        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
                mCellNetworkAgent);
        CallbackInfo cbi = cellNetworkCallback.expectCallback(
                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        Set<InetAddress> dnsServers = new HashSet<>();
        checkDnsServers(cbi.arg, dnsServers);

        // Send a validation event for a server that is not part of the current
        // resolver config. The validation event should be ignored.
        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
                mCellNetworkAgent.getNetwork().netId, "", "145.100.185.18", true);
        cellNetworkCallback.assertNoCallback();

        // Add a dns server to the LinkProperties.
        LinkProperties lp2 = new LinkProperties(lp);
        lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
        mCellNetworkAgent.sendLinkProperties(lp2);
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        dnsServers.add(InetAddress.getByName("145.100.185.16"));
        checkDnsServers(cbi.arg, dnsServers);

        // Send a validation event containing a hostname that is not part of
        // the current resolver config. The validation event should be ignored.
        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "hostname", true);
        cellNetworkCallback.assertNoCallback();

        // Send a validation event where validation failed.
        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", false);
        cellNetworkCallback.assertNoCallback();

        // Send a validation event where validation succeeded for a server in
        // the current resolver config. A LinkProperties callback with updated
        // private dns fields should be sent.
        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        checkDnsServers(cbi.arg, dnsServers);

        // The private dns fields in LinkProperties should be preserved when
        // the network agent sends unrelated changes.
        LinkProperties lp3 = new LinkProperties(lp2);
        lp3.setMtu(1300);
        mCellNetworkAgent.sendLinkProperties(lp3);
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        checkDnsServers(cbi.arg, dnsServers);
        assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());

        // Removing the only validated server should affect the private dns
        // fields in LinkProperties.
        LinkProperties lp4 = new LinkProperties(lp3);
        lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
        mCellNetworkAgent.sendLinkProperties(lp4);
        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                mCellNetworkAgent);
        cellNetworkCallback.assertNoCallback();
        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
        dnsServers.remove(InetAddress.getByName("145.100.185.16"));
        checkDnsServers(cbi.arg, dnsServers);
        assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
    }
    }


    private void checkDirectlyConnectedRoutes(Object callbackObj,
    private void checkDirectlyConnectedRoutes(Object callbackObj,
@@ -3854,6 +3977,13 @@ public class ConnectivityServiceTest {
        assertTrue(observedRoutes.containsAll(expectedRoutes));
        assertTrue(observedRoutes.containsAll(expectedRoutes));
    }
    }


    private static void checkDnsServers(Object callbackObj, Set<InetAddress> dnsServers) {
        assertTrue(callbackObj instanceof LinkProperties);
        LinkProperties lp = (LinkProperties) callbackObj;
        assertEquals(dnsServers.size(), lp.getDnsServers().size());
        assertTrue(lp.getDnsServers().containsAll(dnsServers));
    }

    private static <T> void assertEmpty(T[] ts) {
    private static <T> void assertEmpty(T[] ts) {
        int length = ts.length;
        int length = ts.length;
        assertEquals("expected empty array, but length was " + length, 0, length);
        assertEquals("expected empty array, but length was " + length, 0, length);
+201 −0

File added.

Preview size limit exceeded, changes collapsed.