Merge "Report discovery metrics data" into main
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 062ecc5..119fbc6 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -17,7 +17,8 @@
 package com.android.networkstack.tethering.apishim.api31;
 
 import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
 
 import android.net.IpPrefix;
 import android.net.MacAddress;
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 6affb62..56b5c2e 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -28,11 +28,11 @@
 import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
 import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
 import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
 import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
 import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
 import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 18c2171..50d6c4b 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -16,7 +16,6 @@
 
 package android.net.ip;
 
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.system.OsConstants.AF_INET6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
 import static android.system.OsConstants.SOCK_RAW;
@@ -30,6 +29,7 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
 import static com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_NEIGHBOR;
 import static com.android.networkstack.tethering.util.TetheringUtils.getAllNodesForScopeId;
 
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index b1f875b..4413d26 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -20,6 +20,8 @@
 import static android.system.OsConstants.AF_UNIX;
 import static android.system.OsConstants.SOCK_STREAM;
 
+import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
@@ -34,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -57,7 +60,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.io.FileDescriptor;
@@ -75,8 +77,9 @@
     private OffloadHardwareInterface mOffloadHw;
     private OffloadHalCallback mOffloadHalCallback;
 
-    @Mock private IOffloadHal mIOffload;
-    @Mock private NativeHandle mNativeHandle;
+    private IOffloadHal mIOffload;
+    private NativeHandle mNativeHandle1;
+    private NativeHandle mNativeHandle2;
 
     // Random values to test Netlink message.
     private static final short TEST_TYPE = 184;
@@ -97,7 +100,9 @@
 
         @Override
         public NativeHandle createConntrackSocket(final int groups) {
-            return mNativeHandle;
+            return groups == (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)
+                    ? mNativeHandle1
+                    : mNativeHandle2;
         }
     }
 
@@ -105,45 +110,89 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mOffloadHalCallback = new OffloadHalCallback();
-        when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class),
-                any(OffloadHalCallback.class))).thenReturn(true);
+        mIOffload = mock(IOffloadHal.class);
     }
 
-    private void startOffloadHardwareInterface(int offloadHalVersion)
+    private void startOffloadHardwareInterface(int offloadHalVersion, boolean isHalInitSuccess)
             throws Exception {
+        startOffloadHardwareInterface(offloadHalVersion, isHalInitSuccess, mock(NativeHandle.class),
+                mock(NativeHandle.class));
+    }
+
+    private void startOffloadHardwareInterface(int offloadHalVersion, boolean isHalInitSuccess,
+            NativeHandle handle1, NativeHandle handle2) throws Exception {
         final SharedLog log = new SharedLog("test");
         final Handler handler = new Handler(mTestLooper.getLooper());
-        final int num = offloadHalVersion != OFFLOAD_HAL_VERSION_NONE ? 1 : 0;
+        final boolean hasNullHandle = handle1 == null || handle2 == null;
+        // If offloadHalVersion is OFFLOAD_HAL_VERSION_NONE or it has null NativeHandle arguments,
+        // mIOffload.initOffload() shouldn't be called.
+        final int initNum = (offloadHalVersion != OFFLOAD_HAL_VERSION_NONE && !hasNullHandle)
+                ? 1
+                : 0;
+        // If it is HIDL or has null NativeHandle argument, NativeHandles should be closed.
+        final int handleCloseNum = (hasNullHandle
+                || offloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_0
+                || offloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_1) ? 1 : 0;
+        mNativeHandle1 = handle1;
+        mNativeHandle2 = handle2;
+        when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class),
+                any(OffloadHalCallback.class))).thenReturn(isHalInitSuccess);
         mOffloadHw = new OffloadHardwareInterface(handler, log,
                 new MyDependencies(handler, log, offloadHalVersion));
-        assertEquals(offloadHalVersion, mOffloadHw.initOffload(mOffloadHalCallback));
-        verify(mIOffload, times(num)).initOffload(any(NativeHandle.class), any(NativeHandle.class),
-                eq(mOffloadHalCallback));
+        assertEquals(isHalInitSuccess && !hasNullHandle
+                ? offloadHalVersion
+                : OFFLOAD_HAL_VERSION_NONE,
+                mOffloadHw.initOffload(mOffloadHalCallback));
+        verify(mIOffload, times(initNum)).initOffload(any(NativeHandle.class),
+                any(NativeHandle.class), eq(mOffloadHalCallback));
+        if (mNativeHandle1 != null) verify(mNativeHandle1, times(handleCloseNum)).close();
+        if (mNativeHandle2 != null) verify(mNativeHandle2, times(handleCloseNum)).close();
     }
 
     @Test
     public void testInitFailureWithNoHal() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE, true);
     }
 
     @Test
     public void testInitSuccessWithAidl() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, true);
     }
 
     @Test
     public void testInitSuccessWithHidl_1_0() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
     }
 
     @Test
     public void testInitSuccessWithHidl_1_1() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, true);
