Add VCN gateway option to enable data stall handling

This commit allows caller to enable VCN's ability of handling data
stall by setting a VCN gateway option.

Bug: 261499808
Test: atest FrameworksVcnTests (new tests), CtsVcnTestCases
Change-Id: If37b26faf851153d64face66d10201d7eecad685
This commit is contained in:
Yan Yan
2022-12-06 02:29:37 +00:00
parent 99f5d0a07d
commit 923011ed09
2 changed files with 198 additions and 6 deletions

View File

@@ -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.
*
* <p>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<Integer> 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<Integer> mGatewayOptions;
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
@NonNull String gatewayConnectionName,
@@ -208,12 +239,14 @@ public final class VcnGatewayConnectionConfig {
@NonNull Set<Integer> exposedCapabilities,
@NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
@IntRange(from = MIN_MTU_V6) int maxMtu,
@NonNull Set<Integer> 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<Integer> 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);
}
}
}

View File

@@ -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<Integer> 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<Integer> 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<Integer> gatewayOptions) {
return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS);
}
private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
Set<Integer> 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<Integer> 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);
}
}