diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java index ad8396b06d..93fc379924 100644 --- a/framework/src/android/net/NetworkAgentConfig.java +++ b/framework/src/android/net/NetworkAgentConfig.java @@ -232,6 +232,20 @@ public final class NetworkAgentConfig implements Parcelable { 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 @@ public final class NetworkAgentConfig implements Parcelable { legacySubType = nac.legacySubType; legacySubTypeName = nac.legacySubTypeName; mLegacyExtraInfo = nac.mLegacyExtraInfo; + excludeLocalRouteVpn = nac.excludeLocalRouteVpn; } } @@ -406,6 +421,17 @@ public final class NetworkAgentConfig implements Parcelable { return this; } + /** + * 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. */ @@ -429,14 +455,15 @@ public final class NetworkAgentConfig implements Parcelable { && 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 @@ public final class NetworkAgentConfig implements Parcelable { + ", hasShownBroken = " + hasShownBroken + ", legacyTypeName = '" + legacyTypeName + '\'' + ", legacyExtraInfo = '" + mLegacyExtraInfo + '\'' + + ", excludeLocalRouteVpn = '" + excludeLocalRouteVpn + '\'' + "}"; } @@ -475,6 +503,7 @@ public final class NetworkAgentConfig implements Parcelable { out.writeInt(legacySubType); out.writeString(legacySubTypeName); out.writeString(mLegacyExtraInfo); + out.writeInt(excludeLocalRouteVpn ? 1 : 0); } public static final @NonNull Creator CREATOR = @@ -494,6 +523,7 @@ public final class NetworkAgentConfig implements Parcelable { 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 afaae1cf85..ed9995c97a 100644 --- a/tests/common/java/android/net/NetworkAgentConfigTest.kt +++ b/tests/common/java/android/net/NetworkAgentConfigTest.kt @@ -48,9 +48,16 @@ class NetworkAgentConfigTest { 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 a945a1f2cd..960a9f1b10 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 @@ public class VpnProfileTest { 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 @@ public class VpnProfileTest { @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 @@ public class VpnProfileTest { 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())); } @@ -182,6 +190,17 @@ public class VpnProfileTest { assertFalse(decoded.isRestrictedToTestNetworks); } + @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);