+    }
+
+    @Test
+    public void testInitFailWithAidl() throws Exception {
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, false);
+    }
+
+    @Test
+    public void testInitFailWithHidl_1_0() throws Exception {
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, false);
+    }
+
+    @Test
+    public void testInitFailWithHidl_1_1() throws Exception {
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, false);
+    }
+
+    @Test
+    public void testInitFailDueToNullHandles() throws Exception {
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL, true, mock(NativeHandle.class),
+                null);
     }
 
     @Test
     public void testGetForwardedStats() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         ForwardedStats stats = new ForwardedStats(12345, 56780);
         when(mIOffload.getForwardedStats(anyString())).thenReturn(stats);
         assertEquals(mOffloadHw.getForwardedStats(RMNET0), stats);
@@ -152,7 +201,7 @@
 
     @Test
     public void testSetLocalPrefixes() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         final ArrayList<String> localPrefixes = new ArrayList<>();
         localPrefixes.add("127.0.0.0/8");
         localPrefixes.add("fe80::/64");
@@ -165,7 +214,7 @@
 
     @Test
     public void testSetDataLimit() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         final long limit = 12345;
         when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(true);
         assertTrue(mOffloadHw.setDataLimit(RMNET0, limit));
@@ -177,7 +226,7 @@
     @Test
     public void testSetDataWarningAndLimitFailureWithHidl_1_0() throws Exception {
         // Verify V1.0 control HAL would reject the function call with exception.
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         final long warning = 12345;
         final long limit = 67890;
         assertThrows(UnsupportedOperationException.class,
@@ -187,7 +236,7 @@
     @Test
     public void testSetDataWarningAndLimit() throws Exception {
         // Verify V1.1 control HAL could receive this function call.
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1, true);
         final long warning = 12345;
         final long limit = 67890;
         when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
@@ -199,7 +248,7 @@
 
     @Test
     public void testSetUpstreamParameters() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         final String v4addr = "192.168.10.1";
         final String v4gateway = "192.168.10.255";
         final ArrayList<String> v6gws = new ArrayList<>(0);
@@ -220,7 +269,7 @@
 
     @Test
     public void testUpdateDownstream() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         final String ifName = "wlan1";
         final String prefix = "192.168.43.0/24";
         when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(true);
@@ -237,7 +286,7 @@
 
     @Test
     public void testSendIpv4NfGenMsg() throws Exception {
-        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+        startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0, true);
         FileDescriptor writeSocket = new FileDescriptor();
         FileDescriptor readSocket = new FileDescriptor();
         try {
@@ -246,9 +295,9 @@
             fail();
             return;
         }
-        when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket);
+        when(mNativeHandle1.getFileDescriptor()).thenReturn(writeSocket);
 
-        mOffloadHw.sendIpv4NfGenMsg(mNativeHandle, TEST_TYPE, TEST_FLAGS);
+        mOffloadHw.sendIpv4NfGenMsg(mNativeHandle1, TEST_TYPE, TEST_FLAGS);
 
         ByteBuffer buffer = ByteBuffer.allocate(9823);  // Arbitrary value > expectedLen.
         buffer.order(ByteOrder.nativeOrder());
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 9c6904d..770507e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -68,6 +68,7 @@
 import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
 import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
 import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
 import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
@@ -157,7 +158,6 @@
 import android.net.ip.DadProxy;
 import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.NetworkConstants;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
@@ -559,7 +559,7 @@
             prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2"));
             prop.addLinkAddress(
                     new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
-                            NetworkConstants.RFC7421_PREFIX_LENGTH));
+                            RFC7421_PREFIX_LENGTH));
             prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
                     InetAddresses.parseNumericAddress("2001:db8::1"),
                     interfaceName, RTN_UNICAST));
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index be604f9..1326db0 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -65,8 +65,6 @@
     uint64_t rxPackets;
     uint64_t txBytes;
     uint64_t txPackets;
