Merge "Allow VPN network agent to exclude local traffic"
diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java
index ad8396b..93fc379 100644
--- a/framework/src/android/net/NetworkAgentConfig.java
+++ b/framework/src/android/net/NetworkAgentConfig.java
@@ -232,6 +232,20 @@
return mLegacyExtraInfo;
}
+ /**
+ * If the {@link Network} is a VPN, whether the local traffic is exempted from the VPN.
+ * @hide
+ */
+ public boolean excludeLocalRouteVpn = false;
+
+ /**
+ * @return whether local traffic is excluded from the VPN network.
+ * @hide
+ */
+ public boolean getExcludeLocalRouteVpn() {
+ return excludeLocalRouteVpn;
+ }
+
/** @hide */
public NetworkAgentConfig() {
}
@@ -251,6 +265,7 @@
legacySubType = nac.legacySubType;
legacySubTypeName = nac.legacySubTypeName;
mLegacyExtraInfo = nac.mLegacyExtraInfo;
+ excludeLocalRouteVpn = nac.excludeLocalRouteVpn;
}
}
@@ -407,6 +422,17 @@
}
/**
+ * Sets whether the local traffic is exempted from VPN.
+ *
+ * @return this builder, to facilitate chaining.
+ * @hide TODO(184750836): Unhide once the implementation is completed.
+ */
+ public Builder setExcludeLocalRoutesVpn(boolean excludeLocalRoutes) {
+ mConfig.excludeLocalRouteVpn = excludeLocalRoutes;
+ return this;
+ }
+
+ /**
* Returns the constructed {@link NetworkAgentConfig} object.
*/
@NonNull
@@ -429,14 +455,15 @@
&& legacyType == that.legacyType
&& Objects.equals(subscriberId, that.subscriberId)
&& Objects.equals(legacyTypeName, that.legacyTypeName)
- && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo);
+ && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo)
+ && excludeLocalRouteVpn == that.excludeLocalRouteVpn;
}
@Override
public int hashCode() {
return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated,
acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId,
- skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo);
+ skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo, excludeLocalRouteVpn);
}
@Override
@@ -453,6 +480,7 @@
+ ", hasShownBroken = " + hasShownBroken
+ ", legacyTypeName = '" + legacyTypeName + '\''
+ ", legacyExtraInfo = '" + mLegacyExtraInfo + '\''
+ + ", excludeLocalRouteVpn = '" + excludeLocalRouteVpn + '\''
+ "}";
}
@@ -475,6 +503,7 @@
out.writeInt(legacySubType);
out.writeString(legacySubTypeName);
out.writeString(mLegacyExtraInfo);
+ out.writeInt(excludeLocalRouteVpn ? 1 : 0);
}
public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
@@ -494,6 +523,7 @@
networkAgentConfig.legacySubType = in.readInt();
networkAgentConfig.legacySubTypeName = in.readString();
networkAgentConfig.mLegacyExtraInfo = in.readString();
+ networkAgentConfig.excludeLocalRouteVpn = in.readInt() != 0;
return networkAgentConfig;
}
diff --git a/tests/common/java/android/net/NetworkAgentConfigTest.kt b/tests/common/java/android/net/NetworkAgentConfigTest.kt
index afaae1c..ed9995c 100644
--- a/tests/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/common/java/android/net/NetworkAgentConfigTest.kt
@@ -48,9 +48,16 @@
setBypassableVpn(true)
}
}.build()
+ // This test can be run as unit test against the latest system image, as CTS to verify
+ // an Android release that is as recent as the test, or as MTS to verify the
+ // Connectivity module. In the first two cases NetworkAgentConfig will be as recent
+ // as the test. In the last case, starting from S NetworkAgentConfig is updated as part
+ // of Connectivity, so it is also as recent as the test. For MTS on Q and R,
+ // NetworkAgentConfig is not updatable, so it may have a different number of fields.
if (isAtLeastS()) {
- // From S, the config will have 12 items
- assertParcelSane(config, 12)
+ // When this test is run on S+, NetworkAgentConfig is as recent as the test,
+ // so this should be the most recent known number of fields.
+ assertParcelSane(config, 13)
} else {
// For R or below, the config will have 10 items
assertParcelSane(config, 10)
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index a945a1f..960a9f1 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
@@ -48,6 +49,7 @@
private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+ private static final int ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE = 25;
@Test
public void testDefaults() throws Exception {
@@ -126,7 +128,12 @@
@Test
public void testParcelUnparcel() {
- assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+ if (isAtLeastT()) {
+ // excludeLocalRoutes is added in T.
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 24);
+ } else {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+ }
}
@Test
@@ -166,7 +173,8 @@
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_AUTH_PARAMS_INLINE,
- ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
}
@@ -183,6 +191,17 @@
}
@Test
+ public void testEncodeDecodeMissingExcludeLocalRoutes() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
+
+ // Verify decoding without isRestrictedToTestNetworks defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.excludeLocalRoutes);
+ }
+
+ @Test
public void testEncodeDecodeLoginsNotSaved() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
profile.saveLogin = false;