summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java11
-rw-r--r--services/core/java/com/android/server/connectivity/ConnectivityConstants.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java4
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java37
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java28
5 files changed, 55 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c995c1e90fd4..bdb6d08b9d5a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2339,9 +2339,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private boolean networkRequiresValidation(NetworkAgentInfo nai) {
- return NetworkMonitor.isValidationRequired(
- mDefaultRequest.networkCapabilities, nai.networkCapabilities);
+ private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
+ return nai.networkMonitor.isPrivateDnsValidationRequired();
}
private void handlePrivateDnsSettingsChanged() {
@@ -2349,16 +2348,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
handlePerNetworkPrivateDnsConfig(nai, cfg);
- if (networkRequiresValidation(nai)) {
+ if (networkRequiresPrivateDnsValidation(nai)) {
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
}
}
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;
+ if (!networkRequiresPrivateDnsValidation(nai)) return;
// Notify the NetworkMonitor thread in case it needs to cancel or
// schedule DNS resolutions. If a DNS resolution is required the
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
index 24865bcd9a09..cd7ef9e76f1d 100644
--- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -45,7 +45,7 @@ public class ConnectivityConstants {
//
// This ensures that a) the explicitly selected network is never trumped by anything else, and
// 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
// let them win every single time.
public static final int VPN_DEFAULT_SCORE = 101;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 505480ea537e..5ee572ecb7b1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -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
// a higher-scoring network (e.g., Ethernet) is available.
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
- return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
+ return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
int score = currentScore;
- if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
+ if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index d3dad1d6f494..208fb105325a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -243,12 +243,6 @@ public class NetworkMonitor extends StateMachine {
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 Handler mConnectivityServiceHandler;
private final NetworkAgentInfo mNetworkAgentInfo;
@@ -381,11 +375,19 @@ public class NetworkMonitor extends StateMachine {
return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
}
- private boolean isValidationRequired() {
- return isValidationRequired(
- mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+ @VisibleForTesting
+ public boolean isValidationRequired() {
+ // 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) {
mConnectivityServiceHandler.sendMessage(obtainMessage(
@@ -455,7 +457,7 @@ public class NetworkMonitor extends StateMachine {
return HANDLED;
case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
- if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) {
+ if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) {
// No DNS resolution required.
//
// 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
// packets over the network may be undesirable, for example an extremely
// 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()) {
- validationLog("Network would not satisfy default request, not validating");
- transitionTo(mValidatedState);
+ 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);
+ }
return HANDLED;
}
mAttempts++;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 64e82eab10f2..4826120ef52b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
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.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
@@ -385,7 +386,7 @@ public class ConnectivityServiceTest {
MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
- final String typeName = ConnectivityManager.getNetworkTypeName(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport);
@@ -1104,6 +1105,8 @@ public class ConnectivityServiceTest {
return TYPE_WIFI;
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
+ case TRANSPORT_VPN:
+ return TYPE_VPN;
default:
return TYPE_NONE;
}
@@ -4406,7 +4409,7 @@ public class ConnectivityServiceTest {
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.assertNoCallback();
- // Bring up a VPN that has the INTERNET capability but does not provide Internet access.
+ // 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;
@@ -4419,8 +4422,8 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
mMockVpn.connect();
- // The VPN validates and becomes the default network for our app.
- callback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ // 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();
@@ -4430,9 +4433,24 @@ public class ConnectivityServiceTest {
assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
- assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED));
+ 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);