-    uint64_t tcpRxPackets;
-    uint64_t tcpTxPackets;
 } Stats;
 
 typedef struct {
diff --git a/framework-t/api/OWNERS b/framework-t/api/OWNERS
index de0f905..af583c3 100644
--- a/framework-t/api/OWNERS
+++ b/framework-t/api/OWNERS
@@ -1 +1,2 @@
 file:platform/packages/modules/Connectivity:master:/nearby/OWNERS
+file:platform/packages/modules/Connectivity:master:/remoteauth/OWNERS
diff --git a/service-t/jni/com_android_server_net_NetworkStatsService.cpp b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
index 8d8dc32..ae553f0 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
@@ -47,8 +47,8 @@
     RX_PACKETS = 1,
     TX_BYTES = 2,
     TX_PACKETS = 3,
-    TCP_RX_PACKETS = 4,
-    TCP_TX_PACKETS = 5
+    TCP_RX_PACKETS = 4,  // not supported, always returns UNKNOWN (-1)
+    TCP_TX_PACKETS = 5,  // not supported, always returns UNKNOWN (-1)
 };
 
 static uint64_t getStatsType(Stats* stats, StatsType type) {
@@ -62,9 +62,7 @@
         case TX_PACKETS:
             return stats->txPackets;
         case TCP_RX_PACKETS:
-            return stats->tcpRxPackets;
         case TCP_TX_PACKETS:
-            return stats->tcpTxPackets;
         default:
             return UNKNOWN;
     }
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 2e6e3e5..317c3f9 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -46,8 +46,6 @@
     stats->rxPackets = 0;
     stats->txBytes = 0;
     stats->txPackets = 0;
-    stats->tcpRxPackets = -1;
-    stats->tcpTxPackets = -1;
 }
 
 int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index 097dbe0..2ef7368 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -34,7 +34,6 @@
 import java.net.Inet6Address;
 import java.net.InetSocketAddress;
 import java.util.List;
