Loading core/java/android/net/vcn/VcnTransportInfo.java +43 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.net.vcn; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.NetworkCapabilities; Loading @@ -29,6 +31,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.SubscriptionManager; import com.android.internal.util.Preconditions; import java.util.Objects; /** Loading @@ -47,6 +51,7 @@ import java.util.Objects; * * @hide */ // TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore public class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; Loading Loading @@ -195,4 +200,42 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return new VcnTransportInfo[size]; } }; /** This class can be used to construct a {@link VcnTransportInfo}. */ public static final class Builder { private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; /** Construct Builder */ public Builder() {} /** * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout. * * <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. * * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN * Gateway. * @return this {@link Builder} instance, for chaining */ @NonNull public Builder setMinUdpPort4500NatTimeoutSeconds( @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, "Timeout must be at least 120s"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return Builder.this; } /** Build a VcnTransportInfo instance */ @NonNull public VcnTransportInfo build() { return new VcnTransportInfo( null /* wifiInfo */, INVALID_SUBSCRIPTION_ID, mMinUdpPort4500NatTimeoutSeconds); } } } core/java/android/net/vcn/VcnUtils.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkSpecifier; import android.net.TelephonyNetworkSpecifier; import android.net.TransportInfo; import android.net.wifi.WifiInfo; import java.util.List; /** * Utility class for VCN callers get information from VCN network * * @hide */ public class VcnUtils { /** Get the WifiInfo of the VCN's underlying WiFi network */ @Nullable public static WifiInfo getWifiInfoFromVcnCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { final NetworkCapabilities underlyingCaps = getVcnUnderlyingCaps(connectivityMgr, networkCapabilities); if (underlyingCaps == null) { return null; } final TransportInfo underlyingTransportInfo = underlyingCaps.getTransportInfo(); if (!(underlyingTransportInfo instanceof WifiInfo)) { return null; } return (WifiInfo) underlyingTransportInfo; } /** Get the subscription ID of the VCN's underlying Cell network */ public static int getSubIdFromVcnCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { final NetworkCapabilities underlyingCaps = getVcnUnderlyingCaps(connectivityMgr, networkCapabilities); if (underlyingCaps == null) { return INVALID_SUBSCRIPTION_ID; } final NetworkSpecifier underlyingNetworkSpecifier = underlyingCaps.getNetworkSpecifier(); if (!(underlyingNetworkSpecifier instanceof TelephonyNetworkSpecifier)) { return INVALID_SUBSCRIPTION_ID; } return ((TelephonyNetworkSpecifier) underlyingNetworkSpecifier).getSubscriptionId(); } @Nullable private static NetworkCapabilities getVcnUnderlyingCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { // Return null if it is not a VCN network if (networkCapabilities.getTransportInfo() == null || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) { return null; } // As of Android 16, VCN has one underlying network, and only one. If there are more // than one networks due to future changes in the VCN mainline code, just take the first // network final List<Network> underlyingNws = networkCapabilities.getUnderlyingNetworks(); if (underlyingNws == null) { return null; } return connectivityMgr.getNetworkCapabilities(underlyingNws.get(0)); } } tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +23 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import android.net.NetworkCapabilities; import android.net.wifi.WifiConfiguration; Loading @@ -39,6 +40,7 @@ public class VcnTransportInfoTest { private static final int SUB_ID = 1; private static final int NETWORK_ID = 5; private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120; private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID = 119; private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().setNetworkId(NETWORK_ID).build(); Loading @@ -47,6 +49,27 @@ public class VcnTransportInfoTest { private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT); @Test public void testBuilder() { final VcnTransportInfo transportInfo = new VcnTransportInfo.Builder() .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT) .build(); assertEquals( MIN_UDP_PORT_4500_NAT_TIMEOUT, transportInfo.getMinUdpPort4500NatTimeoutSeconds()); } @Test public void testBuilder_withInvalidNatTimeout() { try { new VcnTransportInfo.Builder() .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID); fail("Expected to fail due to invalid NAT timeout"); } catch (Exception expected) { } } @Test public void testGetWifiInfo() { assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); Loading tests/vcn/java/android/net/vcn/VcnUtilsTest.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.wifi.WifiInfo; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import java.util.Collections; public class VcnUtilsTest { private static final int SUB_ID = 1; private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().build(); private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); private static final VcnTransportInfo VCN_TRANSPORT_INFO = new VcnTransportInfo.Builder().build(); private ConnectivityManager mMockConnectivityManager; private Network mMockWifiNetwork; private Network mMockCellNetwork; private NetworkCapabilities mVcnCapsWithUnderlyingWifi; private NetworkCapabilities mVcnCapsWithUnderlyingCell; @Before public void setUp() { mMockConnectivityManager = mock(ConnectivityManager.class); mMockWifiNetwork = mock(Network.class); mVcnCapsWithUnderlyingWifi = newVcnCaps(VCN_TRANSPORT_INFO, mMockWifiNetwork); final NetworkCapabilities wifiCaps = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_WIFI) .setTransportInfo(WIFI_INFO) .build(); when(mMockConnectivityManager.getNetworkCapabilities(mMockWifiNetwork)) .thenReturn(wifiCaps); mMockCellNetwork = mock(Network.class); mVcnCapsWithUnderlyingCell = newVcnCaps(VCN_TRANSPORT_INFO, mMockCellNetwork); final NetworkCapabilities cellCaps = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_CELLULAR) .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) .build(); when(mMockConnectivityManager.getNetworkCapabilities(mMockCellNetwork)) .thenReturn(cellCaps); } private static NetworkCapabilities newVcnCaps( VcnTransportInfo vcnTransportInfo, Network underlyingNetwork) { return new NetworkCapabilities.Builder() .setTransportInfo(vcnTransportInfo) .setUnderlyingNetworks(Collections.singletonList(underlyingNetwork)) .build(); } @Test public void getWifiInfoFromVcnCaps() { assertEquals( WIFI_INFO, VcnUtils.getWifiInfoFromVcnCaps( mMockConnectivityManager, mVcnCapsWithUnderlyingWifi)); } @Test public void getWifiInfoFromVcnCaps_onVcnWithUnderlyingCell() { assertNull( VcnUtils.getWifiInfoFromVcnCaps( mMockConnectivityManager, mVcnCapsWithUnderlyingCell)); } @Test public void getSubIdFromVcnCaps() { assertEquals( SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingCell)); } @Test public void getSubIdFromVcnCaps_onVcnWithUnderlyingWifi() { assertEquals( INVALID_SUBSCRIPTION_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingWifi)); } @Test public void getSubIdFromVcnCaps_onNonVcnNetwork() { assertEquals( INVALID_SUBSCRIPTION_ID, VcnUtils.getSubIdFromVcnCaps( mMockConnectivityManager, new NetworkCapabilities.Builder().build())); } @Test public void getSubIdFromVcnCaps_withMultipleUnderlyingNetworks() { final NetworkCapabilities vcnCaps = new NetworkCapabilities.Builder(mVcnCapsWithUnderlyingCell) .setUnderlyingNetworks( Arrays.asList( new Network[] {mMockCellNetwork, mock(Network.class)})) .build(); assertEquals(SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, vcnCaps)); } } Loading
core/java/android/net/vcn/VcnTransportInfo.java +43 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.net.vcn; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.NetworkCapabilities; Loading @@ -29,6 +31,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.SubscriptionManager; import com.android.internal.util.Preconditions; import java.util.Objects; /** Loading @@ -47,6 +51,7 @@ import java.util.Objects; * * @hide */ // TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore public class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; Loading Loading @@ -195,4 +200,42 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return new VcnTransportInfo[size]; } }; /** This class can be used to construct a {@link VcnTransportInfo}. */ public static final class Builder { private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; /** Construct Builder */ public Builder() {} /** * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout. * * <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. * * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN * Gateway. * @return this {@link Builder} instance, for chaining */ @NonNull public Builder setMinUdpPort4500NatTimeoutSeconds( @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, "Timeout must be at least 120s"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return Builder.this; } /** Build a VcnTransportInfo instance */ @NonNull public VcnTransportInfo build() { return new VcnTransportInfo( null /* wifiInfo */, INVALID_SUBSCRIPTION_ID, mMinUdpPort4500NatTimeoutSeconds); } } }
core/java/android/net/vcn/VcnUtils.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkSpecifier; import android.net.TelephonyNetworkSpecifier; import android.net.TransportInfo; import android.net.wifi.WifiInfo; import java.util.List; /** * Utility class for VCN callers get information from VCN network * * @hide */ public class VcnUtils { /** Get the WifiInfo of the VCN's underlying WiFi network */ @Nullable public static WifiInfo getWifiInfoFromVcnCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { final NetworkCapabilities underlyingCaps = getVcnUnderlyingCaps(connectivityMgr, networkCapabilities); if (underlyingCaps == null) { return null; } final TransportInfo underlyingTransportInfo = underlyingCaps.getTransportInfo(); if (!(underlyingTransportInfo instanceof WifiInfo)) { return null; } return (WifiInfo) underlyingTransportInfo; } /** Get the subscription ID of the VCN's underlying Cell network */ public static int getSubIdFromVcnCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { final NetworkCapabilities underlyingCaps = getVcnUnderlyingCaps(connectivityMgr, networkCapabilities); if (underlyingCaps == null) { return INVALID_SUBSCRIPTION_ID; } final NetworkSpecifier underlyingNetworkSpecifier = underlyingCaps.getNetworkSpecifier(); if (!(underlyingNetworkSpecifier instanceof TelephonyNetworkSpecifier)) { return INVALID_SUBSCRIPTION_ID; } return ((TelephonyNetworkSpecifier) underlyingNetworkSpecifier).getSubscriptionId(); } @Nullable private static NetworkCapabilities getVcnUnderlyingCaps( @NonNull ConnectivityManager connectivityMgr, @NonNull NetworkCapabilities networkCapabilities) { // Return null if it is not a VCN network if (networkCapabilities.getTransportInfo() == null || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) { return null; } // As of Android 16, VCN has one underlying network, and only one. If there are more // than one networks due to future changes in the VCN mainline code, just take the first // network final List<Network> underlyingNws = networkCapabilities.getUnderlyingNetworks(); if (underlyingNws == null) { return null; } return connectivityMgr.getNetworkCapabilities(underlyingNws.get(0)); } }
tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +23 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import android.net.NetworkCapabilities; import android.net.wifi.WifiConfiguration; Loading @@ -39,6 +40,7 @@ public class VcnTransportInfoTest { private static final int SUB_ID = 1; private static final int NETWORK_ID = 5; private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120; private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID = 119; private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().setNetworkId(NETWORK_ID).build(); Loading @@ -47,6 +49,27 @@ public class VcnTransportInfoTest { private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT); @Test public void testBuilder() { final VcnTransportInfo transportInfo = new VcnTransportInfo.Builder() .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT) .build(); assertEquals( MIN_UDP_PORT_4500_NAT_TIMEOUT, transportInfo.getMinUdpPort4500NatTimeoutSeconds()); } @Test public void testBuilder_withInvalidNatTimeout() { try { new VcnTransportInfo.Builder() .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID); fail("Expected to fail due to invalid NAT timeout"); } catch (Exception expected) { } } @Test public void testGetWifiInfo() { assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); Loading
tests/vcn/java/android/net/vcn/VcnUtilsTest.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.wifi.WifiInfo; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import java.util.Collections; public class VcnUtilsTest { private static final int SUB_ID = 1; private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().build(); private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); private static final VcnTransportInfo VCN_TRANSPORT_INFO = new VcnTransportInfo.Builder().build(); private ConnectivityManager mMockConnectivityManager; private Network mMockWifiNetwork; private Network mMockCellNetwork; private NetworkCapabilities mVcnCapsWithUnderlyingWifi; private NetworkCapabilities mVcnCapsWithUnderlyingCell; @Before public void setUp() { mMockConnectivityManager = mock(ConnectivityManager.class); mMockWifiNetwork = mock(Network.class); mVcnCapsWithUnderlyingWifi = newVcnCaps(VCN_TRANSPORT_INFO, mMockWifiNetwork); final NetworkCapabilities wifiCaps = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_WIFI) .setTransportInfo(WIFI_INFO) .build(); when(mMockConnectivityManager.getNetworkCapabilities(mMockWifiNetwork)) .thenReturn(wifiCaps); mMockCellNetwork = mock(Network.class); mVcnCapsWithUnderlyingCell = newVcnCaps(VCN_TRANSPORT_INFO, mMockCellNetwork); final NetworkCapabilities cellCaps = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_CELLULAR) .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) .build(); when(mMockConnectivityManager.getNetworkCapabilities(mMockCellNetwork)) .thenReturn(cellCaps); } private static NetworkCapabilities newVcnCaps( VcnTransportInfo vcnTransportInfo, Network underlyingNetwork) { return new NetworkCapabilities.Builder() .setTransportInfo(vcnTransportInfo) .setUnderlyingNetworks(Collections.singletonList(underlyingNetwork)) .build(); } @Test public void getWifiInfoFromVcnCaps() { assertEquals( WIFI_INFO, VcnUtils.getWifiInfoFromVcnCaps( mMockConnectivityManager, mVcnCapsWithUnderlyingWifi)); } @Test public void getWifiInfoFromVcnCaps_onVcnWithUnderlyingCell() { assertNull( VcnUtils.getWifiInfoFromVcnCaps( mMockConnectivityManager, mVcnCapsWithUnderlyingCell)); } @Test public void getSubIdFromVcnCaps() { assertEquals( SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingCell)); } @Test public void getSubIdFromVcnCaps_onVcnWithUnderlyingWifi() { assertEquals( INVALID_SUBSCRIPTION_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingWifi)); } @Test public void getSubIdFromVcnCaps_onNonVcnNetwork() { assertEquals( INVALID_SUBSCRIPTION_ID, VcnUtils.getSubIdFromVcnCaps( mMockConnectivityManager, new NetworkCapabilities.Builder().build())); } @Test public void getSubIdFromVcnCaps_withMultipleUnderlyingNetworks() { final NetworkCapabilities vcnCaps = new NetworkCapabilities.Builder(mVcnCapsWithUnderlyingCell) .setUnderlyingNetworks( Arrays.asList( new Network[] {mMockCellNetwork, mock(Network.class)})) .build(); assertEquals(SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, vcnCaps)); } }