Update NSD tests without AsyncChannel

Update the tests to use the newer NsdManager based on Binder interfaces
instead of AsyncChannel.

Bug: 190249673
Test: atest NsdManagerTest NsdServiceTest
Change-Id: I0991b598331e335a0bc211f010da7f034fb2441b
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index de77d23..30b8fcd 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -20,38 +20,32 @@
 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.util.AsyncChannel;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
-import com.android.testutils.HandlerUtils;
+import com.android.testutils.ExceptionUtils;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -67,9 +61,10 @@
 
     @Mock Context mContext;
     @Mock INsdManager mService;
-    MockServiceHandler mServiceHandler;
+    @Mock INsdServiceConnector mServiceConn;
 
     NsdManager mManager;
+    INsdManagerCallback mCallback;
 
     long mTimeoutMs = 200; // non-final so that tests can adjust the value.
 
@@ -77,91 +72,85 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mServiceHandler = spy(MockServiceHandler.create(mContext));
-        doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
-        mServiceHandler.chan.disconnect();
-        mServiceHandler.stop();
-        if (mManager != null) {
-            mManager.disconnect();
-        }
+        doReturn(mServiceConn).when(mService).connect(any());
+        mManager = new NsdManager(mContext, mService);
+        final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
+                INsdManagerCallback.class);
+        verify(mService).connect(cbCaptor.capture());
+        mCallback = cbCaptor.getValue();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testResolveServiceS() {
-        mManager = makeNsdManagerS();
+    public void testResolveServiceS() throws Exception {
+        verify(mServiceConn, never()).startDaemon();
         doTestResolveService();
     }
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testResolveServicePreS() {
-        mManager = makeNsdManagerPreS();
+    public void testResolveServicePreS() throws Exception {
+        verify(mServiceConn).startDaemon();
         doTestResolveService();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testDiscoverServiceS() {
-        mManager = makeNsdManagerS();
+    public void testDiscoverServiceS() throws Exception {
+        verify(mServiceConn, never()).startDaemon();
         doTestDiscoverService();
     }
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testDiscoverServicePreS() {
-        mManager = makeNsdManagerPreS();
+    public void testDiscoverServicePreS() throws Exception {
+        verify(mServiceConn).startDaemon();
         doTestDiscoverService();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testParallelResolveServiceS() {
-        mManager = makeNsdManagerS();
+    public void testParallelResolveServiceS() throws Exception {
+        verify(mServiceConn, never()).startDaemon();
         doTestParallelResolveService();
     }
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testParallelResolveServicePreS() {
-        mManager = makeNsdManagerPreS();
+    public void testParallelResolveServicePreS() throws Exception {
+        verify(mServiceConn).startDaemon();
         doTestParallelResolveService();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testInvalidCallsS() {
-        mManager = makeNsdManagerS();
+    public void testInvalidCallsS() throws Exception {
+        verify(mServiceConn, never()).startDaemon();
         doTestInvalidCalls();
     }
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testInvalidCallsPreS() {
-        mManager = makeNsdManagerPreS();
+    public void testInvalidCallsPreS() throws Exception {
+        verify(mServiceConn).startDaemon();
         doTestInvalidCalls();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testRegisterServiceS() {
-        mManager = makeNsdManagerS();
+    public void testRegisterServiceS() throws Exception {
+        verify(mServiceConn, never()).startDaemon();
         doTestRegisterService();
     }
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testRegisterServicePreS() {
-        mManager = makeNsdManagerPreS();
+    public void testRegisterServicePreS() throws Exception {
+        verify(mServiceConn).startDaemon();
         doTestRegisterService();
     }
 
-    public void doTestResolveService() {
+    private void doTestResolveService() throws Exception {
         NsdManager manager = mManager;
 
         NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -169,18 +158,19 @@
         NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
 
         manager.resolveService(request, listener);
-        int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+        int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
         int err = 33;
-        sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null);
+        mCallback.onResolveServiceFailed(key1, err);
         verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
 
         manager.resolveService(request, listener);
-        int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
-        sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
+        int key2 = getRequestKey(req ->
+                verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
+        mCallback.onResolveServiceSucceeded(key2, reply);
         verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
     }
 
-    public void doTestParallelResolveService() {
+    private void doTestParallelResolveService() throws Exception {
         NsdManager manager = mManager;
 
         NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -190,19 +180,20 @@
         NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
 
         manager.resolveService(request, listener1);
-        int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+        int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
 
         manager.resolveService(request, listener2);
-        int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+        int key2 = getRequestKey(req ->
+                verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
 
-        sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
-        sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply);
+        mCallback.onResolveServiceSucceeded(key2, reply);
+        mCallback.onResolveServiceSucceeded(key1, reply);
 
         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
         verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
     }
 
-    public void doTestRegisterService() {
+    private void doTestRegisterService() throws Exception {
         NsdManager manager = mManager;
 
         NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
@@ -214,40 +205,43 @@
 
         // Register two services
         manager.registerService(request1, PROTOCOL, listener1);
-        int key1 = verifyRequest(NsdManager.REGISTER_SERVICE);
+        int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
 
         manager.registerService(request2, PROTOCOL, listener2);
-        int key2 = verifyRequest(NsdManager.REGISTER_SERVICE);
+        int key2 = getRequestKey(req ->
+                verify(mServiceConn, times(2)).registerService(req.capture(), any()));
 
         // First reques fails, second request succeeds
-        sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2);
+        mCallback.onRegisterServiceSucceeded(key2, request2);
         verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
 
         int err = 1;
-        sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1);
+        mCallback.onRegisterServiceFailed(key1, err);
         verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
 
         // Client retries first request, it succeeds
         manager.registerService(request1, PROTOCOL, listener1);
-        int key3 = verifyRequest(NsdManager.REGISTER_SERVICE);
+        int key3 = getRequestKey(req ->
+                verify(mServiceConn, times(3)).registerService(req.capture(), any()));
 
-        sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1);
+        mCallback.onRegisterServiceSucceeded(key3, request1);
         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
 
         // First request is unregistered, it succeeds
         manager.unregisterService(listener1);
-        int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+        int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture()));
         assertEquals(key3, key3again);
 
-        sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null);
+        mCallback.onUnregisterServiceSucceeded(key3again);
         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
 
         // Second request is unregistered, it fails
         manager.unregisterService(listener2);
-        int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+        int key2again = getRequestKey(req ->
+                verify(mServiceConn, times(2)).unregisterService(req.capture()));
         assertEquals(key2, key2again);
 
-        sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null);
+        mCallback.onUnregisterServiceFailed(key2again, err);
         verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
 
         // TODO: do not unregister listener until service is unregistered
@@ -260,7 +254,7 @@
         //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
     }
 
-    public void doTestDiscoverService() {
+    private void doTestDiscoverService() throws Exception {
         NsdManager manager = mManager;
 
         NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
@@ -271,69 +265,73 @@
 
         // Client registers for discovery, request fails
         manager.discoverServices("a_type", PROTOCOL, listener);
-        int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+        int key1 = getRequestKey(req ->
+                verify(mServiceConn).discoverServices(req.capture(), any()));
 
         int err = 1;
-        sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null);
+        mCallback.onDiscoverServicesFailed(key1, err);
         verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
 
         // Client retries, request succeeds
         manager.discoverServices("a_type", PROTOCOL, listener);
-        int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+        int key2 = getRequestKey(req ->
+                verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
 
-        sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1);
+        mCallback.onDiscoverServicesStarted(key2, reply1);
         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
 
 
         // mdns notifies about services
-        sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1);
+        mCallback.onServiceFound(key2, reply1);
         verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
 
-        sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2);
+        mCallback.onServiceFound(key2, reply2);
         verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
 
-        sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2);
+        mCallback.onServiceLost(key2, reply2);
         verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
 
 
         // Client unregisters its listener
         manager.stopServiceDiscovery(listener);
-        int key2again = verifyRequest(NsdManager.STOP_DISCOVERY);
+        int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture()));
         assertEquals(key2, key2again);
 
         // TODO: unregister listener immediately and stop notifying it about services
         // Notifications are still passed to the client's listener
-        sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1);
+        mCallback.onServiceLost(key2, reply1);
         verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
 
         // Client is notified of complete unregistration
-        sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type");
+        mCallback.onStopDiscoverySucceeded(key2again);
         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
 
         // Notifications are not passed to the client anymore
-        sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3);
+        mCallback.onServiceFound(key2, reply3);
         verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
 
 
         // Client registers for service discovery
         reset(listener);
         manager.discoverServices("a_type", PROTOCOL, listener);
-        int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+        int key3 = getRequestKey(req ->
+                verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
 
-        sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1);
+        mCallback.onDiscoverServicesStarted(key3, reply1);
         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
 
         // Client unregisters immediately, it fails
         manager.stopServiceDiscovery(listener);
-        int key3again = verifyRequest(NsdManager.STOP_DISCOVERY);
+        int key3again = getRequestKey(req ->
+                verify(mServiceConn, times(2)).stopDiscovery(req.capture()));
         assertEquals(key3, key3again);
 
         err = 2;
-        sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type");
+        mCallback.onStopDiscoveryFailed(key3again, err);
         verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
 
         // New notifications are not passed to the client anymore
-        sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1);
+        mCallback.onServiceFound(key3, reply1);
         verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
     }
 
@@ -398,77 +396,10 @@
         }
     }
 
