Use NetworkCallback to determine active data sub

- SubscriptionManager#getActiveDataSubscriptionId() does not return
the current active data subscription immediately; it takes about 30s
to update its cache.
- Moreover when internet is on WiFi, this API returns the default
data subscription, instead of INVALID_SUB_ID.
- IWLAN aligns with QNS on using the ConnectivityManager.NetworkCallback
to determine the actie data sub from NetworkCapabilities.

Bug: 249609998
Test: Live test in P10. Updated IwlanDataServiceTest and
IwlanNetworkServiceTest. New UTs:
IwlanDataServiceTest#testNetworkNotConnectedWithCellularAndCrossSimDisabled
IwlanDataServiceTest#testCrossSimNetworkConnectedWithTelephonyNetwork
IwlanDataServiceTest#testCrossSimNetworkConnectedWithVcn
IwlanNetworkServiceTest#
testNetworkRegistrationInfoHomeForCellularVcnOnDifferentSubAndCstEnabled

Change-Id: I25803aba4182ac123b3df7b84b4a299649f252b8
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 9ae95f2..0131ed0 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -26,6 +26,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -81,6 +85,11 @@
     private HandlerThread mIwlanDataServiceHandlerThread;
     private static final Map<Integer, IwlanDataServiceProvider> sIwlanDataServiceProviders =
             new ConcurrentHashMap<>();
+    private static final int INVALID_SUB_ID = -1;
+
+    // The current subscription with the active internet PDN. Need not be the default data sub.
+    // If internet is over WiFi, this value will be INVALID_SUB_ID.
+    private static int mConnectedDataSub = INVALID_SUB_ID;
 
     private static final int EVENT_BASE = IwlanEventListener.DATA_SERVICE_INTERNAL_EVENT_BASE;
     private static final int EVENT_TUNNEL_OPENED = EVENT_BASE;
@@ -140,6 +149,7 @@
         @Override
         public void onLost(Network network) {
             Log.d(TAG, "onLost: " + network);
+            IwlanDataService.setConnectedDataSub(INVALID_SUB_ID);
             IwlanDataService.setNetworkConnected(false, network, Transport.UNSPECIFIED_NETWORK);
         }
 
@@ -170,9 +180,11 @@
             if (networkCapabilities != null) {
                 if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
                     Log.d(TAG, "Network " + network + " connected using transport MOBILE");
+                    IwlanDataService.setConnectedDataSub(getConnectedDataSub(networkCapabilities));
                     IwlanDataService.setNetworkConnected(true, network, Transport.MOBILE);
                 } else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
                     Log.d(TAG, "Network " + network + " connected using transport WIFI");
+                    IwlanDataService.setConnectedDataSub(INVALID_SUB_ID);
                     IwlanDataService.setNetworkConnected(true, network, Transport.WIFI);
                 } else {
                     Log.w(TAG, "Network does not have cellular or wifi capability");
@@ -839,7 +851,7 @@
                         getTunnelManager().closeTunnel(entry.getKey(), true);
                     } else {
                         if (mIwlanDataService.isNetworkConnected(
-                                IwlanHelper.isActiveDataOnOtherSlot(mContext, getSlotIndex()),
+                                isActiveDataOnOtherSub(getSlotIndex()),
                                 IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()))) {
                             getTunnelManager().updateNetwork(network, entry.getKey());
                         }
@@ -866,7 +878,7 @@
         private void dnsPrefetchCheck() {
             boolean networkConnected =
                     mIwlanDataService.isNetworkConnected(
-                            IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
+                            isActiveDataOnOtherSub(getSlotIndex()),
                             IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()));
             /* Check if we need to do prefecting */
             if (networkConnected == true
@@ -909,19 +921,20 @@
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             pw.println("---- IwlanDataServiceProvider[" + getSlotIndex() + "] ----");
             boolean isDDS = IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex());
