Merge changes I8fcaa1f0,I8925fbe4,I78394645,Iadf9f060

* changes:
  FinalizePausedKeepalive in handleStopAllKeepalives.
  Add tests for pause, resume and stop keepalives.
  Ensure autoKi is not stored when keepalive stops or is not started.
  Add tests for NATT keepalives stopped internally in KeepaliveTracker.
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d2f6d6a..0326bf2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -224,6 +224,14 @@
           "exclude-annotation": "androidx.test.filters.LargeTest"
         }
       ]
+    },
+    {
+      "name": "CtsTetheringTestLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.NetworkStackModuleTest"
+        }
+      ]
     }
   ],
   "mainline-postsubmit": [
@@ -231,6 +239,15 @@
     {
       "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
       "keywords": ["sim"]
+    },
+    {
+      "name": "CtsTetheringTestLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+      "keywords": ["sim"],
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.NetworkStackModuleTest"
+        }
+      ]
     }
   ],
   "imports": [
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 23ce1a4..4478b1e 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -195,7 +195,7 @@
     certificate: "networkstack",
     manifest: "AndroidManifest.xml",
     use_embedded_native_libs: true,
-    required: ["privapp_allowlist_com.android.tethering"],
+    privapp_allowlist: ":privapp_allowlist_com.android.tethering",
     apex_available: ["com.android.tethering"],
     lint: { strict_updatability_linting: true },
 }
@@ -211,7 +211,7 @@
     certificate: "networkstack",
     manifest: "AndroidManifest.xml",
     use_embedded_native_libs: true,
-    required: ["privapp_allowlist_com.android.tethering"],
+    privapp_allowlist: ":privapp_allowlist_com.android.tethering",
     apex_available: ["com.android.tethering"],
     lint: { strict_updatability_linting: true },
 }
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 4bfd1fb..cf9b359 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -112,10 +112,7 @@
         "ServiceConnectivityResources",
         "HalfSheetUX",
     ],
-    prebuilts: [
-        "current_sdkinfo",
-        "privapp_allowlist_com.android.tethering",
-    ],
+    prebuilts: ["current_sdkinfo"],
     manifest: "manifest.json",
     key: "com.android.tethering.key",
     // Indicates that pre-installed version of this apex can be compressed.
diff --git a/Tethering/apex/permissions/Android.bp b/Tethering/apex/permissions/Android.bp
index ac9ec65..69c1aa2 100644
--- a/Tethering/apex/permissions/Android.bp
+++ b/Tethering/apex/permissions/Android.bp
@@ -19,10 +19,7 @@
     default_visibility: ["//packages/modules/Connectivity/Tethering:__subpackages__"],
 }
 
