Support matches transport types in NetworkTemplate

Flag: EXEMPT implemented in the counter-part CL
Test: atest CtsNetTestCases --test-filter \
      'NetworkStats*' --update-device
Test: atest ConnectivityCoverageTests --test-filter \
      'NetworkStats*' --update-device
Test: atest ConnectivityCoverageTests --test-filter \
      'NetworkTemplate*' --update-device
Bug: 419123367
Change-Id: I0c8bf2e24502befd810081f519ff915491b0a905
This commit is contained in:
Junyu Lai 2025-05-22 17:38:12 +08:00
commit 3fd36e5f93
4 changed files with 268 additions and 32 deletions

View file

@ -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);
}
}
}

View file

@ -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])
}
}

View file

@ -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<String>(),
@ -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)

View file

@ -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