-import java.util.Objects;
 
 /**
  * The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
@@ -49,10 +48,9 @@
     @NonNull private final MdnsSocketProvider mSocketProvider;
     @NonNull private final SharedLog mSharedLog;
 
-    private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
+    private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mSocketRequests =
             new ArrayMap<>();
-    private final ArrayMap<MdnsInterfaceSocket, ReadPacketHandler> mSocketPacketHandlers =
-            new ArrayMap<>();
+    private final ArrayMap<SocketKey, ReadPacketHandler> mSocketPacketHandlers = new ArrayMap<>();
     private MdnsSocketClientBase.Callback mCallback = null;
     private int mReceivedPacketNumber = 0;
 
@@ -68,8 +66,7 @@
         @NonNull
         private final SocketCreationCallback mSocketCreationCallback;
         @NonNull
-        private final ArrayMap<MdnsInterfaceSocket, SocketKey> mActiveSockets =
-                new ArrayMap<>();
+        private final ArrayMap<SocketKey, MdnsInterfaceSocket> mActiveSockets = new ArrayMap<>();
 
         InterfaceSocketCallback(SocketCreationCallback socketCreationCallback) {
             mSocketCreationCallback = socketCreationCallback;
@@ -80,27 +77,27 @@
                 @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {
             // The socket may be already created by other request before, try to get the stored
             // ReadPacketHandler.
-            ReadPacketHandler handler = mSocketPacketHandlers.get(socket);
+            ReadPacketHandler handler = mSocketPacketHandlers.get(socketKey);
             if (handler == null) {
                 // First request to create this socket. Initial a ReadPacketHandler for this socket.
                 handler = new ReadPacketHandler(socketKey);
-                mSocketPacketHandlers.put(socket, handler);
+                mSocketPacketHandlers.put(socketKey, handler);
             }
             socket.addPacketHandler(handler);
-            mActiveSockets.put(socket, socketKey);
+            mActiveSockets.put(socketKey, socket);
             mSocketCreationCallback.onSocketCreated(socketKey);
         }
 
         @Override
         public void onInterfaceDestroyed(@NonNull SocketKey socketKey,
                 @NonNull MdnsInterfaceSocket socket) {
-            notifySocketDestroyed(socket);
-            maybeCleanupPacketHandler(socket);
+            notifySocketDestroyed(socketKey);
+            maybeCleanupPacketHandler(socketKey);
         }
 
-        private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
-            final SocketKey socketKey = mActiveSockets.remove(socket);
-            if (!isSocketActive(socket)) {
+        private void notifySocketDestroyed(@NonNull SocketKey socketKey) {
+            mActiveSockets.remove(socketKey);
+            if (!isSocketActive(socketKey)) {
                 mSocketCreationCallback.onSocketDestroyed(socketKey);
             }
         }
@@ -108,35 +105,38 @@
         void onNetworkUnrequested() {
             for (int i = mActiveSockets.size() - 1; i >= 0; i--) {
                 // Iterate from the end so the socket can be removed
-                final MdnsInterfaceSocket socket = mActiveSockets.keyAt(i);
-                notifySocketDestroyed(socket);
-                maybeCleanupPacketHandler(socket);
+                final SocketKey socketKey = mActiveSockets.keyAt(i);
+                notifySocketDestroyed(socketKey);
+                maybeCleanupPacketHandler(socketKey);
             }
         }
     }
 
-    private boolean isSocketActive(@NonNull MdnsInterfaceSocket socket) {
-        for (int i = 0; i < mRequestedNetworks.size(); i++) {
-            final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
-            if (isc.mActiveSockets.containsKey(socket)) {
+    private boolean isSocketActive(@NonNull SocketKey socketKey) {
+        for (int i = 0; i < mSocketRequests.size(); i++) {
+            final InterfaceSocketCallback ifaceSocketCallback = mSocketRequests.valueAt(i);
+            if (ifaceSocketCallback.mActiveSockets.containsKey(socketKey)) {
                 return true;
             }
         }
         return false;
     }
 
-    private ArrayMap<MdnsInterfaceSocket, SocketKey> getActiveSockets() {
-        final ArrayMap<MdnsInterfaceSocket, SocketKey> sockets = new ArrayMap<>();
-        for (int i = 0; i < mRequestedNetworks.size(); i++) {
-            final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
-            sockets.putAll(isc.mActiveSockets);
+    @Nullable
+    private MdnsInterfaceSocket getTargetSocket(@NonNull SocketKey targetSocketKey) {
+        for (int i = 0; i < mSocketRequests.size(); i++) {
+            final InterfaceSocketCallback ifaceSocketCallback = mSocketRequests.valueAt(i);
+            final int index = ifaceSocketCallback.mActiveSockets.indexOfKey(targetSocketKey);
+            if (index >= 0) {
+                return ifaceSocketCallback.mActiveSockets.valueAt(index);
+            }
         }
-        return sockets;
+        return null;
     }
 
-    private void maybeCleanupPacketHandler(@NonNull MdnsInterfaceSocket socket) {
-        if (isSocketActive(socket)) return;
-        mSocketPacketHandlers.remove(socket);
+    private void maybeCleanupPacketHandler(@NonNull SocketKey socketKey) {
+        if (isSocketActive(socketKey)) return;
+        mSocketPacketHandlers.remove(socketKey);
     }
 
     private class ReadPacketHandler implements MulticastPacketReader.PacketHandler {
@@ -171,14 +171,14 @@
     public void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
             @Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback) {
         ensureRunningOnHandlerThread(mHandler);
-        InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
+        InterfaceSocketCallback callback = mSocketRequests.get(listener);
         if (callback != null) {
             throw new IllegalArgumentException("Can not register duplicated listener");
         }
 
         if (DBG) mSharedLog.v("notifyNetworkRequested: network=" + network);
         callback = new InterfaceSocketCallback(socketCreationCallback);
-        mRequestedNetworks.put(listener, callback);
+        mSocketRequests.put(listener, callback);
         mSocketProvider.requestSocket(network, callback);
     }
 
@@ -186,14 +186,14 @@
     @Override
     public void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) {
         ensureRunningOnHandlerThread(mHandler);
-        final InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
+        final InterfaceSocketCallback callback = mSocketRequests.get(listener);
         if (callback == null) {
             mSharedLog.e("Can not be unrequested with unknown listener=" + listener);
             return;
         }
         callback.onNetworkUnrequested();
-        // onNetworkUnrequested does cleanups based on mRequestedNetworks, only remove afterwards
-        mRequestedNetworks.remove(listener);
+        // onNetworkUnrequested does cleanups based on mSocketRequests, only remove afterwards
+        mSocketRequests.remove(listener);
         mSocketProvider.unrequestSocket(callback);
     }
 
@@ -209,42 +209,28 @@
 
     private void sendMdnsPacket(@NonNull DatagramPacket packet, @NonNull SocketKey targetSocketKey,
             boolean onlyUseIpv6OnIpv6OnlyNetworks) {
+        final MdnsInterfaceSocket socket = getTargetSocket(targetSocketKey);
+        if (socket == null) {
+            mSharedLog.e("No socket matches targetSocketKey=" + targetSocketKey);
+            return;
+        }
+
         final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
                 instanceof Inet6Address;
         final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
                 instanceof Inet4Address;
-        final ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets = getActiveSockets();
-        boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlySockets(
-                activeSockets, targetSocketKey);
-        for (int i = 0; i < activeSockets.size(); i++) {
-            final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
-            final SocketKey socketKey = activeSockets.valueAt(i);
-            // Check ip capability and network before sending packet
-            if (((isIpv6 && socket.hasJoinedIpv6() && shouldQueryIpv6)
-                    || (isIpv4 && socket.hasJoinedIpv4()))
-                    && Objects.equals(socketKey, targetSocketKey)) {
-                try {
-                    socket.send(packet);
-                } catch (IOException e) {
-                    mSharedLog.e("Failed to send a mDNS packet.", e);
-                }
+        final boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || !socket.hasJoinedIpv4();
+        // Check ip capability and network before sending packet
+        if ((isIpv6 && socket.hasJoinedIpv6() && shouldQueryIpv6)
+                || (isIpv4 && socket.hasJoinedIpv4())) {
+            try {
+                socket.send(packet);
+            } catch (IOException e) {
+                mSharedLog.e("Failed to send a mDNS packet.", e);
             }
         }
     }
 
-    private boolean isIpv6OnlySockets(
-            @NonNull ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets,
-            @NonNull SocketKey targetSocketKey) {
-        for (int i = 0; i < activeSockets.size(); i++) {
-            final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
-            final SocketKey socketKey = activeSockets.valueAt(i);
-            if (Objects.equals(socketKey, targetSocketKey) && socket.hasJoinedIpv4()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private void processResponsePacket(byte[] recvbuf, int length, @NonNull SocketKey socketKey) {
         int packetNumber = ++mReceivedPacketNumber;
 
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 7aff6a4..f08ffc3 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -1143,7 +1143,7 @@
         }
     }
 
-    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     private static native void native_init(boolean startSkDestroyListener);
 
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
index 2469710..da4fe28 100644
--- a/tests/cts/net/native/dns/Android.bp
+++ b/tests/cts/net/native/dns/Android.bp
@@ -30,6 +30,7 @@
     ],
     // To be compatible with R devices, the min_sdk_version must be 30.
     min_sdk_version: "30",
+    host_required: ["net-tests-utils-host-common"],
 }
 
 cc_test {
diff --git a/tests/cts/net/native/dns/AndroidTest.xml b/tests/cts/net/native/dns/AndroidTest.xml
index 6d03c23..d49696b 100644
--- a/tests/cts/net/native/dns/AndroidTest.xml
+++ b/tests/cts/net/native/dns/AndroidTest.xml
@@ -24,6 +24,8 @@
         <option name="push" value="CtsNativeNetDnsTestCases->/data/local/tmp/CtsNativeNetDnsTestCases" />
         <option name="append-bitness" value="true" />
     </target_preparer>
+    <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
+    </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="CtsNativeNetDnsTestCases" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index e978664..bce08df 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2744,7 +2744,6 @@
      */
     @AppModeFull(reason = "Instant apps cannot create test networks")
     @Test