-prebuilt_etc {
+filegroup {
     name: "privapp_allowlist_com.android.tethering",
-    sub_dir: "permissions",
-    filename: "permissions.xml",
-    src: "permissions.xml",
-    installable: false,
+    srcs: ["permissions.xml"],
 }
\ No newline at end of file
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 4ee5c42..5d57aa5 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -65,6 +65,7 @@
 import com.android.net.module.util.structs.UdpHeader;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.NetworkStackModuleTest;
 import com.android.testutils.TapPacketReader;
 
 import org.junit.Rule;
@@ -860,6 +861,8 @@
             (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf
     };
 
+    // This test requires the update in NetworkStackModule(See b/269692093).
+    @NetworkStackModuleTest
     @Test
     public void testTetherZeroLengthDhcpPacket() throws Exception {
         final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
diff --git a/netd/Android.bp b/netd/Android.bp
index 473460d..4325d89 100644
--- a/netd/Android.bp
+++ b/netd/Android.bp
@@ -35,6 +35,9 @@
         "BpfHandler.cpp",
         "NetdUpdatable.cpp",
     ],
+    static_libs: [
+        "libmodules-utils-build",
+    ],
     shared_libs: [
         "libbase",
         "liblog",
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 8081d12..6409374 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -21,6 +21,7 @@
 #include <linux/bpf.h>
 
 #include <android-base/unique_fd.h>
+#include <android-modules-utils/sdk_level.h>
 #include <bpf/WaitForProgsLoaded.h>
 #include <log/log.h>
 #include <netdutils/UidConstants.h>
@@ -74,9 +75,11 @@
 }
 
 static Status initPrograms(const char* cg2_path) {
+    if (modules::sdklevel::IsAtLeastU() && !!strcmp(cg2_path, "/sys/fs/cgroup")) abort();
+
     unique_fd cg_fd(open(cg2_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
     if (cg_fd == -1) {
-        int ret = errno;
+        const int ret = errno;
         ALOGE("Failed to open the cgroup directory: %s", strerror(ret));
         return statusFromErrno(ret, "Open the cgroup directory failed");
     }
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 29a03d1..47a1022 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -73,7 +73,6 @@
 import com.android.server.connectivity.mdns.MdnsSearchOptions;
 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
 import com.android.server.connectivity.mdns.MdnsServiceInfo;
-import com.android.server.connectivity.mdns.MdnsSocketClientBase;
 import com.android.server.connectivity.mdns.MdnsSocketProvider;
 import com.android.server.connectivity.mdns.util.MdnsUtils;
 
@@ -620,6 +619,7 @@
                             final MdnsSearchOptions.Builder optionsBuilder =
                                     MdnsSearchOptions.newBuilder()
                                             .setNetwork(info.getNetwork())
+                                            .setRemoveExpiredService(true)
                                             .setIsPassiveMode(true);
                             if (typeAndSubtype.second != null) {
                                 // The parsing ensures subtype starts with an underscore.
@@ -814,6 +814,7 @@
                                     .setNetwork(info.getNetwork())
                                     .setIsPassiveMode(true)
                                     .setResolveInstanceName(info.getServiceName())
+                                    .setRemoveExpiredService(true)
                                     .build();
                             mMdnsDiscoveryManager.registerListener(
                                     resolveServiceType, listener, options);
@@ -907,6 +908,7 @@
                                 .setNetwork(info.getNetwork())
                                 .setIsPassiveMode(true)
                                 .setResolveInstanceName(info.getServiceName())
+                                .setRemoveExpiredService(true)
                                 .build();
                         mMdnsDiscoveryManager.registerListener(
                                 resolveServiceType, listener, options);
@@ -1394,8 +1396,7 @@
         mMdnsSocketClient =
                 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
         mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
-                mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"),
-                handler.getLooper());
+                mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
         handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
         mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
                 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
@@ -1453,9 +1454,8 @@
          */
         public MdnsDiscoveryManager makeMdnsDiscoveryManager(
                 @NonNull ExecutorProvider executorProvider,
-                @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog,
-                @NonNull Looper looper) {
-            return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog, looper);
+                @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) {
+            return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
         }
 
         /**
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 866ecba..84faf12 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -24,6 +24,7 @@
 import android.util.Pair;
 
 import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
 
 import java.io.IOException;
 import java.lang.ref.WeakReference;
@@ -75,6 +76,8 @@
     private final boolean sendDiscoveryQueries;
     @NonNull
     private final List<MdnsResponse> servicesToResolve;
+    @NonNull
+    private final MdnsResponseDecoder.Clock clock;
 
     EnqueueMdnsQueryCallable(
             @NonNull MdnsSocketClientBase requestSender,
@@ -85,7 +88,8 @@
             int transactionId,
             @Nullable Network network,
             boolean sendDiscoveryQueries,
-            @NonNull Collection<MdnsResponse> servicesToResolve) {
+            @NonNull Collection<MdnsResponse> servicesToResolve,
+            @NonNull MdnsResponseDecoder.Clock clock) {
         weakRequestSender = new WeakReference<>(requestSender);
         this.packetWriter = packetWriter;
         serviceTypeLabels = TextUtils.split(serviceType, "\\.");
@@ -95,6 +99,7 @@
         this.network = network;
         this.sendDiscoveryQueries = sendDiscoveryQueries;
         this.servicesToResolve = new ArrayList<>(servicesToResolve);
+        this.clock = clock;
     }
 
     // Incompatible return type for override of Callable#call().
@@ -119,22 +124,24 @@
 
             // List of (name, type) to query
             final ArrayList<Pair<String[], Integer>> missingKnownAnswerRecords = new ArrayList<>();
+            final long now = clock.elapsedRealtime();
             for (MdnsResponse response : servicesToResolve) {
-                // TODO: also send queries to renew record TTL (as per RFC6762 7.1 no need to query
-                // if remaining TTL is more than half the original one, so send the queries if half
-                // the TTL has passed).
-                if (response.isComplete()) continue;
                 final String[] serviceName = response.getServiceName();
                 if (serviceName == null) continue;
-                if (!response.hasTextRecord()) {
+                if (!response.hasTextRecord() || MdnsUtils.isRecordRenewalNeeded(
+                        response.getTextRecord(), now)) {
                     missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_TXT));
                 }
-                if (!response.hasServiceRecord()) {
+                if (!response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded(
+                        response.getServiceRecord(), now)) {
                     missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV));
                     // The hostname is not yet known, so queries for address records will be sent
                     // the next time the EnqueueMdnsQueryCallable is enqueued if the reply does not
                     // contain them. In practice, advertisers should include the address records
                     // when queried for SRV, although it's not a MUST requirement (RFC6763 12.2).
+                    // TODO: Figure out how to renew the A/AAAA record. Usually A/AAAA record will
+                    //  be included in the response to the SRV record so in high chances there is
+                    //  no need to renew them individually.
                 } else if (!response.hasInet4AddressRecord() && !response.hasInet6AddressRecord()) {
                     final String[] host = response.getServiceRecord().getServiceHost();
                     missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_A));
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
index 75c7e6c..761c477 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
@@ -94,10 +94,6 @@
         return false;
     }
 
-    public static boolean allowSearchOptionsToRemoveExpiredService() {
-        return false;
-    }
-
     public static boolean allowNetworkInterfaceIndexPropagation() {
         return true;
     }
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 06ca914..d3cbf82 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -17,7 +17,6 @@
 package com.android.server.connectivity.mdns;
 
 import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
-import static com.android.server.connectivity.mdns.util.MdnsUtils.isRunningOnHandlerThread;
 
 import android.Manifest.permission;
 import android.annotation.NonNull;
@@ -25,7 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.net.Network;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -53,6 +52,7 @@
 
     @NonNull private final PerNetworkServiceTypeClients perNetworkServiceTypeClients;
     @NonNull private final Handler handler;
+    @Nullable private final HandlerThread handlerThread;
 
     private static class PerNetworkServiceTypeClients {
         private final ArrayMap<Pair<String, Network>, MdnsServiceTypeClient> clients =
@@ -109,17 +109,23 @@
     }
 
     public MdnsDiscoveryManager(@NonNull ExecutorProvider executorProvider,
-            @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog,
-            @NonNull Looper looper) {
+            @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog) {
         this.executorProvider = executorProvider;
         this.socketClient = socketClient;
         this.sharedLog = sharedLog;
-        perNetworkServiceTypeClients = new PerNetworkServiceTypeClients();
-        handler = new Handler(looper);
+        this.perNetworkServiceTypeClients = new PerNetworkServiceTypeClients();
+        if (socketClient.getLooper() != null) {
+            this.handlerThread = null;
+            this.handler = new Handler(socketClient.getLooper());
+        } else {
+            this.handlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName());
+            this.handlerThread.start();
+            this.handler = new Handler(handlerThread.getLooper());
+        }
     }
 
     private void checkAndRunOnHandlerThread(@NonNull Runnable function) {
-        if (isRunningOnHandlerThread(handler)) {
+        if (this.handlerThread == null) {
             function.run();
         } else {
             handler.post(function);
@@ -127,6 +133,15 @@
     }
 
     /**
+     * Do the cleanup of the MdnsDiscoveryManager
+     */
+    public void shutDown() {
+        if (this.handlerThread != null) {
+            this.handlerThread.quitSafely();
+        }
+    }
+
+    /**
      * Starts (or continue) to discovery mDNS services with given {@code serviceType}, and registers
      * {@code listener} for receiving mDNS service discovery responses.
      *
@@ -236,11 +251,19 @@
     private void handleOnResponseReceived(@NonNull MdnsPacket packet, int interfaceIndex,
             @Nullable Network network) {
         for (MdnsServiceTypeClient serviceTypeClient
-                : perNetworkServiceTypeClients.getByNetwork(network)) {
+                : getMdnsServiceTypeClient(network)) {
             serviceTypeClient.processResponse(packet, interfaceIndex, network);
         }
     }
 
+    private List<MdnsServiceTypeClient> getMdnsServiceTypeClient(@Nullable Network network) {
+        if (socketClient.supportsRequestingSpecificNetworks()) {
+            return perNetworkServiceTypeClients.getByNetwork(network);
+        } else {
+            return perNetworkServiceTypeClients.getByNetwork(null);
+        }
+    }
+
     @Override
     public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
             @Nullable Network network) {
@@ -251,7 +274,7 @@
     private void handleOnFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
             @Nullable Network network) {
         for (MdnsServiceTypeClient serviceTypeClient
-                : perNetworkServiceTypeClients.getByNetwork(network)) {
+                : getMdnsServiceTypeClient(network)) {
             serviceTypeClient.onFailedToParseMdnsResponse(receivedPacketNumber, errorCode);
         }
     }
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 d6f2d06..73e4497 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -205,6 +205,16 @@
         mSocketProvider.unrequestSocket(callback);
     }
 
+    @Override
+    public Looper getLooper() {
+        return mHandler.getLooper();
+    }
+
+    @Override
+    public boolean supportsRequestingSpecificNetworks() {
+        return true;
+    }
+
     private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork) {
         final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
                 instanceof Inet6Address;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index c2c0db2..809750d 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -66,9 +66,6 @@
     private final Map<String, MdnsResponse> instanceNameToResponse = new HashMap<>();
     private final boolean removeServiceAfterTtlExpires =
             MdnsConfigs.removeServiceAfterTtlExpires();
-    private final boolean allowSearchOptionsToRemoveExpiredService =
-            MdnsConfigs.allowSearchOptionsToRemoveExpiredService();
-
     private final MdnsResponseDecoder.Clock clock;
 
     @Nullable private MdnsSearchOptions searchOptions;
@@ -374,9 +371,7 @@
         if (removeServiceAfterTtlExpires) {
             return true;
         }
-        return allowSearchOptionsToRemoveExpiredService
-                && searchOptions != null
-                && searchOptions.removeExpiredService();
+        return searchOptions != null && searchOptions.removeExpiredService();
     }
 
     @VisibleForTesting
@@ -537,7 +532,8 @@
                                 config.transactionId,
                                 config.network,
                                 sendDiscoveryQueries,
-                                servicesToResolve)
+                                servicesToResolve,
+                                clock)
                                 .call();
             } catch (RuntimeException e) {
                 sharedLog.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 1144d16..b982644 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -238,6 +238,11 @@
         socketCreationCallback.onSocketCreated(null);
     }
 
+    @Override
+    public boolean supportsRequestingSpecificNetworks() {
+        return false;
+    }
+
     private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse) {
         if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) {
             LOGGER.w("sendMdnsPacket() is called after discovery already stopped");
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
index 22cf326..e0762f9 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Network;
+import android.os.Looper;
 
 import java.io.IOException;
 import java.net.DatagramPacket;
@@ -61,6 +62,14 @@
     /*** Notify that the network is unrequested */
     default void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) { }
 
+    /*** Gets looper that used by the socket client */
+    default Looper getLooper() {
+        return null;
+    }
+
+    /** Returns whether the socket client support requesting per network */
+    boolean supportsRequestingSpecificNetworks();
+
     /*** Callback for mdns response  */
     interface Callback {
         /*** Receive a mdns response */
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 2fa1ae4..dc09bef 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -25,7 +25,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.LinkAddress;
@@ -35,6 +38,9 @@
 import android.net.NetworkRequest;
 import android.net.TetheringManager;
 import android.net.TetheringManager.TetheringEventCallback;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.ArrayMap;
@@ -51,6 +57,7 @@
 import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * The {@link MdnsSocketProvider} manages the multiple sockets for mDns.
@@ -92,6 +99,60 @@
     private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
     private boolean mMonitoringSockets = false;
     private boolean mRequestStop = false;
+    private String mWifiP2pTetherInterface = null;
+
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String newP2pIface = getWifiP2pInterface(intent);
+
+            if (!mMonitoringSockets || !hasAllNetworksRequest()) {
+                mWifiP2pTetherInterface = newP2pIface;
+                return;
+            }
+
+            // If already serving from the correct interface, nothing to do.
+            if (Objects.equals(mWifiP2pTetherInterface, newP2pIface)) return;
+
+            if (mWifiP2pTetherInterface != null) {
+                if (newP2pIface != null) {
+                    Log.wtf(TAG, "Wifi p2p interface is changed from " + mWifiP2pTetherInterface
+                            + " to " + newP2pIface + " without null broadcast");
+                }
+                // Remove the socket.
+                removeTetherInterfaceSocket(mWifiP2pTetherInterface);
+            }
+
+            // Update mWifiP2pTetherInterface
+            mWifiP2pTetherInterface = newP2pIface;
+
+            // Check whether the socket for wifi p2p interface is created or not.
+            final boolean socketAlreadyExists = mTetherInterfaceSockets.get(newP2pIface) != null;
+            if (newP2pIface != null && !socketAlreadyExists) {
+                // Create a socket for wifi p2p interface.
+                final int ifaceIndex =
+                        mDependencies.getNetworkInterfaceIndexByName(newP2pIface);
+                createSocket(LOCAL_NET, createLPForTetheredInterface(newP2pIface, ifaceIndex));
+            }
+        }
+    };
+
+    @Nullable
+    private static String getWifiP2pInterface(final Intent intent) {
+        final WifiP2pGroup group =
+                intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
+        final WifiP2pInfo p2pInfo =
+                intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
+        if (group == null || p2pInfo == null) {
+            return null;
+        }
+
+        if (!p2pInfo.groupFormed) {
+            return null;
+        } else {
+            return group.getInterface();
+        }
+    }
 
     public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
             @NonNull SharedLog sharedLog) {
@@ -138,6 +199,18 @@
 
         mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler,
                 mSharedLog.forSubComponent("NetlinkMonitor"), new NetLinkMessageProcessor());
