Merge "bpf_helpers.h: add load_byte/half/word() llvm/clang asm magic"
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index d8a397d..1fe6c2c 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -304,6 +304,30 @@
lint: { strict_updatability_linting: true },
}
+java_library {
+ name: "net-utils-device-common-async",
+ srcs: [
+ "device/com/android/net/module/util/async/*.java",
+ ],
+ sdk_version: "module_current",
+ min_sdk_version: "29",
+ visibility: [
+ "//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/libs/net/common/testutils:__subpackages__",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ ],
+ static_libs: [
+ ],
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ lint: { strict_updatability_linting: true },
+}
+
// Use a filegroup and not a library for telephony sources, as framework-annotations cannot be
// included either (some annotations would be duplicated on the bootclasspath).
filegroup {
diff --git a/staticlibs/device/com/android/net/module/util/async/Assertions.java b/staticlibs/device/com/android/net/module/util/async/Assertions.java
new file mode 100644
index 0000000..ce701d0
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/Assertions.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import android.os.Build;
+
+/**
+ * Implements basic assert functions for runtime error-checking.
+ *
+ * @hide
+ */
+public final class Assertions {
+ public static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
+
+ public static void throwsIfOutOfBounds(int totalLength, int pos, int len) {
+ if (!IS_USER_BUILD && ((totalLength | pos | len) < 0 || pos > totalLength - len)) {
+ throw new ArrayIndexOutOfBoundsException(
+ "length=" + totalLength + "; regionStart=" + pos + "; regionLength=" + len);
+ }
+ }
+
+ public static void throwsIfOutOfBounds(byte[] buffer, int pos, int len) {
+ throwsIfOutOfBounds(buffer != null ? buffer.length : 0, pos, len);
+ }
+
+ private Assertions() {}
+}
diff --git a/staticlibs/device/com/android/net/module/util/async/AsyncFile.java b/staticlibs/device/com/android/net/module/util/async/AsyncFile.java
new file mode 100644
index 0000000..2a3231b
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/AsyncFile.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import java.io.IOException;
+
+/**
+ * Represents an EventManager-managed file with Async IO semantics.
+ *
+ * Implements level-based Asyn IO semantics. This means that:
+ * - onReadReady() callback would keep happening as long as there's any remaining
+ * data to read, or the user calls enableReadEvents(false)
+ * - onWriteReady() callback would keep happening as long as there's remaining space
+ * to write to, or the user calls enableWriteEvents(false)
+ *
+ * All operations except close() must be called on the EventManager thread.
+ *
+ * @hide
+ */
+public interface AsyncFile {
+ /**
+ * Receives notifications when file readability or writeability changes.
+ * @hide
+ */
+ public interface Listener {
+ /** Invoked after the underlying file has been closed. */
+ void onClosed(AsyncFile file);
+
+ /** Invoked while the file has readable data and read notifications are enabled. */
+ void onReadReady(AsyncFile file);
+
+ /** Invoked while the file has writeable space and write notifications are enabled. */
+ void onWriteReady(AsyncFile file);
+ }
+
+ /** Requests this file to be closed. */
+ void close();
+
+ /** Enables or disables onReadReady() events. */
+ void enableReadEvents(boolean enable);
+
+ /** Enables or disables onWriteReady() events. */
+ void enableWriteEvents(boolean enable);
+
+ /** Returns true if the input stream has reached its end, or has been closed. */
+ boolean reachedEndOfFile();
+
+ /**
+ * Reads available data from the given non-blocking file descriptor.
+ *
+ * Returns zero if there's no data to read at this moment.
+ * Returns -1 if the file has reached its end or the input stream has been closed.
+ * Otherwise returns the number of bytes read.
+ */
+ int read(byte[] buffer, int pos, int len) throws IOException;
+
+ /**
+ * Writes data into the given non-blocking file descriptor.
+ *
+ * Returns zero if there's no buffer space to write to at this moment.
+ * Otherwise returns the number of bytes written.
+ */
+ int write(byte[] buffer, int pos, int len) throws IOException;
+}
diff --git a/staticlibs/device/com/android/net/module/util/async/CircularByteBuffer.java b/staticlibs/device/com/android/net/module/util/async/CircularByteBuffer.java
new file mode 100644
index 0000000..92daa08
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/CircularByteBuffer.java
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import java.util.Arrays;
+
+/**
+ * Implements a circular read-write byte buffer.
+ *
+ * @hide
+ */
+public final class CircularByteBuffer implements ReadableByteBuffer {
+ private final byte[] mBuffer;
+ private final int mCapacity;
+ private int mReadPos;
+ private int mWritePos;
+ private int mSize;
+
+ public CircularByteBuffer(int capacity) {
+ mCapacity = capacity;
+ mBuffer = new byte[mCapacity];
+ }
+
+ @Override
+ public void clear() {
+ mReadPos = 0;
+ mWritePos = 0;
+ mSize = 0;
+ Arrays.fill(mBuffer, (byte) 0);
+ }
+
+ @Override
+ public int capacity() {
+ return mCapacity;
+ }
+
+ @Override
+ public int size() {
+ return mSize;
+ }
+
+ /** Returns the amount of remaining writeable space in this buffer. */
+ public int freeSize() {
+ return mCapacity - mSize;
+ }
+
+ @Override
+ public byte peek(int offset) {
+ if (offset < 0 || offset >= size()) {
+ throw new IllegalArgumentException("Invalid offset=" + offset + ", size=" + size());
+ }
+
+ return mBuffer[(mReadPos + offset) % mCapacity];
+ }
+
+ @Override
+ public void readBytes(byte[] dst, int dstPos, int dstLen) {
+ if (dst != null) {
+ Assertions.throwsIfOutOfBounds(dst, dstPos, dstLen);
+ }
+ if (dstLen > size()) {
+ throw new IllegalArgumentException("Invalid len=" + dstLen + ", size=" + size());
+ }
+
+ while (dstLen > 0) {
+ final int copyLen = getCopyLen(mReadPos, mWritePos, dstLen);
+ if (dst != null) {
+ System.arraycopy(mBuffer, mReadPos, dst, dstPos, copyLen);
+ }
+ dstPos += copyLen;
+ dstLen -= copyLen;
+ mSize -= copyLen;
+ mReadPos = (mReadPos + copyLen) % mCapacity;
+ }
+
+ if (mSize == 0) {
+ // Reset to the beginning for better contiguous access.
+ mReadPos = 0;
+ mWritePos = 0;
+ }
+ }
+
+ @Override
+ public void peekBytes(int offset, byte[] dst, int dstPos, int dstLen) {
+ Assertions.throwsIfOutOfBounds(dst, dstPos, dstLen);
+ if (offset + dstLen > size()) {
+ throw new IllegalArgumentException("Invalid len=" + dstLen
+ + ", offset=" + offset + ", size=" + size());
+ }
+
+ int tmpReadPos = (mReadPos + offset) % mCapacity;
+ while (dstLen > 0) {
+ final int copyLen = getCopyLen(tmpReadPos, mWritePos, dstLen);
+ System.arraycopy(mBuffer, tmpReadPos, dst, dstPos, copyLen);
+ dstPos += copyLen;
+ dstLen -= copyLen;
+ tmpReadPos = (tmpReadPos + copyLen) % mCapacity;
+ }
+ }
+
+ @Override
+ public int getDirectReadSize() {
+ if (size() == 0) {
+ return 0;
+ }
+ return (mReadPos < mWritePos ? (mWritePos - mReadPos) : (mCapacity - mReadPos));
+ }
+
+ @Override
+ public int getDirectReadPos() {
+ return mReadPos;
+ }
+
+ @Override
+ public byte[] getDirectReadBuffer() {
+ return mBuffer;
+ }
+
+ @Override
+ public void accountForDirectRead(int len) {
+ if (len < 0 || len > size()) {
+ throw new IllegalArgumentException("Invalid len=" + len + ", size=" + size());
+ }
+
+ mSize -= len;
+ mReadPos = (mReadPos + len) % mCapacity;
+ }
+
+ /** Copies given data to the end of the buffer. */
+ public void writeBytes(byte[] buffer, int pos, int len) {
+ Assertions.throwsIfOutOfBounds(buffer, pos, len);
+ if (len > freeSize()) {
+ throw new IllegalArgumentException("Invalid len=" + len + ", size=" + freeSize());
+ }
+
+ while (len > 0) {
+ final int copyLen = getCopyLen(mWritePos, mReadPos,len);
+ System.arraycopy(buffer, pos, mBuffer, mWritePos, copyLen);
+ pos += copyLen;
+ len -= copyLen;
+ mSize += copyLen;
+ mWritePos = (mWritePos + copyLen) % mCapacity;
+ }
+ }
+
+ private int getCopyLen(int startPos, int endPos, int len) {
+ if (startPos < endPos) {
+ return Math.min(len, endPos - startPos);
+ } else {
+ return Math.min(len, mCapacity - startPos);
+ }
+ }
+
+ /** Returns the amount of contiguous writeable space. */
+ public int getDirectWriteSize() {
+ if (freeSize() == 0) {
+ return 0; // Return zero in case buffer is full.
+ }
+ return (mWritePos < mReadPos ? (mReadPos - mWritePos) : (mCapacity - mWritePos));
+ }
+
+ /** Returns the position of contiguous writeable space. */
+ public int getDirectWritePos() {
+ return mWritePos;
+ }
+
+ /** Returns the buffer reference for direct write operation. */
+ public byte[] getDirectWriteBuffer() {
+ return mBuffer;
+ }
+
+ /** Must be called after performing a direct write using getDirectWriteBuffer(). */
+ public void accountForDirectWrite(int len) {
+ if (len < 0 || len > freeSize()) {
+ throw new IllegalArgumentException("Invalid len=" + len + ", size=" + freeSize());
+ }
+
+ mSize += len;
+ mWritePos = (mWritePos + len) % mCapacity;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("CircularByteBuffer{c=");
+ sb.append(mCapacity);
+ sb.append(",s=");
+ sb.append(mSize);
+ sb.append(",r=");
+ sb.append(mReadPos);
+ sb.append(",w=");
+ sb.append(mWritePos);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/async/EventManager.java b/staticlibs/device/com/android/net/module/util/async/EventManager.java
new file mode 100644
index 0000000..4ed4a70
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/EventManager.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages Async IO files and scheduled alarms, and executes all related callbacks
+ * in its own thread.
+ *
+ * All callbacks of AsyncFile, Alarm and EventManager will execute on EventManager's thread.
+ *
+ * Methods of this interface can be called from any thread.
+ *
+ * @hide
+ */
+public interface EventManager extends Executor {
+ /**
+ * Represents a scheduled alarm, allowing caller to attempt to cancel that alarm
+ * before it executes.
+ *
+ * @hide
+ */
+ public interface Alarm {
+ /** @hide */
+ public interface Listener {
+ void onAlarm(Alarm alarm, long elapsedTimeMs);
+ void onAlarmCancelled(Alarm alarm);
+ }
+
+ /**
+ * Attempts to cancel this alarm. Note that this request is inherently
+ * racy if executed close to the alarm's expiration time.
+ */
+ void cancel();
+ }
+
+ /**
+ * Requests EventManager to manage the given file.
+ *
+ * The file descriptors are not cloned, and EventManager takes ownership of all files passed.
+ *
+ * No event callbacks are enabled by this method.
+ */
+ AsyncFile registerFile(FileHandle fileHandle, AsyncFile.Listener listener) throws IOException;
+
+ /**
+ * Schedules Alarm with the given timeout.
+ *
+ * Timeout of zero can be used for immediate execution.
+ */
+ Alarm scheduleAlarm(long timeout, Alarm.Listener callback);
+
+ /** Schedules Runnable for immediate execution. */
+ @Override
+ void execute(Runnable callback);
+
+ /** Throws a runtime exception if the caller is not executing on this EventManager's thread. */
+ void assertInThread();
+}
diff --git a/staticlibs/device/com/android/net/module/util/async/FileHandle.java b/staticlibs/device/com/android/net/module/util/async/FileHandle.java
new file mode 100644
index 0000000..9f7942d
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/FileHandle.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents an file descriptor or another way to access a file.
+ *
+ * @hide
+ */
+public final class FileHandle {
+ private final ParcelFileDescriptor mFd;
+ private final Closeable mCloseable;
+ private final InputStream mInputStream;
+ private final OutputStream mOutputStream;
+
+ public static FileHandle fromFileDescriptor(ParcelFileDescriptor fd) {
+ if (fd == null) {
+ throw new NullPointerException();
+ }
+ return new FileHandle(fd, null, null, null);
+ }
+
+ public static FileHandle fromBlockingStream(
+ Closeable closeable, InputStream is, OutputStream os) {
+ if (closeable == null || is == null || os == null) {
+ throw new NullPointerException();
+ }
+ return new FileHandle(null, closeable, is, os);
+ }
+
+ private FileHandle(ParcelFileDescriptor fd, Closeable closeable,
+ InputStream is, OutputStream os) {
+ mFd = fd;
+ mCloseable = closeable;
+ mInputStream = is;
+ mOutputStream = os;
+ }
+
+ ParcelFileDescriptor getFileDescriptor() {
+ return mFd;
+ }
+
+ Closeable getCloseable() {
+ return mCloseable;
+ }
+
+ InputStream getInputStream() {
+ return mInputStream;
+ }
+
+ OutputStream getOutputStream() {
+ return mOutputStream;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/async/ReadableByteBuffer.java b/staticlibs/device/com/android/net/module/util/async/ReadableByteBuffer.java
new file mode 100644
index 0000000..7f82404
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/async/ReadableByteBuffer.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+/**
+ * Allows reading from a buffer of bytes. The data can be read and thus removed,
+ * or peeked at without disturbing the buffer state.
+ *
+ * @hide
+ */
+public interface ReadableByteBuffer {
+ /** Returns the size of the buffered data. */
+ int size();
+
+ /**
+ * Returns the maximum amount of the buffered data.
+ *
+ * The caller may use this method in combination with peekBytes()
+ * to estimate when the buffer needs to be emptied using readData().
+ */
+ int capacity();
+
+ /** Clears all buffered data. */
+ void clear();
+
+ /** Returns a single byte at the given offset. */
+ byte peek(int offset);
+
+ /** Copies an array of bytes from the given offset to "dst". */
+ void peekBytes(int offset, byte[] dst, int dstPos, int dstLen);
+
+ /** Reads and removes an array of bytes from the head of the buffer. */
+ void readBytes(byte[] dst, int dstPos, int dstLen);
+
+ /** Returns the amount of contiguous readable data. */
+ int getDirectReadSize();
+
+ /** Returns the position of contiguous readable data. */
+ int getDirectReadPos();
+
+ /** Returns the buffer reference for direct read operation. */
+ byte[] getDirectReadBuffer();
+
+ /** Must be called after performing a direct read using getDirectReadBuffer(). */
+ void accountForDirectRead(int len);
+}
diff --git a/staticlibs/framework/com/android/net/module/util/ConnectivitySettingsUtils.java b/staticlibs/framework/com/android/net/module/util/ConnectivitySettingsUtils.java
index b7eb70b..f4856b3 100644
--- a/staticlibs/framework/com/android/net/module/util/ConnectivitySettingsUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/ConnectivitySettingsUtils.java
@@ -60,6 +60,10 @@
}
private static int getPrivateDnsModeAsInt(String mode) {
+ // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+ // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+ if (TextUtils.isEmpty(mode))
+ return PRIVATE_DNS_MODE_OPPORTUNISTIC;
switch (mode) {
case "off":
return PRIVATE_DNS_MODE_OFF;
@@ -68,7 +72,10 @@
case "opportunistic":
return PRIVATE_DNS_MODE_OPPORTUNISTIC;
default:
- throw new IllegalArgumentException("Invalid private dns mode: " + mode);
+ // b/260211513: adb shell settings put global private_dns_mode foo
+ // can result in arbitrary strings - treat any unknown value as empty string.
+ // throw new IllegalArgumentException("Invalid private dns mode: " + mode);
+ return PRIVATE_DNS_MODE_OPPORTUNISTIC;
}
}
@@ -82,9 +89,6 @@
final ContentResolver cr = context.getContentResolver();
String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
- // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
- // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
- if (TextUtils.isEmpty(mode)) return PRIVATE_DNS_MODE_OPPORTUNISTIC;
return getPrivateDnsModeAsInt(mode);
}
diff --git a/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h b/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h
index e0e53a9..29a36e6 100644
--- a/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h
+++ b/staticlibs/native/bpf_headers/include/bpf/KernelVersion.h
@@ -16,8 +16,7 @@
#pragma once
-#include <stdlib.h>
-#include <string.h>
+#include <stdio.h>
#include <sys/utsname.h>
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 8c66b91..66dccf3 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -167,8 +167,8 @@
.uid = (usr), \
.gid = (grp), \
.mode = (md), \
- .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \
- .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \
+ .bpfloader_min_ver = BPFLOADER_MIN_VER, \
+ .bpfloader_max_ver = BPFLOADER_MAX_VER, \
.min_kver = (minkver), \
.max_kver = (maxkver), \
.selinux_context = (selinux), \
@@ -301,8 +301,8 @@
.min_kver = (min_kv), \
.max_kver = (max_kv), \
.optional = (opt), \
- .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \
- .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \
+ .bpfloader_min_ver = BPFLOADER_MIN_VER, \
+ .bpfloader_max_ver = BPFLOADER_MAX_VER, \
.selinux_context = selinux, \
.pin_subdir = pindir, \
}; \
diff --git a/staticlibs/native/tcutils/Android.bp b/staticlibs/native/tcutils/Android.bp
index 88a2683..84a32ed 100644
--- a/staticlibs/native/tcutils/Android.bp
+++ b/staticlibs/native/tcutils/Android.bp
@@ -20,7 +20,7 @@
name: "libtcutils",
srcs: ["tcutils.cpp"],
export_include_dirs: ["include"],
- header_libs: ["bpf_syscall_wrappers"],
+ header_libs: ["bpf_headers"],
shared_libs: [
"liblog",
],
@@ -53,7 +53,7 @@
"-Werror",
"-Wno-error=unused-variable",
],
- header_libs: ["bpf_syscall_wrappers"],
+ header_libs: ["bpf_headers"],
static_libs: [
"libgmock",
"libtcutils",
diff --git a/staticlibs/native/tcutils/kernelversion.h b/staticlibs/native/tcutils/kernelversion.h
deleted file mode 100644
index 9aab31d..0000000
--- a/staticlibs/native/tcutils/kernelversion.h
+++ /dev/null
@@ -1,56 +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.
- */
-
-// -----------------------------------------------------------------------------
-// TODO - This should be replaced with BpfUtils in bpf_headers.
-// Currently, bpf_headers contains a bunch requirements it doesn't actually provide, such as a
-// non-ndk liblog version, and some version of libbase. libtcutils does not have access to either of
-// these, so I think this will have to wait until we figure out a way around this.
-//
-// In the mean time copying verbatim from:
-// frameworks/libs/net/common/native/bpf_headers
-
-#pragma once
-
-#include <stdio.h>
-#include <sys/utsname.h>
-
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-
-namespace android {
-
-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 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 android
diff --git a/staticlibs/native/tcutils/tcutils.cpp b/staticlibs/native/tcutils/tcutils.cpp
index 4101885..37a7ec8 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 "kernelversion.h"
+#include "bpf/KernelVersion.h"
#include "scopeguard.h"
#include <arpa/inet.h>
@@ -504,7 +504,7 @@
// shipped with Android S, so (for safety) let's limit ourselves to
// >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
// longer need this non-upstream compatibility logic
- static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
+ static bool is_pre_5_11_kernel = !bpf::isAtLeastKernelVersion(5, 11, 0);
if (is_pre_5_11_kernel)
return false;
}
diff --git a/staticlibs/native/tcutils/tests/tcutils_test.cpp b/staticlibs/native/tcutils/tests/tcutils_test.cpp
index 3a89696..53835d7 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 "kernelversion.h"
+#include "bpf/KernelVersion.h"
#include <tcutils/tcutils.h>
#include <BpfSyscallWrappers.h>
@@ -82,7 +82,7 @@
// TODO: this should likely be in the tethering module, where using netd.h would be ok
static constexpr char bpfProgPath[] =
"/sys/fs/bpf/tethering/prog_offload_schedcls_tether_downstream6_ether";
- const int errNOENT = isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
+ const int errNOENT = bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
// static test values
static constexpr bool ingress = true;
@@ -118,7 +118,7 @@
ASSERT_LE(3, fd);
close(fd);
- const int errNOENT = isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
+ const int errNOENT = bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
// static test values
static constexpr unsigned rateInBytesPerSec =
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index 21e8c64..6e223bd 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -12,14 +12,15 @@
min_sdk_version: "29",
defaults: ["framework-connectivity-test-defaults"],
static_libs: [
- "net-utils-framework-common",
- "net-utils-device-common-ip",
"androidx.test.rules",
"mockito-target-extended-minus-junit4",
- "net-utils-device-common",
- "net-utils-device-common-bpf",
- "net-tests-utils",
"netd-client",
+ "net-tests-utils",
+ "net-utils-framework-common",
+ "net-utils-device-common",
+ "net-utils-device-common-async",
+ "net-utils-device-common-bpf",
+ "net-utils-device-common-ip",
],
libs: [
"android.test.runner",
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/async/CircularByteBufferTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/async/CircularByteBufferTest.java
new file mode 100644
index 0000000..01abee2
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/async/CircularByteBufferTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.async;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CircularByteBufferTest {
+ @Test
+ public void writeBytes() {
+ final int capacity = 23;
+ CircularByteBuffer buffer = new CircularByteBuffer(capacity);
+ assertEquals(0, buffer.size());
+ assertEquals(0, buffer.getDirectReadSize());
+ assertEquals(capacity, buffer.freeSize());
+ assertEquals(capacity, buffer.getDirectWriteSize());
+
+ final byte[] writeBuffer = new byte[15];
+ buffer.writeBytes(writeBuffer, 0, writeBuffer.length);
+
+ assertEquals(writeBuffer.length, buffer.size());
+ assertEquals(writeBuffer.length, buffer.getDirectReadSize());
+ assertEquals(capacity - writeBuffer.length, buffer.freeSize());
+ assertEquals(capacity - writeBuffer.length, buffer.getDirectWriteSize());
+
+ buffer.clear();
+ assertEquals(0, buffer.size());
+ assertEquals(0, buffer.getDirectReadSize());
+ assertEquals(capacity, buffer.freeSize());
+ assertEquals(capacity, buffer.getDirectWriteSize());
+ }
+
+ @Test
+ public void writeBytes_withRollover() {
+ doTestReadWriteWithRollover(new BufferAccessor(false, false));
+ }
+
+ @Test
+ public void writeBytes_withFullBuffer() {
+ doTestReadWriteWithFullBuffer(new BufferAccessor(false, false));
+ }
+
+ @Test
+ public void directWriteBytes_withRollover() {
+ doTestReadWriteWithRollover(new BufferAccessor(true, true));
+ }
+
+ @Test
+ public void directWriteBytes_withFullBuffer() {
+ doTestReadWriteWithFullBuffer(new BufferAccessor(true, true));
+ }
+
+ private void doTestReadWriteWithFullBuffer(BufferAccessor accessor) {
+ CircularByteBuffer buffer = doTestReadWrite(accessor, 20, 5, 4);
+
+ assertEquals(0, buffer.size());
+ assertEquals(20, buffer.freeSize());
+ }
+
+ private void doTestReadWriteWithRollover(BufferAccessor accessor) {
+ // All buffer sizes are prime numbers to ensure that some read or write
+ // operations will roll over the end of the internal buffer.
+ CircularByteBuffer buffer = doTestReadWrite(accessor, 31, 13, 7);
+
+ assertNotEquals(0, buffer.size());
+ }
+
+ private CircularByteBuffer doTestReadWrite(BufferAccessor accessor,
+ final int capacity, final int writeLen, final int readLen) {
+ CircularByteBuffer buffer = new CircularByteBuffer(capacity);
+
+ final byte[] writeBuffer = new byte[writeLen + 2];
+ final byte[] peekBuffer = new byte[readLen + 2];
+ final byte[] readBuffer = new byte[readLen + 2];
+
+ final int numIterations = 1011;
+ final int maxRemaining = readLen - 1;
+
+ int currentWriteSymbol = 0;
+ int expectedReadSymbol = 0;
+ int expectedSize = 0;
+ int totalWritten = 0;
+ int totalRead = 0;
+
+ for (int i = 0; i < numIterations; i++) {
+ // Fill in with write buffers as much as possible.
+ while (buffer.freeSize() >= writeLen) {
+ currentWriteSymbol = fillTestBytes(writeBuffer, 1, writeLen, currentWriteSymbol);
+ accessor.writeBytes(buffer, writeBuffer, 1, writeLen);
+
+ expectedSize += writeLen;
+ totalWritten += writeLen;
+ assertEquals(expectedSize, buffer.size());
+ assertEquals(capacity - expectedSize, buffer.freeSize());
+ }
+
+ // Keep reading into read buffers while there's still data.
+ while (buffer.size() >= readLen) {
+ peekBuffer[1] = 0;
+ peekBuffer[2] = 0;
+ buffer.peekBytes(2, peekBuffer, 3, readLen - 2);
+ assertEquals(0, peekBuffer[1]);
+ assertEquals(0, peekBuffer[2]);
+
+ peekBuffer[2] = buffer.peek(1);
+
+ accessor.readBytes(buffer, readBuffer, 1, readLen);
+ peekBuffer[1] = readBuffer[1];
+
+ expectedReadSymbol = checkTestBytes(
+ readBuffer, 1, readLen, expectedReadSymbol, totalRead);
+
+ assertArrayEquals(peekBuffer, readBuffer);
+
+ expectedSize -= readLen;
+ totalRead += readLen;
+ assertEquals(expectedSize, buffer.size());
+ assertEquals(capacity - expectedSize, buffer.freeSize());
+ }
+
+ if (buffer.size() > maxRemaining) {
+ fail("Too much data remaining: " + buffer.size());
+ }
+ }
+
+ final int maxWritten = capacity * numIterations;
+ final int minWritten = maxWritten / 2;
+ if (totalWritten < minWritten || totalWritten > maxWritten
+ || (totalWritten - totalRead) > maxRemaining) {
+ fail("Unexpected counts: read=" + totalRead + ", written=" + totalWritten
+ + ", minWritten=" + minWritten + ", maxWritten=" + maxWritten);
+ }
+
+ return buffer;
+ }
+
+ @Test
+ public void readBytes_overflow() {
+ CircularByteBuffer buffer = new CircularByteBuffer(23);
+
+ final byte[] dataBuffer = new byte[15];
+ buffer.writeBytes(dataBuffer, 0, dataBuffer.length - 2);
+
+ try {
+ buffer.readBytes(dataBuffer, 0, dataBuffer.length);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(13, buffer.size());
+ assertEquals(10, buffer.freeSize());
+ }
+
+ @Test
+ public void writeBytes_overflow() {
+ CircularByteBuffer buffer = new CircularByteBuffer(23);
+
+ final byte[] dataBuffer = new byte[15];
+ buffer.writeBytes(dataBuffer, 0, dataBuffer.length);
+
+ try {
+ buffer.writeBytes(dataBuffer, 0, dataBuffer.length);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(15, buffer.size());
+ assertEquals(8, buffer.freeSize());
+ }
+
+ private static int fillTestBytes(byte[] buffer, int pos, int len, int startValue) {
+ for (int i = 0; i < len; i++) {
+ buffer[pos + i] = (byte) (startValue & 0xFF);
+ startValue = (startValue + 1) % 256;
+ }
+ return startValue;
+ }
+
+ private static int checkTestBytes(
+ byte[] buffer, int pos, int len, int startValue, int totalRead) {
+ for (int i = 0; i < len; i++) {
+ byte expectedValue = (byte) (startValue & 0xFF);
+ if (expectedValue != buffer[pos + i]) {
+ fail("Unexpected byte=" + (((int) buffer[pos + i]) & 0xFF)
+ + ", expected=" + (((int) expectedValue) & 0xFF)
+ + ", pos=" + (totalRead + i));
+ }
+ startValue = (startValue + 1) % 256;
+ }
+ return startValue;
+ }
+
+ private static final class BufferAccessor {
+ private final boolean mDirectRead;
+ private final boolean mDirectWrite;
+
+ BufferAccessor(boolean directRead, boolean directWrite) {
+ mDirectRead = directRead;
+ mDirectWrite = directWrite;
+ }
+
+ void writeBytes(CircularByteBuffer buffer, byte[] src, int pos, int len) {
+ if (mDirectWrite) {
+ while (len > 0) {
+ if (buffer.getDirectWriteSize() == 0) {
+ fail("Direct write size is zero: free=" + buffer.freeSize()
+ + ", size=" + buffer.size());
+ }
+ int copyLen = Math.min(len, buffer.getDirectWriteSize());
+ System.arraycopy(src, pos, buffer.getDirectWriteBuffer(),
+ buffer.getDirectWritePos(), copyLen);
+ buffer.accountForDirectWrite(copyLen);
+ len -= copyLen;
+ pos += copyLen;
+ }
+ } else {
+ buffer.writeBytes(src, pos, len);
+ }
+ }
+
+ void readBytes(CircularByteBuffer buffer, byte[] dst, int pos, int len) {
+ if (mDirectRead) {
+ while (len > 0) {
+ if (buffer.getDirectReadSize() == 0) {
+ fail("Direct read size is zero: free=" + buffer.freeSize()
+ + ", size=" + buffer.size());
+ }
+ int copyLen = Math.min(len, buffer.getDirectReadSize());
+ System.arraycopy(
+ buffer.getDirectReadBuffer(), buffer.getDirectReadPos(), dst, pos, copyLen);
+ buffer.accountForDirectRead(copyLen);
+ len -= copyLen;
+ pos += copyLen;
+ }
+ } else {
+ buffer.readBytes(dst, pos, len);
+ }
+ }
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
new file mode 100644
index 0000000..46a3588
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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 android.os.Handler
+import android.os.HandlerThread
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val ATTEMPTS = 50 // Causes testWaitForIdle to take about 150ms on aosp_crosshatch-eng
+private const val TIMEOUT_MS = 200
+
+@RunWith(JUnit4::class)
+class HandlerUtilsTest {
+ @Test
+ fun testWaitForIdle() {
+ val handlerThread = HandlerThread("testHandler").apply { start() }
+
+ // Tests that waitForIdle can be called many times without ill impact if the service is
+ // already idle.
+ repeat(ATTEMPTS) {
+ handlerThread.waitForIdle(TIMEOUT_MS)
+ }
+
+ // Tests that calling waitForIdle waits for messages to be processed. Use both an
+ // inline runnable that's instantiated at each loop run and a runnable that's instantiated
+ // once for all.
+ val tempRunnable = object : Runnable {
+ // Use StringBuilder preferentially to StringBuffer because StringBuilder is NOT
+ // thread-safe. It's part of the point that both runnables run on the same thread
+ // so if anything is wrong in that space it's better to opportunistically use a class
+ // where things might go wrong, even if there is no guarantee of failure.
+ var memory = StringBuilder()
+ override fun run() {
+ memory.append("b")
+ }
+ }
+ repeat(ATTEMPTS) { i ->
+ handlerThread.threadHandler.post { tempRunnable.memory.append("a"); }
+ handlerThread.threadHandler.post(tempRunnable)
+ handlerThread.waitForIdle(TIMEOUT_MS)
+ assertEquals(tempRunnable.memory.toString(), "ab".repeat(i + 1))
+ }
+ }
+
+ // Statistical test : even if visibleOnHandlerThread doesn't work this is likely to succeed,
+ // but it will be at least flaky.
+ @Test
+ fun testVisibleOnHandlerThread() {
+ val handlerThread = HandlerThread("testHandler").apply { start() }
+ val handler = Handler(handlerThread.looper)
+
+ repeat(ATTEMPTS) { attempt ->
+ var x = -10
+ visibleOnHandlerThread(handler) { x = attempt }
+ assertEquals(attempt, x)
+ handler.post { assertEquals(attempt, x) }
+ }
+ }
+}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt b/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
index 861f45e..6871349 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/HandlerUtils.kt
@@ -21,9 +21,14 @@
import android.os.ConditionVariable
import android.os.Handler
import android.os.HandlerThread
+import android.util.Log
+import com.android.testutils.FunctionalUtils.ThrowingRunnable
+import java.lang.Exception
import java.util.concurrent.Executor
import kotlin.test.fail
+private const val TAG = "HandlerUtils"
+
/**
* Block until the specified Handler or HandlerThread becomes idle, or until timeoutMs has passed.
*/
@@ -48,3 +53,28 @@
fail("Executor did not become idle after ${timeoutMs}ms")
}
}
+
+/**
+ * Executes a block of code, 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.
+ * To achieve this, this method runs the passed block on the handler and blocks this thread
+ * 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) {
+ val cv = ConditionVariable()
+ handler.post {
+ try {
+ r.run()
+ } catch (exception: Exception) {
+ Log.e(TAG, "visibleOnHandlerThread caught exception", exception)
+ }
+ cv.open()
+ }
+ // After block() returns, the handler thread has seen the change (since it ran it)
+ // and this thread also has seen the change (since cv.open() happens-before cv.block()
+ // returns).
+ cv.block()
+}