-            boolean isActiveDataOnOtherSlot =
-                    IwlanHelper.isActiveDataOnOtherSlot(mContext, getSlotIndex());
             boolean isCSTEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex());
             pw.println(
                     "isDefaultDataSlot: "
                             + isDDS
-                            + " isActiveDataOnOtherSlot: "
-                            + isActiveDataOnOtherSlot
+                            + "subID: "
+                            + IwlanHelper.getSubId(mContext, getSlotIndex())
+                            + " mConnectedDataSub: "
+                            + mConnectedDataSub
                             + " isCrossSimEnabled: "
                             + isCSTEnabled);
             pw.println(
                     "isNetworkConnected: "
-                            + isNetworkConnected(isActiveDataOnOtherSlot, isCSTEnabled)
+                            + isNetworkConnected(
+                                    isActiveDataOnOtherSub(getSlotIndex()), isCSTEnabled)
                             + " Wfc enabled: "
                             + mWfcEnabled);
             for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
@@ -1137,18 +1150,15 @@
                     }
 
                     slotId = iwlanDataServiceProvider.getSlotIndex();
-                    boolean isActiveDataOnOtherSlot =
-                            IwlanHelper.isActiveDataOnOtherSlot(mContext, slotId);
                     boolean isCSTEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, slotId);
                     boolean networkConnected =
-                            isNetworkConnected(isActiveDataOnOtherSlot, isCSTEnabled);
+                            isNetworkConnected(isActiveDataOnOtherSub(slotId), isCSTEnabled);
                     Log.d(
                             TAG + "[" + slotId + "]",
                             "isDds: "
-                                    + IwlanHelper.isDefaultDataSlot(
-                                            mContext, slotId)
-                                    + ", isActiveDataOnOtherSlot: "
-                                    + isActiveDataOnOtherSlot
+                                    + IwlanHelper.isDefaultDataSlot(mContext, slotId)
+                                    + ", isActiveDataOnOtherSub: "
+                                    + isActiveDataOnOtherSub(slotId)
                                     + ", isCstEnabled: "
                                     + isCSTEnabled
                                     + ", transport: "
@@ -1273,7 +1283,7 @@
                     slotId = iwlanDataServiceProvider.getSlotIndex();
                     boolean isNetworkLost =
                             !isNetworkConnected(
-                                    IwlanHelper.isActiveDataOnOtherSlot(mContext, slotId),
+                                    isActiveDataOnOtherSub(slotId),
                                     IwlanHelper.isCrossSimCallingEnabled(mContext, slotId));
                     boolean isHandOutSuccessful = (reason == REQUEST_REASON_HANDOVER);
 
@@ -1457,11 +1467,34 @@
         }
     }
 
+    static int getConnectedDataSub(NetworkCapabilities networkCapabilities) {
+        int connectedDataSub = INVALID_SUB_ID;
+        NetworkSpecifier specifier = networkCapabilities.getNetworkSpecifier();
+        TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+
+        if (specifier != null && specifier instanceof TelephonyNetworkSpecifier) {
+            connectedDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+        } else if (transportInfo != null && transportInfo instanceof VcnTransportInfo) {
+            connectedDataSub = ((VcnTransportInfo) transportInfo).getSubId();
+        }
+        return connectedDataSub;
+    }
+
+    static void setConnectedDataSub(int subId) {
+        mConnectedDataSub = subId;
+    }
+
     @VisibleForTesting
