From 1e91f8dbada6df090843699439c03fd25fb30969 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Wed, 9 Nov 2022 23:59:25 +0000 Subject: [PATCH 1/2] Support configuring the VCN restriction policy This commit implements the logic to support configuring VCN restriction policy on the underlying network. This commit also adds a stub for later CLs to add the configuration interface. Bug: 239104955 Test: atest FrameworksVcnTests(new tests) Test: atest CtsVcnTestCases Change-Id: I1687df029b5bbd8f20e0e4839ba8bd1717051dcf --- .../android/server/VcnManagementService.java | 68 +++++- .../server/VcnManagementServiceTest.java | 198 +++++++++++++----- 2 files changed, 206 insertions(+), 60 deletions(-) diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 76cac934fdfea..6f49db1adbfd6 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -18,8 +18,10 @@ package com.android.server; import static android.Manifest.permission.DUMP; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnGatewayConnectionConfig.ALLOWED_CAPABILITIES; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED; @@ -68,6 +70,7 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; import android.util.Slog; @@ -361,6 +364,16 @@ public class VcnManagementService extends IVcnManagementService.Stub { public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) { return new LocationPermissionChecker(context); } + + /** Gets the transports that need to be marked as restricted by the VCN */ + public Set getRestrictedTransports( + ParcelUuid subGrp, + Map vcnConfigs, + TelephonySubscriptionSnapshot lastSnapshot) { + // TODO: b/239104955 Read restriction policy configurations + + return Collections.singleton(TRANSPORT_WIFI); + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ @@ -1000,7 +1013,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { final ParcelUuid subGrp = getSubGroupForNetworkCapabilities(ncCopy); boolean isVcnManagedNetwork = false; - boolean isRestrictedCarrierWifi = false; + boolean isRestricted = false; synchronized (mLock) { final Vcn vcn = mVcns.get(subGrp); if (vcn != null) { @@ -1008,9 +1021,19 @@ public class VcnManagementService extends IVcnManagementService.Stub { isVcnManagedNetwork = true; } - if (ncCopy.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - // Carrier WiFi always restricted if VCN exists (even in safe mode). - isRestrictedCarrierWifi = true; + final Set restrictedTransports = + mDeps.getRestrictedTransports(subGrp, mConfigs, mLastSnapshot); + for (int restrictedTransport : restrictedTransports) { + if (ncCopy.hasTransport(restrictedTransport)) { + if (restrictedTransport == TRANSPORT_CELLULAR) { + // Only make a cell network as restricted when the VCN is in + // active mode. + isRestricted |= (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE); + } else { + isRestricted = true; + break; + } + } } } } @@ -1024,14 +1047,16 @@ public class VcnManagementService extends IVcnManagementService.Stub { ncBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); } - if (isRestrictedCarrierWifi) { + if (isRestricted) { ncBuilder.removeCapability( NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } final NetworkCapabilities result = ncBuilder.build(); final VcnUnderlyingNetworkPolicy policy = new VcnUnderlyingNetworkPolicy( - mTrackingNetworkCallback.requiresRestartForCarrierWifi(result), result); + mTrackingNetworkCallback + .requiresRestartForImmutableCapabilityChanges(result), + result); logVdbg("getUnderlyingNetworkPolicy() called for caps: " + networkCapabilities + "; and lp: " + linkProperties + "; result = " + policy); @@ -1296,15 +1321,38 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } - private boolean requiresRestartForCarrierWifi(NetworkCapabilities caps) { - if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubscriptionIds() == null) { + private Set getNonTestTransportTypes(NetworkCapabilities caps) { + final Set transportTypes = new ArraySet<>(); + for (int t : caps.getTransportTypes()) { + transportTypes.add(t); + } + return transportTypes; + } + + private boolean hasSameTransportsAndCapabilities( + NetworkCapabilities caps, NetworkCapabilities capsOther) { + if (!Objects.equals( + getNonTestTransportTypes(caps), getNonTestTransportTypes(capsOther))) { + return false; + } + + for (int capability : ALLOWED_CAPABILITIES) { + if (caps.hasCapability(capability) != capsOther.hasCapability(capability)) { + return false; + } + } + return true; + } + + private boolean requiresRestartForImmutableCapabilityChanges(NetworkCapabilities caps) { + if (caps.getSubscriptionIds() == null) { return false; } synchronized (mCaps) { for (NetworkCapabilities existing : mCaps.values()) { - if (existing.hasTransport(TRANSPORT_WIFI) - && caps.getSubscriptionIds().equals(existing.getSubscriptionIds())) { + if (caps.getSubscriptionIds().equals(existing.getSubscriptionIds()) + && hasSameTransportsAndCapabilities(caps, existing)) { // Restart if any immutable capabilities have changed return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index f924b2e9b932f..478afe8aed467 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -17,6 +17,7 @@ package com.android.server; import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -252,6 +253,10 @@ public class VcnManagementServiceTest { .when(mMockContext) .enforceCallingOrSelfPermission( eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + doReturn(Collections.singleton(TRANSPORT_WIFI)) + .when(mMockDeps) + .getRestrictedTransports(any(), any(), any()); } @@ -1032,63 +1037,135 @@ public class VcnManagementServiceTest { new LinkProperties()); } - @Test - public void testGetUnderlyingNetworkPolicyCellular() throws Exception { + private void checkGetUnderlyingNetworkPolicy( + int transportType, + boolean isTransportRestricted, + boolean isActive, + boolean expectVcnManaged, + boolean expectRestricted) + throws Exception { + + final Set restrictedTransports = new ArraySet(); + if (isTransportRestricted) { + restrictedTransports.add(transportType); + } + doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any()); + final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR); + TEST_SUBSCRIPTION_ID, TEST_UUID_2, isActive, transportType); + + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + transportType, + expectVcnManaged, + expectRestricted); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictCell() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + false /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictCellSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + false /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictCell() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + true /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + true /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictCellSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_CELLULAR, + true /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictWifi() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + false /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_unrestrictWifiSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + false /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + false /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictWifi() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + true /* isTransportRestricted */, + true /* isActive */, + true /* expectVcnManaged */, + true /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicy_restrictWifiSafeMode() throws Exception { + checkGetUnderlyingNetworkPolicy( + TRANSPORT_WIFI, + true /* isTransportRestricted */, + false /* isActive */, + false /* expectVcnManaged */, + true /* expectRestricted */); + } + + @Test + public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception { + doReturn(Collections.singleton(TRANSPORT_WIFI)) + .when(mMockDeps) + .getRestrictedTransports(any(), any(), any()); + + setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */); + + // Get the policy for a cellular network and expect it won't be affected by the wifi + // restriction policy + final VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + getNetworkCapabilitiesBuilderForTransport( + TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .build(), + new LinkProperties()); assertFalse(policy.isTeardownRequested()); verifyMergedNetworkCapabilities( policy.getMergedNetworkCapabilities(), TRANSPORT_CELLULAR, - true /* isVcnManaged */, - false /* isRestricted */); - } - - @Test - public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception { - final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, - TEST_UUID_2, - false /* isActive */, - TRANSPORT_CELLULAR); - - assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_CELLULAR, - false /* isVcnManaged */, - false /* isRestricted */); - } - - @Test - public void testGetUnderlyingNetworkPolicyWifi() throws Exception { - final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI); - - assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_WIFI, - true /* isVcnManaged */, - true /* isRestricted */); - } - - @Test - public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception { - final VcnUnderlyingNetworkPolicy policy = - startVcnAndGetPolicyForTransport( - TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI); - - assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), - NetworkCapabilities.TRANSPORT_WIFI, - false /* isVcnManaged */, - true /* isRestricted */); + true /* expectVcnManaged */, + false /* expectRestricted */); } private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) { @@ -1138,6 +1215,27 @@ public class VcnManagementServiceTest { assertTrue(policy.isTeardownRequested()); } + @Test + public void testGetUnderlyingNetworkPolicyForRestrictedImsWhenUnrestrictingCell() + throws Exception { + final NetworkCapabilities existingNetworkCaps = + getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NET_CAPABILITY_IMS) + .build(); + setupTrackedCarrierWifiNetwork(existingNetworkCaps); + + final VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + getNetworkCapabilitiesBuilderForTransport( + TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_IMS) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .build(), + new LinkProperties()); + assertFalse(policy.isTeardownRequested()); + } + @Test public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */); From 29024bfb223cd9d0e6ffbf98deb0b358715871ee Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Thu, 10 Nov 2022 21:56:31 +0000 Subject: [PATCH 2/2] Add restrictions on underlying networks with CarrierConfig This commit updates the VCN code to use CarrierConfig to store the restriction policy configuration, and notify policy listners when the CarrierConfig gets updated. Bug: 239104955 Test: atest FrameworksVcnTests(new tests) Test: atest CtsVcnTestCases Test: manually verified by overriding carrier configs Change-Id: Iaeb1871b7d9f5c3d1b4e5ec24cfbf79eea58256f --- core/java/android/net/vcn/VcnManager.java | 13 ++- .../android/server/VcnManagementService.java | 53 ++++++++++-- .../vcn/TelephonySubscriptionTracker.java | 9 +- .../vcn/util/PersistableBundleUtils.java | 14 +++ .../server/VcnManagementServiceTest.java | 85 ++++++++++++++++++- .../vcn/TelephonySubscriptionTrackerTest.java | 35 ++++++++ 6 files changed, 197 insertions(+), 12 deletions(-) diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 40e4083c02db2..3a7aea5d5194d 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -104,12 +104,23 @@ public class VcnManager { // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz + /** + * Key for transports that need to be marked as restricted by the VCN + * + *

Defaults to TRANSPORT_WIFI if the config does not exist + * + * @hide + */ + public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY = + "vcn_restricted_transports"; + /** 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_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY }; private static final Map< diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 6f49db1adbfd6..61f7f301a838e 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnGatewayConnectionConfig.ALLOWED_CAPABILITIES; +import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED; @@ -55,6 +56,7 @@ import android.net.vcn.VcnManager.VcnStatusCode; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.wifi.WifiInfo; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -86,6 +88,7 @@ import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import com.android.server.vcn.util.PersistableBundleUtils; +import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import java.io.File; import java.io.FileDescriptor; @@ -162,6 +165,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); private static final int LOCAL_LOG_LINE_COUNT = 512; + private static final Set RESTRICTED_TRANSPORTS_DEFAULT = + Collections.singleton(TRANSPORT_WIFI); + // Public for use in all other VCN classes @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT); @@ -367,12 +373,30 @@ public class VcnManagementService extends IVcnManagementService.Stub { /** Gets the transports that need to be marked as restricted by the VCN */ public Set getRestrictedTransports( - ParcelUuid subGrp, - Map vcnConfigs, - TelephonySubscriptionSnapshot lastSnapshot) { - // TODO: b/239104955 Read restriction policy configurations + ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) { + if (!Build.IS_ENG && !Build.IS_USERDEBUG) { + return RESTRICTED_TRANSPORTS_DEFAULT; + } - return Collections.singleton(TRANSPORT_WIFI); + final PersistableBundleWrapper carrierConfig = + lastSnapshot.getCarrierConfigForSubGrp(subGrp); + if (carrierConfig == null) { + return RESTRICTED_TRANSPORTS_DEFAULT; + } + + final int[] defaultValue = + RESTRICTED_TRANSPORTS_DEFAULT.stream().mapToInt(i -> i).toArray(); + final int[] restrictedTransportsArray = + carrierConfig.getIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + defaultValue); + + // Convert to a boxed set + final Set restrictedTransports = new ArraySet<>(); + for (int transport : restrictedTransportsArray) { + restrictedTransports.add(transport); + } + return restrictedTransports; } } @@ -530,6 +554,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } + boolean needNotifyAllPolicyListeners = false; // Schedule teardown of any VCN instances that have lost carrier privileges (after a // delay) for (Entry entry : mVcns.entrySet()) { @@ -577,6 +602,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { } else { // If this VCN's status has not changed, update it with the new snapshot entry.getValue().updateSubscriptionSnapshot(mLastSnapshot); + needNotifyAllPolicyListeners |= + !Objects.equals( + oldSnapshot.getCarrierConfigForSubGrp(subGrp), + mLastSnapshot.getCarrierConfigForSubGrp(subGrp)); } } @@ -586,6 +615,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { getSubGroupToSubIdMappings(mLastSnapshot); if (!currSubGrpMappings.equals(oldSubGrpMappings)) { garbageCollectAndWriteVcnConfigsLocked(); + needNotifyAllPolicyListeners = true; + } + + if (needNotifyAllPolicyListeners) { notifyAllPolicyListenersLocked(); } } @@ -930,6 +963,14 @@ 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 @@ -1022,7 +1063,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { } final Set restrictedTransports = - mDeps.getRestrictedTransports(subGrp, mConfigs, mLastSnapshot); + mDeps.getRestrictedTransports(subGrp, mLastSnapshot); for (int restrictedTransport : restrictedTransports) { if (ncCopy.hasTransport(restrictedTransport)) { if (restrictedTransport == TRANSPORT_CELLULAR) { diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index 5c305c6902af3..ca4a32f553aea 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -390,8 +390,13 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { Objects.requireNonNull(privilegedPackages, "privilegedPackages was null"); Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null"); - mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap); - mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap); + mSubIdToInfoMap = + Collections.unmodifiableMap( + new HashMap(subIdToInfoMap)); + mSubIdToCarrierConfigMap = + Collections.unmodifiableMap( + new HashMap( + subIdToCarrierConfigMap)); final Map> unmodifiableInnerSets = new ArrayMap<>(); for (Entry> entry : privilegedPackages.entrySet()) { diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java index 999d4064c9516..d22ec0ad456d2 100644 --- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java @@ -544,6 +544,20 @@ public class PersistableBundleUtils { return mBundle.getInt(key, defaultValue); } + /** + * Returns the value associated with the given key, or null if no mapping of the desired + * type exists for the given key or a null value is explicitly associated with the key. + * + * @param key a String, or null + * @param defaultValue the value to return if key does not exist + * @return an int[] value, or null + */ + @Nullable + public int[] getIntArray(@Nullable String key, @Nullable int[] defaultValue) { + final int[] value = mBundle.getIntArray(key); + return value == null ? defaultValue : value; + } + @Override public int hashCode() { return getHashCode(mBundle); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 478afe8aed467..eae8873eba9f5 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -96,6 +97,7 @@ import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import com.android.server.vcn.util.PersistableBundleUtils; +import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import org.junit.Before; import org.junit.Test; @@ -256,7 +258,7 @@ public class VcnManagementServiceTest { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) - .getRestrictedTransports(any(), any(), any()); + .getRestrictedTransports(any(), any()); } @@ -1037,6 +1039,59 @@ public class VcnManagementServiceTest { new LinkProperties()); } + private void checkGetRestrictedTransports( + ParcelUuid subGrp, + TelephonySubscriptionSnapshot lastSnapshot, + Set expectedTransports) { + Set result = + new VcnManagementService.Dependencies() + .getRestrictedTransports(subGrp, lastSnapshot); + assertEquals(expectedTransports, result); + } + + @Test + public void testGetRestrictedTransports() { + final Set restrictedTransports = new ArraySet<>(); + restrictedTransports.add(TRANSPORT_CELLULAR); + restrictedTransports.add(TRANSPORT_WIFI); + + PersistableBundle carrierConfigBundle = new PersistableBundle(); + carrierConfigBundle.putIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + restrictedTransports.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)); + + checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); + } + + @Test + public void testGetRestrictedTransports_noRestrictPolicyConfigured() { + final Set restrictedTransports = Collections.singleton(TRANSPORT_WIFI); + + final PersistableBundleWrapper carrierConfig = + new PersistableBundleWrapper(new PersistableBundle()); + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); + } + + @Test + public void testGetRestrictedTransports_noCarrierConfig() { + final Set restrictedTransports = Collections.singleton(TRANSPORT_WIFI); + + final TelephonySubscriptionSnapshot lastSnapshot = + mock(TelephonySubscriptionSnapshot.class); + + checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports); + } + private void checkGetUnderlyingNetworkPolicy( int transportType, boolean isTransportRestricted, @@ -1049,7 +1104,7 @@ public class VcnManagementServiceTest { if (isTransportRestricted) { restrictedTransports.add(transportType); } - doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any()); + doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any()); final VcnUnderlyingNetworkPolicy policy = startVcnAndGetPolicyForTransport( @@ -1147,7 +1202,7 @@ public class VcnManagementServiceTest { public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) - .getRestrictedTransports(any(), any(), any()); + .getRestrictedTransports(any(), any()); setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */); @@ -1316,6 +1371,30 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } + @Test + public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListenerForTest(mMockPolicyListener); + + final TelephonySubscriptionSnapshot snapshot = + buildSubscriptionSnapshot( + TEST_SUBSCRIPTION_ID, + TEST_UUID_2, + Collections.singleton(TEST_UUID_2), + Collections.emptyMap(), + true /* hasCarrierPrivileges */); + + final PersistableBundleWrapper mockCarrierConfig = mock(PersistableBundleWrapper.class); + doReturn(mockCarrierConfig).when(snapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2)); + + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); + cb.onNewSnapshot(snapshot); + + verify(mMockPolicyListener).onPolicyChanged(); + } + private void triggerVcnSafeMode( @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot, diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 09080be9ee41f..965b073ff0db4 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -16,6 +16,9 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; @@ -39,6 +42,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -493,6 +497,37 @@ public class TelephonySubscriptionTrackerTest { verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap()))); } + @Test + public void testCarrierConfigUpdatedAfterValidTriggersCallbacks() throws Exception { + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES))); + reset(mCallback); + + final PersistableBundle updatedConfig = new PersistableBundle(); + updatedConfig.putIntArray( + VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR}); + doReturn(updatedConfig) + .when(mCarrierConfigManager) + .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1)); + + Map subIdToCarrierConfigMap = new HashMap<>(); + subIdToCarrierConfigMap.put( + TEST_SUBSCRIPTION_ID_1, new PersistableBundleWrapper(updatedConfig)); + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + + verify(mCallback) + .onNewSnapshot( + eq( + buildExpectedSnapshot( + 0, + TEST_SUBID_TO_INFO_MAP, + subIdToCarrierConfigMap, + TEST_PRIVILEGED_PACKAGES))); + } + @Test public void testSlotClearedAfterValidTriggersCallbacks() throws Exception { mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));