-    NsdManager makeNsdManagerS() {
-        // Expect we'll get 2 AsyncChannel related msgs.
-        return makeManager(2);
-    }
-
-    NsdManager makeNsdManagerPreS() {
-        // Expect we'll get 3 msgs. 2 AsyncChannel related msgs + 1 additional daemon startup msg.
-        return makeManager(3);
-    }
-
-    NsdManager makeManager(int expectedMsgCount) {
-        NsdManager manager = new NsdManager(mContext, mService);
-        // Acknowledge first two messages connecting the AsyncChannel.
-        verify(mServiceHandler, timeout(mTimeoutMs).times(expectedMsgCount)).handleMessage(any());
-
-        reset(mServiceHandler);
-        assertNotNull(mServiceHandler.chan);
-        return manager;
-    }
-
-    int verifyRequest(int expectedMessageType) {
-        HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
-        verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
-        reset(mServiceHandler);
-        Message received = mServiceHandler.getLastMessage();
-        assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
-        return received.arg2;
-    }
-
-    void sendResponse(int replyType, int arg, int key, Object obj) {
-        mServiceHandler.chan.sendMessage(replyType, arg, key, obj);
-    }
-
-    // Implements the server side of AsyncChannel connection protocol
-    public static class MockServiceHandler extends Handler {
-        public final Context context;
-        public AsyncChannel chan;
-        public Message lastMessage;
-
-        MockServiceHandler(Looper l, Context c) {
-            super(l);
-            context = c;
-        }
-
-        synchronized Message getLastMessage() {
-            return lastMessage;
-        }
-
-        synchronized void setLastMessage(Message msg) {
-            lastMessage = obtainMessage();
-            lastMessage.copyFrom(msg);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            setLastMessage(msg);
-            if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
-                chan = new AsyncChannel();
-                chan.connect(context, this, msg.replyTo);
-                chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
-            }
-        }
-
-        void stop() {
-            getLooper().quitSafely();
-        }
-
-        static MockServiceHandler create(Context context) {
-            HandlerThread t = new HandlerThread("mock-service-handler");
-            t.start();
-            return new MockServiceHandler(t.getLooper(), context);
-        }
+    int getRequestKey(ExceptionUtils.ThrowingConsumer<ArgumentCaptor<Integer>> verifier)
+            throws Exception {
+        final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+        verifier.accept(captor);
+        return captor.getValue();
     }
 }
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 4d2970a..4172553 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -20,6 +20,7 @@
 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -33,14 +34,19 @@
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
 import android.net.nsd.NsdManager;
 import android.net.nsd.NsdServiceInfo;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.server.NsdService.DaemonConnection;