+
+        // Register a intent receiver to listen wifi p2p interface changes.
+        // Note: The wifi p2p interface change is only notified via
+        // TetheringEventCallback#onLocalOnlyInterfacesChanged if the device is the wifi p2p group
+        // owner. In this case, MdnsSocketProvider will receive duplicate interface changes and must
+        // ignore the later notification because the socket has already been created. There is only
+        // one notification from the wifi p2p connection change intent if the device is not the wifi
+        // p2p group owner.
+        final IntentFilter intentFilter =
+                new IntentFilter(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+        mContext.registerReceiver(
+                mIntentReceiver, intentFilter, null /* broadcastPermission */, mHandler);
     }
 
     /**
@@ -376,17 +449,28 @@
         if (!hasAllNetworksRequest()) {
             // Currently, the network for tethering can not be requested, so the sockets for
             // tethering are only created if there is a request for all networks (interfaces).
-            // Therefore, this change can skip if there is no such request.
+            // Therefore, only update the interface list and skip this change if no such request.
             if (DBG) {
                 Log.d(TAG, "Ignore tether interfaces change. There is no request for all"
                         + " networks.");
             }
+            current.clear();
+            current.addAll(updated);
             return;
         }
 
         final CompareResult<String> interfaceDiff = new CompareResult<>(
                 current, updated);
         for (String name : interfaceDiff.added) {
+            // Check if a socket has been created for the interface
+            final SocketInfo socketInfo = mTetherInterfaceSockets.get(name);
+            if (socketInfo != null) {
+                if (DBG) {
+                    mSharedLog.i("Socket is existed for interface:" + name);
+                }
+                continue;
+            }
+
             int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
             createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
         }
@@ -580,6 +664,11 @@
             for (String tetheredInterface : mTetheredInterfaces) {
                 retrieveAndNotifySocketFromInterface(tetheredInterface, cb);
             }
+
+            if (mWifiP2pTetherInterface != null
+                    && !mLocalOnlyInterfaces.contains(mWifiP2pTetherInterface)) {
+                retrieveAndNotifySocketFromInterface(mWifiP2pTetherInterface, cb);
+            }
         } else {
             retrieveAndNotifySocketFromNetwork(network, cb);
         }
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
index bc94869..eb12b9a 100644
--- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -152,4 +152,15 @@
         encoder.encode(CharBuffer.wrap(originalName), out, true /* endOfInput */);
         return new String(out.array(), 0, out.position(), utf8);
     }
+
+    /**
+     * Checks if the MdnsRecord needs to be renewed or not.
+     *
+     * <p>As per RFC6762 7.1 no need to query if remaining TTL is more than half the original one,
+     * so send the queries if half the TTL has passed.
+     */
+    public static boolean isRecordRenewalNeeded(@NonNull MdnsRecord mdnsRecord, final long now) {
+        return mdnsRecord.getTtl() > 0
+                && mdnsRecord.getRemainingTTL(now) <= mdnsRecord.getTtl() / 2;
+    }
 }
\ No newline at end of file
diff --git a/service/libconnectivity/Android.bp b/service/libconnectivity/Android.bp
index 391ceac..e063af7 100644
--- a/service/libconnectivity/Android.bp
+++ b/service/libconnectivity/Android.bp
@@ -27,6 +27,7 @@
     min_sdk_version: "30",
     static_libs: [
         "connectivity_native_aidl_interface-V1-ndk",
+        "libmodules-utils-build",
     ],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/service/libconnectivity/src/connectivity_native.cpp b/service/libconnectivity/src/connectivity_native.cpp
index a476498..f39bb0a 100644
--- a/service/libconnectivity/src/connectivity_native.cpp
+++ b/service/libconnectivity/src/connectivity_native.cpp
@@ -17,6 +17,7 @@
 #include "connectivity_native.h"
 
 #include <android/binder_manager.h>
+#include <android-modules-utils/sdk_level.h>
 #include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
 
 using aidl::android::net::connectivity::aidl::IConnectivityNative;
