Merge "Update CSSatelliteNetworkTest to use restricted satellite network" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 375397b..4cf93a8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -77,18 +77,6 @@
"name": "libnetworkstats_test"
},
{
- "name": "NetHttpCoverageTests",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- // These sometimes take longer than 1 min which is the presubmit timeout
- "exclude-annotation": "androidx.test.filters.LargeTest"
- }
- ]
- },
- {
"name": "CtsTetheringTestLatestSdk",
"options": [
{
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 81e18ab..00d9152 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -69,7 +69,6 @@
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.SharedLog;
-import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value;
@@ -1343,19 +1342,6 @@
pw.decreaseIndent();
}
- private <K extends Struct, V extends Struct> void dumpRawMap(IBpfMap<K, V> map,
- IndentingPrintWriter pw) throws ErrnoException {
- if (map == null) {
- pw.println("No BPF support");
- return;
- }
- if (map.isEmpty()) {
- pw.println("No entries");
- return;
- }
- map.forEach((k, v) -> pw.println(BpfDump.toBase64EncodedString(k, v)));
- }
-
/**
* Dump raw BPF map into the base64 encoded strings "<base64 key>,<base64 value>".
* Allow to dump only one map path once. For test only.
@@ -1375,16 +1361,16 @@
// TODO: dump downstream4 map.
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
try (IBpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
- dumpRawMap(statsMap, pw);
- } catch (ErrnoException | IOException e) {
+ BpfDump.dumpRawMap(statsMap, pw);
+ } catch (IOException e) {
pw.println("Error dumping stats map: " + e);
}
return;
}
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
try (IBpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
- dumpRawMap(upstreamMap, pw);
- } catch (ErrnoException | IOException e) {
+ BpfDump.dumpRawMap(upstreamMap, pw);
+ } catch (IOException e) {
pw.println("Error dumping IPv4 map: " + e);
}
return;
diff --git a/framework/src/android/net/apf/ApfCapabilities.java b/framework/src/android/net/apf/ApfCapabilities.java
index 8efb182..6b18629 100644
--- a/framework/src/android/net/apf/ApfCapabilities.java
+++ b/framework/src/android/net/apf/ApfCapabilities.java
@@ -112,12 +112,12 @@
/**
* Determines whether the APF interpreter advertises support for the data buffer access opcodes
* LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and
- * STDW (STore Data Word) support is present from APFv4 on.
+ * STDW (STore Data Word) support is present from APFv3 on.
*
* @return {@code true} if the IWifiStaIface#readApfPacketFilterData is supported.
*/
public boolean hasDataAccess() {
- return apfVersionSupported >= 4;
+ return apfVersionSupported > 2;
}
/**
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index c726dab..51df8ab 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -121,6 +121,20 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public static final long NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION = 333340911L;
+ /**
+ * Enable caching for TrafficStats#get* APIs.
+ *
+ * Apps targeting Android V or later or running on Android V or later may take up to several
+ * seconds to see the updated results.
+ * Apps targeting lower android SDKs do not see cached result for backward compatibility,
+ * results of TrafficStats#get* APIs are reflecting network statistics immediately.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE = 74210811L;
+
private ConnectivityCompatChanges() {
}
}
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index 59986d4..80df552 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -54,6 +54,7 @@
namespace android {
namespace bpf {
+using base::StartsWith;
using base::EndsWith;
using std::string;
@@ -228,6 +229,30 @@
return !access("/metadata/gsi/dsu/booted", F_OK);
}
+static bool hasGSM() {
+ static string ph = base::GetProperty("gsm.current.phone-type", "");
+ static bool gsm = (ph != "");
+ static bool logged = false;
+ if (!logged) {
+ logged = true;
+ ALOGI("hasGSM(gsm.current.phone-type='%s'): %s", ph.c_str(), gsm ? "true" : "false");
+ }
+ return gsm;
+}
+
+static bool isTV() {
+ if (hasGSM()) return false; // TVs don't do GSM
+
+ static string key = base::GetProperty("ro.oem.key1", "");
+ static bool tv = StartsWith(key, "ATV00");
+ static bool logged = false;
+ if (!logged) {
+ logged = true;
+ ALOGI("isTV(ro.oem.key1='%s'): %s.", key.c_str(), tv ? "true" : "false");
+ }
+ return tv;
+}
+
static int doLoad(char** argv, char * const envp[]) {
const int device_api_level = android_get_device_api_level();
const bool isAtLeastT = (device_api_level >= __ANDROID_API_T__);
@@ -261,24 +286,36 @@
return 1;
}
+ // both S and T require kernel 4.9 (and eBpf support)
if (isAtLeastT && !isAtLeastKernelVersion(4, 9, 0)) {
ALOGE("Android T requires kernel 4.9.");
return 1;
}
+ // U bumps the kernel requirement up to 4.14
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
ALOGE("Android U requires kernel 4.14.");
return 1;
}
+ // V bumps the kernel requirement up to 4.19
+ // see also: //system/netd/tests/kernel_test.cpp TestKernel419
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
ALOGE("Android V requires kernel 4.19.");
return 1;
}
- if (isAtLeastV && isX86() && !isKernel64Bit()) {
+ // Technically already required by U, but only enforce on V+
+ // see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
+ if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16, 0)) {
+ ALOGE("Android V+ platform with 32 bit kernel version >= 5.16.0 is unsupported");
+ if (!isTV()) return 1;
+ }
+
+ // Various known ABI layout issues, particularly wrt. bpf and ipsec/xfrm.
+ if (isAtLeastV && isKernel32Bit() && isX86()) {
ALOGE("Android V requires X86 kernel to be 64-bit.");
- return 1;
+ if (!isTV()) return 1;
}
if (isAtLeastV) {
@@ -325,12 +362,16 @@
* Some of these have userspace or kernel workarounds/hacks.
* Some of them don't...
* We're going to be removing the hacks.
+ * (for example "ANDROID: xfrm: remove in_compat_syscall() checks").
+ * Note: this check/enforcement only applies to *system* userspace code,
+ * it does not affect unprivileged apps, the 32-on-64 compatibility
+ * problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
*
* Additionally the 32-bit kernel jit support is poor,
* and 32-bit userspace on 64-bit kernel bpf ringbuffer compatibility is broken.
*/
ALOGE("64-bit userspace required on 6.2+ kernels.");
- return 1;
+ if (!isTV()) return 1;
}
// Ensure we can determine the Android build type.
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 91fec90..925ee50 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -85,31 +85,6 @@
return Status("U+ platform with kernel version < 4.14.0 is unsupported");
}
- if (modules::sdklevel::IsAtLeastV()) {
- // V bumps the kernel requirement up to 4.19
- // see also: //system/netd/tests/kernel_test.cpp TestKernel419
- if (!bpf::isAtLeastKernelVersion(4, 19, 0)) {
- return Status("V+ platform with kernel version < 4.19.0 is unsupported");
- }
-
- // Technically already required by U, but only enforce on V+
- // see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
- if (bpf::isKernel32Bit() && bpf::isAtLeastKernelVersion(5, 16, 0)) {
- return Status("V+ platform with 32 bit kernel, version >= 5.16.0 is unsupported");
- }
- }
-
- // Linux 6.1 is highest version supported by U, starting with V new kernels,
- // ie. 6.2+ we are dropping various kernel/system userspace 32-on-64 hacks
- // (for example "ANDROID: xfrm: remove in_compat_syscall() checks").
- // Note: this check/enforcement only applies to *system* userspace code,
- // it does not affect unprivileged apps, the 32-on-64 compatibility
- // problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
- // see also: //system/bpf/bpfloader/BpfLoader.cpp main()
- if (bpf::isUserspace32bit() && bpf::isAtLeastKernelVersion(6, 2, 0)) {
- return Status("32 bit userspace with Kernel version >= 6.2.0 is unsupported");
- }
-
// U mandates this mount point (though it should also be the case on T)
if (modules::sdklevel::IsAtLeastU() && !!strcmp(cg2_path, "/sys/fs/cgroup")) {
return Status("U+ platform with cg2_path != /sys/fs/cgroup is unsupported");
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index 385adc6..a92dfaf 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -193,7 +193,8 @@
* @param sentQueryCount The count of sent queries before stop discovery.
*/
public void reportServiceDiscoveryStop(boolean isLegacy, int transactionId, long durationMs,
- int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount) {
+ int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount,
+ boolean isServiceFromCache) {
final Builder builder = makeReportedBuilder(isLegacy, transactionId);
builder.setType(NsdEventType.NET_DISCOVER);
builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STOP);
@@ -202,6 +203,7 @@
builder.setLostCallbackCount(lostCallbackCount);
builder.setFoundServiceCount(servicesCount);
builder.setSentQueryCount(sentQueryCount);
+ builder.setIsKnownService(isServiceFromCache);
mDependencies.statsWrite(builder.build());
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 0a8adf0..f8b0d53 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1634,6 +1634,12 @@
NsdManager.nameOf(code), transactionId));
switch (code) {
case NsdManager.SERVICE_FOUND:
+ // Set the ServiceFromCache flag only if the service is actually being
+ // retrieved from the cache. This flag should not be overridden by later
+ // service found event, which may not be cached.
+ if (event.mIsServiceFromCache) {
+ request.setServiceFromCache(true);
+ }
clientInfo.onServiceFound(clientRequestId, info, request);
break;
case NsdManager.SERVICE_LOST:
@@ -2887,7 +2893,8 @@
request.getFoundServiceCount(),
request.getLostServiceCount(),
request.getServicesCount(),
- request.getSentQueryCount());
+ request.getSentQueryCount(),
+ request.isServiceFromCache());
} else if (listener instanceof ResolutionListener) {
mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
request.calculateRequestDurationMs(mClock.elapsedRealtime()),
@@ -2927,7 +2934,8 @@
request.getFoundServiceCount(),
request.getLostServiceCount(),
request.getServicesCount(),
- NO_SENT_QUERY_COUNT);
+ NO_SENT_QUERY_COUNT,
+ request.isServiceFromCache());
break;
case NsdManager.RESOLVE_SERVICE:
stopResolveService(transactionId);
@@ -3050,7 +3058,8 @@
request.getFoundServiceCount(),
request.getLostServiceCount(),
request.getServicesCount(),
- request.getSentQueryCount());
+ request.getSentQueryCount(),
+ request.isServiceFromCache());
try {
mCb.onStopDiscoverySucceeded(listenerKey);
} catch (RemoteException e) {
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 8305c1e..5323392 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -57,6 +57,7 @@
import static android.net.TrafficStats.TYPE_TX_PACKETS;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
@@ -91,6 +92,7 @@
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
import android.app.usage.NetworkStatsManager;
import android.content.ApexEnvironment;
import android.content.BroadcastReceiver;
@@ -472,7 +474,9 @@
private final TrafficStatsRateLimitCache mTrafficStatsUidCache;
static final String TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG =
"trafficstats_rate_limit_cache_enabled_flag";
- private final boolean mSupportTrafficStatsRateLimitCache;
+ private final boolean mAlwaysUseTrafficStatsRateLimitCache;
+ private final int mTrafficStatsRateLimitCacheExpiryDuration;
+ private final int mTrafficStatsRateLimitCacheMaxEntries;
private final Object mOpenSessionCallsLock = new Object();
@@ -663,15 +667,18 @@
mEventLogger = null;
}
- final long cacheExpiryDurationMs = mDeps.getTrafficStatsRateLimitCacheExpiryDuration();
- final int cacheMaxEntries = mDeps.getTrafficStatsRateLimitCacheMaxEntries();
- mSupportTrafficStatsRateLimitCache = mDeps.supportTrafficStatsRateLimitCache(mContext);
+ mAlwaysUseTrafficStatsRateLimitCache =
+ mDeps.alwaysUseTrafficStatsRateLimitCache(mContext);
+ mTrafficStatsRateLimitCacheExpiryDuration =
+ mDeps.getTrafficStatsRateLimitCacheExpiryDuration();
+ mTrafficStatsRateLimitCacheMaxEntries =
+ mDeps.getTrafficStatsRateLimitCacheMaxEntries();
mTrafficStatsTotalCache = new TrafficStatsRateLimitCache(mClock,
- cacheExpiryDurationMs, cacheMaxEntries);
+ mTrafficStatsRateLimitCacheExpiryDuration, mTrafficStatsRateLimitCacheMaxEntries);
mTrafficStatsIfaceCache = new TrafficStatsRateLimitCache(mClock,
- cacheExpiryDurationMs, cacheMaxEntries);
+ mTrafficStatsRateLimitCacheExpiryDuration, mTrafficStatsRateLimitCacheMaxEntries);
mTrafficStatsUidCache = new TrafficStatsRateLimitCache(mClock,
- cacheExpiryDurationMs, cacheMaxEntries);
+ mTrafficStatsRateLimitCacheExpiryDuration, mTrafficStatsRateLimitCacheMaxEntries);
// TODO: Remove bpfNetMaps creation and always start SkDestroyListener
// Following code is for the experiment to verify the SkDestroyListener refactoring. Based
@@ -920,13 +927,14 @@
}
/**
- * Get whether TrafficStats rate-limit cache is supported.
+ * Get whether TrafficStats rate-limit cache is always applied.
*
* This method should only be called once in the constructor,
* to ensure that the code does not need to deal with flag values changing at runtime.
*/
- public boolean supportTrafficStatsRateLimitCache(@NonNull Context ctx) {
- return false;
+ public boolean alwaysUseTrafficStatsRateLimitCache(@NonNull Context ctx) {
+ return SdkLevel.isAtLeastV() && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
+ ctx, TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG);
}
/**
@@ -954,6 +962,13 @@
}
/**
+ * Wrapper method for {@link CompatChanges#isChangeEnabled(long, int)}
+ */
+ public boolean isChangeEnabled(final long changeId, final int uid) {
+ return CompatChanges.isChangeEnabled(changeId, uid);
+ }
+
+ /**
* Retrieves native network total statistics.
*
* @return A NetworkStats.Entry containing the native statistics, or
@@ -2084,14 +2099,14 @@
}
if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
- if (!mSupportTrafficStatsRateLimitCache) {
- return getEntryValueForType(mDeps.nativeGetUidStat(uid), type);
+ if (mAlwaysUseTrafficStatsRateLimitCache
+ || mDeps.isChangeEnabled(ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, callingUid)) {
+ final NetworkStats.Entry entry = mTrafficStatsUidCache.getOrCompute(IFACE_ALL, uid,
+ () -> mDeps.nativeGetUidStat(uid));
+ return getEntryValueForType(entry, type);
}
- final NetworkStats.Entry entry = mTrafficStatsUidCache.getOrCompute(IFACE_ALL, uid,
- () -> mDeps.nativeGetUidStat(uid));
-
- return getEntryValueForType(entry, type);
+ return getEntryValueForType(mDeps.nativeGetUidStat(uid), type);
}
@Nullable
@@ -2113,14 +2128,15 @@
Objects.requireNonNull(iface);
if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
- if (!mSupportTrafficStatsRateLimitCache) {
- return getEntryValueForType(getIfaceStatsInternal(iface), type);
+ if (mAlwaysUseTrafficStatsRateLimitCache
+ || mDeps.isChangeEnabled(
+ ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, Binder.getCallingUid())) {
+ final NetworkStats.Entry entry = mTrafficStatsIfaceCache.getOrCompute(iface, UID_ALL,
+ () -> getIfaceStatsInternal(iface));
+ return getEntryValueForType(entry, type);
}
- final NetworkStats.Entry entry = mTrafficStatsIfaceCache.getOrCompute(iface, UID_ALL,
- () -> getIfaceStatsInternal(iface));
-
- return getEntryValueForType(entry, type);
+ return getEntryValueForType(getIfaceStatsInternal(iface), type);
}
private long getEntryValueForType(@Nullable NetworkStats.Entry entry, int type) {
@@ -2166,14 +2182,15 @@
@Override
public long getTotalStats(int type) {
if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
- if (!mSupportTrafficStatsRateLimitCache) {
- return getEntryValueForType(getTotalStatsInternal(), type);
+ if (mAlwaysUseTrafficStatsRateLimitCache
+ || mDeps.isChangeEnabled(
+ ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, Binder.getCallingUid())) {
+ final NetworkStats.Entry entry = mTrafficStatsTotalCache.getOrCompute(
+ IFACE_ALL, UID_ALL, () -> getTotalStatsInternal());
+ return getEntryValueForType(entry, type);
}
- final NetworkStats.Entry entry = mTrafficStatsTotalCache.getOrCompute(IFACE_ALL, UID_ALL,
- () -> getTotalStatsInternal());
-
- return getEntryValueForType(entry, type);
+ return getEntryValueForType(getTotalStatsInternal(), type);
}
@Override
@@ -2937,13 +2954,12 @@
} catch (IOException e) {
pw.println("(failed to dump FastDataInput counters)");
}
- pw.print("trafficstats.cache.supported", mSupportTrafficStatsRateLimitCache);
+ pw.print("trafficstats.cache.alwaysuse", mAlwaysUseTrafficStatsRateLimitCache);
pw.println();
pw.print(TRAFFIC_STATS_CACHE_EXPIRY_DURATION_NAME,
- mDeps.getTrafficStatsRateLimitCacheExpiryDuration());
+ mTrafficStatsRateLimitCacheExpiryDuration);
pw.println();
- pw.print(TRAFFIC_STATS_CACHE_MAX_ENTRIES_NAME,
- mDeps.getTrafficStatsRateLimitCacheMaxEntries());
+ pw.print(TRAFFIC_STATS_CACHE_MAX_ENTRIES_NAME, mTrafficStatsRateLimitCacheMaxEntries);
pw.println();
pw.decreaseIndent();
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ca99935..bb77a9d 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -403,6 +403,8 @@
private static final String NETWORK_ARG = "networks";
private static final String REQUEST_ARG = "requests";
private static final String TRAFFICCONTROLLER_ARG = "trafficcontroller";
+ public static final String CLATEGRESS4RAWBPFMAP_ARG = "clatEgress4RawBpfMap";
+ public static final String CLATINGRESS6RAWBPFMAP_ARG = "clatIngress6RawBpfMap";
private static final boolean DBG = true;
private static final boolean DDBG = Log.isLoggable(TAG, Log.DEBUG);
@@ -4141,6 +4143,12 @@
boolean verbose = !CollectionUtils.contains(args, SHORT_ARG);
dumpTrafficController(pw, fd, verbose);
return;
+ } else if (CollectionUtils.contains(args, CLATEGRESS4RAWBPFMAP_ARG)) {
+ dumpClatBpfRawMap(pw, true /* isEgress4Map */);
+ return;
+ } else if (CollectionUtils.contains(args, CLATINGRESS6RAWBPFMAP_ARG)) {
+ dumpClatBpfRawMap(pw, false /* isEgress4Map */);
+ return;
}
pw.println("NetworkProviders for:");
@@ -4400,6 +4408,15 @@
}
}
+ private void dumpClatBpfRawMap(IndentingPrintWriter pw, boolean isEgress4Map) {
+ for (NetworkAgentInfo nai : networksSortedById()) {
+ if (nai.clatd != null) {
+ nai.clatd.dumpRawBpfMap(pw, isEgress4Map);
+ break;
+ }
+ }
+ }
+
private void dumpAllRequestInfoLogsToLogcat() {
try (PrintWriter logPw = new PrintWriter(new Writer() {
@Override
@@ -4777,7 +4794,15 @@
final String logMsg = !TextUtils.isEmpty(redirectUrl)
? " with redirect to " + redirectUrl
: "";
- log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ final String statusMsg;
+ if (valid) {
+ statusMsg = "passed";
+ } else if (!TextUtils.isEmpty(redirectUrl)) {
+ statusMsg = "detected a portal";
+ } else {
+ statusMsg = "failed";
+ }
+ log(nai.toShortString() + " validation " + statusMsg + logMsg);
}
if (valid != wasValidated) {
final FullScore oldScore = nai.getScore();
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 634a8fa..d886182 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.BpfDump;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
@@ -659,6 +660,8 @@
final int pid = mDeps.startClatd(tunFd.getFileDescriptor(),
readSock6.getFileDescriptor(), writeSock6.getFileDescriptor(), iface, pfx96Str,
v4Str, v6Str);
+ // The file descriptors have been duplicated (dup2) to clatd in native_startClatd().
+ // Close these file descriptor stubs in finally block.
// [6] Initialize and store clatd tracker object.
mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
@@ -677,20 +680,17 @@
Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
}
}
- try {
- if (tunFd != null) {
- tunFd.close();
- }
- if (readSock6 != null) {
- readSock6.close();
- }
- if (writeSock6 != null) {
- writeSock6.close();
- }
- } catch (IOException e2) {
- Log.e(TAG, "Fail to cleanup fd ", e);
- }
throw new IOException("Failed to start clat ", e);
+ } finally {
+ if (tunFd != null) {
+ tunFd.close();
+ }
+ if (readSock6 != null) {
+ readSock6.close();
+ }
+ if (writeSock6 != null) {
+ writeSock6.close();
+ }
}
}
@@ -795,6 +795,29 @@
}
/**
+ * Dump raw BPF map into base64 encoded strings {@literal "<base64 key>,<base64 value>"}.
+ * Allow to dump only one map in each call. For test only.
+ *
+ * @param pw print writer.
+ * @param isEgress4Map whether to dump the egress4 map (true) or the ingress6 map (false).
+ *
+ * Usage:
+ * $ dumpsys connectivity {clatEgress4RawBpfMap|clatIngress6RawBpfMap}
+ *
+ * Output:
+ * {@literal <base64 encoded key #1>,<base64 encoded value #1>}
+ * {@literal <base64 encoded key #2>,<base64 encoded value #2>}
+ * ..
+ */
+ public void dumpRawMap(@NonNull IndentingPrintWriter pw, boolean isEgress4Map) {
+ if (isEgress4Map) {
+ BpfDump.dumpRawMap(mEgressMap, pw);
+ } else {
+ BpfDump.dumpRawMap(mIngressMap, pw);
+ }
+ }
+
+ /**
* Dump the coordinator information.
*
* @param pw print writer.
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 489dab5..a979681 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -622,6 +622,18 @@
}
}
+ /**
+ * Dump the raw BPF maps in 464XLAT
+ *
+ * @param pw print writer.
+ * @param isEgress4Map whether to dump the egress4 map (true) or the ingress6 map (false).
+ */
+ public void dumpRawBpfMap(IndentingPrintWriter pw, boolean isEgress4Map) {
+ if (SdkLevel.isAtLeastT()) {
+ mClatCoordinator.dumpRawMap(pw, isEgress4Map);
+ }
+ }
+
@Override
public String toString() {
return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
diff --git a/staticlibs/device/com/android/net/module/util/BpfDump.java b/staticlibs/device/com/android/net/module/util/BpfDump.java
index 7549e71..4227194 100644
--- a/staticlibs/device/com/android/net/module/util/BpfDump.java
+++ b/staticlibs/device/com/android/net/module/util/BpfDump.java
@@ -81,6 +81,26 @@
}
/**
+ * Dump the BpfMap entries with base64 format encoding.
+ */
+ public static <K extends Struct, V extends Struct> void dumpRawMap(IBpfMap<K, V> map,
+ PrintWriter pw) {
+ try {
+ if (map == null) {
+ pw.println("BPF map is null");
+ return;
+ }
+ if (map.isEmpty()) {
+ pw.println("No entries");
+ return;
+ }
+ map.forEach((k, v) -> pw.println(toBase64EncodedString(k, v)));
+ } catch (ErrnoException e) {
+ pw.println("Map dump end with error: " + Os.strerror(e.errno));
+ }
+ }
+
+ /**
* Dump the BpfMap name and entries
*/
public static <K extends Struct, V extends Struct> void dumpMap(IBpfMap<K, V> map,
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index 19d8bbe..319d51a 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -180,6 +180,7 @@
public static final int ICMPV6_NS_HEADER_LEN = 24;
public static final int ICMPV6_NA_HEADER_LEN = 24;
public static final int ICMPV6_ND_OPTION_TLLA_LEN = 8;
+ public static final int ICMPV6_ND_OPTION_SLLA_LEN = 8;
public static final int NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER = 1 << 31;
public static final int NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED = 1 << 30;
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index cb02de8..2a0e8e0 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -128,20 +128,16 @@
});
}
-inline int mapRetrieve(const char* pathname, uint32_t flag) {
- return bpfFdGet(pathname, flag);
-}
-
inline int mapRetrieveRW(const char* pathname) {
- return mapRetrieve(pathname, 0);
+ return bpfFdGet(pathname, 0);
}
inline int mapRetrieveRO(const char* pathname) {
- return mapRetrieve(pathname, BPF_F_RDONLY);
+ return bpfFdGet(pathname, BPF_F_RDONLY);
}
inline int mapRetrieveWO(const char* pathname) {
- return mapRetrieve(pathname, BPF_F_WRONLY);
+ return bpfFdGet(pathname, BPF_F_WRONLY);
}
inline int retrieveProgram(const char* pathname) {
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
index 600a623..6d03042 100644
--- a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
+++ b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
@@ -61,6 +61,7 @@
setUpdaterNetworkingEnabled(testInfo, enableChain = true,
enablePkgs = UPDATER_PKGS.associateWith { false })
runPreparerApk(testInfo)
+ refreshTime(testInfo)
}
private fun runPreparerApk(testInfo: TestInformation) {
@@ -141,6 +142,14 @@
.contains(":deny")
}
+ private fun refreshTime(testInfo: TestInformation,) {
+ // Forces a synchronous time refresh using the network. Time is fetched synchronously but
+ // this does not guarantee that system time is updated when it returns.
+ // This avoids flakes where the system clock rolls back, for example when using test
+ // settings like test_url_expiration_time in NetworkMonitor.
+ testInfo.exec("cmd network_time_update_service force_refresh")
+ }
+
override fun tearDown(testInfo: TestInformation, e: Throwable?) {
if (isTearDownDisabled) return
installer.tearDown(testInfo, e)
diff --git a/tests/cts/multidevices/Android.bp b/tests/cts/multidevices/Android.bp
index 5ac4229..5f9b3ef 100644
--- a/tests/cts/multidevices/Android.bp
+++ b/tests/cts/multidevices/Android.bp
@@ -14,12 +14,16 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_fwk_core_networking",
}
python_test_host {
name: "CtsConnectivityMultiDevicesTestCases",
main: "connectivity_multi_devices_test.py",
- srcs: ["connectivity_multi_devices_test.py"],
+ srcs: [
+ "connectivity_multi_devices_test.py",
+ "tether_utils.py",
+ ],
libs: [
"mobly",
],
diff --git a/tests/cts/multidevices/connectivity_multi_devices_test.py b/tests/cts/multidevices/connectivity_multi_devices_test.py
index ab88504..417db99 100644
--- a/tests/cts/multidevices/connectivity_multi_devices_test.py
+++ b/tests/cts/multidevices/connectivity_multi_devices_test.py
@@ -1,23 +1,16 @@
# Lint as: python3
"""Connectivity multi devices tests."""
-import base64
import sys
-import uuid
-
-from mobly import asserts
from mobly import base_test
from mobly import test_runner
from mobly import utils
from mobly.controllers import android_device
+import tether_utils
+from tether_utils import UpstreamType
CONNECTIVITY_MULTI_DEVICES_SNIPPET_PACKAGE = "com.google.snippet.connectivity"
-class UpstreamType:
- CELLULAR = 1
- WIFI = 2
-
-
class ConnectivityMultiDevicesTest(base_test.BaseTestClass):
def setup_class(self):
@@ -40,66 +33,27 @@
raise_on_exception=True,
)
- @staticmethod
- def generate_uuid32_base64():
- """Generates a UUID32 and encodes it in Base64.
-
- Returns:
- str: The Base64-encoded UUID32 string. Which is 22 characters.
- """
- return base64.b64encode(uuid.uuid1().bytes).decode("utf-8").strip("=")
-
- def _do_test_hotspot_for_upstream_type(self, upstream_type):
- """Test hotspot with the specified upstream type.
-
- This test create a hotspot, make the client connect
- to it, and verify the packet is forwarded by the hotspot.
- """
- server = self.serverDevice.connectivity_multi_devices_snippet
- client = self.clientDevice.connectivity_multi_devices_snippet
-
- # Assert pre-conditions specific to each upstream type.
- asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
- asserts.skip_if(
- not server.hasHotspotFeature(), "Server requires hotspot feature"
- )
- if upstream_type == UpstreamType.CELLULAR:
- asserts.skip_if(
- not server.hasTelephonyFeature(), "Server requires Telephony feature"
- )
- server.requestCellularAndEnsureDefault()
- elif upstream_type == UpstreamType.WIFI:
- asserts.skip_if(
- not server.isStaApConcurrencySupported(),
- "Server requires Wifi AP + STA concurrency",
- )
- server.ensureWifiIsDefault()
- else:
- raise ValueError(f"Invalid upstream type: {upstream_type}")
-
- # Generate ssid/passphrase with random characters to make sure nearby devices won't
- # connect unexpectedly. Note that total length of ssid cannot go over 32.
- testSsid = "HOTSPOT-" + self.generate_uuid32_base64()
- testPassphrase = self.generate_uuid32_base64()
-
- try:
- # Create a hotspot with fixed SSID and password.
- server.startHotspot(testSsid, testPassphrase)
-
- # Make the client connects to the hotspot.
- client.connectToWifi(testSsid, testPassphrase, True)
-
- finally:
- if upstream_type == UpstreamType.CELLULAR:
- server.unrequestCellular()
- # Teardown the hotspot.
- server.stopAllTethering()
-
def test_hotspot_upstream_wifi(self):
- self._do_test_hotspot_for_upstream_type(UpstreamType.WIFI)
+ try:
+ # Connectivity of the client verified by asserting the validated capability.
+ tether_utils.setup_hotspot_and_client_for_upstream_type(
+ self.serverDevice, self.clientDevice, UpstreamType.WIFI
+ )
+ finally:
+ tether_utils.cleanup_tethering_for_upstream_type(
+ self.serverDevice, UpstreamType.WIFI
+ )
def test_hotspot_upstream_cellular(self):
- self._do_test_hotspot_for_upstream_type(UpstreamType.CELLULAR)
+ try:
+ # Connectivity of the client verified by asserting the validated capability.
+ tether_utils.setup_hotspot_and_client_for_upstream_type(
+ self.serverDevice, self.clientDevice, UpstreamType.CELLULAR
+ )
+ finally:
+ tether_utils.cleanup_tethering_for_upstream_type(
+ self.serverDevice, UpstreamType.CELLULAR
+ )
if __name__ == "__main__":
diff --git a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
index 258648f..8805edd 100644
--- a/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/ConnectivityMultiDevicesSnippet.kt
@@ -16,11 +16,11 @@
package com.google.snippet.connectivity
+import android.Manifest.permission.NETWORK_SETTINGS
import android.Manifest.permission.OVERRIDE_WIFI_CONFIG
import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.content.pm.PackageManager.FEATURE_WIFI
import android.net.ConnectivityManager
-import android.net.Network
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkRequest
@@ -30,20 +30,24 @@
import android.net.wifi.SoftApConfiguration
import android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
import android.net.wifi.WifiConfiguration
+import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiNetworkSpecifier
import android.net.wifi.WifiSsid
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.testutils.AutoReleaseNetworkCallbackRule
import com.android.testutils.ConnectUtil
import com.android.testutils.NetworkCallbackHelper
-import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.runAsShell
import com.google.android.mobly.snippet.Snippet
import com.google.android.mobly.snippet.rpc.Rpc
+import org.junit.Rule
class ConnectivityMultiDevicesSnippet : Snippet {
+ @get:Rule
+ val networkCallbackRule = AutoReleaseNetworkCallbackRule()
private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
private val wifiManager = context.getSystemService(WifiManager::class.java)!!
private val cm = context.getSystemService(ConnectivityManager::class.java)!!
@@ -88,10 +92,8 @@
// Suppress warning because WifiManager methods to connect to a config are
// documented not to be deprecated for privileged users.
@Suppress("DEPRECATION")
- fun connectToWifi(ssid: String, passphrase: String, requireValidation: Boolean): Network {
+ fun connectToWifi(ssid: String, passphrase: String): Long {
val specifier = WifiNetworkSpecifier.Builder()
- .setSsid(ssid)
- .setWpa2Passphrase(passphrase)
.setBand(ScanResult.WIFI_BAND_24_GHZ)
.build()
val wifiConfig = WifiConfiguration()
@@ -102,27 +104,28 @@
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
- // Register network callback for the specific wifi.
+ // Add the test configuration and connect to it.
+ val connectUtil = ConnectUtil(context)
+ connectUtil.connectToWifiConfig(wifiConfig)
+
+ // Implement manual SSID matching. Specifying the SSID in
+ // NetworkSpecifier is ineffective
+ // (see WifiNetworkAgentSpecifier#canBeSatisfiedBy for details).
+ // Note that holding permission is necessary when waiting for
+ // the callbacks. The handler thread checks permission; if
+ // it's not present, the SSID will be redacted.
val networkCallback = TestableNetworkCallback()
- val wifiRequest = NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI)
- .setNetworkSpecifier(specifier)
- .build()
- cm.registerNetworkCallback(wifiRequest, networkCallback)
-
- try {
- // Add the test configuration and connect to it.
- val connectUtil = ConnectUtil(context)
- connectUtil.connectToWifiConfig(wifiConfig)
-
- val event = networkCallback.expect<Available>()
- if (requireValidation) {
- networkCallback.eventuallyExpect<CapabilitiesChanged> {
- it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
- }
- }
- return event.network
- } finally {
- cm.unregisterNetworkCallback(networkCallback)
+ val wifiRequest = NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build()
+ return runAsShell(NETWORK_SETTINGS) {
+ // Register the network callback is needed here.
+ // This is to avoid the race condition where callback is fired before
+ // acquiring permission.
+ networkCallbackRule.registerNetworkCallback(wifiRequest, networkCallback)
+ return@runAsShell networkCallback.eventuallyExpect<CapabilitiesChanged> {
+ // Remove double quotes.
+ val ssidFromCaps = (WifiInfo::sanitizeSsid)(it.caps.ssid)
+ ssidFromCaps == ssid && it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
+ }.network.networkHandle
}
}
diff --git a/tests/cts/multidevices/tether_utils.py b/tests/cts/multidevices/tether_utils.py
new file mode 100644
index 0000000..a2d703c
--- /dev/null
+++ b/tests/cts/multidevices/tether_utils.py
@@ -0,0 +1,103 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64
+import uuid
+
+from mobly import asserts
+from mobly.controllers import android_device
+
+
+class UpstreamType:
+ CELLULAR = 1
+ WIFI = 2
+
+
+def generate_uuid32_base64() -> str:
+ """Generates a UUID32 and encodes it in Base64.
+
+ Returns:
+ str: The Base64-encoded UUID32 string. Which is 22 characters.
+ """
+ # Strip padding characters to make it safer for hotspot name length limit.
+ return base64.b64encode(uuid.uuid1().bytes).decode("utf-8").strip("=")
+
+
+def setup_hotspot_and_client_for_upstream_type(
+ server_device: android_device,
+ client_device: android_device,
+ upstream_type: UpstreamType,
+) -> (str, int):
+ """Setup the hotspot with a connected client with the specified upstream type.
+
+ This creates a hotspot, make the client connect
+ to it, and verify the packet is forwarded by the hotspot.
+ And returns interface name of both if successful.
+ """
+ server = server_device.connectivity_multi_devices_snippet
+ client = client_device.connectivity_multi_devices_snippet
+
+ # Assert pre-conditions specific to each upstream type.
+ asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
+ asserts.skip_if(
+ not server.hasHotspotFeature(), "Server requires hotspot feature"
+ )
+ if upstream_type == UpstreamType.CELLULAR:
+ asserts.skip_if(
+ not server.hasTelephonyFeature(), "Server requires Telephony feature"
+ )
+ server.requestCellularAndEnsureDefault()
+ elif upstream_type == UpstreamType.WIFI:
+ asserts.skip_if(
+ not server.isStaApConcurrencySupported(),
+ "Server requires Wifi AP + STA concurrency",
+ )
+ server.ensureWifiIsDefault()
+ else:
+ raise ValueError(f"Invalid upstream type: {upstream_type}")
+
+ # Generate ssid/passphrase with random characters to make sure nearby devices won't
+ # connect unexpectedly. Note that total length of ssid cannot go over 32.
+ test_ssid = "HOTSPOT-" + generate_uuid32_base64()
+ test_passphrase = generate_uuid32_base64()
+
+ # Create a hotspot with fixed SSID and password.
+ hotspot_interface = server.startHotspot(test_ssid, test_passphrase)
+
+ # Make the client connects to the hotspot.
+ client_network = client.connectToWifi(test_ssid, test_passphrase)
+
+ return hotspot_interface, client_network
+
+
+def cleanup_tethering_for_upstream_type(
+ server_device: android_device, upstream_type: UpstreamType
+) -> None:
+ server = server_device.connectivity_multi_devices_snippet
+ if upstream_type == UpstreamType.CELLULAR:
+ server.unrequestCellular()
+ # Teardown the hotspot.
+ server.stopAllTethering()
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 76cfc93..90fb7a9 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -415,7 +415,11 @@
readProgram() // wait for install completion
// Assert that initial ping does not get filtered.
- val payloadSize = 56
+ val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ 68
+ } else {
+ 4
+ }
val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
packetReader.sendPing(data, payloadSize)
assertThat(packetReader.expectPingReply()).isEqualTo(data)
@@ -479,7 +483,11 @@
readProgram() // wait for install completion
// Trigger the program by sending a ping and waiting on the reply.
- val payloadSize = 56
+ val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ 68
+ } else {
+ 4
+ }
val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
packetReader.sendPing(data, payloadSize)
packetReader.expectPingReply()
@@ -489,8 +497,8 @@
expect.withMessage("PROGRAM_SIZE").that(buffer.getInt()).isEqualTo(program.size)
expect.withMessage("RAM_LEN").that(buffer.getInt()).isEqualTo(caps.maximumApfProgramSize)
expect.withMessage("IPV4_HEADER_SIZE").that(buffer.getInt()).isEqualTo(0)
- // Ping packet (64) + IPv6 header (40) + ethernet header (14)
- expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(64 + 40 + 14)
+ // Ping packet payload + ICMPv6 header (8) + IPv6 header (40) + ethernet header (14)
+ expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(payloadSize + 8 + 40 + 14)
expect.withMessage("FILTER_AGE_SECONDS").that(buffer.getInt()).isLessThan(5)
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index bf40462..633f2b6 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -309,6 +309,7 @@
// Airplane Mode BroadcastReceiver Timeout
private static final long AIRPLANE_MODE_CHANGE_TIMEOUT_MS = 10_000L;
+ private static final long CELL_DATA_AVAILABLE_TIMEOUT_MS = 120_000L;
// Timeout for applying uids allowed on restricted networks
private static final long APPLYING_UIDS_ALLOWED_ON_RESTRICTED_NETWORKS_TIMEOUT_MS = 3_000L;
@@ -2242,7 +2243,10 @@
// connectToCell only registers a request, it cannot / does not need to be called twice
mCtsNetUtils.ensureWifiConnected();
if (verifyWifi) waitForAvailable(wifiCb);
- if (supportTelephony) waitForAvailable(telephonyCb);
+ if (supportTelephony) {
+ telephonyCb.eventuallyExpect(
+ CallbackEntry.AVAILABLE, CELL_DATA_AVAILABLE_TIMEOUT_MS);
+ }
} finally {
// Restore the previous state of airplane mode and permissions:
runShellCommand("cmd connectivity airplane-mode "
diff --git a/tests/cts/netpermission/updatestatspermission/Android.bp b/tests/cts/netpermission/updatestatspermission/Android.bp
index 2fde1ce..689ce74 100644
--- a/tests/cts/netpermission/updatestatspermission/Android.bp
+++ b/tests/cts/netpermission/updatestatspermission/Android.bp
@@ -19,11 +19,17 @@
android_test {
name: "CtsNetTestCasesUpdateStatsPermission",
- defaults: ["cts_defaults"],
+ defaults: [
+ "cts_defaults",
+ "framework-connectivity-test-defaults",
+ ],
srcs: ["src/**/*.java"],
-
- static_libs: ["ctstestrunner-axt"],
+ platform_apis: true,
+ static_libs: [
+ "ctstestrunner-axt",
+ "net-tests-utils",
+ ],
// Tag this module as a cts test artifact
test_suites: [
diff --git a/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java b/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java
index bea843c..56bad31 100644
--- a/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java
+++ b/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java
@@ -16,6 +16,10 @@
package android.net.cts.networkpermission.updatestatspermission;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -25,6 +29,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,6 +73,11 @@
out.write(buf);
out.close();
socket.close();
+ // Clear TrafficStats cache is needed to avoid rate-limit caching for
+ // TrafficStats API results on V+ devices.
+ if (SdkLevel.isAtLeastV()) {
+ runAsShell(NETWORK_SETTINGS, () -> TrafficStats.clearRateLimitCaches());
+ }
long uidTxBytesAfter = TrafficStats.getUidTxBytes(Process.myUid());
long uidTxDeltaBytes = uidTxBytesAfter - uidTxBytesBefore;
assertTrue("uidtxb: " + uidTxBytesBefore + " -> " + uidTxBytesAfter + " delta="
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
index aa28e5a..10ba6a4 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -163,7 +163,8 @@
val sentQueryCount = 150
val metrics = NetworkNsdReportedMetrics(clientId, deps)
metrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId, durationMs,
- foundCallbackCount, lostCallbackCount, servicesCount, sentQueryCount)
+ foundCallbackCount, lostCallbackCount, servicesCount, sentQueryCount,
+ true /* isServiceFromCache */)
val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
verify(deps).statsWrite(eventCaptor.capture())
@@ -179,6 +180,7 @@
assertEquals(servicesCount, it.foundServiceCount)
assertEquals(durationMs, it.eventDurationMillisec)
assertEquals(sentQueryCount, it.sentQueryCount)
+ assertTrue(it.isKnownService)
}
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index aece3f7..979e0a1 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -1196,7 +1196,7 @@
Instant.MAX /* expirationTime */);
// Verify onServiceNameDiscovered callback
- listener.onServiceNameDiscovered(foundInfo, false /* isServiceFromCache */);
+ listener.onServiceNameDiscovered(foundInfo, true /* isServiceFromCache */);
verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
info.getServiceName().equals(SERVICE_NAME)
// Service type in discovery callbacks has a dot at the end
@@ -1232,7 +1232,7 @@
verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
verify(mMetrics).reportServiceDiscoveryStop(false /* isLegacy */, discId,
10L /* durationMs */, 1 /* foundCallbackCount */, 1 /* lostCallbackCount */,
- 1 /* servicesCount */, 3 /* sentQueryCount */);
+ 1 /* servicesCount */, 3 /* sentQueryCount */, true /* isServiceFromCache */);
}
@Test
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 9026481..c997b01 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -56,6 +56,7 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
@@ -306,6 +307,7 @@
private HandlerThread mObserverHandlerThread;
final TestDependencies mDeps = new TestDependencies();
final HashMap<String, Boolean> mFeatureFlags = new HashMap<>();
+ final HashMap<Long, Boolean> mCompatChanges = new HashMap<>();
// This will set feature flags from @FeatureFlag annotations
// into the map before setUp() runs.
@@ -594,7 +596,7 @@
}
@Override
- public boolean supportTrafficStatsRateLimitCache(Context ctx) {
+ public boolean alwaysUseTrafficStatsRateLimitCache(Context ctx) {
return mFeatureFlags.getOrDefault(TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG, false);
}
@@ -608,6 +610,14 @@
return DEFAULT_TRAFFIC_STATS_CACHE_MAX_ENTRIES;
}
+ @Override
+ public boolean isChangeEnabled(long changeId, int uid) {
+ return mCompatChanges.getOrDefault(changeId, true);
+ }
+
+ public void setChangeEnabled(long changeId, boolean enabled) {
+ mCompatChanges.put(changeId, enabled);
+ }
@Nullable
@Override
public NetworkStats.Entry nativeGetTotalStat() {
@@ -2413,17 +2423,33 @@
@FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG, enabled = false)
@Test
- public void testTrafficStatsRateLimitCache_disabled() throws Exception {
- doTestTrafficStatsRateLimitCache(false /* cacheEnabled */);
+ public void testTrafficStatsRateLimitCache_disabledWithCompatChangeEnabled() throws Exception {
+ mDeps.setChangeEnabled(ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, true);
+ doTestTrafficStatsRateLimitCache(true /* expectCached */);
}
@FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG)
@Test
- public void testTrafficStatsRateLimitCache_enabled() throws Exception {
- doTestTrafficStatsRateLimitCache(true /* cacheEnabled */);
+ public void testTrafficStatsRateLimitCache_enabledWithCompatChangeEnabled() throws Exception {
+ mDeps.setChangeEnabled(ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, true);
+ doTestTrafficStatsRateLimitCache(true /* expectCached */);
}
- private void doTestTrafficStatsRateLimitCache(boolean cacheEnabled) throws Exception {
+ @FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG, enabled = false)
+ @Test
+ public void testTrafficStatsRateLimitCache_disabledWithCompatChangeDisabled() throws Exception {
+ mDeps.setChangeEnabled(ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, false);
+ doTestTrafficStatsRateLimitCache(false /* expectCached */);
+ }
+
+ @FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG)
+ @Test
+ public void testTrafficStatsRateLimitCache_enabledWithCompatChangeDisabled() throws Exception {
+ mDeps.setChangeEnabled(ENABLE_TRAFFICSTATS_RATE_LIMIT_CACHE, false);
+ doTestTrafficStatsRateLimitCache(true /* expectCached */);
+ }
+
+ private void doTestTrafficStatsRateLimitCache(boolean expectCached) throws Exception {
mockDefaultSettings();
// Calling uid is not injected into the service, use the real uid to pass the caller check.
final int myUid = Process.myUid();
@@ -2433,7 +2459,7 @@
// Verify the values are cached.
incrementCurrentTime(DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS / 2);
mockTrafficStatsValues(65L, 8L, 1055L, 9L);
- if (cacheEnabled) {
+ if (expectCached) {
assertTrafficStatsValues(TEST_IFACE, myUid, 64L, 3L, 1024L, 8L);
} else {
assertTrafficStatsValues(TEST_IFACE, myUid, 65L, 8L, 1055L, 9L);
diff --git a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
index 0e76930..996d22d 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
@@ -30,6 +30,8 @@
import android.net.thread.ActiveOperationalDataset.Builder;
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.util.SparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +40,7 @@
import com.google.common.primitives.Bytes;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +49,7 @@
/** CTS tests for {@link ActiveOperationalDataset}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class ActiveOperationalDatasetTest {
private static final int TYPE_ACTIVE_TIMESTAMP = 14;
@@ -81,6 +85,8 @@
private static final ActiveOperationalDataset DEFAULT_DATASET =
ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
private static byte[] removeTlv(byte[] dataset, int type) {
ByteArrayOutputStream os = new ByteArrayOutputStream(dataset.length);
int i = 0;
diff --git a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
index 9be3d56..4d7c7f1 100644
--- a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
@@ -21,12 +21,15 @@
import static org.junit.Assert.assertThrows;
import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,8 +37,11 @@
/** Tests for {@link OperationalDatasetTimestamp}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class OperationalDatasetTimestampTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
@Test
public void fromInstant_tooLargeInstant_throwsIllegalArgument() {
assertThrows(
diff --git a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
index 0bb18ce..76be054 100644
--- a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
@@ -28,6 +28,8 @@
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
import android.net.thread.OperationalDatasetTimestamp;
import android.net.thread.PendingOperationalDataset;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.util.SparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,6 +38,7 @@
import com.google.common.primitives.Bytes;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,8 +47,11 @@
/** Tests for {@link PendingOperationalDataset}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class PendingOperationalDatasetTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
private static ActiveOperationalDataset createActiveDataset() throws Exception {
SparseArray<byte[]> channelMask = new SparseArray<>(1);
channelMask.put(0, new byte[] {0x00, 0x1f, (byte) 0xff, (byte) 0xe0});
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
index 7d9ae81..4de2e13 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
@@ -25,17 +25,23 @@
import static org.junit.Assert.assertThrows;
import android.net.thread.ThreadNetworkException;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/** CTS tests for {@link ThreadNetworkException}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class ThreadNetworkExceptionTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
@Test
public void constructor_validValues_valuesAreConnectlySet() throws Exception {
ThreadNetworkException errorThreadDisabled =
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index 46cf562..d24059a 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -17,16 +17,16 @@
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
-
import static com.google.common.io.BaseEncoding.base16;
+import static java.util.concurrent.TimeUnit.SECONDS;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.nsd.NsdServiceInfo;
import android.net.thread.ActiveOperationalDataset;
-
+import android.os.Handler;
+import android.os.HandlerThread;
import com.google.errorprone.annotations.FormatMethod;
-
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
@@ -39,6 +39,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -60,10 +62,13 @@
private static final float PING_TIMEOUT_0_1_SECOND = 0.1f;
// 1 second timeout should be used when response is expected.
private static final float PING_TIMEOUT_1_SECOND = 1f;
+ private static final int READ_LINE_TIMEOUT_SECONDS = 5;
private final Process mProcess;
private final BufferedReader mReader;
private final BufferedWriter mWriter;
+ private final HandlerThread mReaderHandlerThread;
+ private final Handler mReaderHandler;
private ActiveOperationalDataset mActiveOperationalDataset;
@@ -87,11 +92,15 @@
}
mReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
mWriter = new BufferedWriter(new OutputStreamWriter(mProcess.getOutputStream()));
+ mReaderHandlerThread = new HandlerThread("FullThreadDeviceReader");
+ mReaderHandlerThread.start();
+ mReaderHandler = new Handler(mReaderHandlerThread.getLooper());
mActiveOperationalDataset = null;
}
public void destroy() {
mProcess.destroy();
+ mReaderHandlerThread.quit();
}
/**
@@ -213,7 +222,7 @@
public String udpReceive() throws IOException {
Pattern pattern =
Pattern.compile("> (\\d+) bytes from ([\\da-f:]+) (\\d+) ([\\x00-\\x7F]+)");
- Matcher matcher = pattern.matcher(mReader.readLine());
+ Matcher matcher = pattern.matcher(readLine());
matcher.matches();
return matcher.group(4);
@@ -500,10 +509,27 @@
}
}
+ private String readLine() throws IOException {
+ final CompletableFuture<String> future = new CompletableFuture<>();
+ mReaderHandler.post(
+ () -> {
+ try {
+ future.complete(mReader.readLine());
+ } catch (IOException e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ return future.get(READ_LINE_TIMEOUT_SECONDS, SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new IOException("Failed to read a line from ot-cli-ftd");
+ }
+ }
+
private List<String> readUntilDone() throws IOException {
ArrayList<String> result = new ArrayList<>();
String line;
- while ((line = mReader.readLine()) != null) {
+ while ((line = readLine()) != null) {
if (line.equals("Done")) {
break;
}
diff --git a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
index bee9ceb..38a6e90 100644
--- a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
+++ b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
@@ -21,7 +21,6 @@
import static org.junit.Assume.assumeTrue;
import android.content.Context;
-import android.net.thread.ThreadNetworkManager;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
@@ -122,7 +121,10 @@
/** Returns {@code true} if this device has the Thread feature supported. */
private static boolean hasThreadFeature() {
final Context context = ApplicationProvider.getApplicationContext();
- return context.getSystemService(ThreadNetworkManager.class) != null;
+
+ // Use service name rather than `ThreadNetworkManager.class` to avoid
+ // `ClassNotFoundException` on U- devices.
+ return context.getSystemService("thread_network") != null;
}
/**