Add NetworkCapabilities transport types into NetworkIdentity.

This change updates NetworkIdentity to have NetworkCapabilities
transport types for improved network identification, and updates
NetworkIdentitySet for backward compatibility with older data.

Flag: EXEMPT implemented in the counter-part CL
Test: atest ConnectivityCoverageTests --test-filter \
      'NetworkIdentity*' --update-device
Test: atest ConnectivityCoverageTests --test-filter \
      'NetworkStats*' --update-device
Bug: 419123367
Change-Id: I26cc770d90b4687d832dd029d9a11db03404db39
This commit is contained in:
Junyu Lai 2025-05-21 10:38:39 +08:00
commit bebd4090ae
11 changed files with 407 additions and 69 deletions

View file

@ -22,6 +22,9 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.net.module.util.CollectionUtils.intArrayToSet;
import static com.android.net.module.util.NetworkCapabilitiesUtils.TYPE_TEST;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@ -43,6 +46,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
/**
* Network definition that includes strong identity. Analogous to combining
@ -86,9 +90,6 @@ public class NetworkIdentity {
private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
// Need to be synchronized with ConnectivityManager.
// TODO: Use {@code ConnectivityManager#*} when visible.
static final int TYPE_TEST = 18;
private static final int MAX_NETWORK_TYPE = TYPE_TEST;
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
@ -101,11 +102,13 @@ public class NetworkIdentity {
final boolean mMetered;
final boolean mDefaultNetwork;
final int mOemManaged;
final long mTransportTypesBits;
/** @hide */
public NetworkIdentity(
int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId) {
boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId,
long transportTypesBits) {
mType = type;
mRatType = ratType;
mSubscriberId = subscriberId;
@ -115,25 +118,26 @@ public class NetworkIdentity {
mDefaultNetwork = defaultNetwork;
mOemManaged = oemManaged;
mSubId = subId;
mTransportTypesBits = transportTypesBits;
}
@Override
public int hashCode() {
return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
mDefaultNetwork, mOemManaged, mSubId);
mDefaultNetwork, mOemManaged, mSubId, mTransportTypesBits);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
if (obj instanceof NetworkIdentity ident) {
return mType == ident.mType && mRatType == ident.mRatType && mRoaming == ident.mRoaming
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
&& Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
&& mMetered == ident.mMetered
&& mDefaultNetwork == ident.mDefaultNetwork
&& mOemManaged == ident.mOemManaged
&& mSubId == ident.mSubId;
&& mSubId == ident.mSubId
&& mTransportTypesBits == ident.mTransportTypesBits;
}
return false;
}
@ -162,6 +166,8 @@ public class NetworkIdentity {
builder.append(", defaultNetwork=").append(mDefaultNetwork);
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
builder.append(", subId=").append(mSubId);
// TODO: Print human friendly names instead.
builder.append(", transports=").append(getTransportTypes());
return builder.append("}").toString();
}
@ -204,7 +210,7 @@ public class NetworkIdentity {
proto.write(NetworkIdentityProto.METERED, mMetered);
proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
proto.write(NetworkIdentityProto.TRANSPORT_TYPES, mTransportTypesBits);
proto.end(start);
}
@ -273,6 +279,14 @@ public class NetworkIdentity {
return mSubId;
}
/**
* Get transport types
* @hide
*/
public Set<Integer> getTransportTypes() {
return intArrayToSet(BitUtils.unpackBits(mTransportTypesBits));
}
/**
* Assemble a {@link NetworkIdentity} from the passed arguments.
*
@ -322,6 +336,9 @@ public class NetworkIdentity {
public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
Objects.requireNonNull(right);
int res = Integer.compare(left.mType, right.mType);
if (res == 0) {
res = Long.compare(left.mTransportTypesBits, right.mTransportTypesBits);
}
if (res == 0) {
res = Integer.compare(left.mRatType, right.mRatType);
}
@ -362,6 +379,7 @@ public class NetworkIdentity {
private boolean mDefaultNetwork;
private int mOemManaged;
private int mSubId;
private long mTransportTypes;
/**
* Creates a new Builder.
@ -377,6 +395,7 @@ public class NetworkIdentity {
mDefaultNetwork = false;
mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
mSubId = INVALID_SUBSCRIPTION_ID;
mTransportTypes = 0L;
}
/**
@ -389,6 +408,7 @@ public class NetworkIdentity {
* - metered
* - oemManaged
* - wifiNetworkKey
* - transportTypes
*
* @param snapshot The target {@link NetworkStateSnapshot} object.
* @return The builder object.
@ -397,6 +417,7 @@ public class NetworkIdentity {
@NonNull
public Builder setNetworkStateSnapshot(@NonNull NetworkStateSnapshot snapshot) {
setType(snapshot.getLegacyType());
setTransportTypes(intArrayToSet(snapshot.getNetworkCapabilities().getTransportTypes()));
setSubscriberId(snapshot.getSubscriberId());
setRoaming(!snapshot.getNetworkCapabilities().hasCapability(
@ -406,6 +427,7 @@ public class NetworkIdentity {
setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities()));
// TODO: Use the transports instead.
if (mType == TYPE_WIFI) {
final NetworkCapabilities nc = snapshot.getNetworkCapabilities();
final TransportInfo transportInfo = nc.getTransportInfo();
@ -580,6 +602,17 @@ public class NetworkIdentity {
return this;
}
/**
* Set the transport types.
*
* @hide
*/
@NonNull
public Builder setTransportTypes(Set<Integer> transportTypes) {
mTransportTypes = BitUtils.packBits(CollectionUtils.toIntArray(transportTypes));
return this;
}
private void ensureValidParameters() {
// Assert non-mobile network cannot have a ratType.
if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
@ -602,7 +635,7 @@ public class NetworkIdentity {
public NetworkIdentity build() {
ensureValidParameters();
return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId);
mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId, mTransportTypes);
}
}
}

