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 76cac934fdfea..61f7f301a838e 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -18,8 +18,11 @@ 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_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; @@ -53,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; @@ -68,6 +72,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; @@ -83,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; @@ -159,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); @@ -361,6 +370,34 @@ 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, TelephonySubscriptionSnapshot lastSnapshot) { + if (!Build.IS_ENG && !Build.IS_USERDEBUG) { + return RESTRICTED_TRANSPORTS_DEFAULT; + } + + 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; + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ @@ -517,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()) { @@ -564,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)); } } @@ -573,6 +615,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { getSubGroupToSubIdMappings(mLastSnapshot); if (!currSubGrpMappings.equals(oldSubGrpMappings)) { garbageCollectAndWriteVcnConfigsLocked(); + needNotifyAllPolicyListeners = true; + } + + if (needNotifyAllPolicyListeners) { notifyAllPolicyListenersLocked(); } } @@ -917,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 @@ -1000,7 +1054,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 +1062,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, 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 +1088,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 +1362,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/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 ad068308d481b..258642ac229e7 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -17,10 +17,12 @@ 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; 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; @@ -95,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; @@ -252,6 +255,10 @@ public class VcnManagementServiceTest { .when(mMockContext) .enforceCallingOrSelfPermission( eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + doReturn(Collections.singleton(TRANSPORT_WIFI)) + .when(mMockDeps) + .getRestrictedTransports(any(), any()); } @@ -1031,63 +1038,188 @@ 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 testGetUnderlyingNetworkPolicyCellular() throws Exception { + 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, + 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()); + 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()); + + 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) { @@ -1137,6 +1269,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 */); @@ -1217,6 +1370,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));