@@ -44,6 +45,7 @@
 }
 
 int AConnectivityNative_blockPortForBind(in_port_t port) {
+    if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
     std::shared_ptr<IConnectivityNative> c = getBinder();
     if (!c) {
         return EAGAIN;
@@ -52,6 +54,7 @@
 }
 
 int AConnectivityNative_unblockPortForBind(in_port_t port) {
+    if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
     std::shared_ptr<IConnectivityNative> c = getBinder();
     if (!c) {
         return EAGAIN;
@@ -60,6 +63,7 @@
 }
 
 int AConnectivityNative_unblockAllPortsForBind() {
+    if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
     std::shared_ptr<IConnectivityNative> c = getBinder();
     if (!c) {
         return EAGAIN;
@@ -68,6 +72,7 @@
 }
 
 int AConnectivityNative_getPortsBlockedForBind(in_port_t *ports, size_t *count) {
+    if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
     std::shared_ptr<IConnectivityNative> c = getBinder();
     if (!c) {
         return EAGAIN;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 98ad861..81cc0e1 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -861,8 +861,7 @@
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
     // the world when it changes.
-    @VisibleForTesting
-    protected final ProxyTracker mProxyTracker;
+    private final ProxyTracker mProxyTracker;
 
     final private SettingsObserver mSettingsObserver;
 
@@ -1866,6 +1865,13 @@
         mHandler.sendEmptyMessage(EVENT_INGRESS_RATE_LIMIT_CHANGED);
     }
 
+    @VisibleForTesting
+    void simulateUpdateProxyInfo(@Nullable final Network network,
+            @NonNull final ProxyInfo proxyInfo) {
+        Message.obtain(mHandler, EVENT_PROXY_HAS_CHANGED,
+                new Pair<>(network, proxyInfo)).sendToTarget();
+    }
+
     private void handleAlwaysOnNetworkRequest(
             NetworkRequest networkRequest, String settingName, boolean defaultValue) {
         final boolean enable = toBool(Settings.Global.getInt(
@@ -8614,10 +8620,18 @@
     }
 
     private void maybeCloseSockets(NetworkAgentInfo nai, Set<UidRange> ranges,
-            Set<Integer> exemptUids) {
+            UidRangeParcel[] uidRangeParcels, int[] exemptUids) {
         if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
             try {
-                mDeps.destroyLiveTcpSockets(UidRange.toIntRanges(ranges), exemptUids);
+                if (mDeps.isAtLeastU()) {
+                    final Set<Integer> exemptUidSet = new ArraySet<>();
+                    for (final int uid: exemptUids) {
+                        exemptUidSet.add(uid);
+                    }
+                    mDeps.destroyLiveTcpSockets(UidRange.toIntRanges(ranges), exemptUidSet);
+                } else {
+                    mNetd.socketDestroy(uidRangeParcels, exemptUids);
+                }
             } catch (Exception e) {
                 loge("Exception in socket destroy: ", e);
             }
@@ -8625,16 +8639,16 @@
     }
 
     private void updateVpnUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
-        final Set<Integer> exemptUids = new ArraySet<>();
+        int[] exemptUids = new int[2];
         // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
         // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
         // starting a legacy VPN, and remove VPN_UID here. (b/176542831)
-        exemptUids.add(VPN_UID);
-        exemptUids.add(nai.networkCapabilities.getOwnerUid());
+        exemptUids[0] = VPN_UID;
+        exemptUids[1] = nai.networkCapabilities.getOwnerUid();
         UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
 
         // Close sockets before modifying uid ranges so that RST packets can reach to the server.
-        maybeCloseSockets(nai, uidRanges, exemptUids);
+        maybeCloseSockets(nai, uidRanges, ranges, exemptUids);
         try {
             if (add) {
                 mNetd.networkAddUidRangesParcel(new NativeUidRangeConfig(
@@ -8648,7 +8662,7 @@
                     " on netId " + nai.network.netId + ". " + e);
         }
         // Close sockets that established connection while requesting netd.
-        maybeCloseSockets(nai, uidRanges, exemptUids);
+        maybeCloseSockets(nai, uidRanges, ranges, exemptUids);
     }
 
     private boolean isProxySetOnAnyDefaultNetwork() {
diff --git a/service/src/com/android/server/connectivity/ProxyTracker.java b/service/src/com/android/server/connectivity/ProxyTracker.java
index bc0929c..b3cbb2a 100644
--- a/service/src/com/android/server/connectivity/ProxyTracker.java
+++ b/service/src/com/android/server/connectivity/ProxyTracker.java
@@ -330,7 +330,7 @@
     public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
         synchronized (mProxyLock) {
             if (Objects.equals(mDefaultProxy, proxyInfo)) return;
-            if (proxyInfo != null &&  !proxyInfo.isValid()) {
+            if (proxyInfo != null && !proxyInfo.isValid()) {
                 if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
                 return;
             }
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index 8e47235..7b5c298 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -31,7 +31,7 @@
 //   (currently, CTS 10, 11, and 12).
 java_defaults {
     name: "ConnectivityTestsLatestSdkDefaults",
-    target_sdk_version: "33",
+    target_sdk_version: "34",
 }
 
 java_library {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index a281aed..6b21dac 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -18,9 +18,7 @@
 
 import static android.app.job.JobScheduler.RESULT_SUCCESS;
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
-import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
-import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
+import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
 
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
@@ -119,10 +117,6 @@
     protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
     protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
 
-    // TODO: Update BatteryManager.BATTERY_PLUGGED_ANY as @TestApi
-    public static final int BATTERY_PLUGGED_ANY =
-            BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
-
     private static final String NETWORK_STATUS_SEPARATOR = "\\|";
     private static final int SECOND_IN_MS = 1000;
     static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 9808137..6c411cf 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -548,8 +548,9 @@
         assertTrue(resolvedService.attributes.containsKey("nullBinaryDataAttr"))
         assertNull(resolvedService.attributes["nullBinaryDataAttr"])
         assertTrue(resolvedService.attributes.containsKey("emptyBinaryDataAttr"))
-        // TODO: change the check to target SDK U when this is what the code implements
-        if (isAtLeastU()) {
+        if (isAtLeastU() || CompatChanges.isChangeEnabled(
+                ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND
+            )) {
             assertArrayEquals(byteArrayOf(), resolvedService.attributes["emptyBinaryDataAttr"])
         } else {
             assertNull(resolvedService.attributes["emptyBinaryDataAttr"])
diff --git a/tests/cts/tethering/AndroidTestTemplate.xml b/tests/cts/tethering/AndroidTestTemplate.xml
index 9b33afe..dd5b23e 100644
--- a/tests/cts/tethering/AndroidTestTemplate.xml
+++ b/tests/cts/tethering/AndroidTestTemplate.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 904e4bd..8066f22 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -11332,6 +11332,212 @@
         assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
     }
 
+    /*
+     * Note for maintainers about how PAC proxies are implemented in Android.
+     *
+     * Generally, a proxy is just a hostname and a port to which requests are sent, instead of
+     * sending them directly. Most HTTP libraries know to use this protocol, and the Java
+     * language has properties to help handling these :
+     *   https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
+     * Unfortunately these properties are very old and do not take multi-networking into account.
+     *
+     * A PAC proxy consists of a javascript file stored on a server, and the client is expected to
+     * download the file and interpret the javascript for each HTTP request to know where to direct
+     * it. The device must therefore run code (namely, a javascript interpreter) to interpret the
+     * PAC file correctly. Most HTTP libraries do not know how to do this, since they do not
+     * embark a javascript interpreter (and it would be generally unreasonable for them to do
+     * so). Some apps, notably browsers, do know how to do this, but they are the exception rather
+     * than the rule.
+     * So to support most apps, Android embarks the javascript interpreter. When a network is
+     * configured to have a PAC proxy, Android will first set the ProxyInfo object to an object
+     * that contains the PAC URL (to communicate that to apps that know how to use it), then
+     * download the PAC file and start a native process which will open a server on localhost,
+     * and uses the interpreter inside WebView to interpret the PAC file. This server takes
+     * a little bit of time to start and will listen on a random port. When the port is known,
+     * the framework receives a notification and it updates the ProxyInfo in LinkProperties
+     * as well as send a broadcast to inform apps. This new ProxyInfo still contains the PAC URL,
+     * but it also contains "localhost" as the host and the port that the server listens to as
+     * the port. This will let HTTP libraries that don't have a javascript interpreter work,
+     * because they'll send the requests to this server running on localhost on the correct port,
+     * and this server will do what is appropriate with each request according to the PAC file.
+     *
+     * Note that at the time of this writing, Android does not support starting multiple local
+     * PAC servers, though this would be possible in theory by just starting multiple instances
+     * of this process running their server on different ports. As a stopgap measure, Android
+     * keeps one local server which is always the one for the default network. If a non-default
+     * network has a PAC proxy, it will have a LinkProperties with a port of -1, which means it
+     * could still be used by apps that know how to use the PAC URL directly, but not by HTTP
+     * libs that don't know how to do that. When a network with a PAC proxy becomes the default,
+     * the local server is started. When a network without a PAC proxy becomes the default, the
+     * local server is stopped if it is running (and the LP for the old default network should
+     * be reset to have a port of -1).
+     *
+     * In principle, each network can be configured with a different proxy (typically in the
+     * network settings for a Wi-Fi network). These end up exposed in the LinkProperties of the
+     * relevant network.
+     * Android also exposes ConnectivityManager#getDefaultProxy, which is simply the proxy for
+     * the default network. This was retrofitted from a time where Android did not support multiple
+     * concurrent networks, hence the difficult architecture.
+     * Note that there is also a "global" proxy that can be set by the DPM. When this is set,
+     * it overrides the proxy settings for every single network at the same time – that is, the
+     * system behaves as if the global proxy is the configured proxy for each network.
+     *
+     * Generally there are four ways for apps to look up the proxy.
+     * - Looking up the ProxyInfo in the LinkProperties for a network.
+     * - Listening to the PROXY_CHANGED_ACTION broadcast
+     * - Calling ConnectivityManager#getDefaultProxy, or ConnectivityManager#getProxyForNetwork
+     *   which can be used to retrieve the proxy for any given network or the default network by
+     *   passing null.
+     * - Reading the standard JVM properties (http{s,}.proxy{Host,Port}). See the Java
+     *   distribution documentation for details on how these are supposed to work :
+     *    https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
+     *   In Android, these are set by ActivityThread in each process in response to the broadcast.
+     *   Many apps actually use these, and it's important they work because it's part of the
+     *   Java standard, meaning they need to be set for existing Java libs to work on Android.
+     */
+    @Test
+    public void testPacProxy() throws Exception {
+        final Uri pacUrl = Uri.parse("https://pac-url");
+        mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellAgent.connect(true);
+
+        final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        mCm.registerNetworkCallback(wifiRequest, wifiCallback);
+
+        mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiAgent.connect(true);
+        wifiCallback.expectAvailableThenValidatedCallbacks(mWiFiAgent);
+
+        final ProxyInfo initialProxyInfo = ProxyInfo.buildPacProxy(pacUrl);
+        final LinkProperties testLinkProperties = new LinkProperties();
+        testLinkProperties.setHttpProxy(initialProxyInfo);
+        mWiFiAgent.sendLinkProperties(testLinkProperties);
+        wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+
+        // At first the local PAC proxy server is unstarted (see the description of what the local
+        // server is in the comment at the top of this method). It will contain the PAC URL and a
+        // port of -1 because it is unstarted. Check that all ways of getting that proxy info
+        // returns the same object that was initially created.
+        final ProxyInfo unstartedDefaultProxyInfo = mService.getProxyForNetwork(null);
+        final ProxyInfo unstartedWifiProxyInfo = mService.getProxyForNetwork(
+                mWiFiAgent.getNetwork());
+        final LinkProperties unstartedLp =
+                mService.getLinkProperties(mWiFiAgent.getNetwork());
+
+        assertEquals(initialProxyInfo, unstartedDefaultProxyInfo);
+        assertEquals(initialProxyInfo, unstartedWifiProxyInfo);
+        assertEquals(initialProxyInfo, unstartedLp.getHttpProxy());
+
+        // Make sure the cell network is unaffected. The LP are per-network and each network has
+        // its own configuration. The default proxy and broadcast are system-wide, and the JVM
+        // properties are per-process, and therefore can have only one value at any given time,
+        // so the code sets them to the proxy of the default network (TODO : really, since the
+        // default process is per-network, the JVM properties (http.proxyHost family – see
+        // the comment at the top of the method for details about these) also should be per-network
+        // and even the broadcast contents should be but none of this is implemented). The LP are
+        // still per-network, and a process that wants to use a non-default network is supposed to
+        // look up the proxy in its LP and it has to be correct.
+        assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
+        assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+
+        // Simulate PacManager sending the notification that the local server has started
+        final ProxyInfo servingProxyInfo = new ProxyInfo(pacUrl, 2097);
+        final ExpectedBroadcast b1 = expectProxyChangeAction(servingProxyInfo);
+        mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
+//      wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+        b1.expectBroadcast();
+
+        final ProxyInfo startedDefaultProxyInfo = mService.getProxyForNetwork(null);
+        final ProxyInfo startedWifiProxyInfo = mService.getProxyForNetwork(
+                mWiFiAgent.getNetwork());
+        final LinkProperties startedLp = mService.getLinkProperties(mWiFiAgent.getNetwork());
+        // TODO : activate these tests when b/138810051 is fixed.
+//      assertEquals(servingProxyInfo, startedDefaultProxyInfo);
+//      assertEquals(servingProxyInfo, startedWifiProxyInfo);
+//      assertEquals(servingProxyInfo, startedLp.getHttpProxy());
+//      // Make sure the cell network is still unaffected
+//      assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
+//      assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+//
+//      final Uri ethPacUrl = Uri.parse("https://ethernet-pac-url");
+//      final TestableNetworkCallback ethernetCallback = new TestableNetworkCallback();
+//      final NetworkRequest ethernetRequest = new NetworkRequest.Builder()
+//              .addTransportType(TRANSPORT_ETHERNET).build();
+//      mEthernetAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+//      mCm.registerNetworkCallback(ethernetRequest, ethernetCallback);
+//      mEthernetAgent.connect(true);
+//      ethernetCallback.expectAvailableThenValidatedCallbacks(mEthernetAgent);
+//      // Wifi is no longer the default, so it should get a callback to LP changed with a PAC
+//      // proxy but a port of -1 (since the local proxy doesn't serve wifi now)
+//      wifiCallback.expect(LINK_PROPERTIES_CHANGED, mWiFiAgent,
+//              lp -> lp.getLp().getHttpProxy().getPort() == -1
+//                      && lp.getLp().getHttpProxy().isPacProxy());
+//      wifiCallback.expect(CallbackEntry.LOSING, mWiFiAgent);
+//
+//      final ProxyInfo ethProxy = ProxyInfo.buildPacProxy(ethPacUrl);
+//      final LinkProperties ethLinkProperties = new LinkProperties();
+//      ethLinkProperties.setHttpProxy(ethProxy);
+//      mEthernetAgent.sendLinkProperties(ethLinkProperties);
+//      ethernetCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mEthernetAgent);
+//      // Default network is Ethernet
+//      assertEquals(ethProxy, mService.getProxyForNetwork(null));
+//      assertEquals(ethProxy, mService.getProxyForNetwork(mEthernetAgent.getNetwork()));
+//      // Proxy info for WiFi ideally should be the old one with the old server still running,
+//      // but as the PAC code only handles one server at any given time, this can't work. Wifi
+//      // having null as a proxy also won't work (apps using WiFi will try to access the network
+//      // without proxy) but is not as bad as having the new proxy (that would send requests
+//      // over the default network).
+//      assertEquals(initialProxyInfo, mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
+//      assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+//
+//      final ProxyInfo servingEthProxy = new ProxyInfo(pacUrl, 2099);
+//      final ExpectedBroadcast b2 = expectProxyChangeAction(servingEthProxy);
+//      final ExpectedBroadcast b3 = expectProxyChangeAction(servingProxyInfo);
+//
+//      mService.simulateUpdateProxyInfo(mEthernetAgent.getNetwork(), servingEthProxy);
+//      ethernetCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mEthernetAgent);
+//      assertEquals(servingEthProxy, mService.getProxyForNetwork(null));
+//      assertEquals(servingEthProxy, mService.getProxyForNetwork(
+//              mEthernetAgent.getNetwork()));
+//      assertEquals(initialProxyInfo,
+//              mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
+//      assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+//      b2.expectBroadcast();
+//
+//      // Ethernet disconnects, back to WiFi
+//      mEthernetAgent.disconnect();
+//      ethernetCallback.expect(CallbackEntry.LOST, mEthernetAgent);
+//      wifiCallback.assertNoCallback();
+//
+//      assertEquals(initialProxyInfo, mService.getProxyForNetwork(null));
+//      assertEquals(initialProxyInfo,
+//              mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
+//      assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+//
+//      // After a while the PAC file for wifi is resolved again
+//      mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
+//      wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+//      assertEquals(servingProxyInfo, mService.getProxyForNetwork(null));
+//      assertEquals(servingProxyInfo,
+//              mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
+//      assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+//      b3.expectBroadcast();
+//
+//      // Expect a broadcast after wifi disconnected. The proxy might be default proxy or an
+//      // empty proxy built by buildDirectProxy. See {@link ProxyTracker.sendProxyBroadcast}.
+//      // Thus here expects a broadcast will be received but does not verify the content of the
+//      // proxy.
+//      final ExpectedBroadcast b4 = expectProxyChangeAction();
+//      mWiFiAgent.disconnect();
+//      b4.expectBroadcast();
+//      wifiCallback.expect(CallbackEntry.LOST, mWiFiAgent);
+//      assertNull(mService.getProxyForNetwork(null));
+        assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
+        assertNull(mService.getGlobalProxy());
+    }
+
     @Test
     public void testGetProxyForVPN() throws Exception {
         final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
@@ -12725,9 +12931,16 @@
             throws Exception {
         InOrder inOrder = inOrder(mMockNetd, mDestroySocketsWrapper);
         final Set<Integer> exemptUidSet = new ArraySet<>(List.of(exemptUid, Process.VPN_UID));
+        ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class);
 
-        inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
-                UidRange.toIntRanges(vpnRanges), exemptUidSet);
+        if (mDeps.isAtLeastU()) {
+            inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+                    UidRange.toIntRanges(vpnRanges), exemptUidSet);
+        } else {
+            inOrder.verify(mMockNetd).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+                    exemptUidCaptor.capture());
+            assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+        }
 
         if (add) {
             inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(
@@ -12739,8 +12952,14 @@
                             toUidRangeStableParcels(vpnRanges), PREFERENCE_ORDER_VPN));
         }
 
-        inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
-                UidRange.toIntRanges(vpnRanges), exemptUidSet);
+        if (mDeps.isAtLeastU()) {
+            inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+                    UidRange.toIntRanges(vpnRanges), exemptUidSet);
+        } else {
+            inOrder.verify(mMockNetd).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+                    exemptUidCaptor.capture());
+            assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+        }
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 955be12..1997215 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -179,7 +179,7 @@
                 anyInt(), anyString(), anyString(), anyString(), anyInt());
         doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
         doReturn(mDiscoveryManager).when(mDeps)
-                .makeMdnsDiscoveryManager(any(), any(), any(), any());
+                .makeMdnsDiscoveryManager(any(), any(), any());
         doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any());
         doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any());
         mService = makeService();
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 52f2712..a54a521 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -97,8 +98,10 @@
         thread = new HandlerThread("MdnsDiscoveryManagerTests");
         thread.start();
         handler = new Handler(thread.getLooper());
-        discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog,
-                    thread.getLooper()) {
+        doReturn(thread.getLooper()).when(socketClient).getLooper();
+        doReturn(true).when(socketClient).supportsRequestingSpecificNetworks();
+        discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient,
+                sharedLog) {
                     @Override
                     MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
                             @Nullable Network network) {
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index f15e8ff..da51240 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -705,34 +705,8 @@
     }
 
     @Test
-    public void processResponse_notAllowRemoveSearch_shouldNotRemove() throws Exception {
-        final String serviceInstanceName = "service-instance-1";
-        client.startSendAndReceive(
-                mockListenerOne,
-                MdnsSearchOptions.newBuilder().build());
-        Runnable firstMdnsTask = currentThreadExecutor.getAndClearSubmittedRunnable();
-
-        // Process the initial response.
-        client.processResponse(createResponse(
-                serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
-                Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
-
-        // Clear the scheduled runnable.
-        currentThreadExecutor.getAndClearLastScheduledRunnable();
-
-        // Simulate the case where the response is after TTL.
-        doReturn(TEST_ELAPSED_REALTIME + TEST_TTL + 1L).when(mockDecoderClock).elapsedRealtime();
-        firstMdnsTask.run();
-
-        // Verify removed callback was not called.
-        verifyServiceRemovedNoCallback(mockListenerOne);
-    }
-
-    @Test
-    @Ignore("MdnsConfigs is not configurable currently.")
-    public void processResponse_allowSearchOptionsToRemoveExpiredService_shouldRemove()
+    public void processResponse_searchOptionsEnableServiceRemoval_shouldRemove()
             throws Exception {
-        //MdnsConfigsFlagsImpl.allowSearchOptionsToRemoveExpiredService.override(true);
         final String serviceInstanceName = "service-instance-1";
         client =
                 new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
@@ -742,7 +716,9 @@
                         return mockPacketWriter;
                     }
                 };
-        client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
+        MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder().setRemoveExpiredService(
+                true).build();
+        client.startSendAndReceive(mockListenerOne, searchOptions);
         Runnable firstMdnsTask = currentThreadExecutor.getAndClearSubmittedRunnable();
 
         // Process the initial response.
@@ -956,15 +932,11 @@
 
         final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
                 new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
-        final List<MdnsRecord> srvTxtQuestions = srvTxtQueryPacket.questions;
 
-        final String[] serviceName = Stream.concat(Stream.of(instanceName),
-                Arrays.stream(SERVICE_TYPE_LABELS)).toArray(String[]::new);
-        assertFalse(srvTxtQuestions.stream().anyMatch(q -> q.getType() == MdnsRecord.TYPE_PTR));
-        assertTrue(srvTxtQuestions.stream().anyMatch(q ->
-                q.getType() == MdnsRecord.TYPE_SRV && Arrays.equals(q.name, serviceName)));
-        assertTrue(srvTxtQuestions.stream().anyMatch(q ->
-                q.getType() == MdnsRecord.TYPE_TXT && Arrays.equals(q.name, serviceName)));
+        final String[] serviceName = getTestServiceName(instanceName);
+        assertFalse(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_PTR));
+        assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName));
+        assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName));
 
         // Process a response with SRV+TXT
         final MdnsPacket srvTxtResponse = new MdnsPacket(
@@ -991,11 +963,8 @@
 
         final MdnsPacket addressQueryPacket = MdnsPacket.parse(
                 new MdnsPacketReader(addressQueryCaptor.getValue()));
-        final List<MdnsRecord> addressQueryQuestions = addressQueryPacket.questions;
-        assertTrue(addressQueryQuestions.stream().anyMatch(q ->
-                q.getType() == MdnsRecord.TYPE_A && Arrays.equals(q.name, hostname)));
-        assertTrue(addressQueryQuestions.stream().anyMatch(q ->
-                q.getType() == MdnsRecord.TYPE_AAAA && Arrays.equals(q.name, hostname)));
+        assertTrue(hasQuestion(addressQueryPacket, MdnsRecord.TYPE_A, hostname));
+        assertTrue(hasQuestion(addressQueryPacket, MdnsRecord.TYPE_AAAA, hostname));
 
         // Process a response with address records
         final MdnsPacket addressResponse = new MdnsPacket(
@@ -1028,6 +997,81 @@
     }
 
     @Test
+    public void testRenewTxtSrvInResolve() throws Exception {
+        client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
+                mockDecoderClock, mockNetwork, mockSharedLog);
+
+        final String instanceName = "service-instance";
+        final String[] hostname = new String[] { "testhost "};
+        final String ipV4Address = "192.0.2.0";
+        final String ipV6Address = "2001:db8::";
+
+        final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
+                .setResolveInstanceName(instanceName).build();
+
+        client.startSendAndReceive(mockListenerOne, resolveOptions);
+        InOrder inOrder = inOrder(mockListenerOne, mockSocketClient);
+
+        // Get the query for SRV/TXT
+        final ArgumentCaptor<DatagramPacket> srvTxtQueryCaptor =
+                ArgumentCaptor.forClass(DatagramPacket.class);
+        currentThreadExecutor.getAndClearLastScheduledRunnable().run();
+        // Send twice for IPv4 and IPv6
+        inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(),
+                eq(mockNetwork));
+
+        final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
+                new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
+
+        final String[] serviceName = getTestServiceName(instanceName);
+        assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName));
+        assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName));
+
+        // Process a response with all records
+        final MdnsPacket srvTxtResponse = new MdnsPacket(
+                0 /* flags */,
+                Collections.emptyList() /* questions */,
+                List.of(
+                        new MdnsServiceRecord(serviceName, TEST_ELAPSED_REALTIME,
+                                true /* cacheFlush */, TEST_TTL, 0 /* servicePriority */,
+                                0 /* serviceWeight */, 1234 /* servicePort */, hostname),
+                        new MdnsTextRecord(serviceName, TEST_ELAPSED_REALTIME,
+                                true /* cacheFlush */, TEST_TTL,
+                                Collections.emptyList() /* entries */),
+                        new MdnsInetAddressRecord(hostname, TEST_ELAPSED_REALTIME,
+                                true /* cacheFlush */, TEST_TTL,
+                                InetAddresses.parseNumericAddress(ipV4Address)),
+                        new MdnsInetAddressRecord(hostname, TEST_ELAPSED_REALTIME,
+                                true /* cacheFlush */, TEST_TTL,
+                                InetAddresses.parseNumericAddress(ipV6Address))),
+                Collections.emptyList() /* authorityRecords */,
+                Collections.emptyList() /* additionalRecords */);
+        client.processResponse(srvTxtResponse, INTERFACE_INDEX, mockNetwork);
+        inOrder.verify(mockListenerOne).onServiceNameDiscovered(any());
+        inOrder.verify(mockListenerOne).onServiceFound(any());
+
+        // Expect no query on the next run
+        currentThreadExecutor.getAndClearLastScheduledRunnable().run();
+        inOrder.verifyNoMoreInteractions();
+
+        // Advance time so 75% of TTL passes and re-execute
+        doReturn(TEST_ELAPSED_REALTIME + (long) (TEST_TTL * 0.75))
+                .when(mockDecoderClock).elapsedRealtime();
+        currentThreadExecutor.getAndClearLastScheduledRunnable().run();
+
+        // Expect a renewal query
+        final ArgumentCaptor<DatagramPacket> renewalQueryCaptor =
+                ArgumentCaptor.forClass(DatagramPacket.class);
+        // Second and later sends are sent as "expect multicast response" queries
+        inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(renewalQueryCaptor.capture(),
+                eq(mockNetwork));
+        final MdnsPacket renewalPacket = MdnsPacket.parse(
+                new MdnsPacketReader(renewalQueryCaptor.getValue()));
+        assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_SRV, serviceName));
+        assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_TXT, serviceName));
+    }
+
+    @Test
     public void testProcessResponse_ResolveExcludesOtherServices() {
         client = new MdnsServiceTypeClient(
                 SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
@@ -1270,6 +1314,20 @@
         }
     }
 