-    @SkipMainlinePresubmit(reason = "Out of SLO flakiness")
     public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
         // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
         // shims, and @IgnoreUpTo does not check that.
@@ -2757,17 +2756,19 @@
         final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
 
         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+        final Network testNetwork = tnt.getNetwork();
 
         testAndCleanup(() -> {
             setOemNetworkPreferenceForMyPackage(
                     OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
-            waitForAvailable(defaultCallback, tnt.getNetwork());
+            waitForAvailable(defaultCallback, testNetwork);
             systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
                     NETWORK_CALLBACK_TIMEOUT_MS, cb -> wifiNetwork.equals(cb.getNetwork()));
         }, /* cleanup */ () -> {
                 runWithShellPermissionIdentity(tnt::teardown);
-                defaultCallback.expect(CallbackEntry.LOST, tnt, NETWORK_CALLBACK_TIMEOUT_MS);
+                defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
+                        cb -> testNetwork.equals(cb.getNetwork()));
 
                 // This network preference should only ever use the test network therefore available
                 // should not trigger when the test network goes down (e.g. switch to cellular).
@@ -3205,7 +3206,6 @@
 
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
-    @SkipPresubmit(reason = "Out of SLO flakiness")
     public void testMobileDataPreferredUids() throws Exception {
         assumeTrue(TestUtils.shouldTestSApis());
         final boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index e4ee8de..7be4f78 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -25,6 +25,7 @@
 import android.net.LinkProperties
 import android.net.LocalSocket
 import android.net.LocalSocketAddress
+import android.net.MacAddress
 import android.net.Network
 import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
@@ -73,6 +74,8 @@
 import android.system.OsConstants.AF_INET6
 import android.system.OsConstants.EADDRNOTAVAIL
 import android.system.OsConstants.ENETUNREACH
+import android.system.OsConstants.ETH_P_IPV6
+import android.system.OsConstants.IPPROTO_IPV6
 import android.system.OsConstants.IPPROTO_UDP
 import android.system.OsConstants.SOCK_DGRAM
 import android.util.Log
@@ -82,13 +85,21 @@
 import com.android.compatibility.common.util.PropertyUtil
 import com.android.modules.utils.build.SdkLevel.isAtLeastU
 import com.android.net.module.util.ArrayTrackRecord
+import com.android.net.module.util.DnsPacket
+import com.android.net.module.util.HexDump
+import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
+import com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN
+import com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN
+import com.android.net.module.util.PacketBuilder
 import com.android.net.module.util.TrackRecord
 import com.android.testutils.ConnectivityModuleTest
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.IPv6UdpFilter
 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
+import com.android.testutils.TapPacketReader
 import com.android.testutils.TestableNetworkAgent
 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
 import com.android.testutils.TestableNetworkCallback
@@ -103,6 +114,7 @@
 import java.net.InetAddress
 import java.net.NetworkInterface
 import java.net.ServerSocket
+import java.nio.ByteBuffer
 import java.nio.charset.StandardCharsets
 import java.util.Random
 import java.util.concurrent.Executor
@@ -133,6 +145,8 @@
 private const val REGISTRATION_TIMEOUT_MS = 10_000L
 private const val DBG = false
 private const val TEST_PORT = 12345
+private const val MDNS_PORT = 5353.toShort()
+private val multicastIpv6Addr = parseNumericAddress("ff02::fb") as Inet6Address
 
 @AppModeFull(reason = "Socket cannot bind in instant app mode")
 @RunWith(DevSdkIgnoreRunner::class)
@@ -194,8 +208,8 @@
 
         inline fun <reified V : NsdEvent> expectCallback(timeoutMs: Long = TIMEOUT_MS): V {
             val nextEvent = nextEvents.poll(timeoutMs)
-            assertNotNull(nextEvent, "No callback received after $timeoutMs ms, expected " +
-                    "${V::class.java.simpleName}")
+            assertNotNull(nextEvent, "No callback received after $timeoutMs ms, " +
+                    "expected ${V::class.java.simpleName}")
             assertTrue(nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
                     nextEvent.javaClass.simpleName)
             return nextEvent
@@ -411,7 +425,6 @@
         val lp = LinkProperties().apply {
             interfaceName = ifaceName
         }
-
         val agent = TestableNetworkAgent(context, handlerThread.looper,
                 NetworkCapabilities().apply {
                     removeCapability(NET_CAPABILITY_TRUSTED)
@@ -1144,6 +1157,176 @@
         }
     }
 
+    @Test
+    fun testRegisterWithConflictDuringProbing() {
+        // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
+        assumeTrue(TestUtils.shouldTestTApis())
+
+        val si = NsdServiceInfo()
+        si.serviceType = serviceType
+        si.serviceName = serviceName
+        si.network = testNetwork1.network
+        si.port = 12345 // Test won't try to connect so port does not matter
+
+        val packetReader = TapPacketReader(Handler(handlerThread.looper),
+                testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+        packetReader.startAsyncForTest()
+        handlerThread.waitForIdle(TIMEOUT_MS)
+
+        // Register service on testNetwork1
+        val registrationRecord = NsdRegistrationRecord()
+        nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, { it.run() },
+                registrationRecord)
+
+        tryTest {
+            assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
+                    "Did not find a probe for the service")
+            packetReader.sendResponse(buildConflictingAnnouncement())
+
+            // Registration must use an updated name to avoid the conflict
+            val cb = registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
+            cb.serviceInfo.serviceName.let {
+                assertTrue("Unexpected registered name: $it",
+                        it.startsWith(serviceName) && it != serviceName)
+            }
+        } cleanupStep {
+            nsdManager.unregisterService(registrationRecord)
+            registrationRecord.expectCallback<ServiceUnregistered>()
+        } cleanup {
+            packetReader.handler.post { packetReader.stop() }
+            handlerThread.waitForIdle(TIMEOUT_MS)
+        }
+    }
+
+    @Test
+    fun testRegisterWithConflictAfterProbing() {
+        // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
+        assumeTrue(TestUtils.shouldTestTApis())
+
+        val si = NsdServiceInfo()
+        si.serviceType = serviceType
+        si.serviceName = serviceName
+        si.network = testNetwork1.network
+        si.port = 12345 // Test won't try to connect so port does not matter
+
+        // Register service on testNetwork1
+        val registrationRecord = NsdRegistrationRecord()
+        val discoveryRecord = NsdDiscoveryRecord()
+        val registeredService = registerService(registrationRecord, si)
+        val packetReader = TapPacketReader(Handler(handlerThread.looper),
+                testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+        packetReader.startAsyncForTest()
+        handlerThread.waitForIdle(TIMEOUT_MS)
+
+        tryTest {
+            assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
+                    "No announcements sent after initial probing")
+
+            assertEquals(si.serviceName, registeredService.serviceName)
+
+            nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
+                testNetwork1.network, { it.run() }, discoveryRecord)
+            discoveryRecord.waitForServiceDiscovered(si.serviceName, serviceType)
+
+            // Send a conflicting announcement
+            val conflictingAnnouncement = buildConflictingAnnouncement()
+            packetReader.sendResponse(conflictingAnnouncement)
+
+            // Expect to see probes (RFC6762 9., service is reset to probing state)
+            assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
+                    "Probe not received within timeout after conflict")
+
+            // Send the conflicting packet again to reply to the probe
+            packetReader.sendResponse(conflictingAnnouncement)
+
+            // Note the legacy mdnsresponder would send an exit announcement here (a 0-lifetime
+            // advertisement just for the PTR record), but not the new advertiser. This probably
+            // follows RFC 6762 8.4, saying that when a record rdata changed, "In the case of shared
+            // records, a host MUST send a "goodbye" announcement with RR TTL zero [...] for the old
+            // rdata, to cause it to be deleted from peer caches, before announcing the new rdata".
+            //
+            // This should be implemented by the new advertiser, but in the case of conflicts it is
+            // not very valuable since an identical PTR record would be used by the conflicting
+            // service (except for subtypes). In that case the exit announcement may be
+            // counter-productive as it conflicts with announcements done by the conflicting
+            // service.
+
+            // Note that before sending the following ServiceRegistered callback for the renamed
+            // service, the legacy mdnsresponder-based implementation would first send a
+            // Service*Registered* callback for the original service name being *unregistered*; it
+            // should have been a ServiceUnregistered callback instead (bug in NsdService
+            // interpretation of the callback).
+            val newRegistration = registrationRecord.expectCallbackEventually<ServiceRegistered>(
+                    REGISTRATION_TIMEOUT_MS) {
+                it.serviceInfo.serviceName.startsWith(serviceName) &&
+                        it.serviceInfo.serviceName != serviceName
+            }
+
+            discoveryRecord.expectCallbackEventually<ServiceFound> {
+                it.serviceInfo.serviceName == newRegistration.serviceInfo.serviceName
+            }
+        } cleanupStep {
+            nsdManager.stopServiceDiscovery(discoveryRecord)
+            discoveryRecord.expectCallback<DiscoveryStopped>()
+        } cleanupStep {
+            nsdManager.unregisterService(registrationRecord)
+            registrationRecord.expectCallback<ServiceUnregistered>()
+        } cleanup {
+            packetReader.handler.post { packetReader.stop() }
+            handlerThread.waitForIdle(TIMEOUT_MS)
+        }
+    }
+
+    private fun buildConflictingAnnouncement(): ByteBuffer {
+        /*
+        Generated with:
+        scapy.raw(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
+                scapy.DNSRRSRV(rrname='NsdTest123456789._nmt123456789._tcp.local',
+                    rclass=0x8001, port=31234, target='conflict.local', ttl=120)
+        )).hex()
+         */
+        val mdnsPayload = HexDump.hexStringToByteArray("000084000000000100000000104e736454657" +
+                "3743132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00002" +
+                "18001000000780016000000007a0208636f6e666c696374056c6f63616c00")
+        val packetBuffer = ByteBuffer.wrap(mdnsPayload)
+        // Replace service name and types in the packet with the random ones used in the test.
+        // Test service name and types have consistent length and are always ASCII
+        val testPacketName = "NsdTest123456789".encodeToByteArray()
+        val testPacketTypePrefix = "_nmt123456789".encodeToByteArray()
+        val encodedServiceName = serviceName.encodeToByteArray()
+        val encodedTypePrefix = serviceType.split('.')[0].encodeToByteArray()
+        assertEquals(testPacketName.size, encodedServiceName.size)
+        assertEquals(testPacketTypePrefix.size, encodedTypePrefix.size)
+        packetBuffer.position(mdnsPayload.indexOf(testPacketName))
+        packetBuffer.put(encodedServiceName)
+        packetBuffer.position(mdnsPayload.indexOf(testPacketTypePrefix))
+        packetBuffer.put(encodedTypePrefix)
+
+        return buildMdnsPacket(mdnsPayload)
+    }
+
+    private fun buildMdnsPacket(mdnsPayload: ByteArray): ByteBuffer {
+        val packetBuffer = PacketBuilder.allocate(true /* hasEther */, IPPROTO_IPV6,
+                IPPROTO_UDP, mdnsPayload.size)
+        val packetBuilder = PacketBuilder(packetBuffer)
+        // Multicast ethernet address for IPv6 to ff02::fb
+        val multicastEthAddr = MacAddress.fromBytes(
+                byteArrayOf(0x33, 0x33, 0, 0, 0, 0xfb.toByte()))
+        packetBuilder.writeL2Header(
+                MacAddress.fromBytes(byteArrayOf(1, 2, 3, 4, 5, 6)) /* srcMac */,
+                multicastEthAddr,
+                ETH_P_IPV6.toShort())
+        packetBuilder.writeIpv6Header(
+                0x60000000, // version=6, traffic class=0x0, flowlabel=0x0
+                IPPROTO_UDP.toByte(),
+                64 /* hop limit */,
+                parseNumericAddress("2001:db8::123") as Inet6Address /* srcIp */,
+                multicastIpv6Addr /* dstIp */)
+        packetBuilder.writeUdpHeader(MDNS_PORT /* srcPort */, MDNS_PORT /* dstPort */)
+        packetBuffer.put(mdnsPayload)
+        return packetBuilder.finalizePacket()
+    }
+
     /**
      * Register a service and return its registration record.
      */
