Merge "Add toString() to Ipv6ForwardingRule"
diff --git a/OWNERS_core_networking_xts b/OWNERS_core_networking_xts
index 8083cbf..1844334 100644
--- a/OWNERS_core_networking_xts
+++ b/OWNERS_core_networking_xts
@@ -1,7 +1,7 @@
lorenzo@google.com
satk@google.com #{LAST_RESORT_SUGGESTION}
-# For cherry-picks of CLs that are already merged in aosp/master.
+# For cherry-picks of CLs that are already merged in aosp/master, or flaky test fixes.
jchalard@google.com #{LAST_RESORT_SUGGESTION}
maze@google.com #{LAST_RESORT_SUGGESTION}
reminv@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 6e30fd1..700a085 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -135,6 +135,37 @@
}
]
},
+ // Test with APK modules only, in cases where APEX is not supported, or the other modules
+ // were simply not updated
+ {
+ "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
+ }
+ ]
+ },
+ // Test with connectivity/tethering module only, to catch integration issues with older versions
+ // of other modules. "new tethering + old NetworkStack" is not a configuration that should
+ // really exist in the field, but there is no strong guarantee, and it is required by MTS
+ // testing for module qualification, where modules are tested independently.
+ {
+ "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ },
{
"name": "bpf_existence_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
},
@@ -159,38 +190,6 @@
{
"name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"keywords": ["sim"]
- },
- // TODO: move to mainline-presubmit when known green.
- // Test with APK modules only, in cases where APEX is not supported, or the other modules were simply not updated
- {
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.RequiresDevice"
- },
- {
- "exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
- }
- ]
- },
- // TODO: move to mainline-presubmit when known green.
- // Test with connectivity/tethering module only, to catch integration issues with older versions of other modules.
- // "new tethering + old NetworkStack" is not a configuration that should really exist in the field, but
- // there is no strong guarantee, and it is required by MTS testing for module qualification, where modules
- // are tested independently.
- {
- "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.RequiresDevice"
- }
- ]
}
],
"imports": [
diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags
index 2905e28..109bbda 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -1,7 +1,10 @@
# Keep class's integer static field for MessageUtils to parsing their name.
--keep class com.android.networkstack.tethering.Tethering$TetherMainSM {
- static final int CMD_*;
- static final int EVENT_*;
+-keepclassmembers class com.android.server.**,android.net.**,com.android.networkstack.** {
+ static final % POLICY_*;
+ static final % NOTIFY_TYPE_*;
+ static final % TRANSPORT_*;
+ static final % CMD_*;
+ static final % EVENT_*;
}
-keep class com.android.networkstack.tethering.util.BpfMap {
@@ -21,12 +24,8 @@
*;
}
--keepclassmembers class android.net.ip.IpServer {
- static final int CMD_*;
-}
-
# The lite proto runtime uses reflection to access fields based on the names in
# the schema, keep all the fields.
-keepclassmembers class * extends com.android.networkstack.tethering.protobuf.MessageLite {
<fields>;
-}
\ No newline at end of file
+}
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index d8e631e..2c6054d 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -75,6 +75,8 @@
.setUserType(userTypeToEnum(callerPkg))
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(ErrorCode.EC_NO_ERROR)
+ .setUpstreamEvents(UpstreamEvents.newBuilder())
+ .setDurationMillis(0)
.build();
mBuilderMap.put(downstreamType, statsBuilder);
}
@@ -110,7 +112,8 @@
reported.getErrorCode().getNumber(),
reported.getDownstreamType().getNumber(),
reported.getUpstreamType().getNumber(),
- reported.getUserType().getNumber());
+ reported.getUserType().getNumber(),
+ null, 0);
if (DBG) {
Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
+ ", downstreamType: " + reported.getDownstreamType().getNumber()
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
index 46a47af..27f2126 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
@@ -21,12 +21,38 @@
import "frameworks/proto_logging/stats/enums/stats/connectivity/tethering.proto";
+// Logs each upstream for a successful switch over
+message UpstreamEvent {
+ // Transport type of upstream network
+ optional .android.stats.connectivity.UpstreamType upstream_type = 1;
+
+ // A time period that an upstream continued
+ optional int64 duration_millis = 2;
+}
+
+message UpstreamEvents {
+ repeated UpstreamEvent upstream_event = 1;
+}
+
/**
* Logs Tethering events
*/
message NetworkTetheringReported {
- optional .android.stats.connectivity.ErrorCode error_code = 1;
- optional .android.stats.connectivity.DownstreamType downstream_type = 2;
- optional .android.stats.connectivity.UpstreamType upstream_type = 3;
- optional .android.stats.connectivity.UserType user_type = 4;
+ // Tethering error code
+ optional .android.stats.connectivity.ErrorCode error_code = 1;
+
+ // Tethering downstream type
+ optional .android.stats.connectivity.DownstreamType downstream_type = 2;
+
+ // Transport type of upstream network
+ optional .android.stats.connectivity.UpstreamType upstream_type = 3 [deprecated = true];
+
+ // The user type of switching tethering
+ optional .android.stats.connectivity.UserType user_type = 4;
+
+ // Log each transport type of upstream network event
+ optional UpstreamEvents upstream_events = 5;
+
+ // A time period that a downstreams exists
+ optional int64 duration_millis = 6;
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index 6a85718..7fdde97 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -88,6 +88,8 @@
.setUserType(user)
.setUpstreamType(UpstreamType.UT_UNKNOWN)
.setErrorCode(error)
+ .setUpstreamEvents(UpstreamEvents.newBuilder())
+ .setDurationMillis(0)
.build();
verify(mTetheringMetrics).write(expectedReport);
}
diff --git a/framework-t/src/android/net/NetworkIdentity.java b/framework-t/src/android/net/NetworkIdentity.java
index 350ed86..48e5092 100644
--- a/framework-t/src/android/net/NetworkIdentity.java
+++ b/framework-t/src/android/net/NetworkIdentity.java
@@ -34,8 +34,8 @@
import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.BitUtils;
import com.android.net.module.util.CollectionUtils;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import java.lang.annotation.Retention;
@@ -172,7 +172,7 @@
if (oemManaged == OEM_NONE) {
return "OEM_NONE";
}
- final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+ final int[] bitPositions = BitUtils.unpackBits(oemManaged);
final ArrayList<String> oemManagedNames = new ArrayList<String>();
for (int position : bitPositions) {
oemManagedNames.add(nameOfOemManaged(1 << position));
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index ea8a3df..e7d049f 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
package android.net;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
import android.annotation.IntDef;
import android.annotation.LongDef;
@@ -36,6 +37,7 @@
import android.util.Range;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.BitUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
@@ -185,10 +187,18 @@
NET_ENTERPRISE_ID_4,
NET_ENTERPRISE_ID_5,
})
-
public @interface EnterpriseId {
}
+ private static final int ALL_VALID_ENTERPRISE_IDS;
+ static {
+ int enterpriseIds = 0;
+ for (int i = NET_ENTERPRISE_ID_1; i <= NET_ENTERPRISE_ID_5; ++i) {
+ enterpriseIds |= 1 << i;
+ }
+ ALL_VALID_ENTERPRISE_IDS = enterpriseIds;
+ }
+
/**
* Bitfield representing the network's enterprise capability identifier. If any are specified
* they will be satisfied by any Network that matches all of them.
@@ -209,7 +219,7 @@
if (hasCapability(NET_CAPABILITY_ENTERPRISE) && mEnterpriseId == 0) {
return new int[]{NET_ENTERPRISE_ID_1};
}
- return NetworkCapabilitiesUtils.unpackBits(mEnterpriseId);
+ return BitUtils.unpackBits(mEnterpriseId);
}
/**
@@ -622,11 +632,20 @@
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
+ private static final int ALL_VALID_CAPABILITIES;
+ static {
+ int caps = 0;
+ for (int i = MIN_NET_CAPABILITY; i <= MAX_NET_CAPABILITY; ++i) {
+ caps |= 1 << i;
+ }
+ ALL_VALID_CAPABILITIES = caps;
+ }
+
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
* network is connected.
*/
- private static final long MUTABLE_CAPABILITIES = NetworkCapabilitiesUtils.packBitList(
+ private static final long MUTABLE_CAPABILITIES = BitUtils.packBitList(
// TRUSTED can change when user explicitly connects to an untrusted network in Settings.
// http://b/18206275
NET_CAPABILITY_TRUSTED,
@@ -661,7 +680,7 @@
/**
* Capabilities that are set by default when the object is constructed.
*/
- private static final long DEFAULT_CAPABILITIES = NetworkCapabilitiesUtils.packBitList(
+ private static final long DEFAULT_CAPABILITIES = BitUtils.packBitList(
NET_CAPABILITY_NOT_RESTRICTED,
NET_CAPABILITY_TRUSTED,
NET_CAPABILITY_NOT_VPN);
@@ -670,7 +689,7 @@
* Capabilities that are managed by ConnectivityService.
*/
private static final long CONNECTIVITY_MANAGED_CAPABILITIES =
- NetworkCapabilitiesUtils.packBitList(
+ BitUtils.packBitList(
NET_CAPABILITY_VALIDATED,
NET_CAPABILITY_CAPTIVE_PORTAL,
NET_CAPABILITY_FOREGROUND,
@@ -683,7 +702,7 @@
* INTERNET, IMS, SUPL, etc.
*/
private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
- NetworkCapabilitiesUtils.packBitList(
+ BitUtils.packBitList(
NET_CAPABILITY_NOT_METERED,
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_NOT_RESTRICTED,
@@ -783,7 +802,7 @@
* @return an array of capability values for this instance.
*/
public @NonNull @NetCapability int[] getCapabilities() {
- return NetworkCapabilitiesUtils.unpackBits(mNetworkCapabilities);
+ return BitUtils.unpackBits(mNetworkCapabilities);
}
/**
@@ -793,7 +812,7 @@
* @hide
*/
public @NetCapability int[] getForbiddenCapabilities() {
- return NetworkCapabilitiesUtils.unpackBits(mForbiddenNetworkCapabilities);
+ return BitUtils.unpackBits(mForbiddenNetworkCapabilities);
}
@@ -805,8 +824,8 @@
*/
public void setCapabilities(@NetCapability int[] capabilities,
@NetCapability int[] forbiddenCapabilities) {
- mNetworkCapabilities = NetworkCapabilitiesUtils.packBits(capabilities);
- mForbiddenNetworkCapabilities = NetworkCapabilitiesUtils.packBits(forbiddenCapabilities);
+ mNetworkCapabilities = BitUtils.packBits(capabilities);
+ mForbiddenNetworkCapabilities = BitUtils.packBits(forbiddenCapabilities);
}
/**
@@ -943,7 +962,7 @@
& NON_REQUESTABLE_CAPABILITIES;
if (nonRequestable != 0) {
- return capabilityNameOf(NetworkCapabilitiesUtils.unpackBits(nonRequestable)[0]);
+ return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
@@ -1146,6 +1165,15 @@
/** @hide */
public static final int MAX_TRANSPORT = TRANSPORT_USB;
+ private static final int ALL_VALID_TRANSPORTS;
+ static {
+ int transports = 0;
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; ++i) {
+ transports |= 1 << i;
+ }
+ ALL_VALID_TRANSPORTS = transports;
+ }
+
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
@@ -1167,7 +1195,7 @@
* Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
*/
private static final long UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
- NetworkCapabilitiesUtils.packBitList(
+ BitUtils.packBitList(
TRANSPORT_TEST,
// Test eth networks are created with EthernetManager#setIncludeTestInterfaces
TRANSPORT_ETHERNET,
@@ -1232,7 +1260,7 @@
*/
@SystemApi
@NonNull public @Transport int[] getTransportTypes() {
- return NetworkCapabilitiesUtils.unpackBits(mTransportTypes);
+ return BitUtils.unpackBits(mTransportTypes);
}
/**
@@ -1242,7 +1270,7 @@
* @hide
*/
public void setTransportTypes(@Transport int[] transportTypes) {
- mTransportTypes = NetworkCapabilitiesUtils.packBits(transportTypes);
+ mTransportTypes = BitUtils.packBits(transportTypes);
}
/**
@@ -2015,9 +2043,9 @@
long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
long newImmutableCapabilities = that.mNetworkCapabilities & mask;
if (oldImmutableCapabilities != newImmutableCapabilities) {
- String before = capabilityNamesOf(NetworkCapabilitiesUtils.unpackBits(
+ String before = capabilityNamesOf(BitUtils.unpackBits(
oldImmutableCapabilities));
- String after = capabilityNamesOf(NetworkCapabilitiesUtils.unpackBits(
+ String after = capabilityNamesOf(BitUtils.unpackBits(
newImmutableCapabilities));
joiner.add(String.format("immutable capabilities changed: %s -> %s", before, after));
}
@@ -2114,9 +2142,9 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mNetworkCapabilities);
- dest.writeLong(mForbiddenNetworkCapabilities);
- dest.writeLong(mTransportTypes);
+ dest.writeLong(mNetworkCapabilities & ALL_VALID_CAPABILITIES);
+ dest.writeLong(mForbiddenNetworkCapabilities & ALL_VALID_CAPABILITIES);
+ dest.writeLong(mTransportTypes & ALL_VALID_TRANSPORTS);
dest.writeInt(mLinkUpBandwidthKbps);
dest.writeInt(mLinkDownBandwidthKbps);
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
@@ -2132,7 +2160,7 @@
dest.writeString(mRequestorPackageName);
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
dest.writeTypedList(mUnderlyingNetworks);
- dest.writeInt(mEnterpriseId);
+ dest.writeInt(mEnterpriseId & ALL_VALID_ENTERPRISE_IDS);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -2140,10 +2168,10 @@
@Override
public NetworkCapabilities createFromParcel(Parcel in) {
NetworkCapabilities netCap = new NetworkCapabilities();
-
- netCap.mNetworkCapabilities = in.readLong();
- netCap.mForbiddenNetworkCapabilities = in.readLong();
- netCap.mTransportTypes = in.readLong();
+ // Validate the unparceled data, in case the parceling party was malicious.
+ netCap.mNetworkCapabilities = in.readLong() & ALL_VALID_CAPABILITIES;
+ netCap.mForbiddenNetworkCapabilities = in.readLong() & ALL_VALID_CAPABILITIES;
+ netCap.mTransportTypes = in.readLong() & ALL_VALID_TRANSPORTS;
netCap.mLinkUpBandwidthKbps = in.readInt();
netCap.mLinkDownBandwidthKbps = in.readInt();
netCap.mNetworkSpecifier = in.readParcelable(null);
@@ -2167,7 +2195,7 @@
netCap.mSubIds.add(subIdInts[i]);
}
netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
- netCap.mEnterpriseId = in.readInt();
+ netCap.mEnterpriseId = in.readInt() & ALL_VALID_ENTERPRISE_IDS;
return netCap;
}
@Override
@@ -2287,32 +2315,6 @@
return sb.toString();
}
-
- private interface NameOf {
- String nameOf(int value);
- }
-
- /**
- * @hide
- */
- public static void appendStringRepresentationOfBitMaskToStringBuilder(@NonNull StringBuilder sb,
- long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator) {
- int bitPos = 0;
- boolean firstElementAdded = false;
- while (bitMask != 0) {
- if ((bitMask & 1) != 0) {
- if (firstElementAdded) {
- sb.append(separator);
- } else {
- firstElementAdded = true;
- }
- sb.append(nameFetcher.nameOf(bitPos));
- }
- bitMask >>= 1;
- ++bitPos;
- }
- }
-
/**
* @hide
*/
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 2196bf8..00f6c56 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -469,7 +469,9 @@
// TODO: Update this logic to only do a restart if required. Although a restart may
// be required due to the capabilities or ipConfiguration values, not all
// capabilities changes require a restart.
- restart();
+ if (mIpClient != null) {
+ restart();
+ }
}
boolean isRestricted() {
@@ -558,6 +560,13 @@
void updateNeighborLostEvent(String logMsg) {
Log.i(TAG, "updateNeighborLostEvent " + logMsg);
+ if (mIpConfig.getIpAssignment() == IpAssignment.STATIC) {
+ // Ignore NUD failures for static IP configurations, where restarting the IpClient
+ // will not fix connectivity.
+ // In this scenario, NetworkMonitor will not verify the network, so it will
+ // eventually be torn down.
+ return;
+ }
// Reachability lost will be seen only if the gateway is not reachable.
// Since ethernet FW doesn't have the mechanism to scan for new networks
// like WiFi, simply restart.
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 6ec478b..95baf81 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -302,9 +302,14 @@
final int state = getInterfaceState(iface);
final int role = getInterfaceRole(iface);
final IpConfiguration config = getIpConfigurationForCallback(iface, state);
+ final boolean isRestricted = isRestrictedInterface(iface);
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
+ if (isRestricted) {
+ final ListenerInfo info = (ListenerInfo) mListeners.getBroadcastCookie(i);
+ if (!info.canUseRestrictedNetworks) continue;
+ }
mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config);
} catch (RemoteException e) {
// Do nothing here.
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 0da7b6f..629bf73 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -56,7 +56,6 @@
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.system.OsConstants.ENOENT;
-import static android.system.OsConstants.R_OK;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -134,7 +133,6 @@
import android.service.NetworkInterfaceProto;
import android.service.NetworkStatsServiceDumpProto;
import android.system.ErrnoException;
-import android.system.Os;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionPlan;
import android.text.TextUtils;
@@ -254,6 +252,8 @@
"/sys/fs/bpf/netd_shared/map_netd_stats_map_A";
private static final String STATS_MAP_B_PATH =
"/sys/fs/bpf/netd_shared/map_netd_stats_map_B";
+ private static final String IFACE_STATS_MAP_PATH =
+ "/sys/fs/bpf/netd_shared/map_netd_iface_stats_map";
/**
* DeviceConfig flag used to indicate whether the files should be stored in the apex data
@@ -413,6 +413,7 @@
private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapA;
private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapB;
private final IBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap;
+ private final IBpfMap<S32, StatsMapValue> mIfaceStatsMap;
/** Data layer operation counters for splicing into other structures. */
private NetworkStats mUidOperations = new NetworkStats(0L, 10);
@@ -592,6 +593,7 @@
mStatsMapA = mDeps.getStatsMapA();
mStatsMapB = mDeps.getStatsMapB();
mAppUidStatsMap = mDeps.getAppUidStatsMap();
+ mIfaceStatsMap = mDeps.getIfaceStatsMap();
// TODO: Remove bpfNetMaps creation and always start SkDestroyListener
// Following code is for the experiment to verify the SkDestroyListener refactoring. Based
@@ -795,6 +797,16 @@
}
}
+ /** Gets interface stats map */
+ public IBpfMap<S32, StatsMapValue> getIfaceStatsMap() {
+ try {
+ return new BpfMap<S32, StatsMapValue>(IFACE_STATS_MAP_PATH,
+ BpfMap.BPF_F_RDWR, S32.class, StatsMapValue.class);
+ } catch (ErrnoException e) {
+ throw new IllegalStateException("Failed to open interface stats map", e);
+ }
+ }
+
/** Gets whether the build is userdebug. */
public boolean isDebuggable() {
return Build.isDebuggable();
@@ -2763,6 +2775,7 @@
dumpAppUidStatsMapLocked(pw);
dumpStatsMapLocked(mStatsMapA, pw, "mStatsMapA");
dumpStatsMapLocked(mStatsMapB, pw, "mStatsMapB");
+ dumpIfaceStatsMapLocked(pw);
pw.decreaseIndent();
}
}
@@ -2830,26 +2843,14 @@
}
}
- private <K extends Struct, V extends Struct> String getMapStatus(
- final IBpfMap<K, V> map, final String path) {
- if (map != null) {
- return "OK";
- }
- try {
- Os.access(path, R_OK);
- return "NULL(map is pinned to " + path + ")";
- } catch (ErrnoException e) {
- return "NULL(map is not pinned to " + path + ": " + Os.strerror(e.errno) + ")";
- }
- }
-
private void dumpMapStatus(final IndentingPrintWriter pw) {
- pw.println("mCookieTagMap: " + getMapStatus(mCookieTagMap, COOKIE_TAG_MAP_PATH));
- pw.println("mUidCounterSetMap: "
- + getMapStatus(mUidCounterSetMap, UID_COUNTERSET_MAP_PATH));
- pw.println("mAppUidStatsMap: " + getMapStatus(mAppUidStatsMap, APP_UID_STATS_MAP_PATH));
- pw.println("mStatsMapA: " + getMapStatus(mStatsMapA, STATS_MAP_A_PATH));
- pw.println("mStatsMapB: " + getMapStatus(mStatsMapB, STATS_MAP_B_PATH));
+ BpfDump.dumpMapStatus(mCookieTagMap, pw, "mCookieTagMap", COOKIE_TAG_MAP_PATH);
+ BpfDump.dumpMapStatus(mUidCounterSetMap, pw, "mUidCounterSetMap", UID_COUNTERSET_MAP_PATH);
+ BpfDump.dumpMapStatus(mAppUidStatsMap, pw, "mAppUidStatsMap", APP_UID_STATS_MAP_PATH);
+ BpfDump.dumpMapStatus(mStatsMapA, pw, "mStatsMapA", STATS_MAP_A_PATH);
+ BpfDump.dumpMapStatus(mStatsMapB, pw, "mStatsMapB", STATS_MAP_B_PATH);
+ // mIfaceStatsMap is always not null but dump status to be consistent with other maps.
+ BpfDump.dumpMapStatus(mIfaceStatsMap, pw, "mIfaceStatsMap", IFACE_STATS_MAP_PATH);
}
@GuardedBy("mStatsLock")
@@ -2909,6 +2910,21 @@
});
}
+ @GuardedBy("mStatsLock")
+ private void dumpIfaceStatsMapLocked(final IndentingPrintWriter pw) {
+ BpfDump.dumpMap(mIfaceStatsMap, pw, "mIfaceStatsMap",
+ "ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets",
+ (key, value) -> {
+ final String ifName = mInterfaceMapUpdater.getIfNameByIndex(key.val);
+ return key.val + " "
+ + (ifName != null ? ifName : "unknown") + " "
+ + value.rxBytes + " "
+ + value.rxPackets + " "
+ + value.txBytes + " "
+ + value.txPackets;
+ });
+ }
+
private NetworkStats readNetworkStatsSummaryDev() {
try {
return mStatsFactory.readNetworkStatsSummaryDev();
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 49b23c9..fc76ae5 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -647,91 +647,7 @@
dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
}
- // Print ifaceStatsMap content
- std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
- " txPackets");
- dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
- const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
- const BpfMap<uint32_t, StatsValue>&) {
- auto ifname = mIfaceIndexNameMap.readValue(key);
- if (!ifname.ok()) {
- ifname = IfaceValue{"unknown"};
- }
- dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
- value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
- return base::Result<void>();
- };
- res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
- if (!res.ok()) {
- dw.println("mIfaceStatsMap print end with error: %s", res.error().message().c_str());
- }
-
dw.blankline();
-
- uint32_t key = UID_RULES_CONFIGURATION_KEY;
- auto configuration = mConfigurationMap.readValue(key);
- if (configuration.ok()) {
- dw.println("current ownerMatch configuration: %d%s", configuration.value(),
- uidMatchTypeToString(configuration.value()).c_str());
- } else {
- dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
- configuration.error().message().c_str());
- }
-
- key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
- configuration = mConfigurationMap.readValue(key);
- if (configuration.ok()) {
- const char* statsMapDescription = "???";
- switch (configuration.value()) {
- case SELECT_MAP_A:
- statsMapDescription = "SELECT_MAP_A";
- break;
- case SELECT_MAP_B:
- statsMapDescription = "SELECT_MAP_B";
- break;
- // No default clause, so if we ever add a third map, this code will fail to build.
- }
- dw.println("current statsMap configuration: %d %s", configuration.value(),
- statsMapDescription);
- } else {
- dw.println("mConfigurationMap read stats map configure failed with error: %s",
- configuration.error().message().c_str());
- }
- dumpBpfMap("mUidOwnerMap", dw, "");
- const auto printUidMatchInfo = [&dw, this](const uint32_t& key, const UidOwnerValue& value,
- const BpfMap<uint32_t, UidOwnerValue>&) {
- if (value.rule & IIF_MATCH) {
- auto ifname = mIfaceIndexNameMap.readValue(value.iif);
- if (ifname.ok()) {
- dw.println("%u %s %s", key, uidMatchTypeToString(value.rule).c_str(),
- ifname.value().name);
- } else {
- dw.println("%u %s %u", key, uidMatchTypeToString(value.rule).c_str(), value.iif);
- }
- } else {
- dw.println("%u %s", key, uidMatchTypeToString(value.rule).c_str());
- }
- return base::Result<void>();
- };
- res = mUidOwnerMap.iterateWithValue(printUidMatchInfo);
- if (!res.ok()) {
- dw.println("mUidOwnerMap print end with error: %s", res.error().message().c_str());
- }
- dumpBpfMap("mUidPermissionMap", dw, "");
- const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
- const BpfMap<uint32_t, uint8_t>&) {
- dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
- return base::Result<void>();
- };
- res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
- if (!res.ok()) {
- dw.println("mUidPermissionMap print end with error: %s", res.error().message().c_str());
- }
-
- dumpBpfMap("mPrivilegedUser", dw, "");
- for (uid_t uid : mPrivilegedUser) {
- dw.println("%u ALLOW_UPDATE_DEVICE_STATS", (uint32_t)uid);
- }
}
} // namespace net
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 8525738..6cb0940 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -793,16 +793,6 @@
"mCookieTagMap:",
fmt::format("cookie={} tag={:#x} uid={}", TEST_COOKIE, TEST_TAG, TEST_UID)};
- ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, HAPPY_BOX_MATCH,
- TrafficController::IptOpInsert)));
- expectedLines.emplace_back("mUidOwnerMap:");
- expectedLines.emplace_back(fmt::format("{} HAPPY_BOX_MATCH", TEST_UID));
-
- mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, {TEST_UID2});
- expectedLines.emplace_back("mUidPermissionMap:");
- expectedLines.emplace_back(fmt::format("{} BPF_PERMISSION_UPDATE_DEVICE_STATS", TEST_UID2));
- expectedLines.emplace_back("mPrivilegedUser:");
- expectedLines.emplace_back(fmt::format("{} ALLOW_UPDATE_DEVICE_STATS", TEST_UID2));
EXPECT_TRUE(expectDumpsysContains(expectedLines));
}
@@ -813,59 +803,12 @@
"Bad file descriptor";
const std::string kErrReadRulesConfig = "read ownerMatch configure failed with error: "
"Read value of map -1 failed: Bad file descriptor";
- const std::string kErrReadStatsMapConfig = "read stats map configure failed with error: "
- "Read value of map -1 failed: Bad file descriptor";
std::vector<std::string> expectedLines = {
- fmt::format("mCookieTagMap {}", kErrIterate),
- fmt::format("mIfaceStatsMap {}", kErrIterate),
- fmt::format("mConfigurationMap {}", kErrReadRulesConfig),
- fmt::format("mConfigurationMap {}", kErrReadStatsMapConfig),
- fmt::format("mUidOwnerMap {}", kErrIterate),
- fmt::format("mUidPermissionMap {}", kErrIterate)};
+ fmt::format("mCookieTagMap {}", kErrIterate)};
EXPECT_TRUE(expectDumpsysContains(expectedLines));
}
-TEST_F(TrafficControllerTest, uidMatchTypeToString) {
- // NO_MATCH(0) can't be verified because match type flag is added by OR operator.
- // See TrafficController::addRule()
- static const struct TestConfig {
- UidOwnerMatchType uidOwnerMatchType;
- std::string expected;
- } testConfigs[] = {
- // clang-format off
- {HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"},
- {DOZABLE_MATCH, "DOZABLE_MATCH"},
- {STANDBY_MATCH, "STANDBY_MATCH"},
- {POWERSAVE_MATCH, "POWERSAVE_MATCH"},
- {HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"},
- {RESTRICTED_MATCH, "RESTRICTED_MATCH"},
- {LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH"},
- {IIF_MATCH, "IIF_MATCH"},
- {LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH"},
- {OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH"},
- {OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH"},
- {OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH"},
- // clang-format on
- };
-
- for (const auto& config : testConfigs) {
- SCOPED_TRACE(fmt::format("testConfig: [{}, {}]", config.uidOwnerMatchType,
- config.expected));
-
- // Test private function uidMatchTypeToString() via dumpsys.
- ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, config.uidOwnerMatchType,
- TrafficController::IptOpInsert)));
- std::vector<std::string> expectedLines;
- expectedLines.emplace_back(fmt::format("{} {}", TEST_UID, config.expected));
- EXPECT_TRUE(expectDumpsysContains(expectedLines));
-
- // Clean up the stubs.
- ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, config.uidOwnerMatchType,
- TrafficController::IptOpDelete)));
- }
-}
-
TEST_F(TrafficControllerTest, getFirewallType) {
static const struct TestConfig {
ChildChain childChain;
diff --git a/service/proguard.flags b/service/proguard.flags
index 478566c..864a28b 100644
--- a/service/proguard.flags
+++ b/service/proguard.flags
@@ -6,3 +6,12 @@
-keepclassmembers public class * extends **.com.android.net.module.util.Struct {
*;
}
+
+-keepclassmembers class com.android.server.**,android.net.**,com.android.networkstack.** {
+ static final % POLICY_*;
+ static final % NOTIFY_TYPE_*;
+ static final % TRANSPORT_*;
+ static final % CMD_*;
+ static final % EVENT_*;
+}
+
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index dbfc383..d560747 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -27,7 +27,9 @@
import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
import static android.net.INetd.PERMISSION_INTERNET;
+import static android.net.INetd.PERMISSION_NONE;
import static android.net.INetd.PERMISSION_UNINSTALLED;
+import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOENT;
@@ -44,12 +46,15 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Log;
+import android.util.Pair;
import android.util.StatsEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfDump;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.IBpfMap;
@@ -62,8 +67,10 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.StringJoiner;
/**
* BpfNetMaps is responsible for providing traffic controller relevant functionality.
@@ -134,6 +141,25 @@
@VisibleForTesting public static final long OEM_DENY_3_MATCH = (1 << 11);
// LINT.ThenChange(packages/modules/Connectivity/bpf_progs/bpf_shared.h)
+ private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
+ Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
+ Pair.create(PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
+ );
+ private static final List<Pair<Long, String>> MATCH_LIST = Arrays.asList(
+ Pair.create(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"),
+ Pair.create(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH"),
+ Pair.create(DOZABLE_MATCH, "DOZABLE_MATCH"),
+ Pair.create(STANDBY_MATCH, "STANDBY_MATCH"),
+ Pair.create(POWERSAVE_MATCH, "POWERSAVE_MATCH"),
+ Pair.create(RESTRICTED_MATCH, "RESTRICTED_MATCH"),
+ Pair.create(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH"),
+ Pair.create(IIF_MATCH, "IIF_MATCH"),
+ Pair.create(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH"),
+ Pair.create(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH"),
+ Pair.create(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH"),
+ Pair.create(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH")
+ );
+
/**
* Set sEnableJavaBpfMap for test.
*/
@@ -914,14 +940,80 @@
return StatsManager.PULL_SUCCESS;
}
+ private String permissionToString(int permissionMask) {
+ if (permissionMask == PERMISSION_NONE) {
+ return "PERMISSION_NONE";
+ }
+ if (permissionMask == PERMISSION_UNINSTALLED) {
+ // PERMISSION_UNINSTALLED should never appear in the map
+ return "PERMISSION_UNINSTALLED error!";
+ }
+
+ final StringJoiner sj = new StringJoiner(" ");
+ for (Pair<Integer, String> permission: PERMISSION_LIST) {
+ final int permissionFlag = permission.first;
+ final String permissionName = permission.second;
+ if ((permissionMask & permissionFlag) != 0) {
+ sj.add(permissionName);
+ permissionMask &= ~permissionFlag;
+ }
+ }
+ if (permissionMask != 0) {
+ sj.add("PERMISSION_UNKNOWN(" + permissionMask + ")");
+ }
+ return sj.toString();
+ }
+
+ private String matchToString(long matchMask) {
+ if (matchMask == NO_MATCH) {
+ return "NO_MATCH";
+ }
+
+ final StringJoiner sj = new StringJoiner(" ");
+ for (Pair<Long, String> match: MATCH_LIST) {
+ final long matchFlag = match.first;
+ final String matchName = match.second;
+ if ((matchMask & matchFlag) != 0) {
+ sj.add(matchName);
+ matchMask &= ~matchFlag;
+ }
+ }
+ if (matchMask != 0) {
+ sj.add("UNKNOWN_MATCH(" + matchMask + ")");
+ }
+ return sj.toString();
+ }
+
+ private void dumpOwnerMatchConfig(final IndentingPrintWriter pw) {
+ try {
+ final long match = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val;
+ pw.println("current ownerMatch configuration: " + match + " " + matchToString(match));
+ } catch (ErrnoException e) {
+ pw.println("Failed to read ownerMatch configuration: " + e);
+ }
+ }
+
+ private void dumpCurrentStatsMapConfig(final IndentingPrintWriter pw) {
+ try {
+ final long config = sConfigurationMap.getValue(CURRENT_STATS_MAP_CONFIGURATION_KEY).val;
+ final String currentStatsMap =
+ (config == STATS_SELECT_MAP_A) ? "SELECT_MAP_A" : "SELECT_MAP_B";
+ pw.println("current statsMap configuration: " + config + " " + currentStatsMap);
+ } catch (ErrnoException e) {
+ pw.println("Falied to read current statsMap configuration: " + e);
+ }
+ }
+
/**
* Dump BPF maps
*
+ * @param pw print writer
* @param fd file descriptor to output
+ * @param verbose verbose dump flag, if true dump the BpfMap contents
* @throws IOException when file descriptor is invalid.
* @throws ServiceSpecificException when the method is called on an unsupported device.
*/
- public void dump(final FileDescriptor fd, boolean verbose)
+ public void dump(final IndentingPrintWriter pw, final FileDescriptor fd, boolean verbose)
throws IOException, ServiceSpecificException {
if (PRE_T) {
throw new ServiceSpecificException(
@@ -929,6 +1021,24 @@
+ " devices, use dumpsys netd trafficcontroller instead.");
}
mDeps.nativeDump(fd, verbose);
+
+ if (verbose) {
+ dumpOwnerMatchConfig(pw);
+ dumpCurrentStatsMapConfig(pw);
+ pw.println();
+
+ BpfDump.dumpMap(sUidOwnerMap, pw, "sUidOwnerMap",
+ (uid, match) -> {
+ if ((match.rule & IIF_MATCH) != 0) {
+ // TODO: convert interface index to interface name by IfaceIndexNameMap
+ return uid.val + " " + matchToString(match.rule) + " " + match.iif;
+ } else {
+ return uid.val + " " + matchToString(match.rule);
+ }
+ });
+ BpfDump.dumpMap(sUidPermissionMap, pw, "sUidPermissionMap",
+ (uid, permission) -> uid.val + " " + permissionToString(permission.val));
+ }
}
private static native void native_init(boolean startSkDestroyListener);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 84cf561..d409d51 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -249,13 +249,13 @@
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BitUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.LocationPermissionChecker;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.PerUidCounter;
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.TcUtils;
@@ -1982,6 +1982,9 @@
@Nullable
public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
enforceAccessPermission();
+ if (uid != mDeps.getCallingUid()) {
+ enforceNetworkStackPermission(mContext);
+ }
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return null;
return getFilteredNetworkInfo(nai, uid, ignoreBlocked);
@@ -3457,7 +3460,7 @@
private void dumpTrafficController(IndentingPrintWriter pw, final FileDescriptor fd,
boolean verbose) {
try {
- mBpfNetMaps.dump(fd, verbose);
+ mBpfNetMaps.dump(pw, fd, verbose);
} catch (ServiceSpecificException e) {
pw.println(e.getMessage());
} catch (IOException e) {
@@ -3647,8 +3650,18 @@
break;
}
case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: {
- // If nai is not yet created, or is already destroyed, ignore.
- if (!shouldDestroyNativeNetwork(nai)) break;
+ if (!nai.isCreated()) {
+ Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString()
+ + ", tearing down instead");
+ teardownUnneededNetwork(nai);
+ break;
+ }
+
+ if (nai.isDestroyed()) {
+ Log.d(TAG, "unregisterAfterReplacement on destroyed " + nai.toShortString()
+ + ", ignoring");
+ break;
+ }
final int timeoutMs = (int) arg.second;
if (timeoutMs < 0 || timeoutMs > NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
@@ -7830,7 +7843,7 @@
@NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
underlyingNetworks = underlyingNetworksOrDefault(
agentCaps.getOwnerUid(), underlyingNetworks);
- long transportTypes = NetworkCapabilitiesUtils.packBits(agentCaps.getTransportTypes());
+ long transportTypes = BitUtils.packBits(agentCaps.getTransportTypes());
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
// metered if any underlying is metered, or originally declared metered by the agent.
@@ -7883,7 +7896,7 @@
suspended = false;
}
- newNc.setTransportTypes(NetworkCapabilitiesUtils.unpackBits(transportTypes));
+ newNc.setTransportTypes(BitUtils.unpackBits(transportTypes));
newNc.setLinkDownstreamBandwidthKbps(downKbps);
newNc.setLinkUpstreamBandwidthKbps(upKbps);
newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
diff --git a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
index 93cf748..b6e9b95 100644
--- a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
+++ b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
@@ -19,7 +19,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static com.android.net.module.util.NetworkCapabilitiesUtils.unpackBits;
+import static com.android.net.module.util.BitUtils.unpackBits;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8dbcc00..4887a78 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -270,7 +270,10 @@
private static final int MIN_KEEPALIVE_INTERVAL = 10;
private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
- private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 5_000;
+ // Timeout for waiting network to be validated. Set the timeout to 30s, which is more than
+ // DNS timeout.
+ // TODO(b/252972908): reset the original timer when aosp/2188755 is ramped up.
+ private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 30_000;
private static final int NO_CALLBACK_TIMEOUT_MS = 100;
private static final int SOCKET_TIMEOUT_MS = 100;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
@@ -2751,6 +2754,27 @@
mCm.getActiveNetwork(), false /* accept */ , false /* always */));
}
+ private void ensureCellIsValidatedBeforeMockingValidationUrls() {
+ // Verify that current supported network is validated so that the mock http server will not
+ // apply to unexpected networks. Also see aosp/2208680.
+ //
+ // This may also apply to wifi in principle, but in practice methods that mock validation
+ // URL all disconnect wifi forcefully anyway, so don't wait for wifi to validate.
+ if (mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
+ ensureValidatedNetwork(makeCellNetworkRequest());
+ }
+ }
+
+ private void ensureValidatedNetwork(NetworkRequest request) {
+ final TestableNetworkCallback cb = new TestableNetworkCallback();
+ mCm.registerNetworkCallback(request, cb);
+ cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NET_CAPABILITY_VALIDATED));
+ mCm.unregisterNetworkCallback(cb);
+ }
+
@AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
@Test
public void testAcceptPartialConnectivity_validatedNetwork() throws Exception {
@@ -2882,7 +2906,8 @@
assertTrue(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
- // Configure response code for unvalidated network
+ // The cell network has already been checked to be validated.
+ // Configure response code for unvalidated network.
configTestServer(Status.INTERNAL_ERROR, Status.INTERNAL_ERROR);
mCm.reportNetworkConnectivity(wifiNetwork, false);
// Default network should stay on unvalidated wifi because avoid bad wifi is disabled.
@@ -2970,6 +2995,8 @@
}
private Network prepareValidatedNetwork() throws Exception {
+ ensureCellIsValidatedBeforeMockingValidationUrls();
+
prepareHttpServer();
configTestServer(Status.NO_CONTENT, Status.NO_CONTENT);
// Disconnect wifi first then start wifi network with configuration.
@@ -2980,6 +3007,8 @@
}
private Network preparePartialConnectivity() throws Exception {
+ ensureCellIsValidatedBeforeMockingValidationUrls();
+
prepareHttpServer();
// Configure response code for partial connectivity
configTestServer(Status.INTERNAL_ERROR /* httpsStatusCode */,
@@ -2993,6 +3022,8 @@
}
private Network prepareUnvalidatedNetwork() throws Exception {
+ ensureCellIsValidatedBeforeMockingValidationUrls();
+
prepareHttpServer();
// Configure response code for unvalidated network
configTestServer(Status.INTERNAL_ERROR /* httpsStatusCode */,
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 74e57c9..776e49f 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -41,6 +41,7 @@
import android.net.MacAddress
import android.net.Network
import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
@@ -69,6 +70,7 @@
import com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
+import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.RouterAdvertisementResponder
import com.android.testutils.TapPacketReader
@@ -101,7 +103,10 @@
import kotlin.test.fail
private const val TAG = "EthernetManagerTest"
-private const val TIMEOUT_MS = 2000L
+// This timeout does not affect the test duration for passing tests. It needs to be long enough to
+// account for RS delay (and potentially the first retry interval (4s)). There have been failures
+// where the interface did not gain provisioning within the allotted timeout.
+private const val TIMEOUT_MS = 10_000L
// Timeout used to confirm no callbacks matching given criteria are received. Must be long enough to
// process all callbacks including ip provisioning when using the updateConfiguration API.
// Note that increasing this timeout increases the test duration.
@@ -114,6 +119,10 @@
.addTransportType(TRANSPORT_ETHERNET)
.removeCapability(NET_CAPABILITY_TRUSTED)
.build()
+private val TEST_CAPS = NetworkCapabilities.Builder(ETH_REQUEST.networkCapabilities)
+ .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .build()
private val STATIC_IP_CONFIGURATION = IpConfiguration.Builder()
.setStaticIpConfiguration(StaticIpConfiguration.Builder()
.setIpAddress(LinkAddress("192.0.2.1/30")).build())
@@ -149,6 +158,7 @@
private val raResponder: RouterAdvertisementResponder
private val tnm: TestNetworkManager
val name get() = tapInterface.interfaceName
+ val onLinkPrefix get() = raResponder.prefix
init {
tnm = runAsShell(MANAGE_TEST_NETWORKS) {
@@ -296,8 +306,8 @@
available.completeExceptionally(IllegalStateException("onUnavailable was called"))
}
- fun expectOnAvailable(): String {
- return available.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ fun expectOnAvailable(timeout: Long = TIMEOUT_MS): String {
+ return available.get(timeout, TimeUnit.MILLISECONDS)
}
fun expectOnUnavailable() {
@@ -374,7 +384,10 @@
setIncludeTestInterfaces(false)
for (listener in addedListeners) {
+ // Even if a given listener was not registered as both an interface and ethernet state
+ // listener, calling remove is safe.
em.removeInterfaceStateListener(listener)
+ em.removeEthernetStateListener(listener)
}
registeredCallbacks.forEach { cm.unregisterNetworkCallback(it) }
releaseTetheredInterface()
@@ -402,6 +415,11 @@
addedListeners.add(listener)
}
+ private fun addEthernetStateListener(listener: EthernetStateListener) {
+ em.addEthernetStateListener(handler::post, listener)
+ addedListeners.add(listener)
+ }
+
// WARNING: setting hasCarrier to false requires kernel support. Call
// assumeChangingCarrierSupported() at the top of your test.
private fun createInterface(hasCarrier: Boolean = true): EthernetTestInterface {
@@ -432,14 +450,18 @@
}
private fun requestNetwork(request: NetworkRequest): TestableNetworkCallback {
- return TestableNetworkCallback().also {
+ return TestableNetworkCallback(
+ timeoutMs = TIMEOUT_MS,
+ noCallbackTimeoutMs = NO_CALLBACK_TIMEOUT_MS).also {
cm.requestNetwork(request, it)
registeredCallbacks.add(it)
}
}
private fun registerNetworkListener(request: NetworkRequest): TestableNetworkCallback {
- return TestableNetworkCallback().also {
+ return TestableNetworkCallback(
+ timeoutMs = TIMEOUT_MS,
+ noCallbackTimeoutMs = NO_CALLBACK_TIMEOUT_MS).also {
cm.registerNetworkCallback(request, it)
registeredCallbacks.add(it)
}
@@ -496,13 +518,8 @@
runAsShell(NETWORK_SETTINGS) { em.setEthernetEnabled(enabled) }
val listener = EthernetStateListener()
- em.addEthernetStateListener(handler::post, listener)
- try {
- listener.eventuallyExpect(
- if (enabled) ETHERNET_STATE_ENABLED else ETHERNET_STATE_DISABLED)
- } finally {
- em.removeEthernetStateListener(listener)
- }
+ addEthernetStateListener(listener)
+ listener.eventuallyExpect(if (enabled) ETHERNET_STATE_ENABLED else ETHERNET_STATE_DISABLED)
}
// NetworkRequest.Builder does not create a copy of the passed NetworkRequest, so in order to
@@ -513,18 +530,18 @@
// It can take multiple seconds for the network to become available.
private fun TestableNetworkCallback.expectAvailable() =
- expectCallback<Available>(anyNetwork(), 5000 /* ms timeout */).network
+ expectCallback<Available>().network
private fun TestableNetworkCallback.expectLost(n: Network = anyNetwork()) =
- expectCallback<Lost>(n, 5000 /* ms timeout */)
+ expectCallback<Lost>(n)
// b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use
// eventuallyExpect(Lost::class) instead.
private fun TestableNetworkCallback.eventuallyExpectLost(n: Network? = null) =
- eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true }
+ eventuallyExpect(Lost::class) { n?.equals(it.network) ?: true }
private fun TestableNetworkCallback.assertNeverLost(n: Network? = null) =
- assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS) {
+ assertNoCallbackThat() {
it is Lost && (n?.equals(it.network) ?: true)
}
@@ -536,17 +553,25 @@
it.networkSpecifier == EthernetNetworkSpecifier(name)
}
- private fun TestableNetworkCallback.expectCapabilitiesWith(cap: Int) =
- expectCapabilitiesThat(anyNetwork(), TIMEOUT_MS) {
- it.hasCapability(cap)
+ private fun TestableNetworkCallback.eventuallyExpectCapabilities(nc: NetworkCapabilities) {
+ // b/233534110: eventuallyExpect<CapabilitiesChanged>() does not advance ReadHead.
+ eventuallyExpect(CapabilitiesChanged::class) {
+ // CS may mix in additional capabilities, so NetworkCapabilities#equals cannot be used.
+ // Check if all expected capabilities are present instead.
+ it is CapabilitiesChanged && nc.capabilities.all { c -> it.caps.hasCapability(c) }
}
+ }
- private fun TestableNetworkCallback.expectLinkPropertiesWithLinkAddress(addr: LinkAddress) =
- expectLinkPropertiesThat(anyNetwork(), TIMEOUT_MS) {
- // LinkAddress.equals isn't possible as the system changes the LinkAddress.flags value.
- // any() must be used since the interface may also have a link-local address.
- it.linkAddresses.any { x -> x.isSameAddressAs(addr) }
+ private fun TestableNetworkCallback.eventuallyExpectLpForStaticConfig(
+ config: StaticIpConfiguration
+ ) {
+ // b/233534110: eventuallyExpect<LinkPropertiesChanged>() does not advance ReadHead.
+ eventuallyExpect(LinkPropertiesChanged::class) {
+ it is LinkPropertiesChanged && it.lp.linkAddresses.any { la ->
+ la.isSameAddressAs(config.ipAddress)
+ }
}
+ }
@Test
fun testCallbacks() {
@@ -605,7 +630,7 @@
// see aosp/2123900.
try {
// assumeException does not exist.
- requestTetheredInterface().expectOnAvailable()
+ requestTetheredInterface().expectOnAvailable(NO_CALLBACK_TIMEOUT_MS)
// interface used for tethering is available, throw an assumption error.
assumeTrue(false)
} catch (e: TimeoutException) {
@@ -689,6 +714,8 @@
// install a listener which will later be used to verify the Lost callback
val listenerCb = registerNetworkListener(ETH_REQUEST)
+ // assert the network is only brought up by a request.
+ listenerCb.assertNeverAvailable()
val cb = requestNetwork(ETH_REQUEST)
val network = cb.expectAvailable()
@@ -798,7 +825,10 @@
val iface = createInterface(false /* hasCarrier */)
val cb = requestNetwork(ETH_REQUEST)
- cb.assertNeverAvailable()
+ // TUNSETCARRIER races with the bring up code, so the network *can* become available despite
+ // it being "created with no carrier".
+ // TODO(b/249611919): re-enable assertion once kernel supports IFF_NO_CARRIER.
+ // cb.assertNeverAvailable()
iface.setCarrierEnabled(true)
cb.expectAvailable()
@@ -807,6 +837,19 @@
cb.eventuallyExpectLost()
}
+ // TODO: move to MTS
+ @Test
+ fun testNetworkRequest_linkPropertiesUpdate() {
+ val iface = createInterface()
+ val cb = requestNetwork(ETH_REQUEST)
+ // b/233534110: eventuallyExpect<LinkPropertiesChanged>() does not advance ReadHead
+ cb.eventuallyExpect(LinkPropertiesChanged::class) {
+ it is LinkPropertiesChanged && it.lp.addresses.any {
+ address -> iface.onLinkPrefix.contains(address)
+ }
+ }
+ }
+
@Test
fun testRemoveInterface_whileInServerMode() {
assumeNoInterfaceForTetheringAvailable()
@@ -868,22 +911,10 @@
val iface = createInterface()
val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
val network = cb.expectAvailable()
- cb.assertNeverLost()
- val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
- val nc = NetworkCapabilities
- .Builder(ETH_REQUEST.networkCapabilities)
- .addCapability(testCapability)
- .build()
- updateConfiguration(iface, STATIC_IP_CONFIGURATION, nc).expectResult(iface.name)
-
- // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
- // will be expected first before available, as part of the restart.
- cb.expectLost(network)
- cb.expectAvailable()
- cb.expectCapabilitiesWith(testCapability)
- cb.expectLinkPropertiesWithLinkAddress(
- STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+ updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+ cb.eventuallyExpectCapabilities(TEST_CAPS)
+ cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
}
@Test
@@ -891,17 +922,9 @@
val iface = createInterface()
val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
val network = cb.expectAvailable()
- cb.assertNeverLost()
updateConfiguration(iface, STATIC_IP_CONFIGURATION).expectResult(iface.name)
-
- // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
- // will be expected first before available, as part of the restart.
- cb.expectLost(network)
- cb.expectAvailable()
- cb.expectCallback<CapabilitiesChanged>()
- cb.expectLinkPropertiesWithLinkAddress(
- STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+ cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
}
@Test
@@ -909,20 +932,9 @@
val iface = createInterface()
val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
val network = cb.expectAvailable()
- cb.assertNeverLost()
- val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
- val nc = NetworkCapabilities
- .Builder(ETH_REQUEST.networkCapabilities)
- .addCapability(testCapability)
- .build()
- updateConfiguration(iface, capabilities = nc).expectResult(iface.name)
-
- // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
- // will be expected first before available, as part of the restart.
- cb.expectLost(network)
- cb.expectAvailable()
- cb.expectCapabilitiesWith(testCapability)
+ updateConfiguration(iface, capabilities = TEST_CAPS).expectResult(iface.name)
+ cb.eventuallyExpectCapabilities(TEST_CAPS)
}
@Test
@@ -957,4 +969,43 @@
// With the test process UID allowed, binding to a restricted network should be successful.
Socket().use { socket -> updatedNetwork.bindSocket(socket) }
}
+
+ // TODO: consider only having this test in MTS as it makes use of the fact that
+ // setEthernetEnabled(false) untracks all interfaces. This behavior is an implementation detail
+ // and may change in the future.
+ @Test
+ fun testUpdateConfiguration_onUntrackedInterface() {
+ assumeFalse(isAdbOverEthernet())
+ val iface = createInterface()
+ setEthernetEnabled(false)
+
+ // Updating the IpConfiguration and NetworkCapabilities on absent interfaces is a supported
+ // use case.
+ updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+
+ setEthernetEnabled(true)
+ val cb = requestNetwork(ETH_REQUEST)
+ cb.expectAvailable()
+ cb.eventuallyExpectCapabilities(TEST_CAPS)
+ cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
+ }
+
+ @Test
+ fun testUpdateConfiguration_withLinkDown() {
+ assumeChangingCarrierSupported()
+ // createInterface without carrier is racy, so create it and then remove carrier.
+ val iface = createInterface()
+ val cb = requestNetwork(ETH_REQUEST)
+ cb.expectAvailable()
+
+ iface.setCarrierEnabled(false)
+ cb.eventuallyExpectLost()
+
+ updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+ cb.assertNoCallback()
+
+ iface.setCarrierEnabled(true)
+ cb.eventuallyExpectCapabilities(TEST_CAPS)
+ cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index d2cd7aa..867d672 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -335,6 +335,28 @@
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
}
+ fun assertLinkPropertiesEventually(
+ n: Network,
+ description: String,
+ condition: (LinkProperties?) -> Boolean
+ ): LinkProperties? {
+ val deadline = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS
+ do {
+ val lp = mCM.getLinkProperties(n)
+ if (condition(lp)) return lp
+ SystemClock.sleep(10 /* ms */)
+ } while (SystemClock.elapsedRealtime() < deadline)
+ fail("Network $n LinkProperties did not $description after $DEFAULT_TIMEOUT_MS ms")
+ }
+
+ fun assertLinkPropertiesEventuallyNotNull(n: Network) {
+ assertLinkPropertiesEventually(n, "become non-null") { it != null }
+ }
+
+ fun assertLinkPropertiesEventuallyNull(n: Network) {
+ assertLinkPropertiesEventually(n, "become null") { it == null }
+ }
+
@Test
fun testSetSubtypeNameAndExtraInfoByAgentConfig() {
val subtypeLTE = TelephonyManager.NETWORK_TYPE_LTE
@@ -1269,8 +1291,40 @@
agent5.expectCallback<OnNetworkDestroyed>()
agent5.expectCallback<OnNetworkUnwanted>()
+ // If unregisterAfterReplacement is called before markConnected, the network disconnects.
+ val specifier6 = UUID.randomUUID().toString()
+ val callback = TestableNetworkCallback()
+ requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
+ val agent6 = createNetworkAgent(specifier = specifier6)
+ val network6 = agent6.register()
+ // No callbacks are sent, so check the LinkProperties to see if the network has connected.
+ assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+
+ // unregisterAfterReplacement tears down the network immediately.
+ // Approximately check that this is the case by picking an unregister timeout that's longer
+ // than the timeout of the expectCallback<OnNetworkUnwanted> below.
+ // TODO: consider adding configurable timeouts to TestableNetworkAgent expectations.
+ val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
+ agent6.unregisterAfterReplacement(timeoutMs)
+ agent6.expectCallback<OnNetworkUnwanted>()
+ if (!SdkLevel.isAtLeastT()) {
+ // Before T, onNetworkDestroyed is called even if the network was never created.
+ agent6.expectCallback<OnNetworkDestroyed>()
+ }
+ // Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
+ // network has not yet been removed from the CS data structures.
+ assertLinkPropertiesEventuallyNull(agent6.network!!)
+ assertFalse(mCM.getAllNetworks().contains(agent6.network!!))
+
+ // After unregisterAfterReplacement is called, the network is no longer usable and
+ // markConnected has no effect.
+ agent6.markConnected()
+ agent6.assertNoCallback()
+ assertNull(mCM.getLinkProperties(agent6.network!!))
+ matchAllCallback.assertNoCallback(200 /* timeoutMs */)
+
// If wifi is replaced within the timeout, the device does not switch to cellular.
- val (cellAgent, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
+ val (_, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
testCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
matchAllCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
diff --git a/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
index ffb34e6..04abd72 100644
--- a/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
+++ b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
@@ -29,6 +29,7 @@
import android.net.SocketRemoteAddressChangedException;
import android.os.Build;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -37,6 +38,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.R)
+@ConnectivityModuleTest
public class QosCallbackExceptionTest {
private static final String ERROR_MESSAGE = "Test Error Message";
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index cb68235..437622b 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -101,7 +101,7 @@
],
static_libs: [
"androidx.test.rules",
- "androidx.test.uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"bouncycastle-repackaged-unbundled",
"core-tests-support",
"FrameworksNetCommonTests",
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index 709b722..126ad55 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -1067,6 +1067,38 @@
}
}
+ @Test
+ public void testClearInterfaces() {
+ final NetworkStats stats = new NetworkStats(TEST_START, 1);
+ final NetworkStats.Entry entry1 = new NetworkStats.Entry(
+ "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+
+ final NetworkStats.Entry entry2 = new NetworkStats.Entry(
+ "test2", 10101, SET_DEFAULT, 0xF0DD, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+
+ stats.insertEntry(entry1);
+ stats.insertEntry(entry2);
+
+ // Verify that the interfaces have indeed been recorded.
+ assertEquals(2, stats.size());
+ assertValues(stats, 0, "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+ assertValues(stats, 1, "test2", 10101, SET_DEFAULT, 0xF0DD, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+
+ // Clear interfaces.
+ stats.clearInterfaces();
+
+ // Verify that the interfaces are cleared.
+ assertEquals(2, stats.size());
+ assertValues(stats, 0, null /* iface */, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+ assertValues(stats, 1, null /* iface */, 10101, SET_DEFAULT, 0xF0DD, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+ }
+
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index 4966aed..0c00bc0 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -37,10 +37,15 @@
import static com.android.server.BpfNetMaps.HAPPY_BOX_MATCH;
import static com.android.server.BpfNetMaps.IIF_MATCH;
import static com.android.server.BpfNetMaps.LOCKDOWN_VPN_MATCH;
+import static com.android.server.BpfNetMaps.LOW_POWER_STANDBY_MATCH;
import static com.android.server.BpfNetMaps.NO_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_1_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_2_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_3_MATCH;
import static com.android.server.BpfNetMaps.PENALTY_BOX_MATCH;
import static com.android.server.BpfNetMaps.POWERSAVE_MATCH;
import static com.android.server.BpfNetMaps.RESTRICTED_MATCH;
+import static com.android.server.BpfNetMaps.STANDBY_MATCH;
import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
import static org.junit.Assert.assertEquals;
@@ -61,6 +66,7 @@
import android.os.Build;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
+import android.util.IndentingPrintWriter;
import androidx.test.filters.SmallTest;
@@ -84,6 +90,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.FileDescriptor;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
@@ -138,6 +146,9 @@
doReturn(0).when(mDeps).synchronizeKernelRCU();
BpfNetMaps.setEnableJavaBpfMapForTest(true /* enable */);
BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
+ mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
+ mConfigurationMap.updateEntry(
+ CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_A));
BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
BpfNetMaps.setUidPermissionMapForTest(mUidPermissionMap);
BpfNetMaps.setCookieTagMapForTest(mCookieTagMap);
@@ -927,4 +938,136 @@
final int ret = mBpfNetMaps.pullBpfMapInfoAtom(-1 /* atomTag */, new ArrayList<>());
assertEquals(StatsManager.PULL_SKIP, ret);
}
+
+ private void assertDumpContains(final String dump, final String message) {
+ assertTrue(String.format("dump(%s) does not contain '%s'", dump, message),
+ dump.contains(message));
+ }
+
+ private String getDump() throws Exception {
+ final StringWriter sw = new StringWriter();
+ mBpfNetMaps.dump(new IndentingPrintWriter(sw), new FileDescriptor(), true /* verbose */);
+ return sw.toString();
+ }
+
+ private void doTestDumpUidPermissionMap(final int permission, final String permissionString)
+ throws Exception {
+ mUidPermissionMap.updateEntry(new S32(TEST_UID), new U8((short) permission));
+ assertDumpContains(getDump(), TEST_UID + " " + permissionString);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidPermissionMap() throws Exception {
+ doTestDumpUidPermissionMap(PERMISSION_NONE, "PERMISSION_NONE");
+ doTestDumpUidPermissionMap(PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
+ "PERMISSION_INTERNET PERMISSION_UPDATE_DEVICE_STATS");
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidPermissionMapInvalidPermission() throws Exception {
+ doTestDumpUidPermissionMap(PERMISSION_UNINSTALLED, "PERMISSION_UNINSTALLED error!");
+ doTestDumpUidPermissionMap(PERMISSION_INTERNET | 1 << 6,
+ "PERMISSION_INTERNET PERMISSION_UNKNOWN(64)");
+ }
+
+ void doTestDumpUidOwnerMap(final int iif, final long match, final String matchString)
+ throws Exception {
+ mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
+ assertDumpContains(getDump(), TEST_UID + " " + matchString);
+ }
+
+ void doTestDumpUidOwnerMap(final long match, final String matchString) throws Exception {
+ doTestDumpUidOwnerMap(0 /* iif */, match, matchString);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidOwnerMap() throws Exception {
+ doTestDumpUidOwnerMap(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
+ doTestDumpUidOwnerMap(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
+ doTestDumpUidOwnerMap(DOZABLE_MATCH, "DOZABLE_MATCH");
+ doTestDumpUidOwnerMap(STANDBY_MATCH, "STANDBY_MATCH");
+ doTestDumpUidOwnerMap(POWERSAVE_MATCH, "POWERSAVE_MATCH");
+ doTestDumpUidOwnerMap(RESTRICTED_MATCH, "RESTRICTED_MATCH");
+ doTestDumpUidOwnerMap(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH");
+ doTestDumpUidOwnerMap(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH");
+ doTestDumpUidOwnerMap(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH");
+ doTestDumpUidOwnerMap(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH");
+ doTestDumpUidOwnerMap(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH");
+
+ doTestDumpUidOwnerMap(HAPPY_BOX_MATCH | POWERSAVE_MATCH,
+ "HAPPY_BOX_MATCH POWERSAVE_MATCH");
+ doTestDumpUidOwnerMap(DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+ "DOZABLE_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH");
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidOwnerMapWithIifMatch() throws Exception {
+ doTestDumpUidOwnerMap(TEST_IF_INDEX, IIF_MATCH, "IIF_MATCH " + TEST_IF_INDEX);
+ doTestDumpUidOwnerMap(TEST_IF_INDEX,
+ IIF_MATCH | DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+ "DOZABLE_MATCH IIF_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH " + TEST_IF_INDEX);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidOwnerMapWithInvalidMatch() throws Exception {
+ final long invalid_match = 1L << 31;
+ doTestDumpUidOwnerMap(invalid_match, "UNKNOWN_MATCH(" + invalid_match + ")");
+ doTestDumpUidOwnerMap(DOZABLE_MATCH | invalid_match,
+ "DOZABLE_MATCH UNKNOWN_MATCH(" + invalid_match + ")");
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpCurrentStatsMapConfig() throws Exception {
+ mConfigurationMap.updateEntry(
+ CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_A));
+ assertDumpContains(getDump(), "current statsMap configuration: 0 SELECT_MAP_A");
+
+ mConfigurationMap.updateEntry(
+ CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_B));
+ assertDumpContains(getDump(), "current statsMap configuration: 1 SELECT_MAP_B");
+ }
+
+ private void doTestDumpOwnerMatchConfig(final long match, final String matchString)
+ throws Exception {
+ mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(match));
+ assertDumpContains(getDump(),
+ "current ownerMatch configuration: " + match + " " + matchString);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidOwnerMapConfig() throws Exception {
+ doTestDumpOwnerMatchConfig(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
+ doTestDumpOwnerMatchConfig(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
+ doTestDumpOwnerMatchConfig(DOZABLE_MATCH, "DOZABLE_MATCH");
+ doTestDumpOwnerMatchConfig(STANDBY_MATCH, "STANDBY_MATCH");
+ doTestDumpOwnerMatchConfig(POWERSAVE_MATCH, "POWERSAVE_MATCH");
+ doTestDumpOwnerMatchConfig(RESTRICTED_MATCH, "RESTRICTED_MATCH");
+ doTestDumpOwnerMatchConfig(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH");
+ doTestDumpOwnerMatchConfig(IIF_MATCH, "IIF_MATCH");
+ doTestDumpOwnerMatchConfig(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH");
+ doTestDumpOwnerMatchConfig(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH");
+ doTestDumpOwnerMatchConfig(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH");
+ doTestDumpOwnerMatchConfig(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH");
+
+ doTestDumpOwnerMatchConfig(HAPPY_BOX_MATCH | POWERSAVE_MATCH,
+ "HAPPY_BOX_MATCH POWERSAVE_MATCH");
+ doTestDumpOwnerMatchConfig(DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+ "DOZABLE_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH");
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDumpUidOwnerMapConfigWithInvalidMatch() throws Exception {
+ final long invalid_match = 1L << 31;
+ doTestDumpOwnerMatchConfig(invalid_match, "UNKNOWN_MATCH(" + invalid_match + ")");
+ doTestDumpOwnerMatchConfig(DOZABLE_MATCH | invalid_match,
+ "DOZABLE_MATCH UNKNOWN_MATCH(" + invalid_match + ")");
+ }
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 7993a5c..c8aa59b 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -1068,38 +1068,41 @@
* @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
*/
public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
- ConnectivityManager.NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
+ final ConditionVariable capsChangedCv = new ConditionVariable();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
+ .clearCapabilities()
+ .build();
if (validated) {
setNetworkValid(isStrictMode);
- NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
- .clearCapabilities()
- .build();
- callback = new ConnectivityManager.NetworkCallback() {
- public void onCapabilitiesChanged(Network network,
- NetworkCapabilities networkCapabilities) {
- if (network.equals(getNetwork()) &&
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ }
+ final NetworkCallback callback = new NetworkCallback() {
+ public void onCapabilitiesChanged(Network network,
+ NetworkCapabilities networkCapabilities) {
+ if (network.equals(getNetwork())) {
+ capsChangedCv.open();
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
validatedCv.open();
}
}
- };
- mCm.registerNetworkCallback(request, callback);
- }
+ }
+ };
+ mCm.registerNetworkCallback(request, callback);
+
if (hasInternet) {
addCapability(NET_CAPABILITY_INTERNET);
}
connectWithoutInternet();
+ waitFor(capsChangedCv);
if (validated) {
// Wait for network to validate.
waitFor(validatedCv);
setNetworkInvalid(isStrictMode);
}
-
- if (callback != null) mCm.unregisterNetworkCallback(callback);
+ mCm.unregisterNetworkCallback(callback);
}
public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
@@ -1605,9 +1608,9 @@
mMockVpn = new MockVpn(userId);
}
- private void mockUidNetworkingBlocked() {
+ private void mockUidNetworkingBlocked(int uid) {
doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1))
- ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+ ).when(mNetworkPolicyManager).isUidNetworkingBlocked(eq(uid), anyBoolean());
}
private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) {
@@ -8994,7 +8997,7 @@
final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback();
mCm.registerNetworkCallback(cellRequest, detailedCallback);
- mockUidNetworkingBlocked();
+ mockUidNetworkingBlocked(Process.myUid());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
@@ -9109,7 +9112,7 @@
public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mockUidNetworkingBlocked();
+ mockUidNetworkingBlocked(Process.myUid());
// No Networkcallbacks invoked before any network is active.
setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
@@ -16878,4 +16881,43 @@
verify(mTetheringManager).getTetherableWifiRegexs();
});
}
+
+ @Test
+ public void testGetNetworkInfoForUid() throws Exception {
+ // Setup and verify getNetworkInfoForUid cannot be called without Network Stack permission,
+ // when querying NetworkInfo for other uid.
+ verifyNoNetwork();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+
+ final int otherUid = Process.myUid() + 1;
+ assertNull(mCm.getActiveNetwork());
+ assertNull(mCm.getNetworkInfoForUid(mCm.getActiveNetwork(),
+ Process.myUid(), false /* ignoreBlocked */));
+ assertThrows(SecurityException.class, () -> mCm.getNetworkInfoForUid(
+ mCm.getActiveNetwork(), otherUid, false /* ignoreBlocked */));
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () ->
+ assertNull(mCm.getNetworkInfoForUid(mCm.getActiveNetwork(),
+ otherUid, false /* ignoreBlocked */)));
+
+ // Bringing up validated wifi and verify again. Make the other uid be blocked,
+ // verify the method returns result accordingly.
+ mWiFiNetworkAgent.connect(true);
+ setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
+ mockUidNetworkingBlocked(otherUid);
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () ->
+ verifyActiveNetwork(TRANSPORT_WIFI));
+ checkNetworkInfo(mCm.getNetworkInfoForUid(mCm.getActiveNetwork(),
+ Process.myUid(), false /* ignoreBlocked */), TYPE_WIFI, DetailedState.CONNECTED);
+ assertThrows(SecurityException.class, () -> mCm.getNetworkInfoForUid(
+ mCm.getActiveNetwork(), otherUid, false /* ignoreBlocked */));
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () ->
+ checkNetworkInfo(mCm.getNetworkInfoForUid(mCm.getActiveNetwork(),
+ otherUid, false /* ignoreBlocked */), TYPE_WIFI, DetailedState.BLOCKED));
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () ->
+ checkNetworkInfo(mCm.getNetworkInfoForUid(mCm.getActiveNetwork(),
+ otherUid, true /* ignoreBlocked */), TYPE_WIFI, DetailedState.CONNECTED));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 1c54651..39fd780 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -20,6 +20,8 @@
import static android.Manifest.permission.CONTROL_VPN;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
@@ -76,6 +78,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.Ikev2VpnProfile;
@@ -115,6 +118,7 @@
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.PowerWhitelistManager;
import android.os.Process;
import android.os.UserHandle;
@@ -245,6 +249,7 @@
@Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private Vpn.VpnNetworkAgentWrapper mMockNetworkAgent;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private ConnectivityDiagnosticsManager mCdm;
@Mock private IpSecService mIpSecService;
@Mock private VpnProfileStore mVpnProfileStore;
@Mock private ScheduledThreadPoolExecutor mExecutor;
@@ -283,6 +288,8 @@
mockService(NotificationManager.class, Context.NOTIFICATION_SERVICE, mNotificationManager);
mockService(ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mConnectivityManager);
mockService(IpSecManager.class, Context.IPSEC_SERVICE, mIpSecManager);
+ mockService(ConnectivityDiagnosticsManager.class, Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ mCdm);
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -1925,6 +1932,56 @@
verifyHandlingNetworkLoss(vpnSnapShot);
}
+ private ConnectivityDiagnosticsCallback getConnectivityDiagCallback() {
+ final ArgumentCaptor<ConnectivityDiagnosticsCallback> cdcCaptor =
+ ArgumentCaptor.forClass(ConnectivityDiagnosticsCallback.class);
+ verify(mCdm).registerConnectivityDiagnosticsCallback(
+ any(), any(), cdcCaptor.capture());
+ return cdcCaptor.getValue();
+ }
+
+ private DataStallReport createDataStallReport() {
+ return new DataStallReport(TEST_NETWORK, 1234 /* reportTimestamp */,
+ 1 /* detectionMethod */, new LinkProperties(), new NetworkCapabilities(),
+ new PersistableBundle());
+ }
+
+ private void verifyMobikeTriggered(List<Network> expected) {
+ final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+ verify(mIkeSessionWrapper).setNetwork(networkCaptor.capture());
+ assertEquals(expected, Collections.singletonList(networkCaptor.getValue()));
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnMobikeDisabled() throws Exception {
+ verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+ getConnectivityDiagCallback();
+ final DataStallReport report = createDataStallReport();
+ connectivityDiagCallback.onDataStallSuspected(report);
+
+ // Should not trigger MOBIKE if MOBIKE is not enabled
+ verify(mIkeSessionWrapper, never()).setNetwork(any());
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnMobikeEnabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+ getConnectivityDiagCallback();
+ final DataStallReport report = createDataStallReport();
+ connectivityDiagCallback.onDataStallSuspected(report);
+
+ // Verify MOBIKE is triggered
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+ }
+
@Test
public void testStartRacoonNumericAddress() throws Exception {
startRacoon("1.2.3.4", "1.2.3.4");
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index aad80d5..949e0c2 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -25,7 +25,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
@@ -306,35 +305,6 @@
}
@Test
- public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
- initEthernetNetworkFactory();
- createAndVerifyProvisionedInterface(TEST_IFACE);
-
- final boolean retDown = mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */);
-
- assertTrue(retDown);
- verifyStop();
-
- final boolean retUp = mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */);
-
- assertTrue(retUp);
- }
-
- @Test
- public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
- initEthernetNetworkFactory();
- createUnprovisionedInterface(TEST_IFACE);
-
- final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */);
-
- assertTrue(ret);
- // There should not be an active IPClient or NetworkAgent.
- verify(mDeps, never()).makeIpClient(any(), any(), any());
- verify(mDeps, never())
- .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
- }
-
- @Test
public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
initEthernetNetworkFactory();
@@ -346,17 +316,6 @@
}
@Test
- public void testUpdateInterfaceLinkStateWithNoChanges() throws Exception {
- initEthernetNetworkFactory();
- createAndVerifyProvisionedInterface(TEST_IFACE);
-
- final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */);
-
- assertFalse(ret);
- verifyNoStopOrStart();
- }
-
- @Test
public void testProvisioningLoss() throws Exception {
initEthernetNetworkFactory();
when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
@@ -390,17 +349,6 @@
}
@Test
- public void testLinkPropertiesChanged() throws Exception {
- initEthernetNetworkFactory();
- createAndVerifyProvisionedInterface(TEST_IFACE);
-
- LinkProperties lp = new LinkProperties();
- mIpClientCallbacks.onLinkPropertiesChange(lp);
- mLooper.dispatchAll();
- verify(mNetworkAgent).sendLinkPropertiesImpl(same(lp));
- }
-
- @Test
public void testNetworkUnwanted() throws Exception {
initEthernetNetworkFactory();
createAndVerifyProvisionedInterface(TEST_IFACE);
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 6448819..2ceb00a 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -263,7 +263,8 @@
StatsMapValue.class);
private TestBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap = new TestBpfMap<>(
UidStatsMapKey.class, StatsMapValue.class);
-
+ private TestBpfMap<S32, StatsMapValue> mIfaceStatsMap = new TestBpfMap<>(
+ S32.class, StatsMapValue.class);
private NetworkStatsService mService;
private INetworkStatsSession mSession;
private AlertObserver mAlertObserver;
@@ -503,6 +504,11 @@
}
@Override
+ public IBpfMap<S32, StatsMapValue> getIfaceStatsMap() {
+ return mIfaceStatsMap;
+ }
+
+ @Override
public boolean isDebuggable() {
return mIsDebuggable == Boolean.TRUE;
}
@@ -2552,4 +2558,25 @@
doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
doTestDumpStatsMap("unknown");
}
+
+ void doTestDumpIfaceStatsMap(final String expectedIfaceName) throws Exception {
+ mIfaceStatsMap.insertEntry(new S32(10), new StatsMapValue(3, 3000, 3, 3000));
+
+ final String dump = getDump();
+ assertDumpContains(dump, "mIfaceStatsMap: OK");
+ assertDumpContains(dump, "ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets");
+ assertDumpContains(dump, "10 " + expectedIfaceName + " 3000 3 3000 3");
+ }
+
+ @Test
+ public void testDumpIfaceStatsMap() throws Exception {
+ doReturn("wlan0").when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+ doTestDumpIfaceStatsMap("wlan0");
+ }
+
+ @Test
+ public void testDumpIfaceStatsMapUnknownInterface() throws Exception {
+ doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+ doTestDumpIfaceStatsMap("unknown");
+ }
}