Merge "Fix crash when socket read is interrupted"
diff --git a/staticlibs/device/com/android/net/module/util/FdEventsReader.java b/staticlibs/device/com/android/net/module/util/FdEventsReader.java
index 4825992..f88883b 100644
--- a/staticlibs/device/com/android/net/module/util/FdEventsReader.java
+++ b/staticlibs/device/com/android/net/module/util/FdEventsReader.java
@@ -180,6 +180,17 @@
}
/**
+ * Called by the subclasses of FdEventsReader, decide whether it should stop reading from the
+ * socket or process the packet and continue to read upon receiving a zero-length packet.
+ *
+ * @return {@code true} if this FdEventsReader should process the zero-length packet.
+ * {@code false} if it should stop reading from the socket.
+ */
+ protected boolean shouldProcessZeroLengthPacket() {
+ return false; // by default, stop reading upon receiving zero-length packet.
+ }
+
+ /**
* Called by the main loop to log errors. In some cases |e| may be null.
*/
protected void logError(@NonNull String msg, @Nullable Exception e) {}
@@ -235,7 +246,7 @@
try {
bytesRead = readPacket(mFd, mBuffer);
- if (bytesRead < 1) {
+ if (bytesRead == 0 && !shouldProcessZeroLengthPacket()) {
if (isRunning()) logError("Socket closed, exiting", null);
break;
}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java b/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
index e69a844..f8b4716 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
@@ -39,6 +39,7 @@
import android.net.util.SocketUtils;
import android.os.Process;
+import android.os.SystemClock;
import android.system.ErrnoException;
import android.util.Log;
import android.util.Range;
@@ -461,11 +462,15 @@
*/
public static void destroyLiveTcpSockets(Set<Range<Integer>> ranges, Set<Integer> exemptUids)
throws SocketException, InterruptedIOException, ErrnoException {
+ final long startTimeMs = SystemClock.elapsedRealtime();
destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
(diagMsg) -> !exemptUids.contains(diagMsg.inetDiagMsg.idiag_uid)
&& containsUid(diagMsg, ranges)
&& !isLoopback(diagMsg)
&& !isAdbSocket(diagMsg));
+ final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
+ Log.d(TAG, "Destroyed live tcp sockets for uids=" + ranges + " exemptUids=" + exemptUids
+ + " in " + durationMs + "ms");
}
/**
@@ -479,10 +484,13 @@
*/
public static void destroyLiveTcpSocketsByOwnerUids(Set<Integer> ownerUids)
throws SocketException, InterruptedIOException, ErrnoException {
+ final long startTimeMs = SystemClock.elapsedRealtime();
destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
(diagMsg) -> ownerUids.contains(diagMsg.inetDiagMsg.idiag_uid)
&& !isLoopback(diagMsg)
&& !isAdbSocket(diagMsg));
+ final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
+ Log.d(TAG, "Destroyed live tcp sockets for uids=" + ownerUids + " in " + durationMs + "ms");
}
@Override
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
index 308ea24..b512a95 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
@@ -93,6 +93,15 @@
if (nlmsghdr == null || nlmsghdr.nlmsg_type != NetlinkConstants.NLMSG_ERROR) {
return null;
}
+
+ final int messageLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len);
+ final int payloadLength = messageLength - StructNlMsgHdr.STRUCT_SIZE;
+ if (payloadLength < 0 || payloadLength > bytes.remaining()) {
+ // Malformed message or runt buffer. Pretend the buffer was consumed.
+ bytes.position(bytes.limit());
+ return null;
+ }
+
return NetlinkErrorMessage.parse(nlmsghdr, bytes);
}
diff --git a/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java b/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
index cd974e6..060d491 100644
--- a/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
@@ -62,13 +62,13 @@
public final long preferred;
@Field(order = 3, type = Type.U32)
public final long valid;
- @Field(order = 4, type = Type.U8)
- public final short prefixLen;
+ @Field(order = 4, type = Type.S8)
+ public final byte prefixLen;
@Field(order = 5, type = Type.ByteArray, arraysize = 16)
public final byte[] prefix;
IaPrefixOption(final short code, final short length, final long preferred,
- final long valid, final short prefixLen, final byte[] prefix) {
+ final long valid, final byte prefixLen, final byte[] prefix) {
this.code = code;
this.length = length;
this.preferred = preferred;
@@ -81,7 +81,7 @@
* Build an IA_PD prefix option with given specific parameters.
*/
public static ByteBuffer build(final short length, final long preferred, final long valid,
- final short prefixLen, final byte[] prefix) {
+ final byte prefixLen, final byte[] prefix) {
final IaPrefixOption option = new IaPrefixOption((byte) DHCP6_OPTION_IAPREFIX,
length /* 25 + IAPrefix options length */, preferred, valid, prefixLen, prefix);
return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
diff --git a/staticlibs/native/bpf_headers/Android.bp b/staticlibs/native/bpf_headers/Android.bp
index 31adef9..41184ea 100644
--- a/staticlibs/native/bpf_headers/Android.bp
+++ b/staticlibs/native/bpf_headers/Android.bp
@@ -33,8 +33,10 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
- "com.android.tethering",
"com.android.art.debug",
+ "com.android.os.statsd",
+ "com.android.resolv",
+ "com.android.tethering",
],
}
diff --git a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
index d23afae..6c0841c 100644
--- a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
+++ b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
@@ -25,6 +25,7 @@
#include "BpfSyscallWrappers.h"
#include "bpf/BpfRingbuf.h"
#include "bpf/BpfUtils.h"
+#include "bpf/KernelUtils.h"
#define TEST_RINGBUF_MAGIC_NUM 12345
@@ -49,10 +50,6 @@
GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
}
- if (sizeof(unsigned long) != 8) {
- GTEST_SKIP() << "BPF ring buffers not supported on 32 bit arch";
- }
-
errno = 0;
mProgram.reset(retrieveProgram(mProgPath.c_str()));
EXPECT_EQ(errno, 0);
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
index cac1e43..dd1504c 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
@@ -24,6 +24,8 @@
#include "bpf/BpfUtils.h"
+#include <atomic>
+
namespace android {
namespace bpf {
@@ -80,8 +82,28 @@
android::base::unique_fd mRingFd;
void* mDataPos = nullptr;
- unsigned long* mConsumerPos = nullptr;
- unsigned long* mProducerPos = nullptr;
+ // The kernel uses an "unsigned long" type for both consumer and producer position.
+ // Unsigned long is a 4 byte value on a 32-bit kernel, and an 8 byte value on a 64-bit kernel.
+ // To support 32-bit kernels, producer pos is capped at 4 bytes (despite it being 8 bytes on
+ // 64-bit kernels) and all comparisons of consumer and producer pos only compare the low-order 4
+ // bytes (an inequality comparison is performed to support overflow).
+ // This solution is bitness agnostic. The consumer only increments the 8 byte consumer pos, which,
+ // in a little-endian architecture, is safe since the entire page is mapped into memory and a
+ // 32-bit kernel will just ignore the high-order bits.
+ std::atomic_uint64_t* mConsumerPos = nullptr;
+ std::atomic_uint32_t* mProducerPos = nullptr;
+
+ // In order to guarantee atomic access in a 32 bit userspace environment, atomic_uint64_t is used
+ // in addition to std::atomic<T>::is_always_lock_free that guarantees that read / write operations
+ // are indeed atomic.
+ // Since std::atomic does not support wrapping preallocated memory, an additional static assert on
+ // the size of the atomic and the underlying type is added to ensure a reinterpret_cast from type
+ // to its atomic version is safe (is_always_lock_free being true should provide additional
+ // confidence).
+ static_assert(std::atomic_uint64_t::is_always_lock_free);
+ static_assert(std::atomic_uint32_t::is_always_lock_free);
+ static_assert(sizeof(std::atomic_uint64_t) == sizeof(uint64_t));
+ static_assert(sizeof(std::atomic_uint32_t) == sizeof(uint32_t));
};
// This is a class wrapper for eBPF ring buffers. An eBPF ring buffer is a
@@ -121,34 +143,8 @@
BpfRingbuf() : BpfRingbufBase(sizeof(Value)) {}
};
-#define ACCESS_ONCE(x) (*(volatile typeof(x)*)&(x))
-
-#if defined(__i386__) || defined(__x86_64__)
-#define smp_sync() asm volatile("" ::: "memory")
-#elif defined(__aarch64__)
-#define smp_sync() asm volatile("dmb ish" ::: "memory")
-#else
-#define smp_sync() __sync_synchronize()
-#endif
-
-#define smp_store_release(p, v) \
- do { \
- smp_sync(); \
- ACCESS_ONCE(*(p)) = (v); \
- } while (0)
-
-#define smp_load_acquire(p) \
- ({ \
- auto ___p = ACCESS_ONCE(*(p)); \
- smp_sync(); \
- ___p; \
- })
inline base::Result<void> BpfRingbufBase::Init(const char* path) {
- if (sizeof(unsigned long) != 8) {
- return android::base::Error()
- << "BpfRingbuf does not support 32 bit architectures";
- }
mRingFd.reset(mapRetrieveRW(path));
if (!mRingFd.ok()) {
return android::base::ErrnoError()
@@ -184,7 +180,7 @@
return android::base::ErrnoError()
<< "failed to mmap ringbuf consumer pages";
}
- mConsumerPos = reinterpret_cast<unsigned long*>(ptr);
+ mConsumerPos = reinterpret_cast<decltype(mConsumerPos)>(ptr);
}
{
@@ -194,7 +190,7 @@
return android::base::ErrnoError()
<< "failed to mmap ringbuf producer page";
}
- mProducerPos = reinterpret_cast<unsigned long*>(ptr);
+ mProducerPos = reinterpret_cast<decltype(mProducerPos)>(ptr);
}
mDataPos = pointerAddBytes<void*>(mProducerPos, getpagesize());
@@ -204,14 +200,19 @@
inline base::Result<int> BpfRingbufBase::ConsumeAll(
const std::function<void(const void*)>& callback) {
int64_t count = 0;
- unsigned long cons_pos = smp_load_acquire(mConsumerPos);
- unsigned long prod_pos = smp_load_acquire(mProducerPos);
- while (cons_pos < prod_pos) {
+ uint32_t prod_pos = mProducerPos->load(std::memory_order_acquire);
+ // Only userspace writes to mConsumerPos, so no need to use std::memory_order_acquire
+ uint64_t cons_pos = mConsumerPos->load(std::memory_order_relaxed);
+ while ((cons_pos & 0xFFFFFFFF) != prod_pos) {
// Find the start of the entry for this read (wrapping is done here).
void* start_ptr = pointerAddBytes<void*>(mDataPos, cons_pos & mPosMask);
// The entry has an 8 byte header containing the sample length.
- uint32_t length = smp_load_acquire(reinterpret_cast<uint32_t*>(start_ptr));
+ // struct bpf_ringbuf_hdr {
+ // u32 len;
+ // u32 pg_off;
+ // };
+ uint32_t length = *reinterpret_cast<volatile uint32_t*>(start_ptr);
// If the sample isn't committed, we're caught up with the producer.
if (length & BPF_RINGBUF_BUSY_BIT) return count;
@@ -220,7 +221,7 @@
if ((length & BPF_RINGBUF_DISCARD_BIT) == 0) {
if (length != mValueSize) {
- smp_store_release(mConsumerPos, cons_pos);
+ mConsumerPos->store(cons_pos, std::memory_order_release);
errno = EMSGSIZE;
return android::base::ErrnoError()
<< "BPF ring buffer message has unexpected size (want "
@@ -230,7 +231,7 @@
count++;
}
- smp_store_release(mConsumerPos, cons_pos);
+ mConsumerPos->store(cons_pos, std::memory_order_release);
}
return count;
@@ -252,10 +253,5 @@
});
}
-#undef ACCESS_ONCE
-#undef smp_sync
-#undef smp_store_release
-#undef smp_load_acquire
-
} // namespace bpf
} // namespace android
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfUtils.h b/staticlibs/native/bpf_headers/include/bpf/BpfUtils.h
index 99c7a91..9dd5822 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfUtils.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfUtils.h
@@ -28,7 +28,7 @@
#include <log/log.h>
-#include "KernelVersion.h"
+#include "KernelUtils.h"
namespace android {
namespace bpf {
diff --git a/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
new file mode 100644
index 0000000..59257b8
--- /dev/null
+++ b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/personality.h>
+#include <sys/utsname.h>
+
+namespace android {
+namespace bpf {
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+static inline unsigned uncachedKernelVersion() {
+ struct utsname buf;
+ if (uname(&buf)) return 0;
+
+ unsigned kver_major = 0;
+ unsigned kver_minor = 0;
+ unsigned kver_sub = 0;
+ (void)sscanf(buf.release, "%u.%u.%u", &kver_major, &kver_minor, &kver_sub);
+ return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline unsigned kernelVersion() {
+ static unsigned kver = uncachedKernelVersion();
+ return kver;
+}
+
+static inline __unused bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+ return kernelVersion() >= KVER(major, minor, sub);
+}
+
+// Figure out the bitness of userspace.
+// Trivial and known at compile time.
+static constexpr bool isUserspace32bit() {
+ return sizeof(void*) == 4;
+}
+
+static constexpr bool isUserspace64bit() {
+ return sizeof(void*) == 8;
+}
+
+#if defined(__LP64__)
+static_assert(isUserspace64bit(), "huh? LP64 must have 64-bit userspace");
+#elif defined(__ILP32__)
+static_assert(isUserspace32bit(), "huh? ILP32 must have 32-bit userspace");
+#else
+#error "huh? must be either LP64 (64-bit userspace) or ILP32 (32-bit userspace)"
+#endif
+
+static_assert(isUserspace32bit() || isUserspace64bit(), "must be either 32 or 64 bit");
+
+// Figure out the bitness of the kernel.
+static inline bool isKernel64Bit() {
+ // a 64-bit userspace requires a 64-bit kernel
+ if (isUserspace64bit()) return true;
+
+ static bool init = false;
+ static bool cache = false;
+ if (init) return cache;
+
+ // Retrieve current personality - on Linux this system call *cannot* fail.
+ int p = personality(0xffffffff);
+ // But if it does just assume kernel and userspace (which is 32-bit) match...
+ if (p == -1) return false;
+
+ // This will effectively mask out the bottom 8 bits, and switch to 'native'
+ // personality, and then return the previous personality of this thread
+ // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
+ int q = personality((p & ~PER_MASK) | PER_LINUX);
+ // Per man page this theoretically could error out with EINVAL,
+ // but kernel code analysis suggests setting PER_LINUX cannot fail.
+ // Either way, assume kernel and userspace (which is 32-bit) match...
+ if (q != p) return false;
+
+ struct utsname u;
+ (void)uname(&u); // only possible failure is EFAULT, but u is on stack.
+
+ // Switch back to previous personality.
+ // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
+ // but then we wouldn't have fetched 'p' from the kernel in the first place.
+ // Either way there's nothing meaningful we can do in case of error.
+ // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
+ // really hurt us either. We're really just switching back to be 'clean'.
+ (void)personality(p);
+
+ // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
+ // x86_64 i686 aarch64 armv7l
+ // additionally observed on arm device:
+ // armv8l
+ // presumably also might just be possible:
+ // i386 i486 i586
+ // and there might be other weird arm32 cases.
+ // We note that the 64 is present in both 64-bit archs,
+ // and in general is likely to be present in only 64-bit archs.
+ cache = !!strstr(u.machine, "64");
+ init = true;
+ return cache;
+}
+
+static inline __unused bool isKernel32Bit() {
+ return !isKernel64Bit();
+}
+
+static constexpr bool isArm() {
+#if defined(__arm__)
+ static_assert(isUserspace32bit(), "huh? arm must be 32 bit");
+ return true;
+#elif defined(__aarch64__)
+ static_assert(isUserspace64bit(), "aarch64 must be LP64 - no support for ILP32");
+ return true;
+#else
+ return false;
+#endif
+}
+
+static constexpr bool isX86() {
+#if defined(__i386__)
+ static_assert(isUserspace32bit(), "huh? i386 must be 32 bit");
+ return true;
+#elif defined(__x86_64__)
+ static_assert(isUserspace64bit(), "x86_64 must be LP64 - no support for ILP32 (x32)");
+ return true;
+#else
+ return false;
+#endif
+}
+
+static constexpr bool isRiscV() {
+#if defined(__riscv)
+ static_assert(isUserspace64bit(), "riscv must be 64 bit");
+ return true;
+#else
+ return false;
+#endif
+}
+
+static_assert(isArm() || isX86() || isRiscV(), "Unknown architecture");
+
+static __unused const char * describeArch() {
+ // ordered so as to make it easier to compile time optimize,
+ // only thing not known at compile time is isKernel64Bit()
+ if (isUserspace64bit()) {
+ if (isArm()) return "64-on-aarch64";
+ if (isX86()) return "64-on-x86-64";
+ if (isRiscV()) return "64-on-riscv64";
+ } else if (isKernel64Bit()) {
+ if (isArm()) return "32-on-aarch64";
+ if (isX86()) return "32-on-x86-64";
+ } else {
+ if (isArm()) return "32-on-arm32";
+ if (isX86()) return "32-on-x86-32";
+ }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h b/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h
deleted file mode 100644
index 29a36e6..0000000
--- a/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <sys/utsname.h>
-
-namespace android {
-namespace bpf {
-
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-
-static inline unsigned uncachedKernelVersion() {
- struct utsname buf;
- if (uname(&buf)) return 0;
-
- unsigned kver_major = 0;
- unsigned kver_minor = 0;
- unsigned kver_sub = 0;
- (void)sscanf(buf.release, "%u.%u.%u", &kver_major, &kver_minor, &kver_sub);
- return KVER(kver_major, kver_minor, kver_sub);
-}
-
-static inline unsigned kernelVersion() {
- static unsigned kver = uncachedKernelVersion();
- return kver;
-}
-
-static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
- return kernelVersion() >= KVER(major, minor, sub);
-}
-
-} // namespace bpf
-} // namespace android
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index 70c0f89..65c0b72 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -99,6 +99,30 @@
#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
#define KVER_INF 0xFFFFFFFFu
+/*
+ * BPFFS (ie. /sys/fs/bpf) labelling is as follows:
+ * subdirectory selinux context mainline usecase / usable by
+ * / fs_bpf no [*] core operating system (ie. platform)
+ * /loader fs_bpf_loader no, U+ (as yet unused)
+ * /net_private fs_bpf_net_private yes, T+ network_stack
+ * /net_shared fs_bpf_net_shared yes, T+ network_stack & system_server
+ * /netd_readonly fs_bpf_netd_readonly yes, T+ network_stack & system_server & r/o to netd
+ * /netd_shared fs_bpf_netd_shared yes, T+ network_stack & system_server & netd [**]
+ * /tethering fs_bpf_tethering yes, S+ network_stack
+ * /vendor fs_bpf_vendor no, T+ vendor
+ *
+ * [*] initial support for bpf was added back in P,
+ * but things worked differently back then with no bpfloader,
+ * and instead netd doing stuff by hand,
+ * bpfloader with pinning into /sys/fs/bpf was (I believe) added in Q
+ * (and was definitely there in R).
+ *
+ * [**] additionally bpf programs are accessible to netutils_wrapper
+ * for use by iptables xt_bpf extensions.
+ *
+ * See cs/p:aosp-master%20-file:prebuilts/%20file:genfs_contexts%20"genfscon%20bpf"
+ */
+
/* generic functions */
/*
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
index 65540e0..e7428b6 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
@@ -179,6 +179,14 @@
bool ignore_on_eng:1;
bool ignore_on_user:1;
bool ignore_on_userdebug:1;
+ // The following 5 ignore_on_* fields were added in version 0.38 (U). These are ignored in
+ // older bpfloader versions, and zero in programs compiled before 0.38.
+ // These are tests on the kernel architecture, ie. they ignore userspace bit-ness.
+ bool ignore_on_arm32:1;
+ bool ignore_on_aarch64:1;
+ bool ignore_on_x86_32:1;
+ bool ignore_on_x86_64:1;
+ bool ignore_on_riscv64:1;
char pad0[2]; // manually pad up to 4 byte alignment, may be used for extensions in the future
@@ -208,6 +216,14 @@
bool ignore_on_eng:1;
bool ignore_on_user:1;
bool ignore_on_userdebug:1;
+ // The following 5 ignore_on_* fields were added in version 0.38 (U). These are ignored in
+ // older bpfloader versions, and zero in programs compiled before 0.38.
+ // These are tests on the kernel architecture, ie. they ignore userspace bit-ness.
+ bool ignore_on_arm32:1;
+ bool ignore_on_aarch64:1;
+ bool ignore_on_x86_32:1;
+ bool ignore_on_x86_64:1;
+ bool ignore_on_riscv64:1;
char pad0[2]; // manually pad up to 4 byte alignment, may be used for extensions in the future
diff --git a/staticlibs/native/bpf_syscall_wrappers/Android.bp b/staticlibs/native/bpf_syscall_wrappers/Android.bp
index 125d631..b3efc21 100644
--- a/staticlibs/native/bpf_syscall_wrappers/Android.bp
+++ b/staticlibs/native/bpf_syscall_wrappers/Android.bp
@@ -33,6 +33,8 @@
"//apex_available:platform",
"com.android.art.debug",
"com.android.mediaprovider",
+ "com.android.os.statsd",
+ "com.android.resolv",
"com.android.tethering",
],
}
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
index 2146d17..f93d6e1 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
@@ -25,7 +25,7 @@
#define BPF_FD_JUST_USE_INT
#include "BpfSyscallWrappers.h"
-#include "bpf/KernelVersion.h"
+#include "bpf/KernelUtils.h"
namespace android {
diff --git a/staticlibs/native/tcutils/tcutils.cpp b/staticlibs/native/tcutils/tcutils.cpp
index 37a7ec8..c82390f 100644
--- a/staticlibs/native/tcutils/tcutils.cpp
+++ b/staticlibs/native/tcutils/tcutils.cpp
@@ -19,7 +19,7 @@
#include "tcutils/tcutils.h"
#include "logging.h"
-#include "bpf/KernelVersion.h"
+#include "bpf/KernelUtils.h"
#include "scopeguard.h"
#include <arpa/inet.h>
diff --git a/staticlibs/native/tcutils/tests/tcutils_test.cpp b/staticlibs/native/tcutils/tests/tcutils_test.cpp
index 53835d7..7732247 100644
--- a/staticlibs/native/tcutils/tests/tcutils_test.cpp
+++ b/staticlibs/native/tcutils/tests/tcutils_test.cpp
@@ -18,7 +18,7 @@
#include <gtest/gtest.h>
-#include "bpf/KernelVersion.h"
+#include "bpf/KernelUtils.h"
#include <tcutils/tcutils.h>
#include <BpfSyscallWrappers.h>
diff --git a/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
index 30e0daf..0f6fa48 100644
--- a/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
@@ -18,8 +18,10 @@
import android.os.Handler
import android.os.HandlerThread
+import com.android.testutils.FunctionalUtils.ThrowingSupplier
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
+import kotlin.test.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -69,13 +71,18 @@
repeat(ATTEMPTS) { attempt ->
var x = -10
- visibleOnHandlerThread(handler) { x = attempt }
+ var y = -11
+ y = visibleOnHandlerThread(handler, ThrowingSupplier<Int> { x = attempt; attempt })
assertEquals(attempt, x)
+ assertEquals(attempt, y)
handler.post { assertEquals(attempt, x) }
}
assertFailsWith<IllegalArgumentException> {
visibleOnHandlerThread(handler) { throw IllegalArgumentException() }
}
+
+ // Null values may be returned by the supplier
+ assertNull(visibleOnHandlerThread(handler, ThrowingSupplier<Nothing?> { null }))
}
}
diff --git a/staticlibs/tests/unit/src/com/android/testutils/TestableNetworkCallbackTest.kt b/staticlibs/tests/unit/src/com/android/testutils/TestableNetworkCallbackTest.kt
index 4ed881a..e838bdc 100644
--- a/staticlibs/tests/unit/src/com/android/testutils/TestableNetworkCallbackTest.kt
+++ b/staticlibs/tests/unit/src/com/android/testutils/TestableNetworkCallbackTest.kt
@@ -354,6 +354,13 @@
}
@Test
+ fun testExpectClass() {
+ val net = Network(1)
+ mCallback.onAvailable(net)
+ assertFails { mCallback.expect(LOST, net) }
+ }
+
+ @Test
fun testPoll() {
assertNull(mCallback.poll(SHORT_TIMEOUT_MS))
TNCInterpreter.interpretTestSpec(initial = mCallback, lineShift = 1,
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index 3382156..be4ccfe 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -83,5 +83,5 @@
],
libs: ["tradefed"],
test_suites: ["ats", "device-tests", "general-tests", "cts", "mts-networking"],
- data: [":ConnectivityChecker"],
+ data: [":ConnectivityTestPreparer"],
}
diff --git a/staticlibs/testutils/app/connectivitychecker/Android.bp b/staticlibs/testutils/app/connectivitychecker/Android.bp
index 58f22db..f7118cf 100644
--- a/staticlibs/testutils/app/connectivitychecker/Android.bp
+++ b/staticlibs/testutils/app/connectivitychecker/Android.bp
@@ -17,7 +17,7 @@
}
android_test_helper_app {
- name: "ConnectivityChecker",
+ name: "ConnectivityTestPreparer",
srcs: ["src/**/*.kt"],
sdk_version: "system_current",
// Allow running the test on any device with SDK Q+, even when built from a branch that uses
diff --git a/staticlibs/testutils/app/connectivitychecker/AndroidManifest.xml b/staticlibs/testutils/app/connectivitychecker/AndroidManifest.xml
index 8e5958c..015b41f 100644
--- a/staticlibs/testutils/app/connectivitychecker/AndroidManifest.xml
+++ b/staticlibs/testutils/app/connectivitychecker/AndroidManifest.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.testutils.connectivitychecker">
+ package="com.android.testutils.connectivitypreparer">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -27,6 +27,6 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.testutils.connectivitychecker"
- android:label="Connectivity checker target preparer" />
+ android:targetPackage="com.android.testutils.connectivitypreparer"
+ android:label="Connectivity test target preparer" />
</manifest>
diff --git a/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitychecker/ConnectivityCheckTest.kt b/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
similarity index 92%
rename from staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitychecker/ConnectivityCheckTest.kt
rename to staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
index f72938d..6bcb8fc 100644
--- a/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitychecker/ConnectivityCheckTest.kt
+++ b/staticlibs/testutils/app/connectivitychecker/src/com/android/testutils/connectivitypreparer/ConnectivityCheckTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.testutils.connectivitychecker
+package com.android.testutils.connectivitypreparer
import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.content.pm.PackageManager.FEATURE_WIFI
@@ -40,7 +40,7 @@
val pm by lazy { context.packageManager }
@Test
- fun testCheckDeviceSetup() {
+ fun testCheckConnectivity() {
checkWifiSetup()
checkTelephonySetup()
}
@@ -57,10 +57,11 @@
val commonError = "Check the test bench. To run the tests anyway for quick & dirty local " +
"testing, you can use atest X -- " +
- "--test-arg com.android.testutils.ConnectivityCheckTargetPreparer:disable:true"
+ "--test-arg com.android.testutils.ConnectivityTestTargetPreparer" +
+ ":ignore-connectivity-check:true"
// Do not use assertEquals: it outputs "expected X, was Y", which looks like a test failure
if (tm.simState == TelephonyManager.SIM_STATE_ABSENT) {
- fail("The device has no SIM card inserted. " + commonError)
+ fail("The device has no SIM card inserted. $commonError")
} else if (tm.simState != TelephonyManager.SIM_STATE_READY) {
fail("The device is not setup with a usable SIM card. Sim state was ${tm.simState}. " +
commonError)
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt b/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
index aa252a5..f00ca11 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
@@ -23,6 +23,7 @@
import android.os.HandlerThread
import android.util.Log
import com.android.testutils.FunctionalUtils.ThrowingRunnable
+import com.android.testutils.FunctionalUtils.ThrowingSupplier
import java.lang.Exception
import java.util.concurrent.Executor
import kotlin.test.fail
@@ -55,7 +56,8 @@
}
/**
- * Executes a block of code, making its side effects visible on the caller and the handler thread
+ * Executes a block of code that returns a value, making its side effects visible on the caller and
+ * the handler thread.
*
* After this function returns, the side effects of the passed block of code are guaranteed to be
* observed both on the thread running the handler and on the thread running this method.
@@ -63,15 +65,15 @@
* until it's executed, so keep in mind this method will block, (including, if the handler isn't
* running, blocking forever).
*/
-fun visibleOnHandlerThread(handler: Handler, r: ThrowingRunnable) {
+fun <T> visibleOnHandlerThread(handler: Handler, supplier: ThrowingSupplier<T>): T {
val cv = ConditionVariable()
- var e: Exception? = null
+ var rv: Result<T> = Result.failure(RuntimeException("Not run"))
handler.post {
try {
- r.run()
+ rv = Result.success(supplier.get())
} catch (exception: Exception) {
Log.e(TAG, "visibleOnHandlerThread caught exception", exception)
- e = exception
+ rv = Result.failure(exception)
}
cv.open()
}
@@ -79,5 +81,10 @@
// and this thread also has seen the change (since cv.open() happens-before cv.block()
// returns).
cv.block()
- e?.let { throw it }
+ return rv.getOrThrow()
+}
+
+/** Overload of visibleOnHandlerThread but executes a block of code that does not return a value. */
+inline fun visibleOnHandlerThread(handler: Handler, r: ThrowingRunnable){
+ visibleOnHandlerThread(handler, ThrowingSupplier<Unit> { r.run() })
}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
index 0e73112..4b6aea2 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
@@ -236,7 +236,11 @@
errorMsg: String? = null,
test: (T) -> Boolean = { true }
) = expect<CallbackEntry>(network, timeoutMs, errorMsg) {
- test(it as? T ?: fail("Expected callback ${type.simpleName}, got $it"))
+ if (type.isInstance(it)) {
+ test(it as T) // Cast can't fail since type.isInstance(it) and type: KClass<T>
+ } else {
+ fail("Expected callback ${type.simpleName}, got $it")
+ }
} as T
@JvmOverloads
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt
deleted file mode 100644
index ccc4064..0000000
--- a/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-package com.android.testutils
-
-import com.android.ddmlib.testrunner.TestResult
-import com.android.tradefed.invoker.TestInformation
-import com.android.tradefed.result.CollectingTestListener
-import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner
-import com.android.tradefed.targetprep.BaseTargetPreparer
-import com.android.tradefed.targetprep.TargetSetupError
-import com.android.tradefed.targetprep.suite.SuiteApkInstaller
-
-private const val CONNECTIVITY_CHECKER_APK = "ConnectivityChecker.apk"
-private const val CONNECTIVITY_PKG_NAME = "com.android.testutils.connectivitychecker"
-// As per the <instrumentation> defined in the checker manifest
-private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner"
-
-/**
- * A target preparer that verifies that the device was setup correctly for connectivity tests.
- *
- * For quick and dirty local testing, can be disabled by running tests with
- * "atest -- --test-arg com.android.testutils.ConnectivityCheckTargetPreparer:disable:true".
- */
-class ConnectivityCheckTargetPreparer : BaseTargetPreparer() {
- val installer = SuiteApkInstaller()
-
- override fun setUp(testInformation: TestInformation) {
- if (isDisabled) return
- installer.setCleanApk(true)
- installer.addTestFileName(CONNECTIVITY_CHECKER_APK)
- installer.setShouldGrantPermission(true)
- installer.setUp(testInformation)
-
- val runner = DefaultRemoteAndroidTestRunner(
- CONNECTIVITY_PKG_NAME,
- CONNECTIVITY_CHECK_RUNNER_NAME,
- testInformation.device.iDevice)
- runner.runOptions = "--no-hidden-api-checks"
-
- val receiver = CollectingTestListener()
- if (!testInformation.device.runInstrumentationTests(runner, receiver)) {
- throw TargetSetupError("Device state check failed to complete",
- testInformation.device.deviceDescriptor)
- }
-
- val runResult = receiver.currentRunResults
- if (runResult.isRunFailure) {
- throw TargetSetupError("Failed to check device state before the test: " +
- runResult.runFailureMessage, testInformation.device.deviceDescriptor)
- }
-
- if (!runResult.hasFailedTests()) return
- val errorMsg = runResult.testResults.mapNotNull { (testDescription, testResult) ->
- if (TestResult.TestStatus.FAILURE != testResult.status) null
- else "$testDescription: ${testResult.stackTrace}"
- }.joinToString("\n")
-
- throw TargetSetupError("Device setup checks failed. Check the test bench: \n$errorMsg",
- testInformation.device.deviceDescriptor)
- }
-
- override fun tearDown(testInformation: TestInformation?, e: Throwable?) {
- if (isTearDownDisabled) return
- installer.tearDown(testInformation, e)
- }
-}
\ No newline at end of file
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
new file mode 100644
index 0000000..3fc74aa
--- /dev/null
+++ b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.testutils
+
+import com.android.ddmlib.testrunner.TestResult
+import com.android.tradefed.config.Option
+import com.android.tradefed.invoker.TestInformation
+import com.android.tradefed.result.CollectingTestListener
+import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner
+import com.android.tradefed.targetprep.BaseTargetPreparer
+import com.android.tradefed.targetprep.TargetSetupError
+import com.android.tradefed.targetprep.suite.SuiteApkInstaller
+
+private const val CONNECTIVITY_CHECKER_APK = "ConnectivityTestPreparer.apk"
+private const val CONNECTIVITY_PKG_NAME = "com.android.testutils.connectivitypreparer"
+private const val CONNECTIVITY_CHECK_CLASS = "$CONNECTIVITY_PKG_NAME.ConnectivityCheckTest"
+// As per the <instrumentation> defined in the checker manifest
+private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner"
+private const val IGNORE_CONN_CHECK_OPTION = "ignore-connectivity-check"
+
+/**
+ * A target preparer that sets up and verifies a device for connectivity tests.
+ *
+ * For quick and dirty local testing, the connectivity check can be disabled by running tests with
+ * "atest -- \
+ * --test-arg com.android.testutils.ConnectivityTestTargetPreparer:ignore-connectivity-check:true".
+ */
+open class ConnectivityTestTargetPreparer : BaseTargetPreparer() {
+ private val installer = SuiteApkInstaller()
+
+ @Option(name = IGNORE_CONN_CHECK_OPTION,
+ description = "Disables the check for mobile data and wifi")
+ private var ignoreConnectivityCheck = false
+
+ override fun setUp(testInformation: TestInformation) {
+ if (isDisabled) return
+ disableGmsUpdate(testInformation)
+ runPreparerApk(testInformation)
+ }
+
+ private fun runPreparerApk(testInformation: TestInformation) {
+ installer.setCleanApk(true)
+ installer.addTestFileName(CONNECTIVITY_CHECKER_APK)
+ installer.setShouldGrantPermission(true)
+ installer.setUp(testInformation)
+
+ val runner = DefaultRemoteAndroidTestRunner(
+ CONNECTIVITY_PKG_NAME,
+ CONNECTIVITY_CHECK_RUNNER_NAME,
+ testInformation.device.iDevice)
+ runner.runOptions = "--no-hidden-api-checks"
+
+ val receiver = CollectingTestListener()
+ if (!testInformation.device.runInstrumentationTests(runner, receiver)) {
+ throw TargetSetupError("Device state check failed to complete",
+ testInformation.device.deviceDescriptor)
+ }
+
+ val runResult = receiver.currentRunResults
+ if (runResult.isRunFailure) {
+ throw TargetSetupError("Failed to check device state before the test: " +
+ runResult.runFailureMessage, testInformation.device.deviceDescriptor)
+ }
+
+ val ignoredTestClasses = mutableSetOf<String>()
+ if (ignoreConnectivityCheck) {
+ ignoredTestClasses.add(CONNECTIVITY_CHECK_CLASS)
+ }
+
+ val errorMsg = runResult.testResults.mapNotNull { (testDescription, testResult) ->
+ if (TestResult.TestStatus.FAILURE != testResult.status ||
+ ignoredTestClasses.contains(testDescription.className)) {
+ null
+ } else {
+ "$testDescription: ${testResult.stackTrace}"
+ }
+ }.joinToString("\n")
+ if (errorMsg.isBlank()) return
+
+ throw TargetSetupError("Device setup checks failed. Check the test bench: \n$errorMsg",
+ testInformation.device.deviceDescriptor)
+ }
+
+ private fun disableGmsUpdate(testInformation: TestInformation) {
+ // This will be a no-op on devices without root (su) or not using gservices, but that's OK.
+ testInformation.device.executeShellCommand("su 0 am broadcast " +
+ "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
+ "-e finsky.play_services_auto_update_enabled false")
+ }
+
+ private fun clearGmsUpdateOverride(testInformation: TestInformation) {
+ testInformation.device.executeShellCommand("su 0 am broadcast " +
+ "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
+ "--esn finsky.play_services_auto_update_enabled")
+ }
+
+ override fun tearDown(testInformation: TestInformation, e: Throwable?) {
+ if (isTearDownDisabled) return
+ installer.tearDown(testInformation, e)
+ clearGmsUpdateOverride(testInformation)
+ }
+}