Loading core/java/android/net/vcn/VcnManager.java +16 −1 Original line number Diff line number Diff line Loading @@ -114,13 +114,28 @@ public class VcnManager { public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY = "vcn_restricted_transports"; /** * Key for maximum number of parallel SAs for tunnel aggregation * * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be * aggregated over the various tunnels. * * <p>Defaults to 1, unless overridden by carrier config * * @hide */ @NonNull public static final String VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY = "vcn_tunnel_aggregation_sa_count_max"; /** List of Carrier Config options to extract from Carrier Config bundles. @hide */ @NonNull public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS = new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, }; private static final Map< Loading services/core/java/com/android/server/vcn/VcnGatewayConnection.java +106 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; import static com.android.server.VcnManagementService.LOCAL_LOG; import static com.android.server.VcnManagementService.VDBG; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -59,6 +60,7 @@ import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.annotations.PolicyDirection; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; Loading @@ -67,11 +69,14 @@ import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionConfiguration; import android.net.ipsec.ike.IkeSessionConnectionInfo; import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTrafficSelector; import android.net.ipsec.ike.IkeTunnelConnectionParams; import android.net.ipsec.ike.TunnelModeChildSessionParams; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.Handler; Loading Loading @@ -169,6 +174,9 @@ import java.util.function.Consumer; public class VcnGatewayConnection extends StateMachine { private static final String TAG = VcnGatewayConnection.class.getSimpleName(); /** Default number of parallel SAs requested */ static final int TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT = 1; // Matches DataConnection.NETWORK_TYPE private constant, and magic string from // ConnectivityManager#getNetworkTypeName() @VisibleForTesting(visibility = Visibility.PRIVATE) Loading Loading @@ -1980,6 +1988,22 @@ public class VcnGatewayConnection extends StateMachine { mChildConfig, oldChildConfig, mIkeConnectionInfo); // Create opportunistic child SAs; this allows SA aggregation in the downlink, // reducing lock/atomic contention in high throughput scenarios. All SAs will // share the same UDP encap socket (and keepalives) as necessary, and are // effectively free. final int parallelTunnelCount = mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup); logInfo("Parallel tunnel count: " + parallelTunnelCount); for (int i = 0; i < parallelTunnelCount - 1; i++) { mIkeSession.openChildSession( buildOpportunisticChildParams(), new VcnChildSessionCallback( mCurrentToken, true /* isOpportunistic */)); } break; case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); Loading Loading @@ -2350,15 +2374,44 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) public class VcnChildSessionCallback implements ChildSessionCallback { private final int mToken; private final boolean mIsOpportunistic; private boolean mIsChildOpened = false; VcnChildSessionCallback(int token) { this(token, false /* isOpportunistic */); } /** * Creates a ChildSessionCallback * * <p>If configured as opportunistic, transforms will not report initial startup, or * associated startup failures. This serves the dual purposes of ensuring that if the server * does not support connection multiplexing, new child SA negotiations will be ignored, and * at the same time, will notify the VCN session if a successfully negotiated opportunistic * child SA is subsequently torn down, which could impact uplink traffic if the SA in use * for outbound/uplink traffic is this opportunistic SA. * * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last * applied outbound transform for outbound traffic. This means that unlike inbound traffic, * outbound does not benefit from these parallel SAs in the same manner. */ VcnChildSessionCallback(int token, boolean isOpportunistic) { mToken = token; mIsOpportunistic = isOpportunistic; } /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ @VisibleForTesting(visibility = Visibility.PRIVATE) void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { logDbg("ChildOpened for token " + mToken); if (mIsOpportunistic) { logDbg("ChildOpened for opportunistic child; suppressing event message"); mIsChildOpened = true; return; } childOpened(mToken, childConfig); } Loading @@ -2370,12 +2423,24 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosed() { logDbg("ChildClosed for token " + mToken); if (mIsOpportunistic && !mIsChildOpened) { logDbg("ChildClosed for unopened opportunistic child; ignoring"); return; } sessionLost(mToken, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { logInfo("ChildClosedExceptionally for token " + mToken, exception); if (mIsOpportunistic && !mIsChildOpened) { logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring"); return; } sessionLost(mToken, exception); } Loading Loading @@ -2580,6 +2645,30 @@ public class VcnGatewayConnection extends StateMachine { return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); } private ChildSessionParams buildOpportunisticChildParams() { final ChildSessionParams baseParams = mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); final TunnelModeChildSessionParams.Builder builder = new TunnelModeChildSessionParams.Builder(); for (ChildSaProposal proposal : baseParams.getChildSaProposals()) { builder.addChildSaProposal(proposal); } for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) { builder.addInboundTrafficSelectors(inboundSelector); } for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) { builder.addOutboundTrafficSelectors(outboundSelector); } builder.setLifetimeSeconds( baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds()); return builder.build(); } @VisibleForTesting(visibility = Visibility.PRIVATE) VcnIkeSession buildIkeSession(@NonNull Network network) { final int token = ++mCurrentToken; Loading Loading @@ -2680,6 +2769,23 @@ public class VcnGatewayConnection extends StateMachine { return 0; } } /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */ public int getParallelTunnelCount( TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT; if (carrierConfig != null) { result = carrierConfig.getInt( VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT); } // Guard against tunnel count < 1 return Math.max(1, result); } } /** Loading services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +5 −0 Original line number Diff line number Diff line Loading @@ -573,5 +573,10 @@ public class PersistableBundleUtils { return isEqual(mBundle, other.mBundle); } @Override public String toString() { return mBundle.toString(); } } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +103 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.IkeSessionConnectionInfo; import android.net.ipsec.ike.TunnelModeChildSessionParams; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; Loading @@ -70,6 +71,7 @@ import android.os.PersistableBundle; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import com.android.server.vcn.util.MtuUtils; Loading @@ -90,6 +92,8 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { private static final int PARALLEL_SA_COUNT = 4; private VcnIkeSession mIkeSession; private VcnNetworkAgent mNetworkAgent; private Network mVcnNetwork; Loading Loading @@ -227,16 +231,29 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection private void verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform) throws Exception { verifyVcnTransformsApplied( vcnGatewayConnection, expectForwardTransform, Collections.singletonList(getChildSessionCallback())); } private void verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform, List<VcnChildSessionCallback> callbacks) throws Exception { for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction); for (VcnChildSessionCallback cb : callbacks) { cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction); } mTestLooper.dispatchAll(); verify(mIpSecSvc) verify(mIpSecSvc, times(callbacks.size())) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); } verify(mIpSecSvc, expectForwardTransform ? times(1) : never()) verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never()) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any()); Loading Loading @@ -416,6 +433,89 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */); } private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested() throws Exception { doReturn(PARALLEL_SA_COUNT) .when(mDeps) .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); // Verify scheduled but not canceled when entering ConnectedState verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); triggerChildOpened(); mTestLooper.dispatchAll(); // Verify new child sessions requested final ArgumentCaptor<VcnChildSessionCallback> captor = ArgumentCaptor.forClass(VcnChildSessionCallback.class); verify(mIkeSession, times(PARALLEL_SA_COUNT - 1)) .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture()); return captor.getAllValues(); } private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas() throws Exception { List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); verifyVcnTransformsApplied(mGatewayConnection, false, callbacks); // Mock IKE calling of onOpened() for (VcnChildSessionCallback cb : callbacks) { cb.onOpened(mock(VcnChildSessionConfiguration.class)); } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); return callbacks; } @Test public void testChildOpenedWithParallelSas() throws Exception { verifyChildOpenedRequestsAndAppliesParallelSas(); } @Test public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception { List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); for (VcnChildSessionCallback cb : callbacks) { cb.onClosed(); cb.onClosedExceptionally(mock(IkeException.class)); } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo()); } private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException) throws Exception { List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas(); for (VcnChildSessionCallback cb : callbacks) { if (shouldCloseWithException) { cb.onClosed(); } else { cb.onClosedExceptionally(mock(IkeException.class)); } } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); } @Test public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception { verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */); } @Test public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally() throws Exception { verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */); } @Test public void testInternalAndDnsAddressesChanged() throws Exception { final List<LinkAddress> startingInternalAddrs = Loading tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +3 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,9 @@ public class VcnGatewayConnectionTestBase { doReturn(mWakeLock) .when(mDeps) .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any()); doReturn(1) .when(mDeps) .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM); setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM); Loading Loading
core/java/android/net/vcn/VcnManager.java +16 −1 Original line number Diff line number Diff line Loading @@ -114,13 +114,28 @@ public class VcnManager { public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY = "vcn_restricted_transports"; /** * Key for maximum number of parallel SAs for tunnel aggregation * * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be * aggregated over the various tunnels. * * <p>Defaults to 1, unless overridden by carrier config * * @hide */ @NonNull public static final String VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY = "vcn_tunnel_aggregation_sa_count_max"; /** List of Carrier Config options to extract from Carrier Config bundles. @hide */ @NonNull public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS = new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, }; private static final Map< Loading
services/core/java/com/android/server/vcn/VcnGatewayConnection.java +106 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; import static com.android.server.VcnManagementService.LOCAL_LOG; import static com.android.server.VcnManagementService.VDBG; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -59,6 +60,7 @@ import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.annotations.PolicyDirection; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; Loading @@ -67,11 +69,14 @@ import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionConfiguration; import android.net.ipsec.ike.IkeSessionConnectionInfo; import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTrafficSelector; import android.net.ipsec.ike.IkeTunnelConnectionParams; import android.net.ipsec.ike.TunnelModeChildSessionParams; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.Handler; Loading Loading @@ -169,6 +174,9 @@ import java.util.function.Consumer; public class VcnGatewayConnection extends StateMachine { private static final String TAG = VcnGatewayConnection.class.getSimpleName(); /** Default number of parallel SAs requested */ static final int TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT = 1; // Matches DataConnection.NETWORK_TYPE private constant, and magic string from // ConnectivityManager#getNetworkTypeName() @VisibleForTesting(visibility = Visibility.PRIVATE) Loading Loading @@ -1980,6 +1988,22 @@ public class VcnGatewayConnection extends StateMachine { mChildConfig, oldChildConfig, mIkeConnectionInfo); // Create opportunistic child SAs; this allows SA aggregation in the downlink, // reducing lock/atomic contention in high throughput scenarios. All SAs will // share the same UDP encap socket (and keepalives) as necessary, and are // effectively free. final int parallelTunnelCount = mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup); logInfo("Parallel tunnel count: " + parallelTunnelCount); for (int i = 0; i < parallelTunnelCount - 1; i++) { mIkeSession.openChildSession( buildOpportunisticChildParams(), new VcnChildSessionCallback( mCurrentToken, true /* isOpportunistic */)); } break; case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj); Loading Loading @@ -2350,15 +2374,44 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) public class VcnChildSessionCallback implements ChildSessionCallback { private final int mToken; private final boolean mIsOpportunistic; private boolean mIsChildOpened = false; VcnChildSessionCallback(int token) { this(token, false /* isOpportunistic */); } /** * Creates a ChildSessionCallback * * <p>If configured as opportunistic, transforms will not report initial startup, or * associated startup failures. This serves the dual purposes of ensuring that if the server * does not support connection multiplexing, new child SA negotiations will be ignored, and * at the same time, will notify the VCN session if a successfully negotiated opportunistic * child SA is subsequently torn down, which could impact uplink traffic if the SA in use * for outbound/uplink traffic is this opportunistic SA. * * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last * applied outbound transform for outbound traffic. This means that unlike inbound traffic, * outbound does not benefit from these parallel SAs in the same manner. */ VcnChildSessionCallback(int token, boolean isOpportunistic) { mToken = token; mIsOpportunistic = isOpportunistic; } /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ @VisibleForTesting(visibility = Visibility.PRIVATE) void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { logDbg("ChildOpened for token " + mToken); if (mIsOpportunistic) { logDbg("ChildOpened for opportunistic child; suppressing event message"); mIsChildOpened = true; return; } childOpened(mToken, childConfig); } Loading @@ -2370,12 +2423,24 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosed() { logDbg("ChildClosed for token " + mToken); if (mIsOpportunistic && !mIsChildOpened) { logDbg("ChildClosed for unopened opportunistic child; ignoring"); return; } sessionLost(mToken, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { logInfo("ChildClosedExceptionally for token " + mToken, exception); if (mIsOpportunistic && !mIsChildOpened) { logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring"); return; } sessionLost(mToken, exception); } Loading Loading @@ -2580,6 +2645,30 @@ public class VcnGatewayConnection extends StateMachine { return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); } private ChildSessionParams buildOpportunisticChildParams() { final ChildSessionParams baseParams = mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams(); final TunnelModeChildSessionParams.Builder builder = new TunnelModeChildSessionParams.Builder(); for (ChildSaProposal proposal : baseParams.getChildSaProposals()) { builder.addChildSaProposal(proposal); } for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) { builder.addInboundTrafficSelectors(inboundSelector); } for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) { builder.addOutboundTrafficSelectors(outboundSelector); } builder.setLifetimeSeconds( baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds()); return builder.build(); } @VisibleForTesting(visibility = Visibility.PRIVATE) VcnIkeSession buildIkeSession(@NonNull Network network) { final int token = ++mCurrentToken; Loading Loading @@ -2680,6 +2769,23 @@ public class VcnGatewayConnection extends StateMachine { return 0; } } /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */ public int getParallelTunnelCount( TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT; if (carrierConfig != null) { result = carrierConfig.getInt( VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT); } // Guard against tunnel count < 1 return Math.max(1, result); } } /** Loading
services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +5 −0 Original line number Diff line number Diff line Loading @@ -573,5 +573,10 @@ public class PersistableBundleUtils { return isEqual(mBundle, other.mBundle); } @Override public String toString() { return mBundle.toString(); } } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +103 −3 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.IkeSessionConnectionInfo; import android.net.ipsec.ike.TunnelModeChildSessionParams; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; Loading @@ -70,6 +71,7 @@ import android.os.PersistableBundle; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import com.android.server.vcn.util.MtuUtils; Loading @@ -90,6 +92,8 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { private static final int PARALLEL_SA_COUNT = 4; private VcnIkeSession mIkeSession; private VcnNetworkAgent mNetworkAgent; private Network mVcnNetwork; Loading Loading @@ -227,16 +231,29 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection private void verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform) throws Exception { verifyVcnTransformsApplied( vcnGatewayConnection, expectForwardTransform, Collections.singletonList(getChildSessionCallback())); } private void verifyVcnTransformsApplied( VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform, List<VcnChildSessionCallback> callbacks) throws Exception { for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction); for (VcnChildSessionCallback cb : callbacks) { cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction); } mTestLooper.dispatchAll(); verify(mIpSecSvc) verify(mIpSecSvc, times(callbacks.size())) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); } verify(mIpSecSvc, expectForwardTransform ? times(1) : never()) verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never()) .applyTunnelModeTransform( eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any()); Loading Loading @@ -416,6 +433,89 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */); } private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested() throws Exception { doReturn(PARALLEL_SA_COUNT) .when(mDeps) .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); // Verify scheduled but not canceled when entering ConnectedState verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */); triggerChildOpened(); mTestLooper.dispatchAll(); // Verify new child sessions requested final ArgumentCaptor<VcnChildSessionCallback> captor = ArgumentCaptor.forClass(VcnChildSessionCallback.class); verify(mIkeSession, times(PARALLEL_SA_COUNT - 1)) .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture()); return captor.getAllValues(); } private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas() throws Exception { List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); verifyVcnTransformsApplied(mGatewayConnection, false, callbacks); // Mock IKE calling of onOpened() for (VcnChildSessionCallback cb : callbacks) { cb.onOpened(mock(VcnChildSessionConfiguration.class)); } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); return callbacks; } @Test public void testChildOpenedWithParallelSas() throws Exception { verifyChildOpenedRequestsAndAppliesParallelSas(); } @Test public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception { List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested(); for (VcnChildSessionCallback cb : callbacks) { cb.onClosed(); cb.onClosedExceptionally(mock(IkeException.class)); } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo()); } private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException) throws Exception { List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas(); for (VcnChildSessionCallback cb : callbacks) { if (shouldCloseWithException) { cb.onClosed(); } else { cb.onClosedExceptionally(mock(IkeException.class)); } } mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); } @Test public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception { verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */); } @Test public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally() throws Exception { verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */); } @Test public void testInternalAndDnsAddressesChanged() throws Exception { final List<LinkAddress> startingInternalAddrs = Loading
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +3 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,9 @@ public class VcnGatewayConnectionTestBase { doReturn(mWakeLock) .when(mDeps) .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any()); doReturn(1) .when(mDeps) .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP)); setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM); setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM); Loading