@@ -1169,7 +1352,65 @@
     }
 }
 
+private fun TapPacketReader.pollForMdnsPacket(
+    timeoutMs: Long = REGISTRATION_TIMEOUT_MS,
+    predicate: (TestDnsPacket) -> Boolean
+): ByteArray? {
+    val mdnsProbeFilter = IPv6UdpFilter(srcPort = MDNS_PORT, dstPort = MDNS_PORT).and {
+        val mdnsPayload = it.copyOfRange(
+                ETHER_HEADER_LEN + IPV6_HEADER_LEN + UDP_HEADER_LEN, it.size)
+        try {
+            predicate(TestDnsPacket(mdnsPayload))
+        } catch (e: DnsPacket.ParseException) {
+            false
+        }
+    }
+    return poll(timeoutMs, mdnsProbeFilter)
+}
+
+private fun TapPacketReader.pollForProbe(
+    serviceName: String,
+    serviceType: String,
+    timeoutMs: Long = REGISTRATION_TIMEOUT_MS
+): ByteArray? = pollForMdnsPacket(timeoutMs) { it.isProbeFor("$serviceName.$serviceType.local") }
+
+private fun TapPacketReader.pollForAdvertisement(
+    serviceName: String,
+    serviceType: String,
+    timeoutMs: Long = REGISTRATION_TIMEOUT_MS
+): ByteArray? = pollForMdnsPacket(timeoutMs) { it.isReplyFor("$serviceName.$serviceType.local") }
+
+private class TestDnsPacket(data: ByteArray) : DnsPacket(data) {
+    fun isProbeFor(name: String): Boolean = mRecords[QDSECTION].any {
+        it.dName == name && it.nsType == 0xff /* ANY */
+    }
+
+    fun isReplyFor(name: String): Boolean = mRecords[ANSECTION].any {
+        it.dName == name && it.nsType == 0x21 /* SRV */
+    }
+}
+
 private fun ByteArray?.utf8ToString(): String {
     if (this == null) return ""
     return String(this, StandardCharsets.UTF_8)
 }
