Loading core/api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -27285,6 +27285,7 @@ package android.net.vcn { public final class VcnConfig implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs(); method @NonNull public java.util.Set<java.lang.Integer> getRestrictedUnderlyingNetworkTransports(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR; } Loading @@ -27293,6 +27294,7 @@ package android.net.vcn { ctor public VcnConfig.Builder(@NonNull android.content.Context); method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig); method @NonNull public android.net.vcn.VcnConfig build(); method @NonNull public android.net.vcn.VcnConfig.Builder setRestrictedUnderlyingNetworkTransports(@NonNull java.util.Set<java.lang.Integer>); } public final class VcnGatewayConnectionConfig { core/java/android/net/vcn/VcnConfig.java +106 −2 Original line number Diff line number Diff line Loading @@ -15,16 +15,23 @@ */ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; Loading @@ -32,6 +39,7 @@ import com.android.server.vcn.util.PersistableBundleUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.Objects; import java.util.Set; Loading @@ -46,22 +54,36 @@ import java.util.Set; public final class VcnConfig implements Parcelable { @NonNull private static final String TAG = VcnConfig.class.getSimpleName(); private static final Set<Integer> ALLOWED_TRANSPORTS = new ArraySet<>(); static { ALLOWED_TRANSPORTS.add(TRANSPORT_WIFI); ALLOWED_TRANSPORTS.add(TRANSPORT_CELLULAR); } private static final String PACKAGE_NAME_KEY = "mPackageName"; @NonNull private final String mPackageName; private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs"; @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs; private static final Set<Integer> RESTRICTED_TRANSPORTS_DEFAULT = Collections.singleton(TRANSPORT_WIFI); private static final String RESTRICTED_TRANSPORTS_KEY = "mRestrictedTransports"; @NonNull private final Set<Integer> mRestrictedTransports; private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile"; private final boolean mIsTestModeProfile; private VcnConfig( @NonNull String packageName, @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs, @NonNull Set<Integer> restrictedTransports, boolean isTestModeProfile) { mPackageName = packageName; mGatewayConnectionConfigs = Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs)); mRestrictedTransports = Collections.unmodifiableSet(new ArraySet<>(restrictedTransports)); mIsTestModeProfile = isTestModeProfile; validate(); Loading @@ -82,6 +104,20 @@ public final class VcnConfig implements Parcelable { new ArraySet<>( PersistableBundleUtils.toList( gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new)); final PersistableBundle restrictedTransportsBundle = in.getPersistableBundle(RESTRICTED_TRANSPORTS_KEY); if (restrictedTransportsBundle == null) { // RESTRICTED_TRANSPORTS_KEY was added in U and does not exist in VcnConfigs created in // older platforms mRestrictedTransports = RESTRICTED_TRANSPORTS_DEFAULT; } else { mRestrictedTransports = new ArraySet<Integer>( PersistableBundleUtils.toList( restrictedTransportsBundle, INTEGER_DESERIALIZER)); } mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY); validate(); Loading @@ -91,6 +127,19 @@ public final class VcnConfig implements Parcelable { Objects.requireNonNull(mPackageName, "packageName was null"); Preconditions.checkCollectionNotEmpty( mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty"); final Iterator<Integer> iterator = mRestrictedTransports.iterator(); while (iterator.hasNext()) { final int transport = iterator.next(); if (!ALLOWED_TRANSPORTS.contains(transport)) { iterator.remove(); Log.w( TAG, "Found invalid transport " + transport + " which might be from a new version of VcnConfig"); } } } /** Loading @@ -109,6 +158,16 @@ public final class VcnConfig implements Parcelable { return Collections.unmodifiableSet(mGatewayConnectionConfigs); } /** * Retrieve the transports that will be restricted by the VCN. * * @see Builder#setRestrictedUnderlyingNetworkTransports(Set) */ @NonNull public Set<Integer> getRestrictedUnderlyingNetworkTransports() { return Collections.unmodifiableSet(mRestrictedTransports); } /** * Returns whether or not this VcnConfig is restricted to test networks. * Loading @@ -134,6 +193,12 @@ public final class VcnConfig implements Parcelable { new ArrayList<>(mGatewayConnectionConfigs), VcnGatewayConnectionConfig::toPersistableBundle); result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle); final PersistableBundle restrictedTransportsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mRestrictedTransports), INTEGER_SERIALIZER); result.putPersistableBundle(RESTRICTED_TRANSPORTS_KEY, restrictedTransportsBundle); result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile); return result; Loading @@ -141,7 +206,8 @@ public final class VcnConfig implements Parcelable { @Override public int hashCode() { return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); return Objects.hash( mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile); } @Override Loading @@ -153,6 +219,7 @@ public final class VcnConfig implements Parcelable { final VcnConfig rhs = (VcnConfig) other; return mPackageName.equals(rhs.mPackageName) && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs) && mRestrictedTransports.equals(rhs.mRestrictedTransports) && mIsTestModeProfile == rhs.mIsTestModeProfile; } Loading Loading @@ -189,12 +256,15 @@ public final class VcnConfig implements Parcelable { @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>(); @NonNull private final Set<Integer> mRestrictedTransports = new ArraySet<>(); private boolean mIsTestModeProfile = false; public Builder(@NonNull Context context) { Objects.requireNonNull(context, "context was null"); mPackageName = context.getOpPackageName(); mRestrictedTransports.addAll(RESTRICTED_TRANSPORTS_DEFAULT); } /** Loading Loading @@ -225,6 +295,36 @@ public final class VcnConfig implements Parcelable { return this; } private void validateRestrictedTransportsOrThrow(Set<Integer> restrictedTransports) { Objects.requireNonNull(restrictedTransports, "transports was null"); for (int transport : restrictedTransports) { if (!ALLOWED_TRANSPORTS.contains(transport)) { throw new IllegalArgumentException("Invalid transport " + transport); } } } /** * Sets transports that will be restricted by the VCN. * * @param transports transports that will be restricted by VCN. Networks that include any * of the transports will be marked as restricted. Only {@link * NetworkCapabilities#TRANSPORT_WIFI} and {@link * NetworkCapabilities#TRANSPORT_CELLULAR} are allowed. {@link * NetworkCapabilities#TRANSPORT_WIFI} is marked restricted by default. * @return this {@link Builder} instance, for chaining * @throws IllegalArgumentException if the input contains unsupported transport types. */ @NonNull public Builder setRestrictedUnderlyingNetworkTransports(@NonNull Set<Integer> transports) { validateRestrictedTransportsOrThrow(transports); mRestrictedTransports.clear(); mRestrictedTransports.addAll(transports); return this; } /** * Restricts this VcnConfig to matching with test networks (only). * Loading @@ -248,7 +348,11 @@ public final class VcnConfig implements Parcelable { */ @NonNull public VcnConfig build() { return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); return new VcnConfig( mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile); } } } services/core/java/com/android/server/VcnManagementService.java +22 −14 Original line number Diff line number Diff line Loading @@ -371,8 +371,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { return new LocationPermissionChecker(context); } /** Gets the transports that need to be marked as restricted by the VCN */ public Set<Integer> getRestrictedTransports( /** Gets transports that need to be marked as restricted by the VCN from CarrierConfig */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Set<Integer> getRestrictedTransportsFromCarrierConfig( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) { if (!Build.IS_ENG && !Build.IS_USERDEBUG) { return RESTRICTED_TRANSPORTS_DEFAULT; Loading @@ -398,6 +399,22 @@ public class VcnManagementService extends IVcnManagementService.Stub { } return restrictedTransports; } /** Gets the transports that need to be marked as restricted by the VCN */ public Set<Integer> getRestrictedTransports( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot, VcnConfig vcnConfig) { final Set<Integer> restrictedTransports = new ArraySet<>(); restrictedTransports.addAll(vcnConfig.getRestrictedUnderlyingNetworkTransports()); // TODO: b/262269892 Remove the ability to configure restricted transports // via CarrierConfig restrictedTransports.addAll( getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot)); return restrictedTransports; } } /** Notifies the VcnManagementService that external dependencies can be set up. */ Loading Loading @@ -719,6 +736,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (mVcns.containsKey(subscriptionGroup)) { final Vcn vcn = mVcns.get(subscriptionGroup); vcn.updateConfig(config); notifyAllPolicyListenersLocked(); } else { // TODO(b/193687515): Support multiple VCNs active at the same time if (isActiveSubGroup(subscriptionGroup, mLastSnapshot)) { Loading Loading @@ -936,7 +954,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { } /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ @GuardedBy("mLock") @Override public void addVcnUnderlyingNetworkPolicyListener( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { Loading @@ -963,16 +980,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { }); } @VisibleForTesting(visibility = Visibility.PRIVATE) void addVcnUnderlyingNetworkPolicyListenerForTest( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { synchronized (mLock) { addVcnUnderlyingNetworkPolicyListener(listener); } } /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ @GuardedBy("mLock") @Override public void removeVcnUnderlyingNetworkPolicyListener( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { Loading Loading @@ -1062,8 +1070,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { isVcnManagedNetwork = true; } final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports(subGrp, mLastSnapshot); final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports( subGrp, mLastSnapshot, mConfigs.get(subGrp)); for (int restrictedTransport : restrictedTransports) { if (ncCopy.hasTransport(restrictedTransport)) { if (restrictedTransport == TRANSPORT_CELLULAR) { Loading tests/vcn/java/android/net/vcn/VcnConfigTest.java +57 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,12 @@ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; Loading @@ -24,6 +29,7 @@ import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.content.Context; import android.os.Parcel; import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -42,19 +48,36 @@ public class VcnConfigTest { private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS = Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig()); private static final Set<Integer> RESTRICTED_TRANSPORTS = new ArraySet<>(); static { RESTRICTED_TRANSPORTS.add(TRANSPORT_WIFI); RESTRICTED_TRANSPORTS.add(TRANSPORT_CELLULAR); } private final Context mContext = mock(Context.class); // Public visibility for VcnManagementServiceTest public static VcnConfig buildTestConfig(@NonNull Context context) { public static VcnConfig buildTestConfig( @NonNull Context context, Set<Integer> restrictedTransports) { VcnConfig.Builder builder = new VcnConfig.Builder(context); for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) { builder.addGatewayConnectionConfig(gatewayConnectionConfig); } if (restrictedTransports != null) { builder.setRestrictedUnderlyingNetworkTransports(restrictedTransports); } return builder.build(); } // Public visibility for VcnManagementServiceTest public static VcnConfig buildTestConfig(@NonNull Context context) { return buildTestConfig(context, null); } @Before public void setUp() throws Exception { doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName(); Loading Loading @@ -91,11 +114,25 @@ public class VcnConfigTest { } @Test public void testBuilderAndGetters() { public void testBuilderAndGettersDefaultValues() { final VcnConfig config = buildTestConfig(mContext); assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); assertFalse(config.isTestModeProfile()); assertEquals( Collections.singleton(TRANSPORT_WIFI), config.getRestrictedUnderlyingNetworkTransports()); } @Test public void testBuilderAndGettersConfigRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); assertFalse(config.isTestModeProfile()); assertEquals(RESTRICTED_TRANSPORTS, config.getRestrictedUnderlyingNetworkTransports()); } @Test Loading @@ -105,6 +142,24 @@ public class VcnConfigTest { assertEquals(config, new VcnConfig(config.toPersistableBundle())); } @Test public void testPersistableBundleWithRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); assertEquals(config, new VcnConfig(config.toPersistableBundle())); } @Test public void testEqualityWithRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); final VcnConfig configEqual = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); final VcnConfig configNotEqual = buildTestConfig(mContext, Collections.singleton(TRANSPORT_WIFI)); assertEquals(config, configEqual); assertNotEquals(config, configNotEqual); } @Test public void testParceling() { final VcnConfig config = buildTestConfig(mContext); Loading tests/vcn/java/com/android/server/VcnManagementServiceTest.java +67 −12 Original line number Diff line number Diff line Loading @@ -258,7 +258,7 @@ public class VcnManagementServiceTest { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any()); .getRestrictedTransports(any(), any(), any()); } Loading Loading @@ -1038,18 +1038,18 @@ public class VcnManagementServiceTest { new LinkProperties()); } private void checkGetRestrictedTransports( private void checkGetRestrictedTransportsFromCarrierConfig( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot, Set<Integer> expectedTransports) { Set<Integer> result = new VcnManagementService.Dependencies() .getRestrictedTransports(subGrp, lastSnapshot); .getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot); assertEquals(expectedTransports, result); } @Test public void testGetRestrictedTransports() { public void testGetRestrictedTransportsFromCarrierConfig() { final Set<Integer> restrictedTransports = new ArraySet<>(); restrictedTransports.add(TRANSPORT_CELLULAR); restrictedTransports.add(TRANSPORT_WIFI); Loading @@ -1065,11 +1065,12 @@ public class VcnManagementServiceTest { mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransports_noRestrictPolicyConfigured() { public void testGetRestrictedTransportsFromCarrierConfig_noRestrictPolicyConfigured() { final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); final PersistableBundleWrapper carrierConfig = Loading @@ -1078,17 +1079,54 @@ public class VcnManagementServiceTest { mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransports_noCarrierConfig() { public void testGetRestrictedTransportsFromCarrierConfig_noCarrierConfig() { final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); final TelephonySubscriptionSnapshot lastSnapshot = mock(TelephonySubscriptionSnapshot.class); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransportsFromCarrierConfigAndVcnConfig() { // Configure restricted transport in CarrierConfig final Set<Integer> restrictedTransportInCarrierConfig = Collections.singleton(TRANSPORT_WIFI); PersistableBundle carrierConfigBundle = new PersistableBundle(); carrierConfigBundle.putIntArray( VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, restrictedTransportInCarrierConfig.stream().mapToInt(i -> i).toArray()); final PersistableBundleWrapper carrierConfig = new PersistableBundleWrapper(carrierConfigBundle); final TelephonySubscriptionSnapshot lastSnapshot = mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); // Configure restricted transport in VcnConfig final Context mockContext = mock(Context.class); doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); final VcnConfig vcnConfig = VcnConfigTest.buildTestConfig( mockContext, Collections.singleton(TRANSPORT_CELLULAR)); // Verifications final Set<Integer> expectedTransports = new ArraySet<>(); expectedTransports.add(TRANSPORT_CELLULAR); expectedTransports.add(TRANSPORT_WIFI); Set<Integer> result = new VcnManagementService.Dependencies() .getRestrictedTransports(TEST_UUID_2, lastSnapshot, vcnConfig); assertEquals(expectedTransports, result); } private void checkGetUnderlyingNetworkPolicy( Loading @@ -1103,7 +1141,7 @@ public class VcnManagementServiceTest { if (isTransportRestricted) { restrictedTransports.add(transportType); } doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any()); doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any()); final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( Loading Loading @@ -1201,7 +1239,7 @@ public class VcnManagementServiceTest { public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any()); .getRestrictedTransports(any(), any(), any()); setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */); Loading Loading @@ -1343,6 +1381,23 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } @Test public void testVcnConfigChangeUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); final Context mockContext = mock(Context.class); doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); final VcnConfig vcnConfig = VcnConfigTest.buildTestConfig( mockContext, Collections.singleton(TRANSPORT_CELLULAR)); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME); verify(mMockPolicyListener).onPolicyChanged(); } @Test public void testRemoveVcnUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); Loading Loading @@ -1375,7 +1430,7 @@ public class VcnManagementServiceTest { setupActiveSubscription(TEST_UUID_2); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListenerForTest(mMockPolicyListener); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); final TelephonySubscriptionSnapshot snapshot = buildSubscriptionSnapshot( Loading Loading
core/api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -27285,6 +27285,7 @@ package android.net.vcn { public final class VcnConfig implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs(); method @NonNull public java.util.Set<java.lang.Integer> getRestrictedUnderlyingNetworkTransports(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR; } Loading @@ -27293,6 +27294,7 @@ package android.net.vcn { ctor public VcnConfig.Builder(@NonNull android.content.Context); method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig); method @NonNull public android.net.vcn.VcnConfig build(); method @NonNull public android.net.vcn.VcnConfig.Builder setRestrictedUnderlyingNetworkTransports(@NonNull java.util.Set<java.lang.Integer>); } public final class VcnGatewayConnectionConfig {
core/java/android/net/vcn/VcnConfig.java +106 −2 Original line number Diff line number Diff line Loading @@ -15,16 +15,23 @@ */ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; Loading @@ -32,6 +39,7 @@ import com.android.server.vcn.util.PersistableBundleUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.Objects; import java.util.Set; Loading @@ -46,22 +54,36 @@ import java.util.Set; public final class VcnConfig implements Parcelable { @NonNull private static final String TAG = VcnConfig.class.getSimpleName(); private static final Set<Integer> ALLOWED_TRANSPORTS = new ArraySet<>(); static { ALLOWED_TRANSPORTS.add(TRANSPORT_WIFI); ALLOWED_TRANSPORTS.add(TRANSPORT_CELLULAR); } private static final String PACKAGE_NAME_KEY = "mPackageName"; @NonNull private final String mPackageName; private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs"; @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs; private static final Set<Integer> RESTRICTED_TRANSPORTS_DEFAULT = Collections.singleton(TRANSPORT_WIFI); private static final String RESTRICTED_TRANSPORTS_KEY = "mRestrictedTransports"; @NonNull private final Set<Integer> mRestrictedTransports; private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile"; private final boolean mIsTestModeProfile; private VcnConfig( @NonNull String packageName, @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs, @NonNull Set<Integer> restrictedTransports, boolean isTestModeProfile) { mPackageName = packageName; mGatewayConnectionConfigs = Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs)); mRestrictedTransports = Collections.unmodifiableSet(new ArraySet<>(restrictedTransports)); mIsTestModeProfile = isTestModeProfile; validate(); Loading @@ -82,6 +104,20 @@ public final class VcnConfig implements Parcelable { new ArraySet<>( PersistableBundleUtils.toList( gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new)); final PersistableBundle restrictedTransportsBundle = in.getPersistableBundle(RESTRICTED_TRANSPORTS_KEY); if (restrictedTransportsBundle == null) { // RESTRICTED_TRANSPORTS_KEY was added in U and does not exist in VcnConfigs created in // older platforms mRestrictedTransports = RESTRICTED_TRANSPORTS_DEFAULT; } else { mRestrictedTransports = new ArraySet<Integer>( PersistableBundleUtils.toList( restrictedTransportsBundle, INTEGER_DESERIALIZER)); } mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY); validate(); Loading @@ -91,6 +127,19 @@ public final class VcnConfig implements Parcelable { Objects.requireNonNull(mPackageName, "packageName was null"); Preconditions.checkCollectionNotEmpty( mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty"); final Iterator<Integer> iterator = mRestrictedTransports.iterator(); while (iterator.hasNext()) { final int transport = iterator.next(); if (!ALLOWED_TRANSPORTS.contains(transport)) { iterator.remove(); Log.w( TAG, "Found invalid transport " + transport + " which might be from a new version of VcnConfig"); } } } /** Loading @@ -109,6 +158,16 @@ public final class VcnConfig implements Parcelable { return Collections.unmodifiableSet(mGatewayConnectionConfigs); } /** * Retrieve the transports that will be restricted by the VCN. * * @see Builder#setRestrictedUnderlyingNetworkTransports(Set) */ @NonNull public Set<Integer> getRestrictedUnderlyingNetworkTransports() { return Collections.unmodifiableSet(mRestrictedTransports); } /** * Returns whether or not this VcnConfig is restricted to test networks. * Loading @@ -134,6 +193,12 @@ public final class VcnConfig implements Parcelable { new ArrayList<>(mGatewayConnectionConfigs), VcnGatewayConnectionConfig::toPersistableBundle); result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle); final PersistableBundle restrictedTransportsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mRestrictedTransports), INTEGER_SERIALIZER); result.putPersistableBundle(RESTRICTED_TRANSPORTS_KEY, restrictedTransportsBundle); result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile); return result; Loading @@ -141,7 +206,8 @@ public final class VcnConfig implements Parcelable { @Override public int hashCode() { return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); return Objects.hash( mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile); } @Override Loading @@ -153,6 +219,7 @@ public final class VcnConfig implements Parcelable { final VcnConfig rhs = (VcnConfig) other; return mPackageName.equals(rhs.mPackageName) && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs) && mRestrictedTransports.equals(rhs.mRestrictedTransports) && mIsTestModeProfile == rhs.mIsTestModeProfile; } Loading Loading @@ -189,12 +256,15 @@ public final class VcnConfig implements Parcelable { @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>(); @NonNull private final Set<Integer> mRestrictedTransports = new ArraySet<>(); private boolean mIsTestModeProfile = false; public Builder(@NonNull Context context) { Objects.requireNonNull(context, "context was null"); mPackageName = context.getOpPackageName(); mRestrictedTransports.addAll(RESTRICTED_TRANSPORTS_DEFAULT); } /** Loading Loading @@ -225,6 +295,36 @@ public final class VcnConfig implements Parcelable { return this; } private void validateRestrictedTransportsOrThrow(Set<Integer> restrictedTransports) { Objects.requireNonNull(restrictedTransports, "transports was null"); for (int transport : restrictedTransports) { if (!ALLOWED_TRANSPORTS.contains(transport)) { throw new IllegalArgumentException("Invalid transport " + transport); } } } /** * Sets transports that will be restricted by the VCN. * * @param transports transports that will be restricted by VCN. Networks that include any * of the transports will be marked as restricted. Only {@link * NetworkCapabilities#TRANSPORT_WIFI} and {@link * NetworkCapabilities#TRANSPORT_CELLULAR} are allowed. {@link * NetworkCapabilities#TRANSPORT_WIFI} is marked restricted by default. * @return this {@link Builder} instance, for chaining * @throws IllegalArgumentException if the input contains unsupported transport types. */ @NonNull public Builder setRestrictedUnderlyingNetworkTransports(@NonNull Set<Integer> transports) { validateRestrictedTransportsOrThrow(transports); mRestrictedTransports.clear(); mRestrictedTransports.addAll(transports); return this; } /** * Restricts this VcnConfig to matching with test networks (only). * Loading @@ -248,7 +348,11 @@ public final class VcnConfig implements Parcelable { */ @NonNull public VcnConfig build() { return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); return new VcnConfig( mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile); } } }
services/core/java/com/android/server/VcnManagementService.java +22 −14 Original line number Diff line number Diff line Loading @@ -371,8 +371,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { return new LocationPermissionChecker(context); } /** Gets the transports that need to be marked as restricted by the VCN */ public Set<Integer> getRestrictedTransports( /** Gets transports that need to be marked as restricted by the VCN from CarrierConfig */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Set<Integer> getRestrictedTransportsFromCarrierConfig( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) { if (!Build.IS_ENG && !Build.IS_USERDEBUG) { return RESTRICTED_TRANSPORTS_DEFAULT; Loading @@ -398,6 +399,22 @@ public class VcnManagementService extends IVcnManagementService.Stub { } return restrictedTransports; } /** Gets the transports that need to be marked as restricted by the VCN */ public Set<Integer> getRestrictedTransports( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot, VcnConfig vcnConfig) { final Set<Integer> restrictedTransports = new ArraySet<>(); restrictedTransports.addAll(vcnConfig.getRestrictedUnderlyingNetworkTransports()); // TODO: b/262269892 Remove the ability to configure restricted transports // via CarrierConfig restrictedTransports.addAll( getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot)); return restrictedTransports; } } /** Notifies the VcnManagementService that external dependencies can be set up. */ Loading Loading @@ -719,6 +736,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (mVcns.containsKey(subscriptionGroup)) { final Vcn vcn = mVcns.get(subscriptionGroup); vcn.updateConfig(config); notifyAllPolicyListenersLocked(); } else { // TODO(b/193687515): Support multiple VCNs active at the same time if (isActiveSubGroup(subscriptionGroup, mLastSnapshot)) { Loading Loading @@ -936,7 +954,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { } /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ @GuardedBy("mLock") @Override public void addVcnUnderlyingNetworkPolicyListener( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { Loading @@ -963,16 +980,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { }); } @VisibleForTesting(visibility = Visibility.PRIVATE) void addVcnUnderlyingNetworkPolicyListenerForTest( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { synchronized (mLock) { addVcnUnderlyingNetworkPolicyListener(listener); } } /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ @GuardedBy("mLock") @Override public void removeVcnUnderlyingNetworkPolicyListener( @NonNull IVcnUnderlyingNetworkPolicyListener listener) { Loading Loading @@ -1062,8 +1070,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { isVcnManagedNetwork = true; } final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports(subGrp, mLastSnapshot); final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports( subGrp, mLastSnapshot, mConfigs.get(subGrp)); for (int restrictedTransport : restrictedTransports) { if (ncCopy.hasTransport(restrictedTransport)) { if (restrictedTransport == TRANSPORT_CELLULAR) { Loading
tests/vcn/java/android/net/vcn/VcnConfigTest.java +57 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,12 @@ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; Loading @@ -24,6 +29,7 @@ import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.content.Context; import android.os.Parcel; import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading @@ -42,19 +48,36 @@ public class VcnConfigTest { private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS = Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig()); private static final Set<Integer> RESTRICTED_TRANSPORTS = new ArraySet<>(); static { RESTRICTED_TRANSPORTS.add(TRANSPORT_WIFI); RESTRICTED_TRANSPORTS.add(TRANSPORT_CELLULAR); } private final Context mContext = mock(Context.class); // Public visibility for VcnManagementServiceTest public static VcnConfig buildTestConfig(@NonNull Context context) { public static VcnConfig buildTestConfig( @NonNull Context context, Set<Integer> restrictedTransports) { VcnConfig.Builder builder = new VcnConfig.Builder(context); for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) { builder.addGatewayConnectionConfig(gatewayConnectionConfig); } if (restrictedTransports != null) { builder.setRestrictedUnderlyingNetworkTransports(restrictedTransports); } return builder.build(); } // Public visibility for VcnManagementServiceTest public static VcnConfig buildTestConfig(@NonNull Context context) { return buildTestConfig(context, null); } @Before public void setUp() throws Exception { doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName(); Loading Loading @@ -91,11 +114,25 @@ public class VcnConfigTest { } @Test public void testBuilderAndGetters() { public void testBuilderAndGettersDefaultValues() { final VcnConfig config = buildTestConfig(mContext); assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); assertFalse(config.isTestModeProfile()); assertEquals( Collections.singleton(TRANSPORT_WIFI), config.getRestrictedUnderlyingNetworkTransports()); } @Test public void testBuilderAndGettersConfigRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName()); assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); assertFalse(config.isTestModeProfile()); assertEquals(RESTRICTED_TRANSPORTS, config.getRestrictedUnderlyingNetworkTransports()); } @Test Loading @@ -105,6 +142,24 @@ public class VcnConfigTest { assertEquals(config, new VcnConfig(config.toPersistableBundle())); } @Test public void testPersistableBundleWithRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); assertEquals(config, new VcnConfig(config.toPersistableBundle())); } @Test public void testEqualityWithRestrictedTransports() { final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); final VcnConfig configEqual = buildTestConfig(mContext, RESTRICTED_TRANSPORTS); final VcnConfig configNotEqual = buildTestConfig(mContext, Collections.singleton(TRANSPORT_WIFI)); assertEquals(config, configEqual); assertNotEquals(config, configNotEqual); } @Test public void testParceling() { final VcnConfig config = buildTestConfig(mContext); Loading
tests/vcn/java/com/android/server/VcnManagementServiceTest.java +67 −12 Original line number Diff line number Diff line Loading @@ -258,7 +258,7 @@ public class VcnManagementServiceTest { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any()); .getRestrictedTransports(any(), any(), any()); } Loading Loading @@ -1038,18 +1038,18 @@ public class VcnManagementServiceTest { new LinkProperties()); } private void checkGetRestrictedTransports( private void checkGetRestrictedTransportsFromCarrierConfig( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot, Set<Integer> expectedTransports) { Set<Integer> result = new VcnManagementService.Dependencies() .getRestrictedTransports(subGrp, lastSnapshot); .getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot); assertEquals(expectedTransports, result); } @Test public void testGetRestrictedTransports() { public void testGetRestrictedTransportsFromCarrierConfig() { final Set<Integer> restrictedTransports = new ArraySet<>(); restrictedTransports.add(TRANSPORT_CELLULAR); restrictedTransports.add(TRANSPORT_WIFI); Loading @@ -1065,11 +1065,12 @@ public class VcnManagementServiceTest { mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransports_noRestrictPolicyConfigured() { public void testGetRestrictedTransportsFromCarrierConfig_noRestrictPolicyConfigured() { final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); final PersistableBundleWrapper carrierConfig = Loading @@ -1078,17 +1079,54 @@ public class VcnManagementServiceTest { mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransports_noCarrierConfig() { public void testGetRestrictedTransportsFromCarrierConfig_noCarrierConfig() { final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI); final TelephonySubscriptionSnapshot lastSnapshot = mock(TelephonySubscriptionSnapshot.class); checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); checkGetRestrictedTransportsFromCarrierConfig( TEST_UUID_2, lastSnapshot, restrictedTransports); } @Test public void testGetRestrictedTransportsFromCarrierConfigAndVcnConfig() { // Configure restricted transport in CarrierConfig final Set<Integer> restrictedTransportInCarrierConfig = Collections.singleton(TRANSPORT_WIFI); PersistableBundle carrierConfigBundle = new PersistableBundle(); carrierConfigBundle.putIntArray( VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, restrictedTransportInCarrierConfig.stream().mapToInt(i -> i).toArray()); final PersistableBundleWrapper carrierConfig = new PersistableBundleWrapper(carrierConfigBundle); final TelephonySubscriptionSnapshot lastSnapshot = mock(TelephonySubscriptionSnapshot.class); doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); // Configure restricted transport in VcnConfig final Context mockContext = mock(Context.class); doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); final VcnConfig vcnConfig = VcnConfigTest.buildTestConfig( mockContext, Collections.singleton(TRANSPORT_CELLULAR)); // Verifications final Set<Integer> expectedTransports = new ArraySet<>(); expectedTransports.add(TRANSPORT_CELLULAR); expectedTransports.add(TRANSPORT_WIFI); Set<Integer> result = new VcnManagementService.Dependencies() .getRestrictedTransports(TEST_UUID_2, lastSnapshot, vcnConfig); assertEquals(expectedTransports, result); } private void checkGetUnderlyingNetworkPolicy( Loading @@ -1103,7 +1141,7 @@ public class VcnManagementServiceTest { if (isTransportRestricted) { restrictedTransports.add(transportType); } doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any()); doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any()); final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( Loading Loading @@ -1201,7 +1239,7 @@ public class VcnManagementServiceTest { public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any()); .getRestrictedTransports(any(), any(), any()); setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */); Loading Loading @@ -1343,6 +1381,23 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } @Test public void testVcnConfigChangeUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); final Context mockContext = mock(Context.class); doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName(); final VcnConfig vcnConfig = VcnConfigTest.buildTestConfig( mockContext, Collections.singleton(TRANSPORT_CELLULAR)); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME); verify(mMockPolicyListener).onPolicyChanged(); } @Test public void testRemoveVcnUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); Loading Loading @@ -1375,7 +1430,7 @@ public class VcnManagementServiceTest { setupActiveSubscription(TEST_UUID_2); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListenerForTest(mMockPolicyListener); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); final TelephonySubscriptionSnapshot snapshot = buildSubscriptionSnapshot( Loading