Merge "Merge 24Q3 to AOSP main" into main
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index d5c6a8e..423b9b8 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -975,7 +975,7 @@
}
protected String getUpstreamInterfaceName() {
- if (mUpstreamReader == null) return null;
+ if (mUpstreamTracker == null) return null;
return mUpstreamTracker.getTestIface().getInterfaceName();
}
diff --git a/bpf/progs/dscpPolicy.c b/bpf/progs/dscpPolicy.c
index de9723d..94d717b 100644
--- a/bpf/progs/dscpPolicy.c
+++ b/bpf/progs/dscpPolicy.c
@@ -31,6 +31,11 @@
DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
+static inline __always_inline uint64_t calculate_u64(uint64_t v) {
+ COMPILER_FORCE_CALCULATION(v);
+ return v;
+}
+
static inline __always_inline void match_policy(struct __sk_buff* skb, const bool ipv4) {
void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
@@ -151,9 +156,9 @@
return; // cached DSCP mutation
}
- // Linear scan ipv4_dscp_policies_map since no stored params match skb.
- int best_score = 0;
- int8_t new_dscp = -1;
+ // Linear scan ipv?_dscp_policies_map since stored params didn't match skb.
+ uint64_t best_score = 0;
+ int8_t new_dscp = -1; // meaning no mutation
for (register uint64_t i = 0; i < MAX_POLICIES; i++) {
// Using a uint64 in for loop prevents infinite loop during BPF load,
@@ -172,35 +177,63 @@
// easier for the verifier to analyze.
if (!policy) return;
+ // Think of 'nomatch' as a 64-bit boolean: false iff zero, true iff non-zero.
+ // Start off with nomatch being false, ie. we assume things *are* matching.
+ uint64_t nomatch = 0;
+
+ // Due to 'a ^ b' being 0 iff a == b:
+ // nomatch |= a ^ b
+ // should/can be read as:
+ // nomatch ||= (a != b)
+ // which you can also think of as:
+ // match &&= (a == b)
+
// If policy iface index does not match skb, then skip to next policy.
- if (policy->ifindex != skb->ifindex) continue;
+ nomatch |= (policy->ifindex ^ skb->ifindex);
- int score = 0;
+ // policy->match_* are normal booleans, and should thus always be 0 or 1,
+ // thus you can think of these as:
+ // if (policy->match_foo) match &&= (foo == policy->foo);
+ nomatch |= policy->match_proto * (protocol ^ policy->proto);
+ nomatch |= policy->match_src_ip * v6_not_equal(src_ip, policy->src_ip);
+ nomatch |= policy->match_dst_ip * v6_not_equal(dst_ip, policy->dst_ip);
+ nomatch |= policy->match_src_port * (sport ^ policy->src_port);
- if (policy->match_proto) {
- if (protocol != policy->proto) continue;
- score += 0xFFFF;
- }
- if (policy->match_src_ip) {
- if (v6_not_equal(src_ip, policy->src_ip)) continue;
- score += 0xFFFF;
- }
- if (policy->match_dst_ip) {
- if (v6_not_equal(dst_ip, policy->dst_ip)) continue;
- score += 0xFFFF;
- }
- if (policy->match_src_port) {
- if (sport != policy->src_port) continue;
- score += 0xFFFF;
- }
- if (dport < policy->dst_port_start) continue;
- if (dport > policy->dst_port_end) continue;
- score += 0xFFFF + policy->dst_port_start - policy->dst_port_end;
+ // Since these values are u16s (<=63 bits), we can rely on u64 subtraction
+ // underflow setting the topmost bit. Basically, you can think of:
+ // nomatch |= (a - b) >> 63
+ // as:
+ // match &&= (a >= b)
+ uint64_t dport64 = dport; // Note: dst_port_{start_end} range is inclusive of both ends.
+ nomatch |= calculate_u64(dport64 - policy->dst_port_start) >> 63;
+ nomatch |= calculate_u64(policy->dst_port_end - dport64) >> 63;
- if (score > best_score) {
- best_score = score;
- new_dscp = policy->dscp_val;
- }
+ // score is 0x10000 for each matched field (proto, src_ip, dst_ip, src_port)
+ // plus 1..0x10000 for the dst_port range match (smaller for bigger ranges)
+ uint64_t score = 0;
+ score += policy->match_proto; // reminder: match_* are boolean, thus 0 or 1
+ score += policy->match_src_ip;
+ score += policy->match_dst_ip;
+ score += policy->match_src_port;
+ score += 1; // for a 1 element dst_port_{start,end} range
+ score <<= 16; // scale up: ie. *= 0x10000
+ // now reduce score if the dst_port range is more than a single element
+ // we want to prioritize (ie. better score) matches of smaller ranges
+ score -= (policy->dst_port_end - policy->dst_port_start); // -= 0..0xFFFF
+
+ // Here we need:
+ // match &&= (score > best_score)
+ // which is the same as
+ // match &&= (score >= best_score + 1)
+ // > not >= because we want equal score matches to prefer choosing earlier policies
+ nomatch |= calculate_u64(score - best_score - 1) >> 63;
+
+ COMPILER_FORCE_CALCULATION(nomatch);
+ if (nomatch) continue;
+
+ // only reachable if we matched the policy and (score > best_score)
+ best_score = score;
+ new_dscp = policy->dscp_val;
}
// Update cache with found policy.
diff --git a/bpf/progs/test.c b/bpf/progs/test.c
index 4f5a827..8585118 100644
--- a/bpf/progs/test.c
+++ b/bpf/progs/test.c
@@ -42,22 +42,12 @@
// Used only by BpfBitmapTest, not by production code.
DEFINE_BPF_MAP_GRW(bitmap, ARRAY, int, uint64_t, 2, AID_NETWORK_STACK)
-DEFINE_BPF_PROG_KVER("xdp/drop_ipv4_udp_ether", AID_ROOT, AID_NETWORK_STACK,
- xdp_test, KVER_5_9)
-(struct xdp_md *ctx) {
- void *data = (void *)(long)ctx->data;
- void *data_end = (void *)(long)ctx->data_end;
-
- struct ethhdr *eth = data;
- int hsize = sizeof(*eth);
-
- struct iphdr *ip = data + hsize;
- hsize += sizeof(struct iphdr);
-
- if (data + hsize > data_end) return XDP_PASS;
- if (eth->h_proto != htons(ETH_P_IP)) return XDP_PASS;
- if (ip->protocol == IPPROTO_UDP) return XDP_DROP;
- return XDP_PASS;
+// we need at least 1 bpf program in the final .o for Android S bpfloader compatibility
+// this program is trivial, and has a 'infinite' minimum kernel version number,
+// so will always be skipped
+DEFINE_BPF_PROG_KVER("skfilter/match", AID_ROOT, AID_ROOT, match, KVER_INF)
+(__unused struct __sk_buff* skb) {
+ return XTBPF_MATCH;
}
LICENSE("Apache 2.0");
diff --git a/bpf/tests/mts/bpf_existence_test.cpp b/bpf/tests/mts/bpf_existence_test.cpp
index 29f5cd2..f3c6907 100644
--- a/bpf/tests/mts/bpf_existence_test.cpp
+++ b/bpf/tests/mts/bpf_existence_test.cpp
@@ -80,11 +80,6 @@
TETHERING "prog_offload_schedcls_tether_upstream6_rawip",
};
-// Provided by *current* mainline module for S+ devices with 5.10+ kernels
-static const set<string> MAINLINE_FOR_S_5_10_PLUS = {
- TETHERING "prog_test_xdp_drop_ipv4_udp_ether",
-};
-
// Provided by *current* mainline module for T+ devices
static const set<string> MAINLINE_FOR_T_PLUS = {
SHARED "map_block_blocked_ports_map",
@@ -159,7 +154,7 @@
NETD "prog_netd_setsockopt_prog",
};
-// Provided by *current* mainline module for U+ devices with 5.10+ kernels
+// Provided by *current* mainline module for V+ devices with 5.10+ kernels
static const set<string> MAINLINE_FOR_V_5_10_PLUS = {
NETD "prog_netd_cgroupsockrelease_inet_release",
};
@@ -194,7 +189,6 @@
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
if (IsAtLeastS()) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
DO_EXPECT(IsAtLeastS(), MAINLINE_FOR_S_PLUS);
- DO_EXPECT(IsAtLeastS() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_S_5_10_PLUS);
// Nothing added or removed in SCv2.
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 5f672e7..8e4ec2f 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1938,8 +1938,21 @@
mContext, MdnsFeatureFlags.NSD_QUERY_WITH_KNOWN_ANSWER))
.setAvoidAdvertisingEmptyTxtRecords(mDeps.isTetheringFeatureNotChickenedOut(
mContext, MdnsFeatureFlags.NSD_AVOID_ADVERTISING_EMPTY_TXT_RECORDS))
- .setOverrideProvider(flag -> mDeps.isFeatureEnabled(
- mContext, FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag))
+ .setOverrideProvider(new MdnsFeatureFlags.FlagOverrideProvider() {
+ @Override
+ public boolean isForceEnabledForTest(@NonNull String flag) {
+ return mDeps.isFeatureEnabled(
+ mContext,
+ FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag);
+ }
+
+ @Override
+ public int getIntValueForTest(@NonNull String flag) {
+ return mDeps.getDeviceConfigPropertyInt(
+ FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag,
+ -1 /* defaultValue */);
+ }
+ })
.build();
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
@@ -2006,6 +2019,14 @@
}
/**
+ * @see DeviceConfigUtils#getDeviceConfigPropertyInt
+ */
+ public int getDeviceConfigPropertyInt(String feature, int defaultValue) {
+ return DeviceConfigUtils.getDeviceConfigPropertyInt(
+ NAMESPACE_TETHERING, feature, defaultValue);
+ }
+
+ /**
* @see MdnsDiscoveryManager
*/
public MdnsDiscoveryManager makeMdnsDiscoveryManager(
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index 7fa605a..a74bdf7 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -134,13 +136,20 @@
this.discoveryExecutor = new DiscoveryExecutor(socketClient.getLooper());
}
- private static class DiscoveryExecutor implements Executor {
+ /**
+ * A utility class to generate a handler, optionally with a looper, and to run functions on the
+ * newly created handler.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static class DiscoveryExecutor implements Executor {
private final HandlerThread handlerThread;
@GuardedBy("pendingTasks")
@Nullable private Handler handler;
+ // Store pending tasks and associated delay time. Each Pair represents a pending task
+ // (first) and its delay time (second).
@GuardedBy("pendingTasks")
- @NonNull private final ArrayList<Runnable> pendingTasks = new ArrayList<>();
+ @NonNull private final ArrayList<Pair<Runnable, Long>> pendingTasks = new ArrayList<>();
DiscoveryExecutor(@Nullable Looper defaultLooper) {
if (defaultLooper != null) {
@@ -154,8 +163,8 @@
protected void onLooperPrepared() {
synchronized (pendingTasks) {
handler = new Handler(getLooper());
- for (Runnable pendingTask : pendingTasks) {
- handler.post(pendingTask);
+ for (Pair<Runnable, Long> pendingTask : pendingTasks) {
+ handler.postDelayed(pendingTask.first, pendingTask.second);
}
pendingTasks.clear();
}
@@ -177,16 +186,20 @@
@Override
public void execute(Runnable function) {
+ executeDelayed(function, 0L /* delayMillis */);
+ }
+
+ public void executeDelayed(Runnable function, long delayMillis) {
final Handler handler;
synchronized (pendingTasks) {
if (this.handler == null) {
- pendingTasks.add(function);
+ pendingTasks.add(Pair.create(function, delayMillis));
return;
} else {
handler = this.handler;
}
}
- handler.post(function);
+ handler.postDelayed(function, delayMillis);
}
void shutDown() {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java b/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java
index 709dc79..b2be6ce 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java
@@ -111,6 +111,12 @@
* Indicates whether the flag should be force-enabled for testing purposes.
*/
boolean isForceEnabledForTest(@NonNull String flag);
+
+
+ /**
+ * Get the int value of the flag for testing purposes.
+ */
+ int getIntValueForTest(@NonNull String flag);
}
/**
@@ -121,6 +127,18 @@
}
/**
+ * Get the int value of the flag for testing purposes.
+ *
+ * @return the test int value, or -1 if it is unset or the OverrideProvider doesn't exist.
+ */
+ private int getIntValueForTest(@NonNull String flag) {
+ if (mOverrideProvider == null) {
+ return -1;
+ }
+ return mOverrideProvider.getIntValueForTest(flag);
+ }
+
+ /**
* Indicates whether {@link #NSD_UNICAST_REPLY_ENABLED} is enabled, including for testing.
*/
public boolean isUnicastReplyEnabled() {
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 990f43e..cb62ae1 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -407,6 +407,7 @@
import java.util.SortedSet;
import java.util.StringJoiner;
import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
@@ -8932,9 +8933,15 @@
@NonNull
final NetworkRequestInfo mDefaultRequest;
// Collection of NetworkRequestInfo's used for default networks.
+ // This set is read and iterated on multiple threads.
+ // Using CopyOnWriteArraySet since number of default network request is small (system default
+ // network request + per-app default network requests) and updated infrequently but read
+ // frequently.
@VisibleForTesting
@NonNull
- final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
+ final CopyOnWriteArraySet<NetworkRequestInfo> mDefaultNetworkRequests =
+ new CopyOnWriteArraySet<>();
+
private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestBpfMap.java b/staticlibs/testutils/devicetests/com/android/testutils/TestBpfMap.java
index 70f20d6..58e6622 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestBpfMap.java
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestBpfMap.java
@@ -65,10 +65,11 @@
@Override
public void insertEntry(K key, V value) throws ErrnoException,
- IllegalArgumentException {
- // The entry is created if and only if it doesn't exist. See BpfMap#insertEntry.
+ IllegalStateException {
+ // The entry is created if and only if it doesn't exist.
+ // And throws exception if it exists. See BpfMap#insertEntry.
if (mMap.get(key) != null) {
- throw new IllegalArgumentException(key + " already exist");
+ throw new IllegalStateException(key + " already exist");
}
mMap.put(key, value);
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
index b5c0132..ec47618 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -19,6 +19,8 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -32,10 +34,12 @@
import android.net.Network;
import android.os.Handler;
import android.os.HandlerThread;
+import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.Pair;
import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.MdnsDiscoveryManager.DiscoveryExecutor;
import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -55,7 +59,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
/** Tests for {@link MdnsDiscoveryManager}. */
@DevSdkIgnoreRunner.MonitorThreadLeak
@@ -390,6 +396,48 @@
verify(mockServiceTypeClientType1NullNetwork).notifySocketDestroyed();
}
+ @Test
+ public void testDiscoveryExecutor() throws Exception {
+ final TestableLooper testableLooper = new TestableLooper(thread.getLooper());
+ final DiscoveryExecutor executor = new DiscoveryExecutor(testableLooper.getLooper());
+ try {
+ // Verify the checkAndRunOnHandlerThread method
+ final CompletableFuture<Boolean> future1 = new CompletableFuture<>();
+ executor.checkAndRunOnHandlerThread(()-> future1.complete(true));
+ assertTrue(future1.isDone());
+ assertTrue(future1.get(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ // Verify the execute method
+ final CompletableFuture<Boolean> future2 = new CompletableFuture<>();
+ executor.execute(()-> future2.complete(true));
+ testableLooper.processAllMessages();
+ assertTrue(future2.isDone());
+ assertTrue(future2.get(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ // Verify the executeDelayed method
+ final CompletableFuture<Boolean> future3 = new CompletableFuture<>();
+ // Schedule a task with 999 ms delay
+ executor.executeDelayed(()-> future3.complete(true), 999L);
+ testableLooper.processAllMessages();
+ assertFalse(future3.isDone());
+
+ // 500 ms have elapsed but do not exceed the target time (999 ms)
+ // The function should not be executed.
+ testableLooper.moveTimeForward(500L);
+ testableLooper.processAllMessages();
+ assertFalse(future3.isDone());
+
+ // 500 ms have elapsed again and have exceeded the target time (999 ms).
+ // The function should be executed.
+ testableLooper.moveTimeForward(500L);
+ testableLooper.processAllMessages();
+ assertTrue(future3.isDone());
+ assertTrue(future3.get(500L, TimeUnit.MILLISECONDS));
+ } finally {
+ testableLooper.destroy();
+ }
+ }
+
private MdnsPacket createMdnsPacket(String serviceType) {
final String[] type = TextUtils.split(serviceType, "\\.");
final ArrayList<String> name = new ArrayList<>(type.length + 1);