+
+private fun ByteArray.indexOf(sub: ByteArray): Int {
+    var subIndex = 0
+    forEachIndexed { i, b ->
+        when (b) {
+            // Still matching: continue comparing with next byte
+            sub[subIndex] -> {
+                subIndex++
+                if (subIndex == sub.size) {
+                    return i - sub.size + 1
+                }
+            }
+            // Not matching next byte but matches first byte: continue comparing with 2nd byte
+            sub[0] -> subIndex = 1
+            // No matches: continue comparing from first byte
+            else -> subIndex = 0
+        }
+    }
+    return -1
+}
diff --git a/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
index 0eb5644..1b22f42 100644
--- a/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
+++ b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
@@ -95,14 +95,17 @@
                 testIface.getFileDescriptor().close();
             }
 
-            if (tunNetworkCallback != null) {
-                sCm.unregisterNetworkCallback(tunNetworkCallback);
-            }
 
             final Network testNetwork = tunNetworkCallback.currentNetwork;
             if (testNetwork != null) {
                 tnm.teardownTestNetwork(testNetwork);
             }
+            // Ensure test network being torn down.
+            tunNetworkCallback.waitForLost();
+
+            if (tunNetworkCallback != null) {
+                sCm.unregisterNetworkCallback(tunNetworkCallback);
+            }
         }
     }
 
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index c294e7b..442d69f 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -33,6 +33,8 @@
 using android::modules::sdklevel::IsAtLeastR;
 using android::modules::sdklevel::IsAtLeastS;
 using android::modules::sdklevel::IsAtLeastT;
+using android::modules::sdklevel::IsAtLeastU;
+using android::modules::sdklevel::IsAtLeastV;
 
 #define PLATFORM "/sys/fs/bpf/"
 #define TETHERING "/sys/fs/bpf/tethering/"
@@ -147,10 +149,14 @@
     // so we should only test for the removal of stuff that was mainline'd,
     // and for the presence of mainline stuff.
 
+    // Note: Q is no longer supported by mainline
+    ASSERT_TRUE(IsAtLeastR());
+
     // R can potentially run on pre-4.9 kernel non-eBPF capable devices.
     DO_EXPECT(IsAtLeastR() && !IsAtLeastS() && isAtLeastKernelVersion(4, 9, 0), PLATFORM_ONLY_IN_R);
 
     // 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);
 
@@ -163,6 +169,10 @@
     DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
 
     // U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
+    if (IsAtLeastU()) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
+
+    // V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
+    if (IsAtLeastV()) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
 
     for (const auto& file : mustExist) {
         EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";