Move the logic of (re)evaluation of Private DNS
Moves this out of ConnectivityService and into each NetworkMonitor
(where it's more self-contained).
Test: as follows
- builds, flashes, boots
- runtest frameworks-net passes
- manual testing with working and non-working hostnames behaves
somewhat (but not entirely) as expected, and not always quickly
Bug: 64133961
Bug: 72345192
Bug: 73872000
Bug: 77140445
Change-Id: Ic4322af3cb49149f2d975cb31f54b2ac7927f907
This commit is contained in:
@@ -902,7 +902,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
// Used only for testing.
|
||||
// TODO: Delete this and either:
|
||||
// 1. Give Fake SettingsProvider the ability to send settings change notifications (requires
|
||||
// 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
|
||||
// changing ContentResolver to make registerContentObserver non-final).
|
||||
// 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
|
||||
// by subclassing SettingsObserver.
|
||||
@@ -911,6 +911,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
|
||||
}
|
||||
|
||||
// See FakeSettingsProvider comment above.
|
||||
@VisibleForTesting
|
||||
void updatePrivateDnsSettings() {
|
||||
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
|
||||
}
|
||||
|
||||
private void handleMobileDataAlwaysOn() {
|
||||
final boolean enable = toBool(Settings.Global.getInt(
|
||||
mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
|
||||
@@ -940,8 +946,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
private void registerPrivateDnsSettingsCallbacks() {
|
||||
for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
|
||||
mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
|
||||
for (Uri uri : DnsManager.getPrivateDnsSettingsUris()) {
|
||||
mSettingsObserver.observe(uri, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,8 +1000,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
if (network == null) {
|
||||
return null;
|
||||
}
|
||||
return getNetworkAgentInfoForNetId(network.netId);
|
||||
}
|
||||
|
||||
private NetworkAgentInfo getNetworkAgentInfoForNetId(int netId) {
|
||||
synchronized (mNetworkForNetId) {
|
||||
return mNetworkForNetId.get(network.netId);
|
||||
return mNetworkForNetId.get(netId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,9 +1145,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
NetworkAgentInfo nai;
|
||||
if (vpnNetId != NETID_UNSET) {
|
||||
synchronized (mNetworkForNetId) {
|
||||
nai = mNetworkForNetId.get(vpnNetId);
|
||||
}
|
||||
nai = getNetworkAgentInfoForNetId(vpnNetId);
|
||||
if (nai != null) return nai.network;
|
||||
}
|
||||
nai = getDefaultNetwork();
|
||||
@@ -2113,41 +2121,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
default:
|
||||
return false;
|
||||
case NetworkMonitor.EVENT_NETWORK_TESTED: {
|
||||
final NetworkAgentInfo nai;
|
||||
synchronized (mNetworkForNetId) {
|
||||
nai = mNetworkForNetId.get(msg.arg2);
|
||||
}
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
|
||||
if (nai == null) break;
|
||||
|
||||
final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
|
||||
final boolean wasValidated = nai.lastValidated;
|
||||
final boolean wasDefault = isDefaultNetwork(nai);
|
||||
|
||||
final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
|
||||
? (PrivateDnsConfig) msg.obj : null;
|
||||
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
|
||||
|
||||
final boolean reevaluationRequired;
|
||||
final String logMsg;
|
||||
if (valid) {
|
||||
reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
|
||||
logMsg = (DBG && (privateDnsCfg != null))
|
||||
? " with " + privateDnsCfg.toString() : "";
|
||||
} else {
|
||||
reevaluationRequired = false;
|
||||
logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
|
||||
? " with redirect to " + redirectUrl : "";
|
||||
}
|
||||
if (DBG) {
|
||||
final String logMsg = !TextUtils.isEmpty(redirectUrl)
|
||||
? " with redirect to " + redirectUrl
|
||||
: "";
|
||||
log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
|
||||
}
|
||||
// If there is a change in Private DNS configuration,
|
||||
// trigger reevaluation of the network to test it.
|
||||
if (reevaluationRequired) {
|
||||
nai.networkMonitor.sendMessage(
|
||||
NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
|
||||
break;
|
||||
}
|
||||
if (valid != nai.lastValidated) {
|
||||
if (wasDefault) {
|
||||
metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
|
||||
@@ -2176,10 +2164,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
|
||||
final int netId = msg.arg2;
|
||||
final boolean visible = toBool(msg.arg1);
|
||||
final NetworkAgentInfo nai;
|
||||
synchronized (mNetworkForNetId) {
|
||||
nai = mNetworkForNetId.get(netId);
|
||||
}
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
|
||||
// If captive portal status has changed, update capabilities or disconnect.
|
||||
if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
|
||||
final int oldScore = nai.getCurrentScore();
|
||||
@@ -2210,18 +2195,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
break;
|
||||
}
|
||||
case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
|
||||
final NetworkAgentInfo nai;
|
||||
synchronized (mNetworkForNetId) {
|
||||
nai = mNetworkForNetId.get(msg.arg2);
|
||||
}
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
|
||||
if (nai == null) break;
|
||||
|
||||
final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
|
||||
final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
|
||||
if (nai.lastValidated && reevaluationRequired) {
|
||||
nai.networkMonitor.sendMessage(
|
||||
NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
|
||||
}
|
||||
updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2259,61 +2236,38 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
|
||||
return NetworkMonitor.isValidationRequired(
|
||||
mDefaultRequest.networkCapabilities, nai.networkCapabilities);
|
||||
}
|
||||
|
||||
private void handlePrivateDnsSettingsChanged() {
|
||||
final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
|
||||
|
||||
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
||||
// Private DNS only ever applies to networks that might provide
|
||||
// Internet access and therefore also require validation.
|
||||
if (!NetworkMonitor.isValidationRequired(
|
||||
mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Notify the NetworkMonitor thread in case it needs to cancel or
|
||||
// schedule DNS resolutions. If a DNS resolution is required the
|
||||
// result will be sent back to us.
|
||||
nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
|
||||
|
||||
if (!cfg.inStrictMode()) {
|
||||
// No strict mode hostname DNS resolution needed, so just update
|
||||
// DNS settings directly. In opportunistic and "off" modes this
|
||||
// just reprograms netd with the network-supplied DNS servers
|
||||
// (and of course the boolean of whether or not to attempt TLS).
|
||||
//
|
||||
// TODO: Consider code flow parity with strict mode, i.e. having
|
||||
// NetworkMonitor relay the PrivateDnsConfig back to us and then
|
||||
// performing this call at that time.
|
||||
updatePrivateDns(nai, cfg);
|
||||
}
|
||||
handlePerNetworkPrivateDnsConfig(nai, cfg);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
|
||||
final boolean reevaluationRequired = true;
|
||||
final boolean dontReevaluate = false;
|
||||
private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
|
||||
// Private DNS only ever applies to networks that might provide
|
||||
// Internet access and therefore also require validation.
|
||||
if (!networkRequiresValidation(nai)) return;
|
||||
|
||||
final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
|
||||
// Notify the NetworkMonitor thread in case it needs to cancel or
|
||||
// schedule DNS resolutions. If a DNS resolution is required the
|
||||
// result will be sent back to us.
|
||||
nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
|
||||
|
||||
// With Private DNS bypass support, we can proceed to update the
|
||||
// Private DNS config immediately, even if we're in strict mode
|
||||
// and have not yet resolved the provider name into a set of IPs.
|
||||
updatePrivateDns(nai, cfg);
|
||||
}
|
||||
|
||||
private void updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
|
||||
mDnsManager.updatePrivateDns(nai.network, newCfg);
|
||||
updateDnses(nai.linkProperties, null, nai.network.netId);
|
||||
|
||||
if (newCfg == null) {
|
||||
if (oldCfg == null) return dontReevaluate;
|
||||
return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
|
||||
}
|
||||
|
||||
if (oldCfg == null) {
|
||||
return newCfg.useTls ? reevaluationRequired : dontReevaluate;
|
||||
}
|
||||
|
||||
if (oldCfg.useTls != newCfg.useTls) {
|
||||
return reevaluationRequired;
|
||||
}
|
||||
|
||||
if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
|
||||
return reevaluationRequired;
|
||||
}
|
||||
|
||||
return dontReevaluate;
|
||||
}
|
||||
|
||||
private void updateLingerState(NetworkAgentInfo nai, long now) {
|
||||
@@ -3252,7 +3206,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
|
||||
return;
|
||||
}
|
||||
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
|
||||
nai.networkMonitor.forceReevaluation(uid);
|
||||
}
|
||||
|
||||
private ProxyInfo getDefaultProxy() {
|
||||
@@ -4871,7 +4825,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
|
||||
if (mNetworkForNetId.get(nai.network.netId) != nai) {
|
||||
if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
|
||||
// Ignore updates for disconnected networks
|
||||
return;
|
||||
}
|
||||
@@ -5451,6 +5405,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
|
||||
}
|
||||
|
||||
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
|
||||
updateLinkProperties(networkAgent, null);
|
||||
|
||||
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
|
||||
|
||||
@@ -61,6 +61,51 @@ import java.util.StringJoiner;
|
||||
* This class it NOT designed for concurrent access. Furthermore, all non-static
|
||||
* methods MUST be called from ConnectivityService's thread.
|
||||
*
|
||||
* [ Private DNS ]
|
||||
* The code handling Private DNS is spread across several components, but this
|
||||
* seems like the least bad place to collect all the observations.
|
||||
*
|
||||
* Private DNS handling and updating occurs in response to several different
|
||||
* events. Each is described here with its corresponding intended handling.
|
||||
*
|
||||
* [A] Event: A new network comes up.
|
||||
* Mechanics:
|
||||
* [1] ConnectivityService gets notifications from NetworkAgents.
|
||||
* [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
|
||||
* into CONNECTED state, the Private DNS configuration is retrieved,
|
||||
* programmed, and strict mode hostname resolution (if applicable) is
|
||||
* enqueued in NetworkAgent's NetworkMonitor, via a call to
|
||||
* handlePerNetworkPrivateDnsConfig().
|
||||
* [3] Re-resolution of strict mode hostnames that fail to return any
|
||||
* IP addresses happens inside NetworkMonitor; it sends itself a
|
||||
* delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
|
||||
* schedule.
|
||||
* [4] Successfully resolved hostnames are sent to ConnectivityService
|
||||
* inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
|
||||
* IP addresses are programmed into netd via:
|
||||
*
|
||||
* updatePrivateDns() -> updateDnses()
|
||||
*
|
||||
* both of which make calls into DnsManager.
|
||||
* [5] Upon a successful hostname resolution NetworkMonitor initiates a
|
||||
* validation attempt in the form of a lookup for a one-time hostname
|
||||
* that uses Private DNS.
|
||||
*
|
||||
* [B] Event: Private DNS settings are changed.
|
||||
* Mechanics:
|
||||
* [1] ConnectivityService gets notifications from its SettingsObserver.
|
||||
* [2] handlePrivateDnsSettingsChanged() is called, which calls
|
||||
* handlePerNetworkPrivateDnsConfig() and the process proceeds
|
||||
* as if from A.3 above.
|
||||
*
|
||||
* [C] Event: An application calls ConnectivityManager#reportBadNetwork().
|
||||
* Mechanics:
|
||||
* [1] NetworkMonitor is notified and initiates a reevaluation, which
|
||||
* always bypasses Private DNS.
|
||||
* [2] Once completed, NetworkMonitor checks if strict mode is in operation
|
||||
* and if so enqueues another evaluation of Private DNS, as if from
|
||||
* step A.5 above.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class DnsManager {
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.android.server;
|
||||
|
||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
|
||||
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
|
||||
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
|
||||
import static android.net.ConnectivityManager.TYPE_ETHERNET;
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
||||
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
|
||||
@@ -70,6 +73,7 @@ import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
@@ -181,6 +185,9 @@ public class ConnectivityServiceTest {
|
||||
private static final int TIMEOUT_MS = 500;
|
||||
private static final int TEST_LINGER_DELAY_MS = 120;
|
||||
|
||||
private static final String MOBILE_IFNAME = "test_rmnet_data0";
|
||||
private static final String WIFI_IFNAME = "test_wlan0";
|
||||
|
||||
private MockContext mServiceContext;
|
||||
private WrappedConnectivityService mService;
|
||||
private WrappedConnectivityManager mCm;
|
||||
@@ -751,7 +758,7 @@ public class ConnectivityServiceTest {
|
||||
|
||||
// NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
|
||||
private class WrappedNetworkMonitor extends NetworkMonitor {
|
||||
public Handler connectivityHandler;
|
||||
public final Handler connectivityHandler;
|
||||
// HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
|
||||
public int gen204ProbeResult = 500;
|
||||
public String gen204ProbeRedirectUrl = null;
|
||||
@@ -926,6 +933,7 @@ public class ConnectivityServiceTest {
|
||||
// Ensure that the default setting for Captive Portals is used for most tests
|
||||
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
|
||||
setMobileDataAlwaysOn(false);
|
||||
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -2580,6 +2588,14 @@ public class ConnectivityServiceTest {
|
||||
waitForIdle();
|
||||
}
|
||||
|
||||
private void setPrivateDnsSettings(String mode, String specifier) {
|
||||
final ContentResolver cr = mServiceContext.getContentResolver();
|
||||
Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_MODE, mode);
|
||||
Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_SPECIFIER, specifier);
|
||||
mService.updatePrivateDnsSettings();
|
||||
waitForIdle();
|
||||
}
|
||||
|
||||
private boolean isForegroundNetwork(MockNetworkAgent network) {
|
||||
NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
|
||||
assertNotNull(nc);
|
||||
@@ -3579,7 +3595,7 @@ public class ConnectivityServiceTest {
|
||||
mCm.registerNetworkCallback(networkRequest, networkCallback);
|
||||
|
||||
LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName("wlan0");
|
||||
lp.setInterfaceName(WIFI_IFNAME);
|
||||
LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24");
|
||||
RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null,
|
||||
NetworkUtils.numericToInetAddress("192.168.12.1"), lp.getInterfaceName());
|
||||
@@ -3668,52 +3684,63 @@ public class ConnectivityServiceTest {
|
||||
|
||||
@Test
|
||||
public void testBasicDnsConfigurationPushed() throws Exception {
|
||||
final String IFNAME = "test_rmnet_data0";
|
||||
final String[] EMPTY_TLS_SERVERS = new String[0];
|
||||
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
|
||||
ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
|
||||
|
||||
// Clear any interactions that occur as a result of CS starting up.
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
|
||||
anyInt(), any(), any(), any(), anyString(), eq(EMPTY_TLS_SERVERS));
|
||||
anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
|
||||
verifyNoMoreInteractions(mNetworkManagementService);
|
||||
|
||||
final LinkProperties cellLp = new LinkProperties();
|
||||
cellLp.setInterfaceName(IFNAME);
|
||||
cellLp.setInterfaceName(MOBILE_IFNAME);
|
||||
// Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
|
||||
// "is-reachable" testing in order to not program netd with unreachable
|
||||
// nameservers that it might try repeated to validate.
|
||||
cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
|
||||
cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), IFNAME));
|
||||
cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
|
||||
MOBILE_IFNAME));
|
||||
cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
|
||||
cellLp.addRoute(
|
||||
new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), IFNAME));
|
||||
cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
|
||||
MOBILE_IFNAME));
|
||||
mCellNetworkAgent.sendLinkProperties(cellLp);
|
||||
mCellNetworkAgent.connect(false);
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
anyString(), eq(EMPTY_TLS_SERVERS));
|
||||
// CS tells netd about the empty DNS config for this network.
|
||||
assertEmpty(mStringArrayCaptor.getValue());
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
|
||||
mCellNetworkAgent.sendLinkProperties(cellLp);
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
anyString(), eq(EMPTY_TLS_SERVERS));
|
||||
eq(""), tlsServers.capture());
|
||||
assertEquals(1, mStringArrayCaptor.getValue().length);
|
||||
assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
|
||||
// Opportunistic mode.
|
||||
assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1"));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
|
||||
mCellNetworkAgent.sendLinkProperties(cellLp);
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
anyString(), eq(EMPTY_TLS_SERVERS));
|
||||
eq(""), tlsServers.capture());
|
||||
assertEquals(2, mStringArrayCaptor.getValue().length);
|
||||
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
|
||||
new String[]{"2001:db8::1", "192.0.2.1"}));
|
||||
// Opportunistic mode.
|
||||
assertEquals(2, tlsServers.getValue().length);
|
||||
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
|
||||
new String[]{"2001:db8::1", "192.0.2.1"}));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
final String TLS_SPECIFIER = "tls.example.com";
|
||||
@@ -3726,7 +3753,7 @@ public class ConnectivityServiceTest {
|
||||
mCellNetworkAgent.getNetwork().netId,
|
||||
new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
eq(TLS_SPECIFIER), eq(TLS_SERVERS));
|
||||
assertEquals(2, mStringArrayCaptor.getValue().length);
|
||||
@@ -3735,6 +3762,77 @@ public class ConnectivityServiceTest {
|
||||
reset(mNetworkManagementService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrivateDnsSettingsChange() throws Exception {
|
||||
final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
|
||||
|
||||
// Clear any interactions that occur as a result of CS starting up.
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
// The default on Android is opportunistic mode ("Automatic").
|
||||
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
|
||||
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
waitForIdle();
|
||||
// CS tells netd about the empty DNS config for this network.
|
||||
verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
|
||||
anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
|
||||
verifyNoMoreInteractions(mNetworkManagementService);
|
||||
|
||||
final LinkProperties cellLp = new LinkProperties();
|
||||
cellLp.setInterfaceName(MOBILE_IFNAME);
|
||||
// Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
|
||||
// "is-reachable" testing in order to not program netd with unreachable
|
||||
// nameservers that it might try repeated to validate.
|
||||
cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
|
||||
cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
|
||||
MOBILE_IFNAME));
|
||||
cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
|
||||
cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
|
||||
MOBILE_IFNAME));
|
||||
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
|
||||
cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
|
||||
|
||||
mCellNetworkAgent.sendLinkProperties(cellLp);
|
||||
mCellNetworkAgent.connect(false);
|
||||
waitForIdle();
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
eq(""), tlsServers.capture());
|
||||
assertEquals(2, mStringArrayCaptor.getValue().length);
|
||||
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
|
||||
new String[]{"2001:db8::1", "192.0.2.1"}));
|
||||
// Opportunistic mode.
|
||||
assertEquals(2, tlsServers.getValue().length);
|
||||
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
|
||||
new String[]{"2001:db8::1", "192.0.2.1"}));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
|
||||
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
eq(""), eq(EMPTY_STRING_ARRAY));
|
||||
assertEquals(2, mStringArrayCaptor.getValue().length);
|
||||
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
|
||||
new String[]{"2001:db8::1", "192.0.2.1"}));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
|
||||
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
|
||||
anyInt(), mStringArrayCaptor.capture(), any(), any(),
|
||||
eq(""), 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::1", "192.0.2.1"}));
|
||||
reset(mNetworkManagementService);
|
||||
|
||||
// Can't test strict mode without properly mocking out the DNS lookups.
|
||||
}
|
||||
|
||||
private void checkDirectlyConnectedRoutes(Object callbackObj,
|
||||
Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
|
||||
assertTrue(callbackObj instanceof LinkProperties);
|
||||
|
||||
Reference in New Issue
Block a user