From 92959d677495644ed7c1bf6bb895bb5c3c148596 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Tue, 11 May 2021 14:50:11 -0700 Subject: [PATCH 1/4] Require MOBIKE for IkeSessionParams in VCN configs. This CL updates VcnGatewayConnectionConfigs to require IkeTunnelConnectionParams with MOBIKE enabled for the IkeSessionParams. This is necessary for VCNs - without it, they do not support IPsec mobility (one of the main features of the VCN). Bug: 187851560 Test: atest FrameworksVcnTests CtsVcnTestCases Change-Id: I94db52a8c42d9fa4681fefb8f787f006933caa18 --- .../net/vcn/VcnGatewayConnectionConfig.java | 8 ++++++++ .../vcn/VcnGatewayConnectionConfigTest.java | 19 +++++++++++++++++++ .../IkeSessionParamsUtilsTest.java | 5 +++-- .../TunnelConnectionParamsUtilsTest.java | 9 +++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index be308d01d98a3..2df3e6c7ecd6d 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -15,6 +15,8 @@ */ package android.net.vcn; +import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; + import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -433,6 +435,8 @@ public final class VcnGatewayConnectionConfig { * distinguish between VcnGatewayConnectionConfigs configured on a single {@link * VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations. * @param tunnelConnectionParams the IKE tunnel connection configuration + * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not + * configured to support MOBIKE * @see IkeTunnelConnectionParams * @see VcnManager.VcnStatusCallback#onGatewayConnectionError */ @@ -441,6 +445,10 @@ public final class VcnGatewayConnectionConfig { @NonNull IkeTunnelConnectionParams tunnelConnectionParams) { Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null"); Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null"); + if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) { + throw new IllegalArgumentException( + "MOBIKE must be configured for the provided IkeSessionParams"); + } mGatewayConnectionName = gatewayConnectionName; mTunnelConnectionParams = tunnelConnectionParams; diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 9410886c3549e..c59dcf879b1c3 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -16,13 +16,17 @@ package android.net.vcn; +import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.net.NetworkCapabilities; +import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest; import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest; import androidx.test.filters.SmallTest; @@ -119,6 +123,21 @@ public class VcnGatewayConnectionConfigTest { } } + @Test + public void testBuilderRequiresMobikeEnabled() { + try { + final IkeSessionParams ikeParams = + IkeSessionParamsUtilsTest.createBuilderMinimum() + .removeIkeOption(IKE_OPTION_MOBIKE) + .build(); + final IkeTunnelConnectionParams tunnelParams = + TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams); + new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams); + fail("Expected exception due to MOBIKE not enabled"); + } catch (IllegalArgumentException e) { + } + } + @Test public void testBuilderRequiresNonEmptyExposedCaps() { try { diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java index 393787f1a8b87..f3851130c68a1 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java @@ -52,8 +52,8 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest public class IkeSessionParamsUtilsTest { - // Package private for use in EncryptedTunnelParamsUtilsTest - static IkeSessionParams.Builder createBuilderMinimum() { + // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest + public static IkeSessionParams.Builder createBuilderMinimum() { final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100"); // TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated. @@ -63,6 +63,7 @@ public class IkeSessionParamsUtilsTest { .setLocalIdentification(new IkeFqdnIdentification("client.test.android.net")) .setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net")) .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500) + .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE) .setAuthPsk("psk".getBytes()); } diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java index 0c8ad32b0c271..f9dc9eb4d5ae9 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java @@ -18,6 +18,7 @@ package android.net.vcn.persistablebundleutils; import static org.junit.Assert.assertEquals; +import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; import androidx.test.filters.SmallTest; @@ -31,9 +32,13 @@ import org.junit.runner.RunWith; public class TunnelConnectionParamsUtilsTest { // Public for use in VcnGatewayConnectionConfigTest public static IkeTunnelConnectionParams buildTestParams() { + return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build()); + } + + // Public for use in VcnGatewayConnectionConfigTest + public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) { return new IkeTunnelConnectionParams( - IkeSessionParamsUtilsTest.createBuilderMinimum().build(), - TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build()); + params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build()); } @Test From fd57083e391763a91bb078289b90867728ed5860 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Fri, 7 May 2021 10:49:27 -0700 Subject: [PATCH 2/4] Create test-mode for VcnConfig. This CL creates a test-mode for VcnConfig so that they will only match with Test Networks. This is necessary for CTS testing so that VCNs can run on test networks and IKE negotiations can be injected over the Test Networks. Bug: 182291467 Test: atest FrameworksVcnTests CtsVcnTestCases Change-Id: I5cc340e5aaa34c5de8efafa52de49185a18d4bd3 --- core/java/android/net/vcn/VcnConfig.java | 43 +++++++++++++++++-- .../android/server/VcnManagementService.java | 9 ++++ .../server/VcnManagementServiceTest.java | 23 ++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index d41c0b4fbdb3e..caab15251f58f 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -52,12 +52,17 @@ public final class VcnConfig implements Parcelable { private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs"; @NonNull private final Set mGatewayConnectionConfigs; + private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile"; + private final boolean mIsTestModeProfile; + private VcnConfig( @NonNull String packageName, - @NonNull Set gatewayConnectionConfigs) { + @NonNull Set gatewayConnectionConfigs, + boolean isTestModeProfile) { mPackageName = packageName; mGatewayConnectionConfigs = Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs)); + mIsTestModeProfile = isTestModeProfile; validate(); } @@ -77,6 +82,7 @@ public final class VcnConfig implements Parcelable { new ArraySet<>( PersistableBundleUtils.toList( gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new)); + mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY); validate(); } @@ -103,6 +109,15 @@ public final class VcnConfig implements Parcelable { return Collections.unmodifiableSet(mGatewayConnectionConfigs); } + /** + * Returns whether or not this VcnConfig is restricted to test networks. + * + * @hide + */ + public boolean isTestModeProfile() { + return mIsTestModeProfile; + } + /** * Serializes this object to a PersistableBundle. * @@ -119,13 +134,14 @@ public final class VcnConfig implements Parcelable { new ArrayList<>(mGatewayConnectionConfigs), VcnGatewayConnectionConfig::toPersistableBundle); result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle); + result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile); return result; } @Override public int hashCode() { - return Objects.hash(mPackageName, mGatewayConnectionConfigs); + return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); } @Override @@ -136,7 +152,8 @@ public final class VcnConfig implements Parcelable { final VcnConfig rhs = (VcnConfig) other; return mPackageName.equals(rhs.mPackageName) - && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs); + && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs) + && mIsTestModeProfile == rhs.mIsTestModeProfile; } // Parcelable methods @@ -172,6 +189,8 @@ public final class VcnConfig implements Parcelable { @NonNull private final Set mGatewayConnectionConfigs = new ArraySet<>(); + private boolean mIsTestModeProfile = false; + public Builder(@NonNull Context context) { Objects.requireNonNull(context, "context was null"); @@ -206,6 +225,22 @@ public final class VcnConfig implements Parcelable { return this; } + /** + * Restricts this VcnConfig to matching with test networks (only). + * + *

This method is for testing only, and must not be used by apps. Calling {@link + * VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage + * is enabled will require the MANAGE_TEST_NETWORKS permission. + * + * @return this {@link Builder} instance, for chaining + * @hide + */ + @NonNull + public Builder setIsTestModeProfile() { + mIsTestModeProfile = true; + return this; + } + /** * Builds and validates the VcnConfig. * @@ -213,7 +248,7 @@ public final class VcnConfig implements Parcelable { */ @NonNull public VcnConfig build() { - return new VcnConfig(mPackageName, mGatewayConnectionConfigs); + return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); } } } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 471d2af2784f4..f274305d313bd 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -421,6 +421,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { "Carrier privilege required for subscription group to set VCN Config"); } + private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) { + if (vcnConfig.isTestModeProfile()) { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_TEST_NETWORKS, + "Test-mode require the MANAGE_TEST_NETWORKS permission"); + } + } + private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback { /** * Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker} @@ -588,6 +596,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); + enforceManageTestNetworksForTestMode(config); enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); Binder.withCleanCallingIdentity(() -> { diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 9ecd82ff6bcbb..8d87348ac48f8 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -66,6 +66,7 @@ import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.IBinder; @@ -527,6 +528,28 @@ public class VcnManagementServiceTest { verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); } + @Test + public void testSetVcnConfigTestModeRequiresPermission() throws Exception { + doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS")) + .when(mMockContext) + .enforceCallingPermission( + eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any()); + + final VcnConfig vcnConfig = + new VcnConfig.Builder(mMockContext) + .addGatewayConnectionConfig( + VcnGatewayConnectionConfigTest.buildTestConfig()) + .setIsTestModeProfile() + .build(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME); + fail("Expected exception due to using test-mode without permission"); + } catch (SecurityException e) { + verify(mMockPolicyListener, never()).onPolicyChanged(); + } + } + @Test public void testSetVcnConfigNotifiesStatusCallback() throws Exception { triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); From 5f65a440e2115f2af7043d3c31bbce64bb16ac61 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Fri, 7 May 2021 15:48:46 -0700 Subject: [PATCH 3/4] Specify if a VCN is in 'test-mode' in VcnContext. This CL updates VcnContext to include whether the VCN instance it is created for is in test-mode (as specified by the VcnConfig). This also changes the lifetime of VcnContext to be created uniquely for each VCN instance (as opposed to a single VcnContext created in VcnManagementService and used for all VCNs). Bug: 182291467 Test: atest FrameworksVcnTests CtsVcnTestCases Change-Id: I6fc6a266bf67ab2aa64202153c3c109ee98a16ca --- .../com/android/server/VcnManagementService.java | 12 +++++++----- .../core/java/com/android/server/vcn/VcnContext.java | 9 ++++++++- .../com/android/server/VcnManagementServiceTest.java | 10 +++++++++- .../server/vcn/UnderlyingNetworkTrackerTest.java | 8 +++++++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index f274305d313bd..b49b77c8998ea 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -167,7 +167,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final VcnNetworkProvider mNetworkProvider; @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb; @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; - @NonNull private final VcnContext mVcnContext; @NonNull private final BroadcastReceiver mPkgChangeReceiver; @NonNull @@ -212,7 +211,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext, mLooper, mTelephonySubscriptionTrackerCb); mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE); - mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider); mPkgChangeReceiver = new BroadcastReceiver() { @Override @@ -336,8 +334,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { public VcnContext newVcnContext( @NonNull Context context, @NonNull Looper looper, - @NonNull VcnNetworkProvider vcnNetworkProvider) { - return new VcnContext(context, looper, vcnNetworkProvider); + @NonNull VcnNetworkProvider vcnNetworkProvider, + boolean getIsInTestMode) { + return new VcnContext(context, looper, vcnNetworkProvider, getIsInTestMode); } /** Creates a new Vcn instance using the provided configuration */ @@ -551,8 +550,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup); + final VcnContext vcnContext = + mDeps.newVcnContext( + mContext, mLooper, mNetworkProvider, config.isTestModeProfile()); final Vcn newInstance = - mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); + mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 7399e56b3a950..d958222ea4070 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -31,14 +31,17 @@ public class VcnContext { @NonNull private final Context mContext; @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; + private final boolean mIsInTestMode; public VcnContext( @NonNull Context context, @NonNull Looper looper, - @NonNull VcnNetworkProvider vcnNetworkProvider) { + @NonNull VcnNetworkProvider vcnNetworkProvider, + boolean isInTestMode) { mContext = Objects.requireNonNull(context, "Missing context"); mLooper = Objects.requireNonNull(looper, "Missing looper"); mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider"); + mIsInTestMode = isInTestMode; } @NonNull @@ -56,6 +59,10 @@ public class VcnContext { return mVcnNetworkProvider; } + public boolean isInTestMode() { + return mIsInTestMode; + } + /** * Verifies that the caller is running on the VcnContext Thread. * diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 8d87348ac48f8..3360d40062a39 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -37,6 +37,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -198,7 +199,8 @@ public class VcnManagementServiceTest { .newVcnContext( eq(mMockContext), eq(mTestLooper.getLooper()), - any(VcnNetworkProvider.class)); + any(VcnNetworkProvider.class), + anyBoolean()); doReturn(mSubscriptionTracker) .when(mMockDeps) .newTelephonySubscriptionTracker( @@ -371,6 +373,12 @@ public class VcnManagementServiceTest { public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); + verify(mMockDeps) + .newVcnContext( + eq(mMockContext), + eq(mTestLooper.getLooper()), + any(VcnNetworkProvider.class), + anyBoolean()); verify(mMockDeps) .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any()); } diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 8289e85dadf9a..2fc4309d49ce3 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -112,7 +112,13 @@ public class UnderlyingNetworkTrackerTest { MockitoAnnotations.initMocks(this); mTestLooper = new TestLooper(); - mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider)); + mVcnContext = + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mVcnNetworkProvider, + false /* isInTestMode */)); doNothing().when(mVcnContext).ensureRunningOnLooperThread(); setupSystemService( From 88c5fe5e60d94d9043747c8ff89b76004b6b1fa7 Mon Sep 17 00:00:00 2001 From: Cody Kesting Date: Fri, 7 May 2021 16:58:34 -0700 Subject: [PATCH 4/4] Request Test Networks for VCNs when in Test Mode. This CL updates UnderlyingNetworkTracker to request Test Networks for route selection when the VCN it is tracking for is in test-mode. Bug: 182291467 Test: atest FrameworksVcnTests CtsVcnTestCases Change-Id: Icb59b15df61ec43fe76e250977a985caa6a1dffc --- .../server/vcn/UnderlyingNetworkTracker.java | 17 +++++++ .../vcn/UnderlyingNetworkTrackerTest.java | 49 +++++++++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index 88180239da677..3c6bb647c5153 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -158,8 +158,15 @@ public class UnderlyingNetworkTracker { * carrier owned networks may be selected, as the request specifies only subIds in the VCN's * subscription group, while the VCN networks are excluded by virtue of not having subIds set on * the VCN-exposed networks. + * + *

If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return + * a NetworkRequest that only matches Test Networks. */ private NetworkRequest getRouteSelectionRequest() { + if (mVcnContext.isInTestMode()) { + return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)); + } + return getBaseNetworkRequestBuilder() .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) .build(); @@ -210,6 +217,16 @@ public class UnderlyingNetworkTracker { .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); } + /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */ + private NetworkRequest getTestNetworkRequest(@NonNull Set subIds) { + return getBaseNetworkRequestBuilder() + .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .setSubscriptionIds(subIds) + .build(); + } + /** * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot. * diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 2fc4309d49ce3..6f63c4b2e1533 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -119,7 +120,7 @@ public class UnderlyingNetworkTrackerTest { mTestLooper.getLooper(), mVcnNetworkProvider, false /* isInTestMode */)); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + resetVcnContext(); setupSystemService( mContext, @@ -138,6 +139,11 @@ public class UnderlyingNetworkTrackerTest { mNetworkTrackerCb); } + private void resetVcnContext() { + reset(mVcnContext); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + } + private static LinkProperties getLinkPropertiesWithName(String iface) { LinkProperties linkProperties = new LinkProperties(); linkProperties.setInterfaceName(iface); @@ -155,7 +161,29 @@ public class UnderlyingNetworkTrackerTest { verifyNetworkRequestsRegistered(INITIAL_SUB_IDS); } + @Test + public void testNetworkCallbacksRegisteredOnStartupForTestMode() { + resetVcnContext(); + when(mVcnContext.isInTestMode()).thenReturn(true); + reset(mConnectivityManager); + + mUnderlyingNetworkTracker = + new UnderlyingNetworkTracker( + mVcnContext, + SUB_GROUP, + mSubscriptionSnapshot, + Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), + mNetworkTrackerCb); + + verifyNetworkRequestsRegistered(INITIAL_SUB_IDS, true /* expectTestMode */); + } + private void verifyNetworkRequestsRegistered(Set expectedSubIds) { + verifyNetworkRequestsRegistered(expectedSubIds, false /* expectTestMode */); + } + + private void verifyNetworkRequestsRegistered( + Set expectedSubIds, boolean expectTestMode) { verify(mConnectivityManager) .requestBackgroundNetwork( eq(getWifiRequest(expectedSubIds)), @@ -168,10 +196,16 @@ public class UnderlyingNetworkTrackerTest { any(NetworkBringupCallback.class), any()); } + final NetworkRequest expectedRouteSelectionRequest = + expectTestMode + ? getTestNetworkRequest(expectedSubIds) + : getRouteSelectionRequest(expectedSubIds); + verify(mConnectivityManager) .requestBackgroundNetwork( - eq(getRouteSelectionRequest(expectedSubIds)), - any(RouteSelectionCallback.class), any()); + eq(expectedRouteSelectionRequest), + any(RouteSelectionCallback.class), + any()); } @Test @@ -210,6 +244,15 @@ public class UnderlyingNetworkTrackerTest { return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build(); } + private NetworkRequest getTestNetworkRequest(Set netCapsSubIds) { + return getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .setSubscriptionIds(netCapsSubIds) + .build(); + } + private NetworkRequest.Builder getExpectedRequestBase() { final NetworkRequest.Builder builder = new NetworkRequest.Builder()