Perform MOBIKE when data stall is suspected on VCN network
This commit updates VCN to listen to the connectivity diagnostics callback so that when there is a suspected data stall VCN will call into IKE to perform MOBIKE to recover. Bug: 261499808 Test: atest FrameworksVcnTests (new tests), CtsVcnTestCases Change-Id: Ic8ec45870b7e5190db4fe6b8b7710f54e7fb61f4
This commit is contained in:
@@ -26,6 +26,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.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 static com.android.server.VcnManagementService.VDBG;
|
||||
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.NetworkAgent;
|
||||
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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
mUnderlyingNetworkController.teardown();
|
||||
|
||||
mGatewayStatusCallback.onQuit();
|
||||
|
||||
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
|
||||
mConnectivityDiagnosticsCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -828,6 +885,20 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -2423,6 +2509,11 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
return mUnderlyingNetworkControllerCallback;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() {
|
||||
return mConnectivityDiagnosticsCallback;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
UnderlyingNetworkRecord getUnderlyingNetwork() {
|
||||
return mUnderlying;
|
||||
|
||||
@@ -149,7 +149,8 @@ public class VcnGatewayConnectionConfigTest {
|
||||
return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS);
|
||||
}
|
||||
|
||||
private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
|
||||
// Public for use in VcnGatewayConnectionTest
|
||||
public static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
|
||||
Set<Integer> gatewayOptions) {
|
||||
return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,11 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
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.ipsec.ike.exceptions.IkeProtocolException;
|
||||
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 @@ import java.util.function.Consumer;
|
||||
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
|
||||
private VcnIkeSession mIkeSession;
|
||||
private VcnNetworkAgent mNetworkAgent;
|
||||
private Network mVcnNetwork;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -98,6 +103,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
.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 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
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 {
|
||||
|
||||
@@ -24,6 +24,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
|
||||
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.assertArrayEquals;
|
||||
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 com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
|
||||
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.Collections;
|
||||
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 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
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 @@ public class VcnGatewayConnectionTestBase {
|
||||
|
||||
@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 @@ public class VcnGatewayConnectionTestBase {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user