+    private static String[] getTestServiceName(String instanceName) {
+        return Stream.concat(Stream.of(instanceName),
+                Arrays.stream(SERVICE_TYPE_LABELS)).toArray(String[]::new);
+    }
+
+    private static boolean hasQuestion(MdnsPacket packet, int type) {
+        return hasQuestion(packet, type, null);
+    }
+
+    private static boolean hasQuestion(MdnsPacket packet, int type, @Nullable String[] name) {
+        return packet.questions.stream().anyMatch(q -> q.getType() == type
+                && (name == null || Arrays.equals(q.name, name)));
+    }
+
     // A fake ScheduledExecutorService that keeps tracking the last scheduled Runnable and its delay
     // time.
     private class FakeExecutor extends ScheduledThreadPoolExecutor {
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index 4b87556..4f56857 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doCallRealMethod;
@@ -40,7 +41,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.LinkAddress;
@@ -49,6 +52,9 @@
 import android.net.NetworkCapabilities;
 import android.net.TetheringManager;
 import android.net.TetheringManager.TetheringEventCallback;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -88,6 +94,7 @@
     private static final String TAG = MdnsSocketProviderTest.class.getSimpleName();
     private static final String TEST_IFACE_NAME = "test";
     private static final String LOCAL_ONLY_IFACE_NAME = "local_only";
+    private static final String WIFI_P2P_IFACE_NAME = "p2p_wifi";
     private static final String TETHERED_IFACE_NAME = "tethered";
     private static final int TETHERED_IFACE_IDX = 32;
     private static final long DEFAULT_TIMEOUT = 2000L;
@@ -136,11 +143,15 @@
         doReturn(true).when(mTetheredIfaceWrapper).supportsMulticast();
         doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
                 .getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
+        doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
+                .getNetworkInterfaceByName(WIFI_P2P_IFACE_NAME);
         doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
         doReturn(mock(MdnsInterfaceSocket.class))
                 .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
         doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
                 TETHERED_IFACE_NAME);
