diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java index 0e9e8d1dde..af588047f7 100644 --- a/framework-t/src/android/net/NetworkTemplate.java +++ b/framework-t/src/android/net/NetworkTemplate.java @@ -55,6 +55,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; +import com.android.net.module.util.BitUtils; import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.NetworkIdentityUtils; @@ -138,6 +139,10 @@ public final class NetworkTemplate implements Parcelable { */ public static final int NETWORK_TYPE_ALL = -1; + /** @hide */ + @VisibleForTesting + public static final long TRANSPORT_TYPES_ALL = 0L; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "OEM_MANAGED_" }, value = { @@ -410,6 +415,8 @@ public final class NetworkTemplate implements Parcelable { // Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}. private final int mOemManaged; + private final long mTransportTypesBits; + private static void checkValidMatchSubscriberIds(int matchRule, String[] matchSubscriberIds) { switch (matchRule) { // CARRIER templates must always specify a valid subscriber ID. @@ -459,7 +466,7 @@ public final class NetworkTemplate implements Parcelable { subscriberId != null ? new String[] { subscriberId } : new String[0], wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0], getMeterednessForBackwardsCompatibility(matchRule), ROAMING_ALL, - DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL); + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL, TRANSPORT_TYPES_ALL); if (matchRule == 6 || matchRule == 7) { Log.e(TAG, "Use MATCH_MOBILE with empty subscriberIds or MATCH_WIFI with empty " + "wifiNetworkKeys instead of template with matchRule=" + matchRule); @@ -495,7 +502,7 @@ public final class NetworkTemplate implements Parcelable { wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0], getMeterednessForBackwardsCompatibility(matchRule), ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL); + OEM_MANAGED_ALL, TRANSPORT_TYPES_ALL); // TODO : this is part of hidden-o txt, does that mean it should be annotated with // @UnsupportedAppUsage(maxTargetSdk = O) ? If yes, can't throwAtLeastU() lest apps // targeting O- crash on those devices. @@ -512,14 +519,15 @@ public final class NetworkTemplate implements Parcelable { // subscriberIds. this(getBackwardsCompatibleMatchRule(matchRule), matchSubscriberIds == null ? new String[]{} : matchSubscriberIds, - matchWifiNetworkKeys, metered, roaming, defaultNetwork, ratType, oemManaged); + matchWifiNetworkKeys, metered, roaming, defaultNetwork, ratType, oemManaged, + TRANSPORT_TYPES_ALL); throwAtLeastU(); } /** @hide */ public NetworkTemplate(int matchRule, String[] matchSubscriberIds, String[] matchWifiNetworkKeys, int metered, int roaming, int defaultNetwork, - int ratType, int oemManaged) { + int ratType, int oemManaged, long transportTypesBits) { Objects.requireNonNull(matchWifiNetworkKeys); Objects.requireNonNull(matchSubscriberIds); mMatchRule = matchRule; @@ -530,6 +538,7 @@ public final class NetworkTemplate implements Parcelable { mDefaultNetwork = defaultNetwork; mRatType = ratType; mOemManaged = oemManaged; + mTransportTypesBits = transportTypesBits; checkValidMatchSubscriberIds(matchRule, matchSubscriberIds); if (!isKnownMatchRule(matchRule)) { throw new IllegalArgumentException("Unknown network template rule " + matchRule @@ -546,6 +555,7 @@ public final class NetworkTemplate implements Parcelable { mDefaultNetwork = in.readInt(); mRatType = in.readInt(); mOemManaged = in.readInt(); + mTransportTypesBits = in.readLong(); } @Override @@ -558,6 +568,7 @@ public final class NetworkTemplate implements Parcelable { dest.writeInt(mDefaultNetwork); dest.writeInt(mRatType); dest.writeInt(mOemManaged); + dest.writeLong(mTransportTypesBits); } @Override @@ -590,6 +601,9 @@ public final class NetworkTemplate implements Parcelable { if (mOemManaged != OEM_MANAGED_ALL) { builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); } + if (mTransportTypesBits != TRANSPORT_TYPES_ALL) { + builder.append(", transportTypes=").append(Arrays.toString(getTransportTypes())); + } return builder.toString(); } @@ -597,7 +611,7 @@ public final class NetworkTemplate implements Parcelable { public int hashCode() { return Objects.hash(mMatchRule, Arrays.hashCode(mMatchSubscriberIds), Arrays.hashCode(mMatchWifiNetworkKeys), mMetered, mRoaming, mDefaultNetwork, - mRatType, mOemManaged); + mRatType, mOemManaged, mTransportTypesBits); } @Override @@ -611,7 +625,8 @@ public final class NetworkTemplate implements Parcelable { && mRatType == other.mRatType && mOemManaged == other.mOemManaged && Arrays.equals(mMatchSubscriberIds, other.mMatchSubscriberIds) - && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys); + && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys) + && mTransportTypesBits == other.mTransportTypesBits; } return false; } @@ -709,6 +724,16 @@ public final class NetworkTemplate implements Parcelable { return mRatType; } + /** + * Get the transport types filter of the template. + * + * @hide + */ + @NonNull + public int[] getTransportTypes() { + return BitUtils.unpackBits(mTransportTypesBits); + } + /** * Get the OEM managed filter of the template. See {@code OEM_MANAGED_*} or * {@code android.net.NetworkIdentity#OEM_*}. @@ -730,7 +755,10 @@ public final class NetworkTemplate implements Parcelable { if (!matchesRoaming(ident)) return false; if (!matchesDefaultNetwork(ident)) return false; if (!matchesOemNetwork(ident)) return false; + if (!matchesTransportTypes(ident)) return false; + // TODO: Convert match rule handling into transport type matches and + // eliminate the need of legacy network types. switch (mMatchRule) { case MATCH_MOBILE: return matchesMobile(ident); @@ -778,6 +806,13 @@ public final class NetworkTemplate implements Parcelable { || (mOemManaged == ident.mOemManaged); } + private boolean matchesTransportTypes(NetworkIdentity ident) { + if (this.mTransportTypesBits == TRANSPORT_TYPES_ALL) return true; + // Otherwise, for a match, the identity must possess ALL transport types + // specified in the template. + return (this.mTransportTypesBits & ident.mTransportTypesBits) == this.mTransportTypesBits; + } + private boolean matchesCollapsedRatType(NetworkIdentity ident) { return mRatType == NETWORK_TYPE_ALL || NetworkStatsManager.getCollapsedRatType(mRatType) @@ -1004,7 +1039,8 @@ public final class NetworkTemplate implements Parcelable { (template.mMatchRule == MATCH_MOBILE || template.mMatchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL); + ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL); } } @@ -1041,6 +1077,7 @@ public final class NetworkTemplate implements Parcelable { private int mRoaming; private int mDefaultNetwork; private int mRatType; + private long mTransportTypesBits; // Bitfield containing OEM network properties {@code NetworkIdentity#OEM_*}. private int mOemManaged; @@ -1059,6 +1096,7 @@ public final class NetworkTemplate implements Parcelable { mDefaultNetwork = DEFAULT_NETWORK_ALL; mRatType = NETWORK_TYPE_ALL; mOemManaged = OEM_MANAGED_ALL; + mTransportTypesBits = TRANSPORT_TYPES_ALL; } /** @@ -1153,6 +1191,25 @@ public final class NetworkTemplate implements Parcelable { return this; } + /** + * Sets the transport types filter for this template. + * + * - If the array is empty, matches any transport type (wildcard). + * - If not empty, matches networks possessing ALL specified transport types. + * For example, a set with {@code TRANSPORT_WIFI} and {@code TRANSPORT_VPN} + * requires a network to have both. + * + * @param transportTypes The array of {@code NetworkCapabilities#TRANSPORT_*} constants. + * An empty array acts as a wildcard for all transports. + * @return This {@code Builder} for chaining. + * @hide + */ + @NonNull + public Builder setTransportTypes(int[] transportTypes) { + mTransportTypesBits = BitUtils.packBits(transportTypes); + return this; + } + /** * Set the OEM managed filter. * @@ -1203,7 +1260,7 @@ public final class NetworkTemplate implements Parcelable { return new NetworkTemplate(mMatchRule, mMatchSubscriberIds.toArray(new String[0]), mMatchWifiNetworkKeys.toArray(new String[0]), mMetered, mRoaming, - mDefaultNetwork, mRatType, mOemManaged); + mDefaultNetwork, mRatType, mOemManaged, mTransportTypesBits); } } } diff --git a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt index 808fa50240..7aedf95faf 100644 --- a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt +++ b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt @@ -16,6 +16,11 @@ package android.net.netstats +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_WIFI import android.net.NetworkStats.DEFAULT_NETWORK_ALL import android.net.NetworkStats.METERED_ALL import android.net.NetworkStats.METERED_YES @@ -30,6 +35,7 @@ import android.net.NetworkTemplate.MATCH_PROXY import android.net.NetworkTemplate.MATCH_WIFI import android.net.NetworkTemplate.NETWORK_TYPE_ALL import android.net.NetworkTemplate.OEM_MANAGED_ALL +import android.net.NetworkTemplate.TRANSPORT_TYPES_ALL import android.os.Build import android.telephony.TelephonyManager import com.android.testutils.ConnectivityModuleTest @@ -74,7 +80,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -93,7 +100,8 @@ class NetworkTemplateTest { ROAMING_YES, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -123,7 +131,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -140,7 +149,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -157,7 +167,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -173,7 +184,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -190,7 +202,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -207,7 +220,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -224,7 +238,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } @@ -273,9 +288,34 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) assertEquals(expectedTemplate, it) } } + + // Verify the builder does not throw, and transports read from the getter is expected. + @Test + fun testBuilderTransportTypes() { + assertGetSetTransportTypes(MATCH_MOBILE, TRANSPORT_CELLULAR) + assertGetSetTransportTypes(MATCH_MOBILE, TRANSPORT_SATELLITE) + assertGetSetTransportTypes(MATCH_WIFI, TRANSPORT_WIFI) + assertGetSetTransportTypes(MATCH_ETHERNET, TRANSPORT_ETHERNET) + assertGetSetTransportTypes(MATCH_BLUETOOTH, TRANSPORT_BLUETOOTH) + assertGetSetTransportTypes(MATCH_PROXY, TRANSPORT_BLUETOOTH) + assertGetSetTransportTypes(MATCH_CARRIER, TRANSPORT_CELLULAR) + assertGetSetTransportTypes(MATCH_CARRIER, TRANSPORT_WIFI) + } + + private fun assertGetSetTransportTypes(matchRule: Int, transport: Int) { + val builder = NetworkTemplate.Builder(matchRule) + .setTransportTypes(intArrayOf(transport)) + if (matchRule == MATCH_CARRIER) { + builder.setSubscriberIds(setOf(TEST_IMSI1)) + } + val template = builder.build() + assertEquals(1, template.transportTypes.size) + assertEquals(transport, template.transportTypes[0]) + } } diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt index f1f455b4b3..10392b47f6 100644 --- a/tests/unit/java/android/net/NetworkTemplateTest.kt +++ b/tests/unit/java/android/net/NetworkTemplateTest.kt @@ -21,7 +21,10 @@ import android.content.Context import android.net.ConnectivityManager.TYPE_MOBILE import android.net.ConnectivityManager.TYPE_TEST import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +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.NetworkIdentity.OEM_NONE import android.net.NetworkIdentity.OEM_PAID @@ -40,6 +43,7 @@ import android.net.NetworkTemplate.NETWORK_TYPE_ALL import android.net.NetworkTemplate.OEM_MANAGED_ALL import android.net.NetworkTemplate.OEM_MANAGED_NO import android.net.NetworkTemplate.OEM_MANAGED_YES +import android.net.NetworkTemplate.TRANSPORT_TYPES_ALL import android.net.NetworkTemplate.buildTemplateMobileAll import android.net.NetworkTemplate.buildTemplateMobileWildcard import android.net.NetworkTemplate.buildTemplateWifiWildcard @@ -47,6 +51,7 @@ import android.net.NetworkTemplate.normalize import android.net.wifi.WifiInfo import android.os.Build import android.telephony.TelephonyManager +import com.android.net.module.util.BitUtils import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.NonNullTestUtils @@ -93,7 +98,8 @@ class NetworkTemplateTest { subscriberId: String? = null, wifiKey: String? = null, oemManaged: Int = OEM_NONE, - metered: Boolean = true + metered: Boolean = true, + transportTypes: IntArray = intArrayOf(), ): NetworkStateSnapshot { `when`(mockWifiInfo.getNetworkKey()).thenReturn(wifiKey) val lp = LinkProperties() @@ -108,6 +114,7 @@ class NetworkTemplateTest { NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, (oemManaged and OEM_PRIVATE) == OEM_PRIVATE ) + setTransportTypes(transportTypes) if (type == TYPE_TEST) { wifiKey?.let { TestNetworkSpecifier(it) }?.let { // Must have a single non-test transport specified to use setNetworkSpecifier. @@ -193,7 +200,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) val identMobile1 = buildNetworkIdentity( @@ -676,6 +684,7 @@ class NetworkTemplateTest { fun testEquals() { val templateImsi1 = NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES) .setSubscriberIds(setOf(TEST_IMSI1)).setRatType(TelephonyManager.NETWORK_TYPE_UMTS) + .setTransportTypes(intArrayOf(TRANSPORT_SATELLITE, TRANSPORT_VPN)) .build() val dupTemplateImsi1 = NetworkTemplate( MATCH_MOBILE, @@ -685,7 +694,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + BitUtils.packBits(intArrayOf(TRANSPORT_SATELLITE, TRANSPORT_VPN)) ) val templateImsi2 = NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES) .setSubscriberIds(setOf(TEST_IMSI2)).setRatType(TelephonyManager.NETWORK_TYPE_UMTS) @@ -696,7 +706,9 @@ class NetworkTemplateTest { assertNotEquals(templateImsi1, templateImsi2) val templateWifiKey1 = NetworkTemplate.Builder(MATCH_WIFI) - .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build() + .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)) + .setTransportTypes(intArrayOf(TRANSPORT_WIFI)) + .build() val dupTemplateWifiKey1 = NetworkTemplate( MATCH_WIFI, emptyArray(), @@ -705,7 +717,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + BitUtils.packBits(intArrayOf(TRANSPORT_WIFI)) ) val templateWifiKey2 = NetworkTemplate.Builder(MATCH_WIFI) .setWifiNetworkKeys(setOf(TEST_WIFI_KEY2)).build() @@ -725,7 +738,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + 0x12345678L ) val templateWifi = NetworkTemplate( MATCH_WIFI, @@ -735,7 +749,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + 0xDEADBEEFL ) val templateOem = NetworkTemplate( MATCH_MOBILE, @@ -745,11 +760,12 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, - OEM_MANAGED_YES + OEM_MANAGED_YES, + 0xFFFFFFFFL ) - assertParcelSane(templateMobile, 8) - assertParcelSane(templateWifi, 8) - assertParcelSane(templateOem, 8) + assertParcelSane(templateMobile, 9) + assertParcelSane(templateWifi, 9) + assertParcelSane(templateOem, 9) } // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with @@ -818,7 +834,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_YES + OEM_MANAGED_YES, + TRANSPORT_TYPES_ALL ) val templateOemAll = NetworkTemplate( matchType, @@ -828,7 +845,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL + OEM_MANAGED_ALL, + TRANSPORT_TYPES_ALL ) for (identityOemManagedState in oemManagedStates) { @@ -854,7 +872,8 @@ class NetworkTemplateTest { ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - templateOemManagedState + templateOemManagedState, + TRANSPORT_TYPES_ALL ) if (identityOemManagedState == templateOemManagedState) { template.assertMatches(ident) @@ -886,6 +905,54 @@ class NetworkTemplateTest { matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, identWifiKey = TEST_WIFI_KEY1) } + @Test + fun testTransportTypesMatches() { + val identSatellite = NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setTransportTypes(setOf(TRANSPORT_SATELLITE)) + .build() + val identMobileVpn = NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setTransportTypes(setOf(TRANSPORT_CELLULAR, TRANSPORT_VPN)) + .build() + val identOnlyCellular = NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setTransportTypes(setOf(TRANSPORT_CELLULAR)) + .build() + + val templateMobileNoTransport = NetworkTemplate.Builder(MATCH_MOBILE) + .build() // Wildcard for transports + val templateMobileWithCellularTransport = NetworkTemplate.Builder(MATCH_MOBILE) + .setTransportTypes(intArrayOf(TRANSPORT_CELLULAR)) + .build() + val templateSatellite = NetworkTemplate.Builder(MATCH_MOBILE) + .setTransportTypes(intArrayOf(TRANSPORT_SATELLITE)) + .build() + val templateVpnAndCellular = NetworkTemplate.Builder(MATCH_MOBILE) + .setTransportTypes(intArrayOf(TRANSPORT_VPN, TRANSPORT_CELLULAR)) + .build() + + // 1. Mobile wildcard template + templateMobileNoTransport.assertMatches(identSatellite) + templateMobileNoTransport.assertMatches(identMobileVpn) + templateMobileNoTransport.assertMatches(identOnlyCellular) + + // 2. Template requiring TRANSPORT_CELLULAR + templateMobileWithCellularTransport.assertDoesNotMatch(identSatellite) + templateMobileWithCellularTransport.assertMatches(identMobileVpn) + templateMobileWithCellularTransport.assertMatches(identOnlyCellular) + + // 3. Template requiring TRANSPORT_SATELLITE + templateSatellite.assertMatches(identSatellite) + templateSatellite.assertDoesNotMatch(identMobileVpn) + templateSatellite.assertDoesNotMatch(identOnlyCellular) + + // 4. Template requiring TRANSPORT_VPN AND TRANSPORT_CELLULAR + templateVpnAndCellular.assertDoesNotMatch(identSatellite) + templateVpnAndCellular.assertMatches(identMobileVpn) + templateVpnAndCellular.assertDoesNotMatch(identOnlyCellular) + } + @Test fun testNormalize() { var mergedImsiList = arrayOf(TEST_IMSI1, TEST_IMSI2) diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java index de54e8d145..90c9fc5c0a 100644 --- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -1535,6 +1535,78 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } + // This test demonstrates that a network identity with legacy type TYPE_MOBILE + // can represent different underlying physical transports (e.g., satellite or cellular). + // NetworkTemplate, using MATCH_MOBILE, can then differentiate these further + // by applying a transportTypes filter. + @Test + public void testGetSatelliteStats() throws Exception { + mockDefaultSettings(); + final NetworkTemplate templateSatellite = + new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES) + .setSubscriberIds(Set.of(IMSI_1)) + .setTransportTypes(new int[]{NetworkCapabilities.TRANSPORT_SATELLITE}) + .build(); + + final NetworkTemplate templateCellularOnly = + new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES) + .setSubscriberIds(Set.of(IMSI_1)) + .setTransportTypes(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}) + .build(); + + // Initial state: Assert all relevant templates are empty + assertUidTotal(sTemplateImsi1, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(templateSatellite, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(templateCellularOnly, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 1. Bring up SATELLITE mobile network for IMSI_1 + NetworkStateSnapshot satelliteState = buildSatelliteMobileState(IMSI_1); + + mService.notifyNetworkStatus(NETWORKS_MOBILE, new NetworkStateSnapshot[]{satelliteState}, + getActiveIface(satelliteState), new UnderlyingNetworkInfo[0]); + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE); + + // Record 100 bytes of data on the satellite network for UID_RED + incrementCurrentTime(HOUR_IN_MILLIS); + mockNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 100L, 10L, 100L, 10L, 0L)); + forcePollAndWaitForIdle(); + + // Verify data attribution for SATELLITE + // sTemplateImsi1 (general mobile) should count satellite data. + assertUidTotal(sTemplateImsi1, UID_RED, 100L, 10L, 100L, 10L, 0); + // sTemplateMobileSatelliteImsi1 (satellite specific) should count satellite data. + assertUidTotal(templateSatellite, UID_RED, 100L, 10L, 100L, 10L, 0); + // templateCellularOnly (cellular specific) should NOT count satellite data. + assertUidTotal(templateCellularOnly, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 2. Switch to CELLULAR mobile network for IMSI_1 on the same interface + NetworkStateSnapshot cellularState = buildMobileState(IMSI_1); + + mService.notifyNetworkStatus(NETWORKS_MOBILE, new NetworkStateSnapshot[]{cellularState}, + getActiveIface(cellularState), new UnderlyingNetworkInfo[0]); + // Mocking UID details to reflect total traffic on the interface since boot/last poll. + // Previous satellite data (100B) + new cellular data (200B) = 300B total on iface. + incrementCurrentTime(HOUR_IN_MILLIS); + mockNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 300L, 30L, 300L, 30L, 0L)); // 100sat + 200cell + forcePollAndWaitForIdle(); + + // Verify data attribution after CELLULAR usage + // sTemplateImsi1 (general mobile) should count cumulative data (satellite + cellular). + // (100 previous + 200 new = 300 total) + assertUidTotal(sTemplateImsi1, UID_RED, 300L, 30L, 300L, 30L, 0); + // sTemplateMobileSatelliteImsi1 should still only have the satellite data. + assertUidTotal(templateSatellite, UID_RED, 100L, 10L, 100L, 10L, 0); + // templateCellularOnly should now have the new cellular data (200B). + // (Current total on iface 300B - previous total on iface before this poll 100B = 200B) + assertUidTotal(templateCellularOnly, UID_RED, 200L, 20L, 200L, 20L, 0); + } + @Test public void testForegroundBackground() throws Exception { // pretend that network comes online