View file

@ -19,10 +19,17 @@ package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.net.module.util.CollectionUtils.toIntArray;
import static com.android.net.module.util.NetworkCapabilitiesUtils.deduceTransportTypeForLegacyNetworkType;
import android.annotation.NonNull;
import android.service.NetworkIdentitySetProto;
import android.util.proto.ProtoOutputStream;
import androidx.annotation.VisibleForTesting;
import com.android.net.module.util.BitUtils;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@ -44,6 +51,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
private static final int VERSION_ADD_SUB_ID = 7;
private static final int VERSION_ADD_TRANSPORT_TYPES = 8;
/**
* Construct a {@link NetworkIdentitySet} object.
@ -112,8 +120,23 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
subId = INVALID_SUBSCRIPTION_ID;
}
final long transportTypesBits;
if (version >= VERSION_ADD_TRANSPORT_TYPES) {
transportTypesBits = in.readLong();
} else {
final int deducedTransport = deduceTransportTypeForLegacyNetworkType(type);
if (deducedTransport != -1) {
transportTypesBits = BitUtils.packBits(new int[]{ deducedTransport });
} else {
// Ignore legacy or unknown types. This is fine since this means
// the legacy data cannot be queried by transport types,
// which is not even supported now.
transportTypesBits = 0;
}
}
add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
defaultNetwork, oemNetCapabilities, subId));
defaultNetwork, oemNetCapabilities, subId, transportTypesBits));
}
}
@ -122,7 +145,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
* @hide
*/
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_SUB_ID);
out.writeInt(VERSION_ADD_TRANSPORT_TYPES);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
@ -134,6 +157,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
out.writeBoolean(ident.isDefaultNetwork());
out.writeInt(ident.getOemManaged());
out.writeInt(ident.getSubId());
out.writeLong(BitUtils.packBits(toIntArray(ident.getTransportTypes())));
}
}
@ -186,7 +210,13 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
return true;
}
private static void writeOptionalString(DataOutput out, String value) throws IOException {
/**
* Writes an optional string to a {@link DataOutput} stream.
*
* @hide
*/
@VisibleForTesting
public static void writeOptionalString(DataOutput out, String value) throws IOException {
if (value != null) {
out.writeByte(1);
out.writeUTF(value);

View file

@ -37,6 +37,8 @@ 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.NetworkCapabilitiesUtils.TYPE_TEST;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@ -870,7 +872,7 @@ public final class NetworkTemplate implements Parcelable {
* all test networks would be matched.
*/
private boolean matchesTest(NetworkIdentity ident) {
return ident.mType == NetworkIdentity.TYPE_TEST
return ident.mType == TYPE_TEST
&& ((CollectionUtils.isEmpty(mMatchWifiNetworkKeys)
|| CollectionUtils.contains(mMatchWifiNetworkKeys, ident.mWifiNetworkKey)));
}

View file

@ -64,6 +64,8 @@ message NetworkIdentityProto {
optional bool default_network = 6;
optional int32 oem_managed_network = 7;
optional int64 transport_types = 8;
}
// Corresponds to NetworkStatsRecorder.

View file

@ -19,6 +19,7 @@ package com.android.net.module.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@ -27,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@ -51,6 +53,18 @@ public final class CollectionUtils {
return collection == null || collection.isEmpty();
}
/**
* Convert an int array to a Integer set.
*/
@NonNull
public static Set<Integer> intArrayToSet(@NonNull int[] array) {
final Set<Integer> set = new ArraySet<>();
for (int item : array) {
set.add(item);
}
return set;
}
/**
* Returns an int array from the given Integer list.
*/

View file

@ -16,6 +16,12 @@
package com.android.net.module.util;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_BIP;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@ -40,13 +46,13 @@ 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_USB;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static com.android.net.module.util.BitUtils.packBits;
import static com.android.net.module.util.BitUtils.unpackBits;
import android.annotation.NonNull;
import android.net.NetworkCapabilities;
@ -58,6 +64,10 @@ import com.android.internal.annotations.VisibleForTesting;
* @hide
*/
public final class NetworkCapabilitiesUtils {
// Need to be synchronized with ConnectivityManager.
// TODO: Use {@code ConnectivityManager#*} when visible.
public static final int TYPE_TEST = 18;
// Transports considered to classify networks in UI, in order of which transport should be
// surfaced when there are multiple transports. Transports not in this list do not have
// an ordering preference (in practice they will have a deterministic order based on the
@ -150,6 +160,32 @@ public final class NetworkCapabilitiesUtils {
return transports[0];
}
/**
* Deduces the primary NetworkCapabilities.TRANSPORT_* type from a legacy
* ConnectivityManager.TYPE_* network type.
*
* This is used to deduce transport type for legacy persistent NetworkStats files
* with NetworkIdentitySet version under VERSION_ADD_TRANSPORT_TYPES.
*
* @param legacyNetworkType One of the deprecated ConnectivityManager.TYPE_* constants
* (e.g., ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_MOBILE).
* @return The corresponding NetworkCapabilities.TRANSPORT_* constant, or
* -1 if no direct or clear mapping exists or unknown.
*/
public static int deduceTransportTypeForLegacyNetworkType(int legacyNetworkType) {
return switch (legacyNetworkType) {
case TYPE_WIFI -> TRANSPORT_WIFI;
case TYPE_MOBILE -> TRANSPORT_CELLULAR;
case TYPE_ETHERNET -> TRANSPORT_ETHERNET;
case TYPE_VPN -> TRANSPORT_VPN;
// TYPE_PROXY (deprecated) historically represented a network primarily
// for proxying traffic, often over Bluetooth (e.g., on Wear OS devices
// tethered to a phone). See ProxyNetworkAgent (deprecated) for more detail.
case TYPE_BLUETOOTH, TYPE_PROXY -> TRANSPORT_BLUETOOTH;
case TYPE_TEST -> TRANSPORT_TEST;
default -> -1; // Ignore legacy types such as TYPE_WIMAX, TYPE_MOBILE_*, etc.
};
}
/**
* Infers that all the capabilities it provides are typically provided by restricted networks

View file

@ -19,15 +19,16 @@ package com.android.net.module.util
import android.util.SparseArray
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.net.module.util.CollectionUtils.intArrayToSet
import kotlin.test.assertContentEquals
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SmallTest
@ -144,13 +145,19 @@ class CollectionUtilsTest {
val listAE = listOf("A", "B", "C", "D", "E")
val list15 = listOf(1, 2, 3, 4, 5)
// Normal #zip returns kotlin.Pair, not android.util.Pair
assertEquals(list15.zip(listAE).map { android.util.Pair(it.first, it.second) },
CollectionUtils.zip(list15, listAE))
assertEquals(
list15.zip(listAE).map { android.util.Pair(it.first, it.second) },
CollectionUtils.zip(list15, listAE)
)
val listNull = listOf("A", null, "B", "C", "D")
assertEquals(list15.zip(listNull).map { android.util.Pair(it.first, it.second) },
CollectionUtils.zip(list15, listNull))
assertEquals(emptyList<android.util.Pair<Int, Int>>(),
CollectionUtils.zip(emptyList<Int>(), emptyList<Int>()))
assertEquals(
list15.zip(listNull).map { android.util.Pair(it.first, it.second) },
CollectionUtils.zip(list15, listNull)
)
assertEquals(
emptyList<android.util.Pair<Int, Int>>(),
CollectionUtils.zip(emptyList<Int>(), emptyList<Int>())
)
assertFailsWith<IllegalArgumentException> {
// Different size
CollectionUtils.zip(listOf(1, 2), list15)
@ -280,4 +287,12 @@ class CollectionUtilsTest {
CollectionUtils.appendArray(String::class.java, stringArr1, "4", "5", "6")
)
}
@Test
fun intArrayToSet_arrayWithDuplicateElements_returnsSetWithUniqueElements() {
val inputArray = intArrayOf(1, 2, 2, 3, 4, 4, 4, 5, 1)
val expectedSet = setOf(1, 2, 3, 4, 5)
val result = intArrayToSet(inputArray)
assertEquals(expectedSet, result)
}
}

View file

@ -17,15 +17,32 @@
package android.net
import android.content.Context
import android.net.ConnectivityManager.TYPE_BLUETOOTH
import android.net.ConnectivityManager.TYPE_ETHERNET
import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.ConnectivityManager.TYPE_VPN
import android.net.ConnectivityManager.TYPE_WIFI
import android.net.ConnectivityManager.TYPE_WIMAX
import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkIdentitySet.writeOptionalString
import android.os.Build
import android.telephony.TelephonyManager
import com.android.net.module.util.NetworkCapabilitiesUtils.TYPE_TEST
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream
import kotlin.test.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import kotlin.test.assertEquals
private const val TEST_IMSI1 = "testimsi1"
@ -34,22 +51,54 @@ private const val TEST_IMSI1 = "testimsi1"
class NetworkIdentitySetTest {
private val mockContext = mock(Context::class.java)
private fun buildMobileNetworkStateSnapshot(
caps: NetworkCapabilities,
subscriberId: String
private fun buildMobileNetworkStateSnapshot(subscriberId: String) =
buildNetworkStateSnapshot(NetworkCapabilities(), subscriberId, TYPE_MOBILE)
private fun buildNetworkStateSnapshot(
caps: NetworkCapabilities,
subscriberId: String,
legacyNetworkType: Int
): NetworkStateSnapshot {
return NetworkStateSnapshot(mock(Network::class.java), caps,
LinkProperties(), subscriberId, TYPE_MOBILE)
return NetworkStateSnapshot(
mock(Network::class.java),
caps,
LinkProperties(),
subscriberId,
legacyNetworkType
)
}
private fun buildNetworkIdentity(
legacyNetworkType: Int,
vararg transportTypes: Int
): NetworkIdentity {
val caps = NetworkCapabilities().apply {
transportTypes.forEach {
addTransportType(it)
}
}
return NetworkIdentity.buildNetworkIdentity(
mockContext,
buildNetworkStateSnapshot(caps, TEST_IMSI1, legacyNetworkType),
false,
0
)
}
@Test
fun testCompare() {
val ident1 = NetworkIdentity.buildNetworkIdentity(mockContext,
buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI1),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
val ident2 = NetworkIdentity.buildNetworkIdentity(mockContext,
buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI1),
true /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
val ident1 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(TEST_IMSI1),
false /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
val ident2 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(TEST_IMSI1),
true /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
// Verify that the results of comparing two empty sets are equal
assertEquals(0, NetworkIdentitySet.compare(NetworkIdentitySet(), NetworkIdentitySet()))
@ -63,4 +112,76 @@ class NetworkIdentitySetTest {
assertEquals(0, NetworkIdentitySet.compare(identSet1, identSet1))
assertEquals(-1, NetworkIdentitySet.compare(identSet1, identSet2))
}
@Test
fun testTransportTypesSerialization() {
val originalSet = NetworkIdentitySet()
originalSet.add(buildNetworkIdentity(TYPE_MOBILE, TRANSPORT_CELLULAR))
originalSet.add(buildNetworkIdentity(TYPE_WIFI, TRANSPORT_VPN, TRANSPORT_WIFI))
val baos = ByteArrayOutputStream()
val outDataStream = DataOutputStream(baos)
originalSet.writeToStream(outDataStream)
val bais = ByteArrayInputStream(baos.toByteArray())
val inDataStream = DataInputStream(bais)
val deserializedSet = NetworkIdentitySet(inDataStream)
assertEquals(originalSet, deserializedSet)
}
/**
* Writes a single NetworkIdentity to the stream in a specific older version format.
* This simulates an old saved state.
* Version: VERSION_ADD_SUB_ID (7), which does NOT include transportTypesBits explicitly.
*/
private fun writeNetworkIdentityV7(out: DataOutputStream, ident: NetworkIdentity) {
out.writeInt(7) // Write the old version number
out.writeInt(1) // Number of identities in the set
out.writeInt(ident.type)
out.writeInt(ident.ratType)
writeOptionalString(out, ident.subscriberId)
writeOptionalString(out, ident.wifiNetworkKey)
out.writeBoolean(ident.isRoaming)
out.writeBoolean(ident.isMetered)
out.writeBoolean(ident.isDefaultNetwork)
out.writeInt(ident.oemManaged)
out.writeInt(ident.subId)
// V7 and earlier versions DO NOT write transportTypesBits
}
@Test
fun testMigrateToSupportTransportTypes() {
doTestMigrateToSupportTransportTypes(TYPE_MOBILE, TRANSPORT_CELLULAR)
doTestMigrateToSupportTransportTypes(TYPE_WIFI, TRANSPORT_WIFI)
doTestMigrateToSupportTransportTypes(TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH)
doTestMigrateToSupportTransportTypes(TYPE_ETHERNET, TRANSPORT_ETHERNET)
doTestMigrateToSupportTransportTypes(TYPE_VPN, TRANSPORT_VPN)
doTestMigrateToSupportTransportTypes(TYPE_TEST, TRANSPORT_TEST)
doTestMigrateToSupportTransportTypes(TYPE_WIMAX)
}
private fun doTestMigrateToSupportTransportTypes(
legacyNetworkType: Int,
vararg expectedTransportTypes: Int
) {
val ident = buildNetworkIdentity(legacyNetworkType)
val baos = ByteArrayOutputStream()
val outDataStream = DataOutputStream(baos)
writeNetworkIdentityV7(outDataStream, ident)
val bais = ByteArrayInputStream(baos.toByteArray())
val inDataStream = DataInputStream(bais)
val deserializedSet = NetworkIdentitySet(inDataStream)
// Prepare the expected identity set, verify the transport type deduced from
// the legacy network type.
val expectedIdent = buildNetworkIdentity(legacyNetworkType, *expectedTransportTypes)
val expectedSet = NetworkIdentitySet().also {
it.add(expectedIdent)
}
assertEquals(expectedSet, deserializedSet)
}
}

View file

@ -16,6 +16,7 @@
package android.net
import android.app.usage.NetworkStatsManager
import android.content.Context
import android.net.ConnectivityManager.MAX_NETWORK_TYPE
import android.net.ConnectivityManager.TYPE_ETHERNET
@ -23,22 +24,26 @@ import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.ConnectivityManager.TYPE_NONE
import android.net.ConnectivityManager.TYPE_WIFI
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_SATELLITE
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
import android.net.NetworkIdentity.getOemBitfield
import android.app.usage.NetworkStatsManager
import android.telephony.TelephonyManager
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 org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
private const val TEST_WIFI_KEY = "testwifikey"
private const val TEST_IMSI1 = "testimsi1"
@ -52,11 +57,16 @@ class NetworkIdentityTest {
private val mockContext = mock(Context::class.java)
private fun buildMobileNetworkStateSnapshot(
caps: NetworkCapabilities,
subscriberId: String
caps: NetworkCapabilities,
subscriberId: String
): NetworkStateSnapshot {
return NetworkStateSnapshot(mock(Network::class.java), caps,
LinkProperties(), subscriberId, TYPE_MOBILE)
return NetworkStateSnapshot(
mock(Network::class.java),
caps,
LinkProperties(),
subscriberId,
TYPE_MOBILE
)
}
@Test
@ -87,18 +97,24 @@ class NetworkIdentityTest {
@Test
fun testIsMetered() {
// Verify network is metered.
val netIdent1 = NetworkIdentity.buildNetworkIdentity(mockContext,
val netIdent1 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI1),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
false /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertTrue(netIdent1.isMetered())
// Verify network is not metered because it has NET_CAPABILITY_NOT_METERED capability.
val capsNotMetered = NetworkCapabilities.Builder().apply {
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
}.build()
val netIdent2 = NetworkIdentity.buildNetworkIdentity(mockContext,
val netIdent2 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(capsNotMetered, TEST_IMSI1),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
false /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertFalse(netIdent2.isMetered())
// In current design, a network that has NET_CAPABILITY_TEMPORARILY_NOT_METERED
@ -106,9 +122,12 @@ class NetworkIdentityTest {
val capsTempNotMetered = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED, true)
}
val netIdent3 = NetworkIdentity.buildNetworkIdentity(mockContext,
val netIdent3 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(capsTempNotMetered, TEST_IMSI1),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
false /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertTrue(netIdent3.isMetered())
}
@ -122,15 +141,20 @@ class NetworkIdentityTest {
setNetworkSpecifier(specifier1)
}
val identFromSnapshot = NetworkIdentity.Builder().setNetworkStateSnapshot(
buildMobileNetworkStateSnapshot(oemPrivateRoamingNotMeteredCap, TEST_IMSI1))
buildMobileNetworkStateSnapshot(oemPrivateRoamingNotMeteredCap, TEST_IMSI1)
)
.setDefaultNetwork(true)
.setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
.setSubId(TEST_SUBID1)
.build()
val identFromLegacyBuild = NetworkIdentity.buildNetworkIdentity(mockContext,
val identFromLegacyBuild = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(oemPrivateRoamingNotMeteredCap, TEST_IMSI1),
true /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
val identFromConstructor = NetworkIdentity(TYPE_MOBILE,
true /* defaultNetwork */,
TelephonyManager.NETWORK_TYPE_UMTS
)
val identFromConstructor = NetworkIdentity(
TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UMTS,
TEST_IMSI1,
null /* wifiNetworkKey */,
@ -138,7 +162,9 @@ class NetworkIdentityTest {
false /* metered */,
true /* defaultNetwork */,
NetworkTemplate.OEM_MANAGED_PRIVATE,
TEST_SUBID1)
TEST_SUBID1,
BitUtils.packBits(intArrayOf(TRANSPORT_CELLULAR))
)
assertEquals(identFromLegacyBuild, identFromSnapshot)
assertEquals(identFromConstructor, identFromSnapshot)
@ -180,8 +206,12 @@ class NetworkIdentityTest {
@Test
fun testBuilder_ratType() {
// Assert illegal ratTypes cannot make an identity.
listOf(Integer.MIN_VALUE, NetworkTemplate.NETWORK_TYPE_ALL,
NetworkStatsManager.NETWORK_TYPE_5G_NSA - 1, Integer.MAX_VALUE)
listOf(
Integer.MIN_VALUE,
NetworkTemplate.NETWORK_TYPE_ALL,
NetworkStatsManager.NETWORK_TYPE_5G_NSA - 1,
Integer.MAX_VALUE
)
.forEach {
assertFailsWith<IllegalArgumentException> {
NetworkIdentity.Builder()
@ -208,8 +238,12 @@ class NetworkIdentityTest {
@Test
fun testBuilder_oemManaged() {
// Assert illegal oemManage values cannot make an identity.
listOf(Integer.MIN_VALUE, NetworkTemplate.OEM_MANAGED_ALL, NetworkTemplate.OEM_MANAGED_YES,
Integer.MAX_VALUE)
listOf(
Integer.MIN_VALUE,
NetworkTemplate.OEM_MANAGED_ALL,
NetworkTemplate.OEM_MANAGED_YES,
Integer.MAX_VALUE
)
.forEach { oemManaged ->
assertFailsWith<IllegalArgumentException> {
NetworkIdentity.Builder()
@ -220,9 +254,13 @@ class NetworkIdentityTest {
}
// Verify legitimate oem managed values can make an identity.
listOf(NetworkTemplate.OEM_MANAGED_NO, NetworkTemplate.OEM_MANAGED_PAID,
NetworkTemplate.OEM_MANAGED_PRIVATE, NetworkTemplate.OEM_MANAGED_PAID or
NetworkTemplate.OEM_MANAGED_PRIVATE)
listOf(
NetworkTemplate.OEM_MANAGED_NO,
NetworkTemplate.OEM_MANAGED_PAID,
NetworkTemplate.OEM_MANAGED_PRIVATE,
NetworkTemplate.OEM_MANAGED_PAID or
NetworkTemplate.OEM_MANAGED_PRIVATE
)
.forEach { oemManaged ->
NetworkIdentity.Builder()
.setOemManaged(oemManaged)
@ -245,14 +283,54 @@ class NetworkIdentityTest {
setNetworkSpecifier(specifier2)
}
val netIdent1 = NetworkIdentity.buildNetworkIdentity(mockContext,
val netIdent1 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(capSUBID1, TEST_IMSI1),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
false,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertEquals(TEST_SUBID1, netIdent1.getSubId())
val netIdent2 = NetworkIdentity.buildNetworkIdentity(mockContext,
val netIdent2 = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(capSUBID2, TEST_IMSI2),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
false,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertEquals(TEST_SUBID2, netIdent2.getSubId())
}
@Test
fun testSetTransportTypes() {
doTestSetTransportTypes(setOf())
doTestSetTransportTypes(setOf(TRANSPORT_WIFI))
doTestSetTransportTypes(setOf(TRANSPORT_CELLULAR, TRANSPORT_VPN, TRANSPORT_ETHERNET))
}
private fun doTestSetTransportTypes(transportTypes: Set<Int>) {
val identity = NetworkIdentity.Builder()
.setType(TYPE_MOBILE) // legacy type, for builder compatibility
.setTransportTypes(transportTypes)
.build()
assertEquals(transportTypes.toSet(), identity.transportTypes)
}
@Test
fun testTransportTypes_fromNetworkStateSnapshot() {
val caps = NetworkCapabilities().apply {
addTransportType(TRANSPORT_SATELLITE)
addTransportType(TRANSPORT_VPN)
addTransportType(TRANSPORT_WIFI)
}
val identity = NetworkIdentity.buildNetworkIdentity(
mockContext,
buildMobileNetworkStateSnapshot(caps, TEST_IMSI1),
false,
TelephonyManager.NETWORK_TYPE_UMTS
)
assertEquals(
setOf(TRANSPORT_SATELLITE, TRANSPORT_VPN, TRANSPORT_WIFI),
identity.transportTypes
)
}
}

View file

@ -17,6 +17,7 @@
package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.IFACE_ALL;
@ -56,6 +57,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.frameworks.tests.net.R;
import com.android.net.module.util.BitUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@ -249,7 +251,8 @@ public class NetworkStatsCollectionTest {
final NetworkStats.Entry entry = new NetworkStats.Entry();
final NetworkIdentitySet identSet = new NetworkIdentitySet();
identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
TEST_IMSI, null, false, true, true, OEM_NONE, TEST_SUBID));
TEST_IMSI, null, false, true, true, OEM_NONE, TEST_SUBID,
BitUtils.packBits(new int[]{TRANSPORT_CELLULAR})));
int myUid = Process.myUid();
int otherUidInSameUser = Process.myUid() + 1;
@ -511,7 +514,8 @@ public class NetworkStatsCollectionTest {
final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS);
final NetworkIdentitySet ident = new NetworkIdentitySet();
ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
false, true, true, OEM_NONE, TEST_SUBID));
false, true, true, OEM_NONE, TEST_SUBID,
BitUtils.packBits(new int[] {TRANSPORT_CELLULAR})));
large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, 12_730_893_164L, 1, 0, 0, 0));

View file

@ -17,6 +17,7 @@
package com.android.server.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@ -54,6 +55,7 @@ import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import com.android.net.module.util.BitUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@ -300,7 +302,8 @@ public class NetworkStatsObserversTest {
identSet.add(new NetworkIdentity(
TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
IMSI_1, null /* networkId */, false /* roaming */, true /* metered */,
true /* defaultNetwork */, OEM_NONE, SUBID_1));
true /* defaultNetwork */, OEM_NONE, SUBID_1,
BitUtils.packBits(new int[] { TRANSPORT_CELLULAR })));
return identSet;
}