+        doReturn(789).when(mDeps).getNetworkInterfaceIndexByName(
+                WIFI_P2P_IFACE_NAME);
         final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
         thread.start();
         mHandler = new Handler(thread.getLooper());
@@ -157,22 +168,41 @@
         mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog);
     }
 
+    private void runOnHandler(Runnable r) {
+        mHandler.post(r);
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+    }
+
+    private BroadcastReceiver expectWifiP2PChangeBroadcastReceiver() {
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(),
+                argThat(filter -> filter.hasAction(
+                        WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)),
+                any(), any());
+        final BroadcastReceiver originalReceiver = receiverCaptor.getValue();
+        return new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                runOnHandler(() -> originalReceiver.onReceive(context, intent));
+            }
+        };
+    }
+
     private void startMonitoringSockets() {
         final ArgumentCaptor<NetworkCallback> nwCallbackCaptor =
                 ArgumentCaptor.forClass(NetworkCallback.class);
         final ArgumentCaptor<TetheringEventCallback> teCallbackCaptor =
                 ArgumentCaptor.forClass(TetheringEventCallback.class);
 
-        mHandler.post(mSocketProvider::startMonitoringSockets);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::startMonitoringSockets);
         verify(mCm).registerNetworkCallback(any(), nwCallbackCaptor.capture(), any());
         verify(mTm).registerTetheringEventCallback(any(), teCallbackCaptor.capture());
 
         mNetworkCallback = nwCallbackCaptor.getValue();
         mTetheringEventCallback = teCallbackCaptor.getValue();
 
