Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70fc4ac5 authored by Benedict Wong's avatar Benedict Wong Committed by Automerger Merge Worker
Browse files

Merge changes Iaeb1871b,I1687df02 am: 42ea9e9d am: 88b61399 am: b3aec3f2

parents 3250c655 b3aec3f2
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -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
     *
     * <p>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<
+99 −10
Original line number Diff line number Diff line
@@ -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<Integer> 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<Integer> 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<Integer> 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<ParcelUuid, Vcn> 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<Integer> 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<Integer> getNonTestTransportTypes(NetworkCapabilities caps) {
            final Set<Integer> 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);
+7 −2
Original line number Diff line number Diff line
@@ -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<Integer, SubscriptionInfo>(subIdToInfoMap));
            mSubIdToCarrierConfigMap =
                    Collections.unmodifiableMap(
                            new HashMap<Integer, PersistableBundleWrapper>(
                                    subIdToCarrierConfigMap));

            final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
            for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
+14 −0
Original line number Diff line number Diff line
@@ -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);
+210 −33
Original line number Diff line number Diff line
@@ -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<Integer> expectedTransports) {
        Set<Integer> result =
                new VcnManagementService.Dependencies()
                        .getRestrictedTransports(subGrp, lastSnapshot);
        assertEquals(expectedTransports, result);
    }

    @Test
    public void testGetRestrictedTransports() {
        final Set<Integer> 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 testGetUnderlyingNetworkPolicyCellular() throws Exception {
    public void testGetRestrictedTransports_noRestrictPolicyConfigured() {
        final Set<Integer> 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<Integer> 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<Integer> 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,
                true /* isVcnManaged */,
                false /* isRestricted */);
                false /* isTransportRestricted */,
                true /* isActive */,
                true /* expectVcnManaged */,
                false /* expectRestricted */);
    }

    @Test
    public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception {
        final VcnUnderlyingNetworkPolicy policy =
                startVcnAndGetPolicyForTransport(
                        TEST_SUBSCRIPTION_ID,
                        TEST_UUID_2,
    public void testGetUnderlyingNetworkPolicy_unrestrictCellSafeMode() throws Exception {
        checkGetUnderlyingNetworkPolicy(
                TRANSPORT_CELLULAR,
                false /* isTransportRestricted */,
                false /* isActive */,
                        TRANSPORT_CELLULAR);
                false /* expectVcnManaged */,
                false /* expectRestricted */);
    }

        assertFalse(policy.isTeardownRequested());
        verifyMergedNetworkCapabilities(
                policy.getMergedNetworkCapabilities(),
                NetworkCapabilities.TRANSPORT_CELLULAR,
                false /* isVcnManaged */,
                false /* isRestricted */);
    @Test
    public void testGetUnderlyingNetworkPolicy_restrictCell() throws Exception {
        checkGetUnderlyingNetworkPolicy(
                TRANSPORT_CELLULAR,
                true /* isTransportRestricted */,
                true /* isActive */,
                true /* expectVcnManaged */,
                true /* expectRestricted */);
    }

    @Test
    public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
        final VcnUnderlyingNetworkPolicy policy =
                startVcnAndGetPolicyForTransport(
                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI);
    public void testGetUnderlyingNetworkPolicy_restrictCellSafeMode() throws Exception {
        checkGetUnderlyingNetworkPolicy(
                TRANSPORT_CELLULAR,
                true /* isTransportRestricted */,
                false /* isActive */,
                false /* expectVcnManaged */,
                false /* expectRestricted */);
    }

        assertFalse(policy.isTeardownRequested());
        verifyMergedNetworkCapabilities(
                policy.getMergedNetworkCapabilities(),
                NetworkCapabilities.TRANSPORT_WIFI,
                true /* isVcnManaged */,
                true /* isRestricted */);
    @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 testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception {
    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 =
                startVcnAndGetPolicyForTransport(
                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
                mVcnMgmtSvc.getUnderlyingNetworkPolicy(
                        getNetworkCapabilitiesBuilderForTransport(
                                        TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR)
                                .build(),
                        new LinkProperties());

        assertFalse(policy.isTeardownRequested());
        verifyMergedNetworkCapabilities(
                policy.getMergedNetworkCapabilities(),
                NetworkCapabilities.TRANSPORT_WIFI,
                false /* isVcnManaged */,
                true /* isRestricted */);
                TRANSPORT_CELLULAR,
                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,
Loading