Support selecting non-INTERNET networks as underlying networks

This commit allows VCN to select underlying Cell networks
according to caller configured network capabilities instead
of always only selecting INTERNET networks as underlying
networks.

This commit also makes sure that if all of the network candidates
fail to match caller configured network templates, VCN can still
select an INTERNET network as a fallback but will never select
a non-INTERNET network.

Bug: 245618320
Test: FrameworksVcnTests(new tests), CtsVcnTestCases
Change-Id: I6eca0331e4bc6804a500fbed9909bb91b1e80d2d
This commit is contained in:
Yan Yan
2022-11-19 00:18:42 +00:00
parent 4153670ea2
commit 4b3eb31c80
7 changed files with 519 additions and 58 deletions

View File

@@ -353,6 +353,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork
return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_RCS);
}
/** @hide */
public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
}
@Override
public int hashCode() {
return Objects.hash(

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.vcn.routeselection;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,6 +46,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription
import com.android.server.vcn.VcnContext;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -69,9 +71,23 @@ class NetworkPriorityClassifier {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
/** Priority for any other networks (including unvalidated, etc) */
/**
* Priority for networks that VCN can fall back to.
*
* <p>If none of the network candidates are validated or match any template, VCN will fall back
* to any INTERNET network.
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int PRIORITY_ANY = Integer.MAX_VALUE;
static final int PRIORITY_FALLBACK = Integer.MAX_VALUE;
/**
* Priority for networks that cannot be selected as VCN's underlying networks.
*
* <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
* template as the underlying network.
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int PRIORITY_INVALID = -1;
/** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
public static int calculatePriorityClass(
@@ -86,12 +102,12 @@ class NetworkPriorityClassifier {
if (networkRecord.isBlocked) {
logWtf("Network blocked for System Server: " + networkRecord.network);
return PRIORITY_ANY;
return PRIORITY_INVALID;
}
if (snapshot == null) {
logWtf("Got null snapshot");
return PRIORITY_ANY;
return PRIORITY_INVALID;
}
int priorityIndex = 0;
@@ -108,7 +124,13 @@ class NetworkPriorityClassifier {
}
priorityIndex++;
}
return PRIORITY_ANY;
final NetworkCapabilities caps = networkRecord.networkCapabilities;
if (caps.hasCapability(NET_CAPABILITY_INTERNET)
|| (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST))) {
return PRIORITY_FALLBACK;
}
return PRIORITY_INVALID;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -297,6 +319,18 @@ class NetworkPriorityClassifier {
return false;
}
for (Map.Entry<Integer, Integer> entry :
networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
final int cap = entry.getKey();
final int matchCriteria = entry.getValue();
if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
return false;
} else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
return false;
}
}
return true;
}

View File

@@ -16,6 +16,9 @@
package com.android.server.vcn.routeselection;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -32,6 +35,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
@@ -40,6 +44,7 @@ import android.os.ParcelUuid;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +54,7 @@ import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +132,63 @@ public class UnderlyingNetworkController {
registerOrUpdateNetworkRequests();
}
private static class CapabilityMatchCriteria {
public final int capability;
public final int matchCriteria;
CapabilityMatchCriteria(int capability, int matchCriteria) {
this.capability = capability;
this.matchCriteria = matchCriteria;
}
@Override
public int hashCode() {
return Objects.hash(capability, matchCriteria);
}
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof CapabilityMatchCriteria)) {
return false;
}
final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
}
}
private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
VcnGatewayConnectionConfig connectionConfig) {
final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();
for (VcnUnderlyingNetworkTemplate template :
connectionConfig.getVcnUnderlyingNetworkPriorities()) {
if (template instanceof VcnCellUnderlyingNetworkTemplate) {
final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();
for (Map.Entry<Integer, Integer> entry :
((VcnCellUnderlyingNetworkTemplate) template)
.getCapabilitiesMatchCriteria()
.entrySet()) {
final int capability = entry.getKey();
final int matchCriteria = entry.getValue();
if (matchCriteria != MATCH_ANY) {
capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
}
}
dedupedCapsMatchSets.add(capsMatchSet);
}
}
dedupedCapsMatchSets.add(
Collections.singleton(
new CapabilityMatchCriteria(
NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
return dedupedCapsMatchSets;
}
private void registerOrUpdateNetworkRequests() {
NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
NetworkCallback oldWifiCallback = mWifiBringupCallback;
@@ -158,11 +221,14 @@ public class UnderlyingNetworkController {
getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
final NetworkBringupCallback cb = new NetworkBringupCallback();
mCellBringupCallbacks.add(cb);
for (Set<CapabilityMatchCriteria> capsMatchCriteria :
dedupAndGetCapRequirementsForCell(mConnectionConfig)) {
final NetworkBringupCallback cb = new NetworkBringupCallback();
mCellBringupCallbacks.add(cb);
mConnectivityManager.requestBackgroundNetwork(
getCellNetworkRequestForSubId(subId), cb, mHandler);
mConnectivityManager.requestBackgroundNetwork(
getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler);
}
}
} else {
mRouteSelectionCallback = null;
@@ -214,6 +280,13 @@ public class UnderlyingNetworkController {
.build();
}
private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
}
/**
* Builds the WiFi bringup request
*
@@ -224,10 +297,7 @@ public class UnderlyingNetworkController {
* but will NEVER bring up a Carrier WiFi network itself.
*/
private NetworkRequest getWifiNetworkRequest() {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
return getBaseWifiNetworkRequestBuilder().build();
}
/**
@@ -238,9 +308,7 @@ public class UnderlyingNetworkController {
* pace to effectively select a short-lived WiFi offload network.
*/
private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
.build();
@@ -254,9 +322,7 @@ public class UnderlyingNetworkController {
* pace to effectively select away from a failing WiFi network.
*/
private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
.build();
@@ -273,11 +339,25 @@ public class UnderlyingNetworkController {
* <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
* in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
*/
private NetworkRequest getCellNetworkRequestForSubId(int subId) {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
private NetworkRequest getCellNetworkRequestForSubId(
int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) {
final NetworkRequest.Builder nrBuilder =
getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
final int cap = capMatchCriteria.capability;
final int matchCriteria = capMatchCriteria.matchCriteria;
if (matchCriteria == MATCH_REQUIRED) {
nrBuilder.addCapability(cap);
} else if (matchCriteria == MATCH_FORBIDDEN) {
nrBuilder.addForbiddenCapability(cap);
}
}
return nrBuilder.build();
}
/**
@@ -285,7 +365,6 @@ public class UnderlyingNetworkController {
*/
private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
return new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -406,7 +485,9 @@ public class UnderlyingNetworkController {
mLastSnapshot,
mCurrentRecord,
mCarrierConfig);
sorted.add(record);
if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
sorted.add(record);
}
}
}

View File

@@ -32,7 +32,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
private static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
// Public for use in UnderlyingNetworkControllerTest
public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
.setMetered(MATCH_FORBIDDEN)
.setMinUpstreamBandwidthKbps(

View File

@@ -100,14 +100,20 @@ public class VcnGatewayConnectionConfigTest {
EXPOSED_CAPS);
}
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
// Public for use in UnderlyingNetworkControllerTest
public static VcnGatewayConnectionConfig buildTestConfig(
List<VcnUnderlyingNetworkTemplate> nwTemplates) {
final VcnGatewayConnectionConfig.Builder builder =
newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
newBuilder().setVcnUnderlyingNetworkPriorities(nwTemplates);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
}
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).

View File

@@ -16,6 +16,7 @@
package com.android.server.vcn.routeselection;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
@@ -24,7 +25,8 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
@@ -64,6 +66,7 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -103,6 +106,7 @@ public class NetworkPriorityClassifierTest {
private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
@@ -501,24 +505,72 @@ public class NetworkPriorityClassifierTest {
mSubscriptionSnapshot));
}
private void verifyMatchCellWithRequiredCapabilities(
VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
assertEquals(
expectMatch,
checkMatchesCellPriorityRule(
mVcnContext,
template,
mCellNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot));
}
@Test
public void testMatchCell() {
final VcnCellUnderlyingNetworkTemplate template =
getCellNetworkPriorityBuilder().setInternet(MATCH_REQUIRED).build();
verifyMatchCellWithRequiredCapabilities(template, true /* expectMatch */);
}
@Test
public void testMatchCellFail_RequiredCapabilitiesMissing() {
final VcnCellUnderlyingNetworkTemplate template =
getCellNetworkPriorityBuilder().setCbs(MATCH_REQUIRED).build();
verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
}
@Test
public void testMatchCellFail_ForbiddenCapabilitiesFound() {
final VcnCellUnderlyingNetworkTemplate template =
getCellNetworkPriorityBuilder().setDun(MATCH_FORBIDDEN).build();
verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
}
@Test
public void testCalculatePriorityClass() throws Exception {
assertEquals(2, mCellNetworkRecord.priorityClass);
}
@Test
public void testCalculatePriorityClassFailToMatchAny() throws Exception {
final NetworkCapabilities nc =
private void checkCalculatePriorityClassFailToMatchAny(
boolean hasInternet, int expectedPriorityClass) throws Exception {
final List<VcnUnderlyingNetworkTemplate> templatesRequireDun =
Collections.singletonList(
new VcnCellUnderlyingNetworkTemplate.Builder()
.setDun(MATCH_REQUIRED)
.build());
final NetworkCapabilities.Builder ncBuilder =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSignalStrength(WIFI_RSSI_LOW)
.setSsid(SSID)
.build();
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (hasInternet) {
ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
}
final UnderlyingNetworkRecord wifiNetworkRecord =
getTestNetworkRecord(
nc, VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
final UnderlyingNetworkRecord nonDunNetworkRecord =
getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
assertEquals(PRIORITY_ANY, wifiNetworkRecord.priorityClass);
assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
}
@Test
public void testCalculatePriorityClassFailToMatchAny_InternetNetwork() throws Exception {
checkCalculatePriorityClassFailToMatchAny(true /* hasInternet */, PRIORITY_FALLBACK);
}
@Test
public void testCalculatePriorityClassFailToMatchAny_NonInternetNetwork() throws Exception {
checkCalculatePriorityClassFailToMatchAny(false /* hasInternet */, PRIORITY_INVALID);
}
}

View File

@@ -16,6 +16,14 @@
package com.android.server.vcn.routeselection;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
@@ -30,6 +38,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -44,7 +53,10 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -66,7 +78,10 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -97,11 +112,39 @@ public class UnderlyingNetworkControllerTest {
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
private static final NetworkCapabilities DUN_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.build();
private static final NetworkCapabilities CBS_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.build();
private static final LinkProperties INITIAL_LINK_PROPERTIES =
getLinkPropertiesWithName("initial_iface");
private static final LinkProperties UPDATED_LINK_PROPERTIES =
getLinkPropertiesWithName("updated_iface");
private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_DUN =
new VcnCellUnderlyingNetworkTemplate.Builder()
.setInternet(MATCH_ANY)
.setDun(MATCH_REQUIRED)
.build();
private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_CBS =
new VcnCellUnderlyingNetworkTemplate.Builder()
.setInternet(MATCH_ANY)
.setCbs(MATCH_REQUIRED)
.build();
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
@@ -203,6 +246,107 @@ public class UnderlyingNetworkControllerTest {
any());
}
private void verifyRequestBackgroundNetwork(
ConnectivityManager cm,
int expectedSubId,
Set<Integer> expectedRequiredCaps,
Set<Integer> expectedForbiddenCaps) {
verify(cm)
.requestBackgroundNetwork(
eq(
getCellRequestForSubId(
expectedSubId,
expectedRequiredCaps,
expectedForbiddenCaps)),
any(NetworkBringupCallback.class),
any());
}
@Test
public void testNetworkCallbacksRegisteredOnStartupForNonInternetCapabilities() {
final ConnectivityManager cm = mock(ConnectivityManager.class);
setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
// Build network templates
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(
VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
.setDun(MATCH_REQUIRED)
.setInternet(MATCH_ANY)
.build());
networkTemplates.add(
VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
.setMms(MATCH_REQUIRED)
.setCbs(MATCH_FORBIDDEN)
.setInternet(MATCH_ANY)
.build());
// Start UnderlyingNetworkController
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
// Verifications
for (final int subId : INITIAL_SUB_IDS) {
verifyRequestBackgroundNetwork(
cm,
subId,
Collections.singleton(NET_CAPABILITY_INTERNET),
Collections.emptySet());
verifyRequestBackgroundNetwork(
cm, subId, Collections.singleton(NET_CAPABILITY_DUN), Collections.emptySet());
verifyRequestBackgroundNetwork(
cm,
subId,
Collections.singleton(NET_CAPABILITY_MMS),
Collections.singleton(NET_CAPABILITY_CBS));
}
}
@Test
public void testNetworkCallbacksRegisteredOnStartupWithDedupedtCapabilities() {
final ConnectivityManager cm = mock(ConnectivityManager.class);
setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
// Build network templates
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
final VcnCellUnderlyingNetworkTemplate.Builder builder =
new VcnCellUnderlyingNetworkTemplate.Builder()
.setMms(MATCH_REQUIRED)
.setCbs(MATCH_FORBIDDEN)
.setInternet(MATCH_ANY);
networkTemplates.add(builder.setMetered(MATCH_REQUIRED).build());
networkTemplates.add(builder.setMetered(MATCH_FORBIDDEN).build());
// Start UnderlyingNetworkController
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
// Verifications
for (final int subId : INITIAL_SUB_IDS) {
verifyRequestBackgroundNetwork(
cm,
subId,
Collections.singleton(NET_CAPABILITY_INTERNET),
Collections.emptySet());
verifyRequestBackgroundNetwork(
cm,
subId,
Collections.singleton(NET_CAPABILITY_MMS),
Collections.singleton(NET_CAPABILITY_CBS));
}
}
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -212,8 +356,13 @@ public class UnderlyingNetworkControllerTest {
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getCellRequestForSubId(subId)),
any(NetworkBringupCallback.class), any());
eq(
getCellRequestForSubId(
subId,
Collections.singleton(NET_CAPABILITY_INTERNET),
Collections.emptySet())),
any(NetworkBringupCallback.class),
any());
}
verify(mConnectivityManager)
@@ -255,6 +404,7 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.build();
}
@@ -263,6 +413,7 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
@@ -272,16 +423,27 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
private NetworkRequest getCellRequestForSubId(int subId) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
private NetworkRequest getCellRequestForSubId(
int subId, Set<Integer> requiredCaps, Set<Integer> forbiddenCaps) {
final NetworkRequest.Builder nqBuilder =
getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
for (int cap : requiredCaps) {
nqBuilder.addCapability(cap);
}
for (int cap : forbiddenCaps) {
nqBuilder.addForbiddenCapability(cap);
}
return nqBuilder.build();
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
@@ -303,7 +465,6 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -506,13 +667,7 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
UnderlyingNetworkRecord expectedRecord =
getTestNetworkRecord(
mNetwork,
buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
INITIAL_LINK_PROPERTIES,
true /* isBlocked */);
verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
verifyOnSelectedUnderlyingNetworkChanged(null);
}
@Test
@@ -548,5 +703,132 @@ public class UnderlyingNetworkControllerTest {
verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
// TODO (b/187991063): Add tests for network prioritization
private UnderlyingNetworkListener setupControllerAndGetNetworkListener(
List<VcnUnderlyingNetworkTemplate> networkTemplates) {
final ConnectivityManager cm = mock(ConnectivityManager.class);
setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
SUB_GROUP,
mSubscriptionSnapshot,
mNetworkControllerCb);
verify(cm)
.registerNetworkCallback(
eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
mUnderlyingNetworkListenerCaptor.capture(),
any());
return mUnderlyingNetworkListenerCaptor.getValue();
}
private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
UnderlyingNetworkListener cb,
NetworkCapabilities requestNetworkCaps,
List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
UnderlyingNetworkRecord currentlySelected) {
final Network network = mock(Network.class);
final NetworkCapabilities responseNetworkCaps =
buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
cb.onAvailable(network);
cb.onCapabilitiesChanged(network, responseNetworkCaps);
cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
cb.onBlockedStatusChanged(network, false /* isFalse */);
return new UnderlyingNetworkRecord(
network,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */,
mVcnContext,
underlyingNetworkTemplates,
SUB_GROUP,
mSubscriptionSnapshot,
currentlySelected,
null /* carrierConfig */);
}
@Test
public void testSelectMorePreferredNetwork() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(CELL_TEMPLATE_DUN);
networkTemplates.add(CELL_TEMPLATE_CBS);
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up CBS network
final UnderlyingNetworkRecord cbsNetworkRecord =
bringupNetworkAndGetRecord(
cb,
CBS_NETWORK_CAPABILITIES,
networkTemplates,
null /* currentlySelected */);
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
// Bring up DUN network
final UnderlyingNetworkRecord dunNetworkRecord =
bringupNetworkAndGetRecord(
cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
}
@Test
public void testNeverSelectLessPreferredNetwork() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(CELL_TEMPLATE_DUN);
networkTemplates.add(CELL_TEMPLATE_CBS);
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up DUN network
final UnderlyingNetworkRecord dunNetworkRecord =
bringupNetworkAndGetRecord(
cb,
DUN_NETWORK_CAPABILITIES,
networkTemplates,
null /* currentlySelected */);
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
// Bring up CBS network
final UnderlyingNetworkRecord cbsNetworkRecord =
bringupNetworkAndGetRecord(
cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
verify(mNetworkControllerCb, never())
.onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
}
@Test
public void testFailtoMatchTemplateAndFallBackToInternetNetwork() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(
new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up an Internet network without DUN capability
final UnderlyingNetworkRecord networkRecord =
bringupNetworkAndGetRecord(
cb,
INITIAL_NETWORK_CAPABILITIES,
networkTemplates,
null /* currentlySelected */);
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
}
@Test
public void testFailtoMatchTemplateAndNeverFallBackToNonInternetNetwork() {
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
networkTemplates.add(
new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
bringupNetworkAndGetRecord(
cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
verify(mNetworkControllerCb, never())
.onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
}
}