-        mHandler.post(mSocketProvider::startNetLinkMonitor);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::startNetLinkMonitor);
     }
 
     private static class TestNetlinkMonitor extends SocketNetlinkMonitor {
@@ -281,9 +311,8 @@
         testLp.setInterfaceName(TEST_IFACE_NAME);
         testLp.setLinkAddresses(List.of(LINKADDRV4));
         final NetworkCapabilities testNc = makeCapabilities(transports);
-        mHandler.post(() -> mNetworkCallback.onCapabilitiesChanged(TEST_NETWORK, testNc));
-        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mNetworkCallback.onCapabilitiesChanged(TEST_NETWORK, testNc));
+        runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
     }
 
     @Test
@@ -291,62 +320,53 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback1 = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
         testCallback1.expectedNoCallback();
 
         postNetworkAvailable(TRANSPORT_WIFI);
         testCallback1.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
 
         final TestSocketCallback testCallback2 = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
         testCallback1.expectedNoCallback();
         testCallback2.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
 
         final TestSocketCallback testCallback3 = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         testCallback3.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
 
-        mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
                 List.of(LOCAL_ONLY_IFACE_NAME)));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
 
-        mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+        runOnHandler(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
                 List.of(TETHERED_IFACE_NAME)));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         verify(mTetheredIfaceWrapper).getNetworkInterface();
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
 
-        mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback1));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback1));
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         testCallback3.expectedNoCallback();
 
-        mHandler.post(() -> mNetworkCallback.onLost(TEST_NETWORK));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mNetworkCallback.onLost(TEST_NETWORK));
         testCallback1.expectedNoCallback();
         testCallback2.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
         testCallback3.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
 
-        mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
 
-        mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback3));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback3));
         testCallback1.expectedNoCallback();
         testCallback2.expectedNoCallback();
         // There was still a tethered interface, but no callback should be sent once unregistered
@@ -376,8 +396,7 @@
     public void testDownstreamNetworkAddressUpdateFromNetlink() {
         startMonitoringSockets();
         final TestSocketCallback testCallbackAll = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
 
         // Address add message arrived before the interface is created.
         RtNetlinkAddressMessage addIpv4AddrMsg = createNetworkAddressUpdateNetLink(
@@ -385,15 +404,13 @@
                 LINKADDRV4,
                 TETHERED_IFACE_IDX,
                 0 /* flags */);
-        mHandler.post(
+        runOnHandler(
                 () -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv4AddrMsg,
                         0 /* whenMs */));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
 
         // Interface is created.
-        mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+        runOnHandler(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
                 List.of(TETHERED_IFACE_NAME)));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         verify(mTetheredIfaceWrapper).getNetworkInterface();
         testCallbackAll.expectedSocketCreatedForNetwork(null /* network */, List.of(LINKADDRV4));
 
@@ -403,10 +420,9 @@
                 LINKADDRV4,
                 TETHERED_IFACE_IDX,
                 0 /* flags */);
-        mHandler.post(
+        runOnHandler(
                 () -> mTestSocketNetLinkMonitor.processNetlinkMessage(removeIpv4AddrMsg,
                         0 /* whenMs */));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of());
 
         // New address added.
@@ -415,9 +431,8 @@
                 LINKADDRV6,
                 TETHERED_IFACE_IDX,
                 0 /* flags */);
-        mHandler.post(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
+        runOnHandler(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
                 0 /* whenMs */));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of(LINKADDRV6));
 
         // Address updated
@@ -426,10 +441,9 @@
                 LINKADDRV6,
                 TETHERED_IFACE_IDX,
                 1 /* flags */);
-        mHandler.post(
+        runOnHandler(
                 () -> mTestSocketNetLinkMonitor.processNetlinkMessage(updateIpv6AddrMsg,
                         0 /* whenMs */));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
         testCallbackAll.expectedAddressesChangedForNetwork(null /* network */,
                 List.of(LINKADDRV6_FLAG_CHANGE));
     }
@@ -439,8 +453,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
         testCallback.expectedNoCallback();
 
         postNetworkAvailable(TRANSPORT_WIFI);
@@ -449,8 +462,7 @@
         final LinkProperties newTestLp = new LinkProperties();
         newTestLp.setInterfaceName(TEST_IFACE_NAME);
         newTestLp.setLinkAddresses(List.of(LINKADDRV4, LINKADDRV6));
-        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
         testCallback.expectedAddressesChangedForNetwork(
                 TEST_NETWORK, List.of(LINKADDRV4, LINKADDRV6));
     }
