diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java index af588047f7..8f62b4d151 100644 --- a/framework-t/src/android/net/NetworkTemplate.java +++ b/framework-t/src/android/net/NetworkTemplate.java @@ -24,6 +24,12 @@ import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkIdentity.OEM_PAID; import static android.net.NetworkIdentity.OEM_PRIVATE; @@ -37,6 +43,7 @@ import static android.net.NetworkStats.ROAMING_ALL; import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.ROAMING_YES; +import static com.android.net.module.util.CollectionUtils.toIntArray; import static com.android.net.module.util.NetworkCapabilitiesUtils.TYPE_TEST; import android.annotation.IntDef; @@ -1237,6 +1244,7 @@ public final class NetworkTemplate implements Parcelable { private void assertRequestableParameters() { validateWifiNetworkKeys(); + validateTransportTypes(); // TODO: Check all the input are legitimate. } @@ -1249,6 +1257,59 @@ public final class NetworkTemplate implements Parcelable { } } + // Validates if transport types are specified (i.e., mTransportTypesBits is not 0), + // they must be consistent with the template's mMatchRule. This means + // the specified transports must include at least one type that is + // deducible or expected for that matchRule. + // For example, if mMatchRule is MATCH_WIFI and transports are specified, + // TRANSPORT_WIFI must be among them. + private void validateTransportTypes() { + assertDeducedTransportTypes(mMatchRule, mTransportTypesBits); + } + + /** + * Determines possible transport types for a match rule. + * @return Set of TRANSPORT_* constants. + * @throws IllegalArgumentException for unknown matchRule. + */ + private static Set getPossibleTransportsForMatchRule(int matchRule) { + return switch (matchRule) { + case MATCH_MOBILE -> Set.of(TRANSPORT_CELLULAR, TRANSPORT_SATELLITE); + case MATCH_WIFI -> Set.of(TRANSPORT_WIFI); + case MATCH_ETHERNET -> Set.of(TRANSPORT_ETHERNET); + // See NetworkCapabilitiesUtils#deduceTransportTypeForLegacyIdentity. + case MATCH_PROXY, MATCH_BLUETOOTH -> Set.of(TRANSPORT_BLUETOOTH); + // Carrier networks are identified by subscriberId and can be either cellular + // or carrier-provided Wi-Fi. + case MATCH_CARRIER -> Set.of(TRANSPORT_CELLULAR, TRANSPORT_WIFI); + case MATCH_TEST -> Set.of(TRANSPORT_TEST); + default -> throw new IllegalArgumentException( + "Unexpected match rule: " + matchRule); + }; + } + + /** + * Asserts template's transport types are consistent with its match rule. + * Called during NetworkTemplate construction. + * + * @param matchRule The NetworkTemplate match rule. + * @param transportTypesBits Bitmask of transport types. No-op if 0. + * @throws IllegalArgumentException if transports are inconsistent with the rule. + */ + private static void assertDeducedTransportTypes(int matchRule, long transportTypesBits) { + // No transports specified, nothing to check. + if (transportTypesBits == 0L) return; + + Set possibleTransportsSet = getPossibleTransportsForMatchRule(matchRule); + final long possibleTransportBitsForMatchRule = + BitUtils.packBits(toIntArray(possibleTransportsSet)); + + // Template transports must overlap with possible transports for the rule. + if ((transportTypesBits & possibleTransportBitsForMatchRule) == 0L) { + throw new IllegalArgumentException("No deduced transport type: " + matchRule); + } + } + /** * Builds the instance of the NetworkTemplate. * diff --git a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt index 7aedf95faf..90109d1430 100644 --- a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt +++ b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt @@ -20,6 +20,8 @@ import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_ETHERNET import android.net.NetworkCapabilities.TRANSPORT_SATELLITE +import android.net.NetworkCapabilities.TRANSPORT_TEST +import android.net.NetworkCapabilities.TRANSPORT_VPN import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkStats.DEFAULT_NETWORK_ALL import android.net.NetworkStats.METERED_ALL @@ -32,6 +34,7 @@ import android.net.NetworkTemplate.MATCH_CARRIER import android.net.NetworkTemplate.MATCH_ETHERNET import android.net.NetworkTemplate.MATCH_MOBILE import android.net.NetworkTemplate.MATCH_PROXY +import android.net.NetworkTemplate.MATCH_TEST import android.net.NetworkTemplate.MATCH_WIFI import android.net.NetworkTemplate.NETWORK_TYPE_ALL import android.net.NetworkTemplate.OEM_MANAGED_ALL @@ -50,6 +53,7 @@ import org.junit.runners.JUnit4 private const val TEST_IMSI1 = "imsi" private const val TEST_WIFI_KEY1 = "wifiKey1" private const val TEST_WIFI_KEY2 = "wifiKey2" +private const val INVALID_TRANSPORT = 100 @RunWith(JUnit4::class) @ConnectivityModuleTest @@ -306,6 +310,7 @@ class NetworkTemplateTest { assertGetSetTransportTypes(MATCH_PROXY, TRANSPORT_BLUETOOTH) assertGetSetTransportTypes(MATCH_CARRIER, TRANSPORT_CELLULAR) assertGetSetTransportTypes(MATCH_CARRIER, TRANSPORT_WIFI) + assertGetSetTransportTypes(MATCH_TEST, TRANSPORT_TEST) } private fun assertGetSetTransportTypes(matchRule: Int, transport: Int) { @@ -318,4 +323,69 @@ class NetworkTemplateTest { assertEquals(1, template.transportTypes.size) assertEquals(transport, template.transportTypes[0]) } + + @Test + fun testValidateTransportTypesNoTransportsSetSuccess() { + // Should succeed for any match rule if no transports are explicitly set. + listOf( + MATCH_WIFI, + MATCH_MOBILE, + MATCH_ETHERNET, + MATCH_BLUETOOTH, + MATCH_PROXY, + MATCH_TEST + ).forEach { matchRule -> + NetworkTemplate.Builder(matchRule) + .setTransportTypes(intArrayOf()) // Explicitly empty, or just don't call it + .build() // Should not throw + } + NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(setOf(TEST_IMSI1)) + .setTransportTypes(intArrayOf()) + .build() // Should not throw + } + + @Test + fun testValidateTransportTypesDeducedTransportPlusCompatibleSuccess() { + NetworkTemplate.Builder(MATCH_WIFI) + .setTransportTypes(intArrayOf(TRANSPORT_WIFI, TRANSPORT_VPN)) + .build() + NetworkTemplate.Builder(MATCH_MOBILE) + .setTransportTypes(intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_TEST)) + .build() + } + + @Test + fun testValidateTransportTypesDeducedTransportMissingThrows() { + assertFailsWith { + NetworkTemplate.Builder(MATCH_WIFI) + .setTransportTypes(intArrayOf(TRANSPORT_CELLULAR)) + .build() + } + assertFailsWith { + NetworkTemplate.Builder(MATCH_MOBILE) + .setTransportTypes(intArrayOf(TRANSPORT_WIFI)) + .build() + } + assertFailsWith { + NetworkTemplate.Builder(MATCH_ETHERNET) + .setTransportTypes(intArrayOf(TRANSPORT_WIFI)) + .build() + } + assertFailsWith { + NetworkTemplate.Builder(MATCH_CARRIER) + .setTransportTypes(intArrayOf(TRANSPORT_SATELLITE)) + .build() + } + assertFailsWith { + NetworkTemplate.Builder(MATCH_TEST) + .setTransportTypes(intArrayOf(TRANSPORT_SATELLITE)) + .build() + } + assertFailsWith { + NetworkTemplate.Builder(MATCH_TEST) + .setTransportTypes(intArrayOf(INVALID_TRANSPORT)) + .build() + } + } }