-    static boolean isNetworkConnected(boolean isActiveDataOnOtherSlot, boolean isCstEnabled) {
-        if (isActiveDataOnOtherSlot && isCstEnabled) {
+    static boolean isActiveDataOnOtherSub(int slotId) {
+        int subId = IwlanHelper.getSubId(mContext, slotId);
+        return mConnectedDataSub != INVALID_SUB_ID && subId != mConnectedDataSub;
+    }
+
+    @VisibleForTesting
+    static boolean isNetworkConnected(boolean isActiveDataOnOtherSub, boolean isCstEnabled) {
+        if (isActiveDataOnOtherSub && isCstEnabled) {
             // For cross-SIM IWLAN (Transport.MOBILE), an active data PDN must be maintained on the
-            // other slot.
+            // other subscription.
             if (sNetworkConnected && (sDefaultDataTransport != Transport.MOBILE)) {
                 Log.e(TAG, "Internet is on other slot, but default transport is not MOBILE!");
             }
diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java
index 34adddd..efda5b7 100644
--- a/src/com/google/android/iwlan/IwlanHelper.java
+++ b/src/com/google/android/iwlan/IwlanHelper.java
@@ -237,37 +237,6 @@
         return false;
     }
 
-    /**
-     * Gets the slot index associated with the current active data subscription.
-     *
-     * <p>The currently active data slot may be different from the default data slot. For instance,
-     * when the DDS is OOS or the nDDS is in a voice call, the data PDN may move to the nDDS.
-     *
-     * @param context application context
-     * @return slot index associated with the active data subscription, or
-     *     SubscriptionManager#INVALID_SIM_SLOT_INDEX if no active data PDN.
-     */
-    private static int getActiveDataSlot(Context context) {
-        SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
-        return sm.getSlotIndex(sm.getActiveDataSubscriptionId());
-    }
-
-    /**
-     * For dual-SIM UEs, determines if the active data PDN is on the other slot to the one provided.
-     *
-     * @param context application context
-     * @param slotIndex slot index associated with current subscription
-     * @return whether the other subscription has active data PDN.
-     */
-    public static boolean isActiveDataOnOtherSlot(Context context, int slotIndex) {
-        int activeDataSlot = getActiveDataSlot(context);
-        Log.d("apsankar:", slotIndex + " " + activeDataSlot);
-        if (activeDataSlot != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            return activeDataSlot != slotIndex;
-        }
-        return false;
-    }
-
     public static boolean isCrossSimCallingEnabled(Context context, int slotId) {
         boolean isCstEnabled = false;
         int subid = getSubId(context, slotId);
diff --git a/src/com/google/android/iwlan/IwlanNetworkService.java b/src/com/google/android/iwlan/IwlanNetworkService.java
index 5e87710..8c5820e 100644
--- a/src/com/google/android/iwlan/IwlanNetworkService.java
+++ b/src/com/google/android/iwlan/IwlanNetworkService.java
@@ -25,6 +25,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
@@ -49,7 +53,7 @@
 
 public class IwlanNetworkService extends NetworkService {
     private static final String TAG = IwlanNetworkService.class.getSimpleName();
-    private Context mContext;
+    private static Context mContext;
     private IwlanNetworkMonitorCallback mNetworkMonitorCallback;
     private IwlanOnSubscriptionsChangedListener mSubsChangeListener;
     private Handler mIwlanNetworkServiceHandler;
@@ -57,6 +61,11 @@
     private static boolean sNetworkConnected;
     private static final Map<Integer, IwlanNetworkServiceProvider> sIwlanNetworkServiceProviders =
             new ConcurrentHashMap<>();
+    private static final int INVALID_SUB_ID = -1;
+
+    // The current subscription with the active internet PDN. Need not be the default data sub.
+    // If internet is over WiFi, this value will be INVALID_SUB_ID.
+    private static int mConnectedDataSub = INVALID_SUB_ID;
 
     private static final int EVENT_BASE = IwlanEventListener.NETWORK_SERVICE_INTERNAL_EVENT_BASE;
     private static final int EVENT_NETWORK_REGISTRATION_INFO_REQUEST = EVENT_BASE;
@@ -99,6 +108,7 @@
         @Override
         public void onLost(Network network) {
             Log.d(TAG, "onLost: " + network);
+            IwlanNetworkService.setConnectedDataSub(INVALID_SUB_ID);
             IwlanNetworkService.setNetworkConnected(false, Transport.UNSPECIFIED_NETWORK);
         }
 
@@ -123,9 +133,12 @@
             Log.d(TAG, "onCapabilitiesChanged: " + network);
             if (networkCapabilities != null) {
                 if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+                    IwlanNetworkService.setConnectedDataSub(
+                            getConnectedDataSub(networkCapabilities));
                     IwlanNetworkService.setNetworkConnected(
                             true, IwlanNetworkService.Transport.MOBILE);
                 } else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+                    IwlanNetworkService.setConnectedDataSub(INVALID_SUB_ID);
                     IwlanNetworkService.setNetworkConnected(
                             true, IwlanNetworkService.Transport.WIFI);
                 } else {
@@ -264,7 +277,7 @@
 
                     slotId = iwlanNetworkServiceProvider.getSlotIndex();
                     if (!IwlanNetworkService.isNetworkConnected(
-                            IwlanHelper.isActiveDataOnOtherSlot(mContext, slotId),
+                            isActiveDataOnOtherSub(slotId),
                             IwlanHelper.isCrossSimCallingEnabled(mContext, slotId))) {
                         nriBuilder
                                 .setRegistrationState(
@@ -360,11 +373,32 @@
         return np;
     }
 
-    public static boolean isNetworkConnected(
-            boolean isActiveDataOnOtherSlot, boolean isCstEnabled) {
-        if (isActiveDataOnOtherSlot && isCstEnabled) {
+    static void setConnectedDataSub(int subId) {
+        mConnectedDataSub = subId;
+    }
+
+    static int getConnectedDataSub(NetworkCapabilities networkCapabilities) {
+        int connectedDataSub = INVALID_SUB_ID;
+        NetworkSpecifier specifier = networkCapabilities.getNetworkSpecifier();
+        TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+
+        if (specifier != null && specifier instanceof TelephonyNetworkSpecifier) {
+            connectedDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+        } else if (transportInfo != null && transportInfo instanceof VcnTransportInfo) {
+            connectedDataSub = ((VcnTransportInfo) transportInfo).getSubId();
+        }
+        return connectedDataSub;
+    }
+
+    static boolean isActiveDataOnOtherSub(int slotId) {
+        int subId = IwlanHelper.getSubId(mContext, slotId);
+        return mConnectedDataSub != INVALID_SUB_ID && subId != mConnectedDataSub;
+    }
+
+    public static boolean isNetworkConnected(boolean isActiveDataOnOtherSub, boolean isCstEnabled) {
+        if (isActiveDataOnOtherSub && isCstEnabled) {
             // For cross-SIM IWLAN (Transport.MOBILE), an active data PDN must be maintained on the
-            // other slot.
+            // other susbcription.
             if (sNetworkConnected && (sDefaultDataTransport != Transport.MOBILE)) {
                 Log.e(TAG, "Internet is on other slot, but default transport is not MOBILE!");
             }
@@ -457,6 +491,11 @@
     }
 
     @VisibleForTesting
+    IwlanNetworkMonitorCallback getNetworkMonitorCallback() {
+        return mNetworkMonitorCallback;
+    }
+
+    @VisibleForTesting
     void initHandler() {
         mIwlanNetworkServiceHandler = new IwlanNetworkServiceHandler(getLooper());
     }
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index f058b2c..5165d31 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -17,6 +17,7 @@
 package com.google.android.iwlan;
 
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 
@@ -31,6 +32,8 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnTransportInfo;
 import android.os.test.TestLooper;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.DataFailCause;
@@ -258,7 +261,7 @@
         verifyNetworkConnected(TRANSPORT_WIFI);
         assertTrue(
                 mIwlanDataService.isNetworkConnected(
-                        false /* isActiveDataOnOtherSlot */, false /* isCstEnabled */));
+                        false /* isActiveDataOnOtherSub */, false /* isCstEnabled */));
     }
 
     @Test
@@ -269,13 +272,62 @@
         verifyNetworkLost();
         assertFalse(
                 mIwlanDataService.isNetworkConnected(
-                        false /* isActiveDataOnOtherSlot */, false /* isCstEnabled */));
+                        false /* isActiveDataOnOtherSub */, false /* isCstEnabled */));
         verify(mMockIwlanDataServiceProvider).forceCloseTunnelsInDeactivatingState();
         mIwlanDataService.removeDataServiceProvider(mMockIwlanDataServiceProvider);
         mTestLooper.dispatchAll();
     }
 
     @Test
+    public void testNetworkNotConnectedWithCellularAndCrossSimDisabled()
+            throws InterruptedException {
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+        boolean isActiveDataOnOtherSub =
+                mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+        assertFalse(isActiveDataOnOtherSub);
+        assertFalse(
+                mIwlanDataService.isNetworkConnected(
+                        isActiveDataOnOtherSub, true /* isCstEnabled */));
+    }
+
+    @Test
+    public void testCrossSimNetworkConnectedWithTelephonyNetwork() throws InterruptedException {
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+        boolean isActiveDataOnOtherSub =
+                mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+        assertTrue(isActiveDataOnOtherSub);
+        assertTrue(
+                mIwlanDataService.isNetworkConnected(
+                        isActiveDataOnOtherSub, true /* isCstEnabled */));
+    }
+
+    @Test
+    public void testCrossSimNetworkConnectedWithVcn() throws InterruptedException {
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, true /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+        boolean isActiveDataOnOtherSub =
+                mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+        assertTrue(isActiveDataOnOtherSub);
+        assertTrue(
+                mIwlanDataService.isNetworkConnected(
+                        isActiveDataOnOtherSub, true /* isCstEnabled */));
+    }
+
+    @Test
     public void testAddDuplicateDataServiceProviderThrows() throws Exception {
         when(mMockIwlanDataServiceProvider.getSlotIndex()).thenReturn(DEFAULT_SLOT_INDEX);
         assertThrows(
@@ -727,17 +779,28 @@
         return dp;
     }
 
+    private NetworkCapabilities prepareNetworkCapabilitiesForTest(
+            int transportType, int subId, boolean isVcn) {
+        NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder().addTransportType(transportType);
+        if (isVcn) {
+            builder.setTransportInfo(new VcnTransportInfo(subId));
+        } else {
+            builder.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+        }
+        return builder.build();
+    }
+
     @Test
     public void testIwlanSetupDataCallFailsWithCellularAndCstDisabled() throws Exception {
         DataProfile dp = buildDataProfile();
-
-        /* Internet is connected through Mobile */
-        mIwlanDataService.setNetworkConnected(
-                true, mMockNetwork, IwlanDataService.Transport.MOBILE);
-
         /* CST is disabled, and data is on the same sub as the data service provider */
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
-        when(mMockSubscriptionManager.getActiveDataSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX);
+
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
 
         mIwlanDataServiceProvider.setupDataCall(
                 AccessNetworkType.IWLAN, /* AccessNetworkType */
@@ -763,13 +826,13 @@
     public void testIwlanSetupDataCallFailsWithCellularOnSameSubAndCstEnabled() throws Exception {
         DataProfile dp = buildDataProfile();
 
-        /* Internet is connected through Mobile */
-        mIwlanDataService.setNetworkConnected(
-                true, mMockNetwork, IwlanDataService.Transport.MOBILE);
-
         /* CST is enabled, but data is on the same sub as the DataServiceProvider */
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
-        when(mMockSubscriptionManager.getActiveDataSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX);
+
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
 
         mSpyIwlanDataServiceProvider.setupDataCall(
                 AccessNetworkType.IWLAN, /* AccessNetworkType */
@@ -796,14 +859,13 @@
             throws Exception {
         DataProfile dp = buildDataProfile();
 
-        /* Internet is connected through Mobile */
-        mIwlanDataService.setNetworkConnected(
-                true, mMockNetwork, IwlanDataService.Transport.MOBILE);
-
-        /* CST is enabled, and data is on a different sub than the DataServiceProvider */
+        /* CST is enabled, but data is on the same sub as the DataServiceProvider */
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
-        when(mMockSubscriptionManager.getActiveDataSubscriptionId())
-                .thenReturn(DEFAULT_SUB_INDEX + 1);
+
+        NetworkCapabilities nc =
+                prepareNetworkCapabilitiesForTest(
+                        TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */);
+        mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
 
         doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
 
diff --git a/test/com/google/android/iwlan/IwlanNetworkServiceTest.java b/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
index 3164bf2..0633900 100644
--- a/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
@@ -24,6 +24,10 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnTransportInfo;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.INetworkService;
 import android.telephony.INetworkServiceCallback;
@@ -59,6 +63,7 @@
     @Mock private ImsManager mMockImsManager;
     @Mock private ImsMmTelManager mMockImsMmTelManager;
     @Mock private INetworkServiceCallback mCallback;
+    @Mock private Network mMockNetwork;
     MockitoSession mStaticMockSession;
 
     IwlanNetworkService mIwlanNetworkService;
@@ -153,6 +158,25 @@
                         eq(expectedStateBuilder.build()));
     }
 
+    private NetworkCapabilities prepareCellularNetworkCapabilitiesForTest(
+            int subId, boolean isVcn) {
+        NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        if (isVcn) {
+            builder.setTransportInfo(new VcnTransportInfo(subId));
+        } else {
+            builder.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+        }
+        return builder.build();
+    }
+
+    private NetworkCapabilities prepareWifiNetworkCapabilitiesForTest() {
+        return new NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
+    }
+
     @Test
     public void testNetworkRegistrationInfoSearchingForCellularAndCstDisabled() throws Exception {
         mIwlanNetworkServiceProvider = initNSP();
@@ -160,7 +184,9 @@
 
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
 
-        mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.MOBILE);
+        NetworkCapabilities nc =
+                prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX, false /* is Vcn */);
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
         mIwlanNetworkServiceProvider.subscriptionChanged();
 
         // Create expected NetworkRegistrationInfo
@@ -186,7 +212,9 @@
 
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
 
-        mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.MOBILE);
+        NetworkCapabilities nc =
+                prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX, false /* is Vcn */);
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
         mIwlanNetworkServiceProvider.subscriptionChanged();
 
         // Create expected NetworkRegistrationInfo
@@ -211,10 +239,41 @@
         assertTrue(mIwlanNetworkServiceProvider != null);
 
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
-        when(mMockSubscriptionManager.getActiveDataSubscriptionId())
-                .thenReturn(DEFAULT_SUB_INDEX + 1);
 
-        mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.MOBILE);
+        // Cellular data is on the other sub
+        NetworkCapabilities nc =
+                prepareCellularNetworkCapabilitiesForTest(
+                        DEFAULT_SUB_INDEX + 1, false /* is Vcn */);
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+        mIwlanNetworkServiceProvider.subscriptionChanged();
+
+        // Create expected NetworkRegistrationInfo
+        NetworkRegistrationInfo.Builder expectedStateBuilder =
+                generateStateBuilder(
+                        NetworkRegistrationInfo.DOMAIN_PS,
+                        true /* isSubActive */,
+                        NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+        mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+
+        verify(mCallback, timeout(1000).times(1))
+                .onRequestNetworkRegistrationInfoComplete(
+                        eq(NetworkServiceCallback.RESULT_SUCCESS),
+                        eq(expectedStateBuilder.build()));
+    }
+
+    @Test
+    public void testNetworkRegistrationInfoHomeForCellularVcnOnDifferentSubAndCstEnabled()
+            throws Exception {
+        mIwlanNetworkServiceProvider = initNSP();
+        assertTrue(mIwlanNetworkServiceProvider != null);
+
+        when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
+
+        // Cellular data as a VCN network is on the other sub
+        NetworkCapabilities nc =
+                prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX + 1, true /* is Vcn */);
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
         mIwlanNetworkServiceProvider.subscriptionChanged();
 
         // Create expected NetworkRegistrationInfo
@@ -239,7 +298,8 @@
 
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
 
-        mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.WIFI);
+        NetworkCapabilities nc = prepareWifiNetworkCapabilitiesForTest();
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
         mIwlanNetworkServiceProvider.subscriptionChanged();
 
         // Create expected NetworkRegistrationInfo
@@ -264,7 +324,9 @@
 
         when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
 
-        mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.WIFI);
+        NetworkCapabilities nc = prepareWifiNetworkCapabilitiesForTest();
+        mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
         mIwlanNetworkServiceProvider.subscriptionChanged();
 
         // Create expected NetworkRegistrationInfo