Merge "netbpfload.rc: add a version comment" into main
diff --git a/bpf/netd/BpfHandler.cpp b/bpf/netd/BpfHandler.cpp
index d41aa81..680c05e 100644
--- a/bpf/netd/BpfHandler.cpp
+++ b/bpf/netd/BpfHandler.cpp
@@ -268,6 +268,16 @@
RETURN_IF_NOT_OK(initMaps());
if (isAtLeast25Q2) {
+ struct rlimit limit = {
+ .rlim_cur = 1u << 30, // 1 GiB
+ .rlim_max = 1u << 30, // 1 GiB
+ };
+ // 25Q2 netd.rc includes "rlimit memlock 1073741824 1073741824"
+ // so this should be a no-op, and thus just succeed.
+ // make sure it isn't lowered in platform netd.rc...
+ if (setrlimit(RLIMIT_MEMLOCK, &limit))
+ return statusFromErrno(errno, "Failed to set 1GiB RLIMIT_MEMLOCK");
+
// Make sure netd can create & write maps. sepolicy is V+, but enough to enforce on 25Q2+
int key = 1;
int value = 123;
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 9b3c7ba..48467ed 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -755,7 +755,17 @@
private void parseEthernetConfig(String configString) {
final EthernetTrackerConfig config = createEthernetTrackerConfig(configString);
NetworkCapabilities nc;
- if (TextUtils.isEmpty(config.mCapabilities)) {
+ // Starting with Android B (API level 36), we provide default NetworkCapabilities
+ // for Ethernet interfaces when no explicit capabilities are specified in the
+ // configuration string. This change is made to ensure consistent and expected
+ // network behavior for Ethernet devices.
+ //
+ // It's possible that OEMs or device manufacturers may have relied on the previous
+ // behavior (where interfaces without specified capabilities would have minimal
+ // capabilities) to prevent certain Ethernet interfaces from becoming
+ // the default network. To avoid breaking existing device configurations, this
+ // change is gated by the SDK level.
+ if (SdkLevel.isAtLeastB() && TextUtils.isEmpty(config.mCapabilities)) {
boolean isTestIface = config.mIface.matches(TEST_IFACE_REGEXP);
nc = createDefaultNetworkCapabilities(isTestIface, config.mTransport);
} else {
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 5c0ba78..f6dbf6c 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -48,6 +48,7 @@
<!-- Configuration values for ThreadNetworkService -->
<item type="bool" name="config_thread_default_enabled" />
+ <item type="bool" name="config_thread_border_router_default_enabled" />
<item type="bool" name="config_thread_location_use_for_country_code_enabled" />
<item type="string" name="config_thread_vendor_name" />
<item type="string" name="config_thread_vendor_oui" />
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
index 0352ad5..149979f 100644
--- a/service/src/com/android/server/L2capNetworkProvider.java
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -597,10 +597,12 @@
final ClientRequestInfo cri = mClientNetworkRequests.get(specifier);
if (cri == null) return;
+ // Release ClientNetworkRequest before sending onUnavailable() to ensure the app
+ // first receives an onLost() callback if a network had been created.
+ releaseClientNetworkRequest(cri);
for (NetworkRequest request : cri.requests) {
mProvider.declareNetworkRequestUnfulfillable(request);
}
- releaseClientNetworkRequest(cri);
}
}
diff --git a/staticlibs/testutils/host/python/apf_utils.py b/staticlibs/testutils/host/python/apf_utils.py
index fa8a1da..4835c23 100644
--- a/staticlibs/testutils/host/python/apf_utils.py
+++ b/staticlibs/testutils/host/python/apf_utils.py
@@ -439,10 +439,10 @@
def wrapper(self, *args, **kwargs):
asserts.abort_class_if(
(not hasattr(self, 'client')) or (not hasattr(self.client, 'isAtLeastB')),
- "client device is not B+"
+ "no valid client attribute"
)
- asserts.skip_if(not self.client.isAtLeastB(), "not B+")
+ asserts.abort_class_if(not self.client.isAtLeastB(), "client device is not Android B+")
return test_function(self, *args, **kwargs)
return wrapper
return decorator
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index fb45f4a..ee2368d 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -14,7 +14,20 @@
from mobly import asserts
from scapy.layers.inet import IP, ICMP, IPOption_Router_Alert
-from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
+from scapy.layers.inet6 import (
+ IPv6,
+ IPv6ExtHdrHopByHop,
+ ICMPv6EchoRequest,
+ ICMPv6EchoReply,
+ ICMPv6MLQuery2,
+ ICMPv6MLReport2,
+ ICMPv6MLDMultAddrRec,
+ ICMPv6NDOptSrcLLAddr,
+ ICMPv6NDOptDstLLAddr,
+ ICMPv6ND_NS,
+ ICMPv6ND_NA,
+ RouterAlert
+)
from scapy.layers.l2 import Ether
from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mq, IGMPv3mr, IGMPv3gr
from net_tests_utils.host.python import apf_test_base, apf_utils, adb_utils, assert_utils, packet_utils
@@ -87,6 +100,24 @@
arp_request, "DROPPED_ARP_REQUEST_REPLIED", arp_reply
)
+ def test_non_dad_ipv6_neighbor_solicitation_offload(self):
+ eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
+ ip = IPv6(src=self.server_ipv6_addresses[0], dst=self.client_ipv6_addresses[0])
+ icmpv6 = ICMPv6ND_NS(tgt=self.client_ipv6_addresses[0])
+ opt = ICMPv6NDOptSrcLLAddr(lladdr=self.server_mac_address)
+ neighbor_solicitation = bytes(eth/ip/icmpv6/opt).hex()
+
+ eth = Ether(src=self.client_mac_address, dst=self.server_mac_address)
+ ip = IPv6(src=self.client_ipv6_addresses[0], dst=self.server_ipv6_addresses[0])
+ icmpv6 = ICMPv6ND_NA(tgt=self.client_ipv6_addresses[0], R=1, S=1, O=1)
+ opt = ICMPv6NDOptDstLLAddr(lladdr=self.client_mac_address)
+ expected_neighbor_advertisement = bytes(eth/ip/icmpv6/opt).hex()
+ self.send_packet_and_expect_reply_received(
+ neighbor_solicitation,
+ "DROPPED_IPV6_NS_REPLIED_NON_DAD",
+ expected_neighbor_advertisement
+ )
+
@apf_utils.at_least_B()
def test_ipv4_icmp_echo_request_offload(self):
eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
@@ -103,6 +134,7 @@
)
@apf_utils.at_least_B()
+ @apf_utils.apf_ram_at_least(3000)
def test_ipv6_icmp_echo_request_offload(self):
eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
ip = IPv6(src=self.server_ipv6_addresses[0], dst=self.client_ipv6_addresses[0])
@@ -161,3 +193,28 @@
self.clientDevice,
f'ip addr del {addr}/32 dev {self.client_iface_name}'
)
+
+ @apf_utils.at_least_B()
+ @apf_utils.apf_ram_at_least(3000)
+ def test_mldv2_general_query_offload(self):
+ ether = Ether(src=self.server_mac_address, dst='33:33:00:00:00:01')
+ ip = IPv6(src=self.server_ipv6_addresses[0], dst='ff02::1', hlim=1)
+ hopOpts = IPv6ExtHdrHopByHop(options=[RouterAlert(otype=5)])
+ mld = ICMPv6MLQuery2()
+ mldv2_general_query = bytes(ether/ip/hopOpts/mld).hex()
+
+ ether = Ether(src=self.client_mac_address, dst='33:33:00:00:00:16')
+ ip = IPv6(src=self.client_ipv6_addresses[0], dst='ff02::16', hlim=1)
+
+ mcast_addrs = apf_utils.get_exclude_all_host_ipv6_multicast_addresses(
+ self.clientDevice, self.client_iface_name
+ )
+
+ mld_records = []
+ for addr in mcast_addrs:
+ mld_records.append(ICMPv6MLDMultAddrRec(dst=addr, rtype=2))
+ mld = ICMPv6MLReport2(records=mld_records)
+ expected_mldv2_report = bytes(ether/ip/hopOpts/mld).hex()
+ self.send_packet_and_expect_reply_received(
+ mldv2_general_query, "DROPPED_IPV6_MLD_V2_GENERAL_QUERY_REPLIED", expected_mldv2_report
+ )
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
index babcba9..ee5b4ee 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
@@ -38,12 +38,14 @@
import android.net.NetworkSpecifier
import android.net.RouteInfo
import android.os.Build
+import android.os.Handler
import android.os.HandlerThread
import android.os.ParcelFileDescriptor
import com.android.server.net.L2capNetwork.L2capIpClient
import com.android.server.net.L2capPacketForwarder
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.RecorderCallback.CallbackEntry.Reserved
import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
import com.android.testutils.TestableNetworkCallback
@@ -59,6 +61,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mockito.doAnswer
@@ -394,4 +397,34 @@
val cb2 = requestNetwork(nr)
cb2.expectAvailableCallbacks(anyNetwork(), validated = false)
}
+
+ /** Test to ensure onLost() is sent before onUnavailable() when the network is torn down. */
+ @Test
+ fun testClientNetwork_gracefulTearDown() {
+ val specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_CLIENT)
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC))
+ .setPsm(PSM)
+ .build()
+
+ val nr = REQUEST.copyWithSpecifier(specifier)
+ val cb = requestNetwork(nr)
+ cb.expectAvailableCallbacks(anyNetwork(), validated = false)
+
+ // Capture the L2capPacketForwarder callback object to tear down the network.
+ val handlerCaptor = ArgumentCaptor.forClass(Handler::class.java)
+ val forwarderCbCaptor = ArgumentCaptor.forClass(L2capPacketForwarder.ICallback::class.java)
+ verify(providerDeps).createL2capPacketForwarder(
+ handlerCaptor.capture(), any(), any(), any(), forwarderCbCaptor.capture())
+ val handler = handlerCaptor.value
+ val forwarderCb = forwarderCbCaptor.value
+
+ // Trigger a forwarding error
+ handler.post { forwarderCb.onError() }
+ handler.waitForIdle(HANDLER_TIMEOUT_MS)
+
+ cb.expect<Lost>()
+ cb.expect<Unavailable>()
+ }
}