@@ -56,11 +62,15 @@
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
+import java.util.LinkedList;
+import java.util.Queue;
+
 // TODOs:
 //  - test client can send requests and receive replies
 //  - test NSD_ON ENABLE/DISABLED listening
@@ -73,6 +83,11 @@
     private static final long CLEANUP_DELAY_MS = 500;
     private static final long TIMEOUT_MS = 500;
 
+    // Records INsdManagerCallback created when NsdService#connect is called.
+    // Only accessed on the test thread, since NsdService#connect is called by the NsdManager
+    // constructor called on the test thread.
+    private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>();
+
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
     @Mock Context mContext;
@@ -83,6 +98,16 @@
     HandlerThread mThread;
     TestHandler mHandler;
 
+    private static class LinkToDeathRecorder extends Binder {
+        IBinder.DeathRecipient mDr;
+
+        @Override
+        public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+            super.linkToDeath(recipient, flags);
+            mDr = recipient;
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -103,26 +128,30 @@
 
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testPreSClients() {
+    public void testPreSClients() throws Exception {
         when(mSettings.isEnabled()).thenReturn(true);
         NsdService service = makeService();
 
         // Pre S client connected, the daemon should be started.
-        NsdManager client1 = connectClient(service);
+        connectClient(service);
         waitForIdle();
+        final INsdManagerCallback cb1 = getCallback();
+        final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
         verify(mDaemon, times(1)).maybeStart();
         verifyDaemonCommands("start-service");
 
-        NsdManager client2 = connectClient(service);
+        connectClient(service);
         waitForIdle();
+        final INsdManagerCallback cb2 = getCallback();
+        final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
         verify(mDaemon, times(1)).maybeStart();
 
-        client1.disconnect();
+        deathRecipient1.binderDied();
         // Still 1 client remains, daemon shouldn't be stopped.
         waitForIdle();
         verify(mDaemon, never()).maybeStop();
 
-        client2.disconnect();
+        deathRecipient2.binderDied();
         // All clients are disconnected, the daemon should be stopped.
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
         verifyDaemonCommands("stop-service");
@@ -130,43 +159,51 @@
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testNoDaemonStartedWhenClientsConnect() {
+    public void testNoDaemonStartedWhenClientsConnect() throws Exception {
         when(mSettings.isEnabled()).thenReturn(true);
-
-        NsdService service = makeService();
+        final NsdService service = makeService();
 
         // Creating an NsdManager will not cause any cmds executed, which means
         // no daemon is started.
-        NsdManager client1 = connectClient(service);
+        connectClient(service);
         waitForIdle();
         verify(mDaemon, never()).execute(any());
+        final INsdManagerCallback cb1 = getCallback();
+        final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
 
         // Creating another NsdManager will not cause any cmds executed.
-        NsdManager client2 = connectClient(service);
+        connectClient(service);
         waitForIdle();
         verify(mDaemon, never()).execute(any());
+        final INsdManagerCallback cb2 = getCallback();
+        final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
 
         // If there is no active request, try to clean up the daemon
         // every time the client disconnects.
-        client1.disconnect();
+        deathRecipient1.binderDied();
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
         reset(mDaemon);
-        client2.disconnect();
+        deathRecipient2.binderDied();
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+    }
 
-        client1.disconnect();
-        client2.disconnect();
+    private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
+            throws Exception {
+        final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr;
+        assertNotNull(dr);
+        return dr;
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testClientRequestsAreGCedAtDisconnection() {
+    public void testClientRequestsAreGCedAtDisconnection() throws Exception {
         when(mSettings.isEnabled()).thenReturn(true);
-
         NsdService service = makeService();
-        NsdManager client = connectClient(service);
 
+        NsdManager client = connectClient(service);
         waitForIdle();
+        final INsdManagerCallback cb1 = getCallback();
+        final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
         verify(mDaemon, never()).maybeStart();
         verify(mDaemon, never()).execute(any());
 
@@ -195,18 +232,16 @@
         verifyDaemonCommand("resolve 4 a_name a_type local.");
 
         // Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
-        client.disconnect();
+        deathRecipient.binderDied();
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
         // checks that request are cleaned
         verifyDaemonCommands("stop-register 2", "stop-discover 3",
                 "stop-resolve 4", "stop-service");
-
-        client.disconnect();
     }
 
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
-    public void testCleanupDelayNoRequestActive() {
+    public void testCleanupDelayNoRequestActive() throws Exception {
         when(mSettings.isEnabled()).thenReturn(true);
 
         NsdService service = makeService();
@@ -218,6 +253,8 @@
         client.registerService(request, PROTOCOL, listener1);
         waitForIdle();
         verify(mDaemon, times(1)).maybeStart();
+        final INsdManagerCallback cb1 = getCallback();
+        final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
         verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
 
         client.unregisterService(listener1);
@@ -226,7 +263,7 @@
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
         verifyDaemonCommand("stop-service");
         reset(mDaemon);
-        client.disconnect();
+        deathRecipient.binderDied();
         // Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
     }
@@ -240,12 +277,29 @@
             mDaemonCallback = callback;
             return mDaemon;
         };
-        NsdService service = new NsdService(mContext, mSettings,
-                mHandler, supplier, CLEANUP_DELAY_MS);
+        final NsdService service = new NsdService(mContext, mSettings,
+                mHandler, supplier, CLEANUP_DELAY_MS) {
+            @Override
+            public INsdServiceConnector connect(INsdManagerCallback baseCb) {
+                // Wrap the callback in a transparent mock, to mock asBinder returning a
+                // LinkToDeathRecorder. This will allow recording the binder death recipient
+                // registered on the callback. Use a transparent mock and not a spy as the actual
+                // implementation class is not public and cannot be spied on by Mockito.
+                final INsdManagerCallback cb = mock(INsdManagerCallback.class,
+                        AdditionalAnswers.delegatesTo(baseCb));
+                doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
+                mCreatedCallbacks.add(cb);
+                return super.connect(cb);
+            }
+        };
         verify(mDaemon, never()).execute(any(String.class));
         return service;
     }
 
+    private INsdManagerCallback getCallback() {
+        return mCreatedCallbacks.remove();
+    }
+
     NsdManager connectClient(NsdService service) {
         return new NsdManager(mContext, service);
     }