Merge changes If8097d58,Ic8ec4587,If37b26fa
* changes:
APIs to enable data stall handling in VCN
Perform MOBIKE when data stall is suspected on VCN network
Add VCN gateway option to enable data stall handling
diff --git a/core/api/current.txt b/core/api/current.txt
index db9c969..1578839 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -27303,13 +27303,17 @@
method @IntRange(from=0x500) public int getMaxMtu();
method @NonNull public long[] getRetryIntervalsMillis();
method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
+ method public boolean hasGatewayOption(int);
+ field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
}
public static final class VcnGatewayConnectionConfig.Builder {
ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 2339656..b8850f4 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -42,6 +42,7 @@
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,30 @@
})
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.
+ */
+ public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"VCN_GATEWAY_OPTION_"},
+ value = {
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY,
+ })
+ 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 +226,9 @@
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 +236,14 @@
@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 +286,20 @@
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 +323,10 @@
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 +363,12 @@
}
}
+ 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.
*
@@ -399,6 +453,19 @@
}
/**
+ * Checks if the given VCN gateway option is enabled.
+ *
+ * @param option the option to check.
+ * @throws IllegalArgumentException if the provided option is invalid.
+ * @see Builder#addGatewayOption(int)
+ * @see Builder#removeGatewayOption(int)
+ */
+ public boolean hasGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ return mGatewayOptions.contains(option);
+ }
+
+ /**
* Converts this config to a PersistableBundle.
*
* @hide
@@ -418,11 +485,16 @@
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 +509,8 @@
mExposedCapabilities,
mUnderlyingNetworkTemplates,
Arrays.hashCode(mRetryIntervalsMs),
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
@Override
@@ -452,7 +525,8 @@
&& 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 +544,8 @@
@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.
@@ -628,6 +704,34 @@
}
/**
+ * 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
+ */
+ @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
+ */
+ @NonNull
+ public Builder removeGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ mGatewayOptions.remove(option);
+ return this;
+ }
+
+ /**
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
@@ -640,7 +744,8 @@
mExposedCapabilities,
mUnderlyingNetworkTemplates,
mRetryIntervalsMs,
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 05df22f..3be16a1 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -26,6 +26,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
@@ -36,6 +37,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
@@ -50,6 +53,7 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
@@ -546,6 +550,39 @@
}
}
+ /**
+ * Sent when there is a suspected data stall on a network
+ *
+ * <p>Only relevant in the Connected state.
+ *
+ * @param arg1 The "all" token; this signal is always honored.
+ * @param obj @NonNull An EventDataStallSuspectedInfo instance with relevant data.
+ */
+ private static final int EVENT_DATA_STALL_SUSPECTED = 13;
+
+ private static class EventDataStallSuspectedInfo implements EventInfo {
+ @NonNull public final Network network;
+
+ EventDataStallSuspectedInfo(@NonNull Network network) {
+ this.network = network;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof EventDataStallSuspectedInfo)) {
+ return false;
+ }
+
+ final EventDataStallSuspectedInfo rhs = (EventDataStallSuspectedInfo) other;
+ return Objects.equals(network, rhs.network);
+ }
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -578,10 +615,13 @@
@NonNull
private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback;
+ @NonNull private final VcnConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
+
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
@NonNull private final ConnectivityManager mConnectivityManager;
+ @NonNull private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
@Nullable private IpSecTunnelInterface mTunnelIface = null;
@@ -748,6 +788,20 @@
mUnderlyingNetworkControllerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+ mConnectivityDiagnosticsManager =
+ mVcnContext.getContext().getSystemService(ConnectivityDiagnosticsManager.class);
+
+ mConnectivityDiagnosticsCallback = new VcnConnectivityDiagnosticsCallback();
+
+ if (mConnectionConfig.hasGatewayOption(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)) {
+ final NetworkRequest diagRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
+ diagRequest,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ mConnectivityDiagnosticsCallback);
+ }
addState(mDisconnectedState);
addState(mDisconnectingState);
@@ -810,6 +864,9 @@
mUnderlyingNetworkController.teardown();
mGatewayStatusCallback.onQuit();
+
+ mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback);
}
/**
@@ -828,6 +885,20 @@
sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
+ private class VcnConnectivityDiagnosticsCallback extends ConnectivityDiagnosticsCallback {
+ @Override
+ public void onDataStallSuspected(ConnectivityDiagnosticsManager.DataStallReport report) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ final Network network = report.getNetwork();
+ logInfo("Data stall suspected on " + network);
+ sendMessageAndAcquireWakeLock(
+ EVENT_DATA_STALL_SUSPECTED,
+ TOKEN_ALL,
+ new EventDataStallSuspectedInfo(network));
+ }
+ }
+
private class VcnUnderlyingNetworkControllerCallback
implements UnderlyingNetworkControllerCallback {
@Override
@@ -1367,7 +1438,8 @@
case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
case EVENT_MIGRATION_COMPLETED: // Fallthrough
- case EVENT_IKE_CONNECTION_INFO_CHANGED:
+ case EVENT_IKE_CONNECTION_INFO_CHANGED: // Fallthrough
+ case EVENT_DATA_STALL_SUSPECTED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -1925,6 +1997,11 @@
mIkeConnectionInfo =
((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
break;
+ case EVENT_DATA_STALL_SUSPECTED:
+ final Network networkWithDataStall =
+ ((EventDataStallSuspectedInfo) msg.obj).network;
+ handleDataStallSuspected(networkWithDataStall);
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1985,6 +2062,15 @@
}
}
+ private void handleDataStallSuspected(Network networkWithDataStall) {
+ if (mUnderlying != null
+ && mNetworkAgent != null
+ && mNetworkAgent.getNetwork().equals(networkWithDataStall)) {
+ logInfo("Perform Mobility update to recover from suspected data stall");
+ mIkeSession.setNetwork(mUnderlying.network);
+ }
+ }
+
protected void setupInterfaceAndNetworkAgent(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
@@ -2424,6 +2510,11 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() {
+ return mConnectivityDiagnosticsCallback;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord getUnderlyingNetwork() {
return mUnderlying;
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 2aef9ae..4040888 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -19,9 +19,11 @@
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 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 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 @@
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,28 @@
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);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ Set<Integer> gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -211,6 +241,15 @@
}
@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 +264,20 @@
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
@@ -235,6 +288,14 @@
}
@Test
+ public void testPersistableBundleWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -318,4 +379,27 @@
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);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 15d4f10..1c21a06 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -50,9 +50,11 @@
import static java.util.Collections.singletonList;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
@@ -63,10 +65,12 @@
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager.VcnErrorCode;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
import org.junit.Before;
@@ -88,6 +92,7 @@
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
private VcnIkeSession mIkeSession;
private VcnNetworkAgent mNetworkAgent;
+ private Network mVcnNetwork;
@Before
public void setUp() throws Exception {
@@ -98,6 +103,9 @@
.when(mDeps)
.newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any());
+ mVcnNetwork = mock(Network.class);
+ doReturn(mVcnNetwork).when(mNetworkAgent).getNetwork();
+
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
@@ -166,6 +174,56 @@
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
+ private void verifyDataStallTriggersMigration(
+ UnderlyingNetworkRecord networkRecord,
+ Network networkWithDataStall,
+ boolean expectMobilityUpdate)
+ throws Exception {
+ mGatewayConnection.setUnderlyingNetwork(networkRecord);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ final DataStallReport report =
+ new DataStallReport(
+ networkWithDataStall,
+ 1234 /* reportTimestamp */,
+ 1 /* detectionMethod */,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ new PersistableBundle());
+
+ mGatewayConnection.getConnectivityDiagnosticsCallback().onDataStallSuspected(report);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ if (expectMobilityUpdate) {
+ verify(mIkeSession).setNetwork(networkRecord.network);
+ } else {
+ verify(mIkeSession, never()).setNetwork(any(Network.class));
+ }
+ }
+
+ @Test
+ public void testDataStallTriggersMigration() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1, mVcnNetwork, true /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenOnOtherNetwork() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1,
+ mock(Network.class),
+ false /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenUnderlyingNetworkLost() throws Exception {
+ verifyDataStallTriggersMigration(
+ null /* networkRecord */, mock(Network.class), false /* expectMobilityUpdate */);
+ }
+
private void verifyVcnTransformsApplied(
VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform)
throws Exception {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 6a9a1e2..a4ee2de 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
@@ -34,20 +35,25 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
@@ -64,6 +70,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
import java.util.Arrays;
@@ -71,7 +78,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.Executor;
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@@ -287,5 +296,60 @@
verify(vcnNetworkAgent).unregister();
verifyWakeLockReleased();
+
+ verify(mConnDiagMgr)
+ .unregisterConnectivityDiagnosticsCallback(
+ mGatewayConnection.getConnectivityDiagnosticsCallback());
+ }
+
+ private VcnGatewayConnection buildConnectionWithDataStallHandling(
+ boolean datatStallHandlingEnabled) throws Exception {
+ Set<Integer> options =
+ datatStallHandlingEnabled
+ ? Collections.singleton(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)
+ : Collections.emptySet();
+ final VcnGatewayConnectionConfig gatewayConfig =
+ VcnGatewayConnectionConfigTest.buildTestConfigWithGatewayOptions(options);
+ final VcnGatewayConnection gatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ gatewayConfig,
+ mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
+ mDeps);
+ return gatewayConnection;
+ }
+
+ @Test
+ public void testDataStallHandlingEnabled() throws Exception {
+ final VcnGatewayConnection gatewayConnection =
+ buildConnectionWithDataStallHandling(true /* datatStallHandlingEnabled */);
+
+ final ArgumentCaptor<NetworkRequest> networkRequestCaptor =
+ ArgumentCaptor.forClass(NetworkRequest.class);
+ verify(mConnDiagMgr)
+ .registerConnectivityDiagnosticsCallback(
+ networkRequestCaptor.capture(),
+ any(Executor.class),
+ eq(gatewayConnection.getConnectivityDiagnosticsCallback()));
+
+ final NetworkRequest nr = networkRequestCaptor.getValue();
+ final NetworkRequest expected =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ assertEquals(expected, nr);
+ }
+
+ @Test
+ public void testDataStallHandlingDisabled() throws Exception {
+ buildConnectionWithDataStallHandling(false /* datatStallHandlingEnabled */);
+
+ verify(mConnDiagMgr, never())
+ .registerConnectivityDiagnosticsCallback(
+ any(NetworkRequest.class),
+ any(Executor.class),
+ any(ConnectivityDiagnosticsCallback.class));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 785bff1..7bafd24 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpSecConfig;
@@ -157,6 +158,7 @@
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final ConnectivityDiagnosticsManager mConnDiagMgr;
@NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
@NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
@@ -186,6 +188,13 @@
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mConnDiagMgr = mock(ConnectivityDiagnosticsManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext,
+ mConnDiagMgr,
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class);
+
mIkeConnectionInfo =
new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();