@@ -458,8 +470,7 @@
     @Test
     public void testStartAndStopMonitoringSockets() {
         // Stop monitoring sockets before start. Should not unregister any network callback.
-        mHandler.post(mSocketProvider::requestStopWhenInactive);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::requestStopWhenInactive);
         verify(mCm, never()).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, never()).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
 
@@ -467,39 +478,32 @@
         startMonitoringSockets();
         // Request a socket then unrequest it. Expect no network callback unregistration.
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
         testCallback.expectedNoCallback();
-        mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback));
         verify(mCm, never()).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, never()).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
         // Request stop and it should unregister network callback immediately because there is no
         // socket request.
-        mHandler.post(mSocketProvider::requestStopWhenInactive);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::requestStopWhenInactive);
         verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, times(1)).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
 
         // Start sockets monitoring and request a socket again.
-        mHandler.post(mSocketProvider::startMonitoringSockets);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::startMonitoringSockets);
         verify(mCm, times(2)).registerNetworkCallback(any(), any(NetworkCallback.class), any());
         verify(mTm, times(2)).registerTetheringEventCallback(
                 any(), any(TetheringEventCallback.class));
         final TestSocketCallback testCallback2 = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
         testCallback2.expectedNoCallback();
         // Try to stop monitoring sockets but should be ignored and wait until all socket are
         // unrequested.
-        mHandler.post(mSocketProvider::requestStopWhenInactive);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::requestStopWhenInactive);
         verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, times(1)).unregisterTetheringEventCallback(any());
         // Unrequest the socket then network callbacks should be unregistered.
-        mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback2));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback2));
         verify(mCm, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, times(2)).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
     }
@@ -510,24 +514,20 @@
 
         // Request a socket with null network.
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
         testCallback.expectedNoCallback();
 
         // Notify a LinkPropertiesChanged with TEST_NETWORK.
         final LinkProperties testLp = new LinkProperties();
         testLp.setInterfaceName(TEST_IFACE_NAME);
         testLp.setLinkAddresses(List.of(LINKADDRV4));
-        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
         verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
         testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
 
         // Try to stop monitoring and unrequest the socket.
-        mHandler.post(mSocketProvider::requestStopWhenInactive);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
-        mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::requestStopWhenInactive);
+        runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback));
         // No callback sent when unregistered
         testCallback.expectedNoCallback();
         verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -535,13 +535,11 @@
 
         // Start sockets monitoring and request a socket again. Expected no socket created callback
         // because all saved LinkProperties has been cleared.
-        mHandler.post(mSocketProvider::startMonitoringSockets);
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(mSocketProvider::startMonitoringSockets);
         verify(mCm, times(2)).registerNetworkCallback(any(), any(NetworkCallback.class), any());
         verify(mTm, times(2)).registerTetheringEventCallback(
                 any(), any(TetheringEventCallback.class));
-        mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
         testCallback.expectedNoCallback();
 
         // Notify a LinkPropertiesChanged with another network.
@@ -550,8 +548,7 @@
         final Network otherNetwork = new Network(456);
         otherLp.setInterfaceName("test2");
         otherLp.setLinkAddresses(List.of(otherAddress));
-        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(otherNetwork, otherLp));
-        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(otherNetwork, otherLp));
         verify(mTestNetworkIfaceWrapper, times(2)).getNetworkInterface();
         testCallback.expectedSocketCreatedForNetwork(otherNetwork, List.of(otherAddress));
     }
@@ -561,7 +558,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_CELLULAR);
         testCallback.expectedNoCallback();
@@ -573,7 +570,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_BLUETOOTH);
         testCallback.expectedNoCallback();
@@ -585,7 +582,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_BLUETOOTH);
         testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
@@ -597,7 +594,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_BLUETOOTH);
         testCallback.expectedNoCallback();
@@ -611,7 +608,7 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_VPN, TRANSPORT_WIFI);
         testCallback.expectedNoCallback();
@@ -623,9 +620,146 @@
         startMonitoringSockets();
 
         final TestSocketCallback testCallback = new TestSocketCallback();
-        mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+        runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
 
         postNetworkAvailable(TRANSPORT_WIFI);
         testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
     }
+
+    private Intent buildWifiP2PConnectionChangedIntent(boolean groupFormed) {
+        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+        final WifiP2pInfo formedInfo = new WifiP2pInfo();
+        formedInfo.groupFormed = groupFormed;
+        final WifiP2pGroup group;
+        if (groupFormed) {
+            group = mock(WifiP2pGroup.class);
+            doReturn(WIFI_P2P_IFACE_NAME).when(group).getInterface();
+        } else {
+            group = null;
+        }
+        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, formedInfo);
+        intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+        return intent;
+    }
+
+    @Test
+    public void testWifiP2PInterfaceChange() {
+        final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+        startMonitoringSockets();
+
+        // Request a socket with null network.
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+
+        // Wifi p2p is connected and the interface is up. Get a wifi p2p change intent then expect
+        // a socket creation.
+        final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+        receiver.onReceive(mContext, formedIntent);
+        verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+        // Wifi p2p is disconnected. Get a wifi p2p change intent then expect the socket destroy.
+        final Intent unformedIntent = buildWifiP2PConnectionChangedIntent(false /* groupFormed */);
+        receiver.onReceive(mContext, unformedIntent);
+        testCallback.expectedInterfaceDestroyedForNetwork(null /* network */);
+    }
+
+    @Test
+    public void testWifiP2PInterfaceChangeBeforeStartMonitoringSockets() {
+        final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+
+        // Get a wifi p2p change intent before start monitoring sockets.
+        final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+        receiver.onReceive(mContext, formedIntent);
+
+        // Start monitoring sockets and request a socket with null network.
+        startMonitoringSockets();
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+        verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+    }
+
+    @Test
+    public void testWifiP2PInterfaceChangeBeforeGetAllNetworksRequest() {
+        final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+        startMonitoringSockets();
+
+        // Get a wifi p2p change intent before request socket for all networks.
+        final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+        receiver.onReceive(mContext, formedIntent);
+
+        // Request a socket with null network.
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+        verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+    }
+
+    @Test
+    public void testNoDuplicatedSocketCreation() {
+        final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+        startMonitoringSockets();
+
+        // Request a socket with null network.
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
+        testCallback.expectedNoCallback();
+
+        // Receive an interface added change for the wifi p2p interface. Expect a socket creation
+        // callback.
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+                List.of(WIFI_P2P_IFACE_NAME)));
+        verify(mLocalOnlyIfaceWrapper, times(1)).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+        // Receive a wifi p2p connected intent. Expect no callback because the socket is created.
+        final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+        receiver.onReceive(mContext, formedIntent);
+        testCallback.expectedNoCallback();
+
+        // Request other socket with null network. Should receive socket created callback once.
+        final TestSocketCallback testCallback2 = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback2));
+        testCallback2.expectedSocketCreatedForNetwork(null /* network */, List.of());
+        testCallback2.expectedNoCallback();
+
+        // Receive a wifi p2p disconnected intent. Expect a socket destroy callback.
+        final Intent unformedIntent = buildWifiP2PConnectionChangedIntent(false /* groupFormed */);
+        receiver.onReceive(mContext, unformedIntent);
+        testCallback.expectedInterfaceDestroyedForNetwork(null /* network */);
+
+        // Receive an interface removed change for the wifi p2p interface. Expect no callback
+        // because the socket is destroyed.
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
+        testCallback.expectedNoCallback();
+
+        // Receive a wifi p2p connected intent again. Expect a socket creation callback.
+        receiver.onReceive(mContext, formedIntent);
+        verify(mLocalOnlyIfaceWrapper, times(2)).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+        // Receive an interface added change for the wifi p2p interface again. Expect no callback
+        // because the socket is created.
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+                List.of(WIFI_P2P_IFACE_NAME)));
+        testCallback.expectedNoCallback();
+    }
+
+    @Test
+    public void testTetherInterfacesChangedBeforeGetAllNetworksRequest() {
+        startMonitoringSockets();
+
+        // Receive an interface added change for the wifi p2p interface. Expect a socket creation
+        // callback.
+        runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+                List.of(TETHERED_IFACE_NAME)));
+        verify(mTetheredIfaceWrapper, never()).getNetworkInterface();
+
+        // Request a socket with null network.
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+        verify(mTetheredIfaceWrapper).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+    }
 }