diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 2339656979b50..7718053ddf7d9 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -42,6 +42,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -130,6 +131,33 @@ public final class VcnGatewayConnectionConfig {
})
public @interface VcnSupportedCapability {}
+ /**
+ * Perform mobility update to attempt recovery from suspected data stalls.
+ *
+ *
If set, the gatway connection will monitor the data stall detection of the VCN network.
+ * When there is a suspected data stall, the gateway connection will attempt recovery by
+ * performing a mobility update on the underlying IKE session.
+ *
+ * @hide
+ */
+ public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"VCN_GATEWAY_OPTION_"},
+ value = {
+ // TODO: b/261499808 Add VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY
+ // when it is exposed
+ })
+ public @interface VcnGatewayOption {}
+
+ private static final Set ALLOWED_GATEWAY_OPTIONS = new ArraySet<>();
+
+ static {
+ ALLOWED_GATEWAY_OPTIONS.add(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
+ }
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -201,6 +229,9 @@ public final class VcnGatewayConnectionConfig {
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
+ private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
+ @NonNull private final Set mGatewayOptions;
+
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
@NonNull String gatewayConnectionName,
@@ -208,12 +239,14 @@ public final class VcnGatewayConnectionConfig {
@NonNull Set exposedCapabilities,
@NonNull List underlyingNetworkTemplates,
@NonNull long[] retryIntervalsMs,
- @IntRange(from = MIN_MTU_V6) int maxMtu) {
+ @IntRange(from = MIN_MTU_V6) int maxMtu,
+ @NonNull Set gatewayOptions) {
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
mExposedCapabilities = new TreeSet(exposedCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
+ mGatewayOptions = Collections.unmodifiableSet(new HashSet(gatewayOptions));
mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
if (mUnderlyingNetworkTemplates.isEmpty()) {
@@ -256,6 +289,20 @@ public final class VcnGatewayConnectionConfig {
VcnUnderlyingNetworkTemplate::fromPersistableBundle);
}
+ final PersistableBundle gatewayOptionsBundle = in.getPersistableBundle(GATEWAY_OPTIONS_KEY);
+
+ if (gatewayOptionsBundle == null) {
+ // GATEWAY_OPTIONS_KEY was added in Android U. Thus VcnGatewayConnectionConfig created
+ // on old platforms will not have this data and will be assigned with the default value
+ mGatewayOptions = Collections.emptySet();
+ } else {
+ mGatewayOptions =
+ new HashSet<>(
+ PersistableBundleUtils.toList(
+ gatewayOptionsBundle,
+ PersistableBundleUtils.INTEGER_DESERIALIZER));
+ }
+
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -279,6 +326,10 @@ public final class VcnGatewayConnectionConfig {
Preconditions.checkArgument(
mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+
+ for (int option : mGatewayOptions) {
+ validateGatewayOption(option);
+ }
}
private static void checkValidCapability(int capability) {
@@ -315,6 +366,12 @@ public final class VcnGatewayConnectionConfig {
}
}
+ private static void validateGatewayOption(int option) {
+ if (!ALLOWED_GATEWAY_OPTIONS.contains(option)) {
+ throw new IllegalArgumentException("Invalid vcn gateway option: " + option);
+ }
+ }
+
/**
* Returns the configured Gateway Connection name.
*
@@ -398,6 +455,18 @@ public final class VcnGatewayConnectionConfig {
return mMaxMtu;
}
+ /**
+ * Checks if the given VCN gateway option is enabled.
+ *
+ * @param option the option to check.
+ * @throws IllegalArgumentException if the provided option is invalid.
+ * @hide
+ */
+ public boolean hasGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ return mGatewayOptions.contains(option);
+ }
+
/**
* Converts this config to a PersistableBundle.
*
@@ -418,11 +487,16 @@ public final class VcnGatewayConnectionConfig {
PersistableBundleUtils.fromList(
mUnderlyingNetworkTemplates,
VcnUnderlyingNetworkTemplate::toPersistableBundle);
+ final PersistableBundle gatewayOptionsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mGatewayOptions),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
+ result.putPersistableBundle(GATEWAY_OPTIONS_KEY, gatewayOptionsBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
@@ -437,7 +511,8 @@ public final class VcnGatewayConnectionConfig {
mExposedCapabilities,
mUnderlyingNetworkTemplates,
Arrays.hashCode(mRetryIntervalsMs),
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
@Override
@@ -452,7 +527,8 @@ public final class VcnGatewayConnectionConfig {
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
&& mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
- && mMaxMtu == rhs.mMaxMtu;
+ && mMaxMtu == rhs.mMaxMtu
+ && mGatewayOptions.equals(rhs.mGatewayOptions);
}
/**
@@ -470,6 +546,8 @@ public final class VcnGatewayConnectionConfig {
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
+ @NonNull private final Set mGatewayOptions = new ArraySet<>();
+
// TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent.
// Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS
// when on Cell.
@@ -627,6 +705,36 @@ public final class VcnGatewayConnectionConfig {
return this;
}
+ /**
+ * Enables the specified VCN gateway option.
+ *
+ * @param option the option to be enabled
+ * @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if the provided option is invalid
+ * @hide
+ */
+ @NonNull
+ public Builder addGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ mGatewayOptions.add(option);
+ return this;
+ }
+
+ /**
+ * Resets (disables) the specified VCN gateway option.
+ *
+ * @param option the option to be disabled
+ * @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if the provided option is invalid
+ * @hide
+ */
+ @NonNull
+ public Builder removeGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ mGatewayOptions.remove(option);
+ return this;
+ }
+
/**
* Builds and validates the VcnGatewayConnectionConfig.
*
@@ -640,7 +748,8 @@ public final class VcnGatewayConnectionConfig {
mExposedCapabilities,
mUnderlyingNetworkTemplates,
mRetryIntervalsMs,
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 2aef9ae7ca327..de846579071ad 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -19,9 +19,11 @@ package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -42,7 +44,9 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -79,6 +83,9 @@ public class VcnGatewayConnectionConfigTest {
};
public static final int MAX_MTU = 1360;
+ private static final Set GATEWAY_OPTIONS =
+ Collections.singleton(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
+
public static final IkeTunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
TunnelConnectionParamsUtilsTest.buildTestParams();
@@ -109,10 +116,16 @@ public class VcnGatewayConnectionConfigTest {
TUNNEL_CONNECTION_PARAMS);
}
- private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
- VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
+ VcnGatewayConnectionConfig.Builder builder,
+ Set gatewayOptions,
+ int... exposedCaps) {
builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ for (int option : gatewayOptions) {
+ builder.addGatewayOption(option);
+ }
+
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
@@ -120,11 +133,27 @@ public class VcnGatewayConnectionConfigTest {
return builder.build();
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+ VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ return buildTestConfigWithExposedCapsAndOptions(
+ builder, Collections.emptySet(), exposedCaps);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ VcnGatewayConnectionConfig.Builder builder, Set gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS);
+ }
+
+ private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ Set gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -210,6 +239,15 @@ public class VcnGatewayConnectionConfigTest {
}
}
+ @Test
+ public void testBuilderRequiresValidOption() {
+ try {
+ newBuilder().addGatewayOption(-1);
+ fail("Expected exception due to the invalid VCN gateway option");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
@Test
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
@@ -225,6 +263,20 @@ public class VcnGatewayConnectionConfigTest {
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
+
+ assertFalse(
+ config.hasGatewayOption(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY));
+ }
+
+ @Test
+ public void testBuilderAndGettersWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ for (int option : GATEWAY_OPTIONS) {
+ assertTrue(config.hasGatewayOption(option));
+ }
}
@Test
@@ -234,6 +286,14 @@ public class VcnGatewayConnectionConfigTest {
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+ @Test
+ public void testPersistableBundleWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
@Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
@@ -318,4 +378,27 @@ public class VcnGatewayConnectionConfigTest {
assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
assertNotEquals(config, configNotEqual);
}
+
+ private static VcnGatewayConnectionConfig buildConfigWithGatewayOptionsForEqualityTest(
+ Set gatewayOptions) {
+ return buildTestConfigWithGatewayOptions(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildConfigWithGatewayOptionsForEqualityTest", TUNNEL_CONNECTION_PARAMS),
+ gatewayOptions);
+ }
+
+ @Test
+ public void testVcnGatewayOptionsEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(Collections.emptySet());
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}