1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import static android.Manifest.permission.DEVICE_POWER;
20 import static android.Manifest.permission.NETWORK_SETTINGS;
21 import static android.Manifest.permission.NETWORK_STACK;
22 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
23 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
24 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
25 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
26 import static android.content.pm.PackageManager.PERMISSION_DENIED;
27 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
28 import static android.net.InetAddresses.parseNumericAddress;
29 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
30 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
31 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
32 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
33 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
34 import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
35 import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
36 import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
37 import static android.net.nsd.NsdManager.FAILURE_MAX_LIMIT;
38 import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
39 
40 import static com.android.networkstack.apishim.api33.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
41 import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF;
42 import static com.android.server.NsdService.MdnsListener;
43 import static com.android.server.NsdService.NO_TRANSACTION;
44 import static com.android.server.NsdService.checkHostname;
45 import static com.android.server.NsdService.parseTypeAndSubtype;
46 import static com.android.testutils.ContextUtils.mockService;
47 
48 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
49 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
50 
51 import static org.junit.Assert.assertArrayEquals;
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertNotNull;
54 import static org.junit.Assert.assertNull;
55 import static org.junit.Assert.assertThrows;
56 import static org.junit.Assert.assertTrue;
57 import static org.junit.Assert.assertFalse;
58 import static org.mockito.ArgumentMatchers.anyInt;
59 import static org.mockito.ArgumentMatchers.anyString;
60 import static org.mockito.ArgumentMatchers.argThat;
61 import static org.mockito.Mockito.any;
62 import static org.mockito.Mockito.doCallRealMethod;
63 import static org.mockito.Mockito.doReturn;
64 import static org.mockito.Mockito.eq;
65 import static org.mockito.Mockito.inOrder;
66 import static org.mockito.Mockito.mock;
67 import static org.mockito.Mockito.never;
68 import static org.mockito.Mockito.reset;
69 import static org.mockito.Mockito.timeout;
70 import static org.mockito.Mockito.times;
71 import static org.mockito.Mockito.verify;
72 import static org.mockito.Mockito.verifyNoMoreInteractions;
73 import static org.mockito.Mockito.when;
74 
75 import android.app.ActivityManager;
76 import android.app.ActivityManager.OnUidImportanceListener;
77 import android.compat.testing.PlatformCompatChangeRule;
78 import android.content.ContentResolver;
79 import android.content.Context;
80 import android.content.pm.PackageManager;
81 import android.net.INetd;
82 import android.net.Network;
83 import android.net.mdns.aidl.DiscoveryInfo;
84 import android.net.mdns.aidl.GetAddressInfo;
85 import android.net.mdns.aidl.IMDnsEventListener;
86 import android.net.mdns.aidl.RegistrationInfo;
87 import android.net.mdns.aidl.ResolutionInfo;
88 import android.net.nsd.AdvertisingRequest;
89 import android.net.nsd.INsdManagerCallback;
90 import android.net.nsd.INsdServiceConnector;
91 import android.net.nsd.MDnsManager;
92 import android.net.nsd.NsdManager;
93 import android.net.nsd.NsdManager.DiscoveryListener;
94 import android.net.nsd.NsdManager.RegistrationListener;
95 import android.net.nsd.NsdManager.ResolveListener;
96 import android.net.nsd.NsdManager.ServiceInfoCallback;
97 import android.net.nsd.NsdServiceInfo;
98 import android.net.nsd.OffloadEngine;
99 import android.net.wifi.WifiManager;
100 import android.os.Binder;
101 import android.os.Build;
102 import android.os.Handler;
103 import android.os.HandlerThread;
104 import android.os.IBinder;
105 import android.os.Looper;
106 import android.os.Message;
107 import android.os.Process;
108 import android.os.RemoteException;
109 import android.util.Pair;
110 
111 import androidx.annotation.NonNull;
112 import androidx.test.filters.SmallTest;
113 
114 import com.android.metrics.NetworkNsdReportedMetrics;
115 import com.android.server.NsdService.Dependencies;
116 import com.android.server.connectivity.mdns.MdnsAdvertiser;
117 import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
118 import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
119 import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
120 import com.android.server.connectivity.mdns.MdnsSearchOptions;
121 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
122 import com.android.server.connectivity.mdns.MdnsServiceInfo;
123 import com.android.server.connectivity.mdns.MdnsSocketProvider;
124 import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor;
125 import com.android.server.connectivity.mdns.util.MdnsUtils;
126 import com.android.testutils.DevSdkIgnoreRule;
127 import com.android.testutils.DevSdkIgnoreRunner;
128 import com.android.testutils.HandlerUtils;
129 
130 import org.junit.After;
131 import org.junit.Before;
132 import org.junit.Rule;
133 import org.junit.Test;
134 import org.junit.rules.TestRule;
135 import org.junit.runner.RunWith;
136 import org.mockito.AdditionalAnswers;
137 import org.mockito.ArgumentCaptor;
138 import org.mockito.InOrder;
139 import org.mockito.Mock;
140 import org.mockito.Mockito;
141 import org.mockito.MockitoAnnotations;
142 
143 import java.net.InetAddress;
144 import java.net.UnknownHostException;
145 import java.time.Duration;
146 import java.time.Instant;
147 import java.util.ArrayList;
148 import java.util.Collections;
149 import java.util.LinkedList;
150 import java.util.List;
151 import java.util.Objects;
152 import java.util.Queue;
153 import java.util.Set;
154 
155 // TODOs:
156 //  - test client can send requests and receive replies
157 //  - test NSD_ON ENABLE/DISABLED listening
158 @DevSdkIgnoreRunner.MonitorThreadLeak
159 @RunWith(DevSdkIgnoreRunner.class)
160 @SmallTest
161 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
162 public class NsdServiceTest {
163     @Rule
164     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
165 
166     static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
167     private static final long CLEANUP_DELAY_MS = 500;
168     private static final long TIMEOUT_MS = 500;
169     private static final long TEST_TIME_MS = 123L;
170     private static final String SERVICE_NAME = "a_name";
171     private static final String SERVICE_TYPE = "_test._tcp";
172     private static final String SERVICE_FULL_NAME = SERVICE_NAME + "." + SERVICE_TYPE;
173     private static final String DOMAIN_NAME = "mytestdevice.local";
174     private static final int PORT = 2201;
175     private static final int IFACE_IDX_ANY = 0;
176     private static final String IPV4_ADDRESS = "192.0.2.0";
177     private static final String IPV6_ADDRESS = "2001:db8::";
178 
179     // Records INsdManagerCallback created when NsdService#connect is called.
180     // Only accessed on the test thread, since NsdService#connect is called by the NsdManager
181     // constructor called on the test thread.
182     private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>();
183 
184     @Rule
185     public TestRule compatChangeRule = new PlatformCompatChangeRule();
186     @Rule
187     public TestRule ignoreRule = new DevSdkIgnoreRule();
188     @Mock Context mContext;
189     @Mock PackageManager mPackageManager;
190     @Mock ContentResolver mResolver;
191     @Mock MDnsManager mMockMDnsM;
192     @Mock Dependencies mDeps;
193     @Mock MdnsDiscoveryManager mDiscoveryManager;
194     @Mock MdnsAdvertiser mAdvertiser;
195     @Mock MdnsSocketProvider mSocketProvider;
196     @Mock WifiManager mWifiManager;
197     @Mock WifiManager.MulticastLock mMulticastLock;
198     @Mock ActivityManager mActivityManager;
199     @Mock NetworkNsdReportedMetrics mMetrics;
200     @Mock MdnsUtils.Clock mClock;
201     SocketRequestMonitor mSocketRequestMonitor;
202     OnUidImportanceListener mUidImportanceListener;
203     HandlerThread mThread;
204     TestHandler mHandler;
205     NsdService mService;
206 
207     private static class LinkToDeathRecorder extends Binder {
208         IBinder.DeathRecipient mDr;
209 
210         @Override
linkToDeath(@onNull DeathRecipient recipient, int flags)211         public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
212             super.linkToDeath(recipient, flags);
213             mDr = recipient;
214         }
215     }
216 
217     @Before
setUp()218     public void setUp() throws Exception {
219         MockitoAnnotations.initMocks(this);
220         mThread = new HandlerThread("mock-service-handler");
221         mThread.start();
222         mHandler = new TestHandler(mThread.getLooper());
223         when(mContext.getContentResolver()).thenReturn(mResolver);
224         mockService(mContext, MDnsManager.class, MDnsManager.MDNS_SERVICE, mMockMDnsM);
225         mockService(mContext, WifiManager.class, Context.WIFI_SERVICE, mWifiManager);
226         mockService(mContext, ActivityManager.class, Context.ACTIVITY_SERVICE, mActivityManager);
227         doReturn(mPackageManager).when(mContext).getPackageManager();
228         if (mContext.getSystemService(MDnsManager.class) == null) {
229             // Test is using mockito-extended
230             doCallRealMethod().when(mContext).getSystemService(MDnsManager.class);
231             doCallRealMethod().when(mContext).getSystemService(WifiManager.class);
232             doCallRealMethod().when(mContext).getSystemService(ActivityManager.class);
233         }
234         doReturn(true).when(mMockMDnsM).registerService(
235                 anyInt(), anyString(), anyString(), anyInt(), any(), anyInt());
236         doReturn(true).when(mMockMDnsM).stopOperation(anyInt());
237         doReturn(true).when(mMockMDnsM).discover(anyInt(), anyString(), anyInt());
238         doReturn(true).when(mMockMDnsM).resolve(
239                 anyInt(), anyString(), anyString(), anyString(), anyInt());
240         doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
241         doReturn(mDiscoveryManager).when(mDeps)
242                 .makeMdnsDiscoveryManager(any(), any(), any(), any());
243         doReturn(mMulticastLock).when(mWifiManager).createMulticastLock(any());
244         doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any(), any());
245         doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt(
246                 eq(NsdService.MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF), anyInt());
247         doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any(), any(),
248                 any());
249         doReturn(mMetrics).when(mDeps).makeNetworkNsdReportedMetrics(anyInt());
250         doReturn(mClock).when(mDeps).makeClock();
251         doReturn(TEST_TIME_MS).when(mClock).elapsedRealtime();
252         mService = makeService();
253         final ArgumentCaptor<SocketRequestMonitor> cbMonitorCaptor =
254                 ArgumentCaptor.forClass(SocketRequestMonitor.class);
255         verify(mDeps).makeMdnsSocketProvider(any(), any(), any(), cbMonitorCaptor.capture());
256         mSocketRequestMonitor = cbMonitorCaptor.getValue();
257 
258         final ArgumentCaptor<OnUidImportanceListener> uidListenerCaptor =
259                 ArgumentCaptor.forClass(OnUidImportanceListener.class);
260         verify(mActivityManager).addOnUidImportanceListener(uidListenerCaptor.capture(), anyInt());
261         mUidImportanceListener = uidListenerCaptor.getValue();
262     }
263 
264     @After
tearDown()265     public void tearDown() throws Exception {
266         if (mThread != null) {
267             mThread.quitSafely();
268             mThread.join();
269         }
270 
271         // Clear inline mocks as there are possible memory leaks if not done (see mockito
272         // doc for clearInlineMocks), and some tests create many of them.
273         Mockito.framework().clearInlineMocks();
274     }
275 
276     // Native mdns provided by Netd is removed after U.
277     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
278     @Test
279     @DisableCompatChanges({
280             RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER,
281             ENABLE_PLATFORM_MDNS_BACKEND})
testPreSClients()282     public void testPreSClients() throws Exception {
283         // Pre S client connected, the daemon should be started.
284         connectClient(mService);
285         final INsdManagerCallback cb1 = getCallback();
286         final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
287         verify(mMockMDnsM, times(1)).registerEventListener(any());
288         verify(mMockMDnsM, times(1)).startDaemon();
289 
290         connectClient(mService);
291         final INsdManagerCallback cb2 = getCallback();
292         final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
293         // Daemon has been started, it should not try to start it again.
294         verify(mMockMDnsM, times(1)).registerEventListener(any());
295         verify(mMockMDnsM, times(1)).startDaemon();
296 
297         deathRecipient1.binderDied();
298         // Still 1 client remains, daemon shouldn't be stopped.
299         waitForIdle();
300         verify(mMockMDnsM, never()).stopDaemon();
301 
302         deathRecipient2.binderDied();
303         // All clients are disconnected, the daemon should be stopped.
304         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
305     }
306 
307     @Test
308     @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
309     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
310     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testNoDaemonStartedWhenClientsConnect()311     public void testNoDaemonStartedWhenClientsConnect() throws Exception {
312         // Creating an NsdManager will not cause daemon startup.
313         connectClient(mService);
314         verify(mMockMDnsM, never()).registerEventListener(any());
315         verify(mMockMDnsM, never()).startDaemon();
316         final INsdManagerCallback cb1 = getCallback();
317         final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
318 
319         // Creating another NsdManager will not cause daemon startup either.
320         connectClient(mService);
321         verify(mMockMDnsM, never()).registerEventListener(any());
322         verify(mMockMDnsM, never()).startDaemon();
323         final INsdManagerCallback cb2 = getCallback();
324         final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
325 
326         // If there is no active request, try to clean up the daemon but should not do it because
327         // daemon has not been started.
328         deathRecipient1.binderDied();
329         verify(mMockMDnsM, never()).unregisterEventListener(any());
330         verify(mMockMDnsM, never()).stopDaemon();
331         deathRecipient2.binderDied();
332         verify(mMockMDnsM, never()).unregisterEventListener(any());
333         verify(mMockMDnsM, never()).stopDaemon();
334     }
335 
verifyLinkToDeath(INsdManagerCallback cb)336     private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
337             throws Exception {
338         final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr;
339         assertNotNull(dr);
340         return dr;
341     }
342 
343     @Test
344     @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
345     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
346     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testClientRequestsAreGCedAtDisconnection()347     public void testClientRequestsAreGCedAtDisconnection() throws Exception {
348         final NsdManager client = connectClient(mService);
349         final INsdManagerCallback cb1 = getCallback();
350         final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
351         verify(mMockMDnsM, never()).registerEventListener(any());
352         verify(mMockMDnsM, never()).startDaemon();
353 
354         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
355         request.setPort(PORT);
356 
357         // Client registration request
358         final RegistrationListener listener1 = mock(RegistrationListener.class);
359         client.registerService(request, PROTOCOL, listener1);
360         waitForIdle();
361         verify(mMockMDnsM).registerEventListener(any());
362         verify(mMockMDnsM).startDaemon();
363         verify(mMockMDnsM).registerService(
364                 eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
365 
366         // Client discovery request
367         final DiscoveryListener listener2 = mock(DiscoveryListener.class);
368         client.discoverServices(SERVICE_TYPE, PROTOCOL, listener2);
369         waitForIdle();
370         verify(mMockMDnsM).discover(3 /* id */, SERVICE_TYPE, IFACE_IDX_ANY);
371 
372         // Client resolve request
373         final ResolveListener listener3 = mock(ResolveListener.class);
374         client.resolveService(request, listener3);
375         waitForIdle();
376         verify(mMockMDnsM).resolve(
377                 4 /* id */, SERVICE_NAME, SERVICE_TYPE, "local." /* domain */, IFACE_IDX_ANY);
378 
379         // Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
380         deathRecipient.binderDied();
381         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
382         // checks that request are cleaned
383         verify(mMockMDnsM).stopOperation(2 /* id */);
384         verify(mMockMDnsM).stopOperation(3 /* id */);
385         verify(mMockMDnsM).stopOperation(4 /* id */);
386     }
387 
388     @Test
389     @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
390     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
391     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testCleanupDelayNoRequestActive()392     public void testCleanupDelayNoRequestActive() throws Exception {
393         final NsdManager client = connectClient(mService);
394 
395         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
396         request.setPort(PORT);
397         final RegistrationListener listener1 = mock(RegistrationListener.class);
398         client.registerService(request, PROTOCOL, listener1);
399         waitForIdle();
400         verify(mMockMDnsM).registerEventListener(any());
401         verify(mMockMDnsM).startDaemon();
402         final INsdManagerCallback cb1 = getCallback();
403         final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
404         verify(mMockMDnsM).registerService(
405                 eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
406 
407         client.unregisterService(listener1);
408         waitForIdle();
409         verify(mMockMDnsM).stopOperation(2 /* id */);
410 
411         verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
412         reset(mMockMDnsM);
413         deathRecipient.binderDied();
414         // Client disconnects, daemon should not be stopped after CLEANUP_DELAY_MS.
415         verify(mMockMDnsM, never()).unregisterEventListener(any());
416         verify(mMockMDnsM, never()).stopDaemon();
417     }
418 
getEventListener()419     private IMDnsEventListener getEventListener() {
420         final ArgumentCaptor<IMDnsEventListener> listenerCaptor =
421                 ArgumentCaptor.forClass(IMDnsEventListener.class);
422         verify(mMockMDnsM).registerEventListener(listenerCaptor.capture());
423         return listenerCaptor.getValue();
424     }
425 
426     @Test
427     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
428     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testDiscoverOnTetheringDownstream()429     public void testDiscoverOnTetheringDownstream() throws Exception {
430         final NsdManager client = connectClient(mService);
431         final int interfaceIdx = 123;
432         final DiscoveryListener discListener = mock(DiscoveryListener.class);
433         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
434         waitForIdle();
435 
436         final IMDnsEventListener eventListener = getEventListener();
437         final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
438         verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE),
439                 eq(0) /* interfaceIdx */);
440         // NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
441         // this needs to use a timeout
442         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
443         final int discId = discIdCaptor.getValue();
444         verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId);
445 
446         final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
447                 discId,
448                 IMDnsEventListener.SERVICE_FOUND,
449                 SERVICE_NAME,
450                 SERVICE_TYPE,
451                 DOMAIN_NAME,
452                 interfaceIdx,
453                 INetd.LOCAL_NET_ID); // LOCAL_NET_ID (99) used on tethering downstreams
454         eventListener.onServiceDiscoveryStatus(discoveryInfo);
455         waitForIdle();
456 
457         final ArgumentCaptor<NsdServiceInfo> discoveredInfoCaptor =
458                 ArgumentCaptor.forClass(NsdServiceInfo.class);
459         verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(discoveredInfoCaptor.capture());
460         final NsdServiceInfo foundInfo = discoveredInfoCaptor.getValue();
461         assertEquals(SERVICE_NAME, foundInfo.getServiceName());
462         assertEquals(SERVICE_TYPE, foundInfo.getServiceType());
463         assertNull(foundInfo.getHost());
464         assertNull(foundInfo.getNetwork());
465         assertEquals(interfaceIdx, foundInfo.getInterfaceIndex());
466 
467         // After discovering the service, verify resolving it
468         final ResolveListener resolveListener = mock(ResolveListener.class);
469         client.resolveService(foundInfo, resolveListener);
470         waitForIdle();
471 
472         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
473         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
474                 eq("local.") /* domain */, eq(interfaceIdx));
475 
476         final int servicePort = 10123;
477         final ResolutionInfo resolutionInfo = new ResolutionInfo(
478                 resolvIdCaptor.getValue(),
479                 IMDnsEventListener.SERVICE_RESOLVED,
480                 null /* serviceName */,
481                 null /* serviceType */,
482                 null /* domain */,
483                 SERVICE_FULL_NAME,
484                 DOMAIN_NAME,
485                 servicePort,
486                 new byte[0] /* txtRecord */,
487                 interfaceIdx);
488 
489         doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
490         eventListener.onServiceResolutionStatus(resolutionInfo);
491         waitForIdle();
492 
493         final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
494         verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
495                 eq(interfaceIdx));
496 
497         final String serviceAddress = "192.0.2.123";
498         final int getAddrId = getAddrIdCaptor.getValue();
499         final GetAddressInfo addressInfo = new GetAddressInfo(
500                 getAddrId,
501                 IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
502                 SERVICE_FULL_NAME,
503                 serviceAddress,
504                 interfaceIdx,
505                 INetd.LOCAL_NET_ID);
506         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
507         eventListener.onGettingServiceAddressStatus(addressInfo);
508         waitForIdle();
509 
510         final ArgumentCaptor<NsdServiceInfo> resInfoCaptor =
511                 ArgumentCaptor.forClass(NsdServiceInfo.class);
512         verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture());
513         verify(mMetrics).reportServiceResolved(true /* isLegacy */, getAddrId, 10L /* durationMs */,
514                 false /* isServiceFromCache */, 0 /* sentQueryCount */);
515 
516         final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
517         assertEquals(SERVICE_NAME, resolvedService.getServiceName());
518         assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType());
519         assertEquals(parseNumericAddress(serviceAddress), resolvedService.getHost());
520         assertEquals(servicePort, resolvedService.getPort());
521         assertNull(resolvedService.getNetwork());
522         assertEquals(interfaceIdx, resolvedService.getInterfaceIndex());
523     }
524 
525     @Test
526     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
testDiscoverOnTetheringDownstream_DiscoveryManager()527     public void testDiscoverOnTetheringDownstream_DiscoveryManager() throws Exception {
528         final NsdManager client = connectClient(mService);
529         final DiscoveryListener discListener = mock(DiscoveryListener.class);
530         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
531         waitForIdle();
532 
533         final ArgumentCaptor<MdnsServiceBrowserListener> discoverListenerCaptor =
534                 ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
535         final InOrder discManagerOrder = inOrder(mDiscoveryManager);
536         final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
537         discManagerOrder.verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
538                 discoverListenerCaptor.capture(), any());
539 
540         final int interfaceIdx = 123;
541         final MdnsServiceInfo mockServiceInfo = new MdnsServiceInfo(
542                 SERVICE_NAME, /* serviceInstanceName */
543                 serviceTypeWithLocalDomain.split("\\."), /* serviceType */
544                 List.of(), /* subtypes */
545                 new String[] {"android", "local"}, /* hostName */
546                 12345, /* port */
547                 List.of(IPV4_ADDRESS),
548                 List.of(IPV6_ADDRESS),
549                 List.of(), /* textStrings */
550                 List.of(), /* textEntries */
551                 interfaceIdx, /* interfaceIndex */
552                 null /* network */,
553                 Instant.MAX /* expirationTime */);
554 
555         // Verify service is found with the interface index
556         discoverListenerCaptor.getValue().onServiceNameDiscovered(
557                 mockServiceInfo, false /* isServiceFromCache */);
558         final ArgumentCaptor<NsdServiceInfo> foundInfoCaptor =
559                 ArgumentCaptor.forClass(NsdServiceInfo.class);
560         verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(foundInfoCaptor.capture());
561         final NsdServiceInfo foundInfo = foundInfoCaptor.getValue();
562         assertNull(foundInfo.getNetwork());
563         assertEquals(interfaceIdx, foundInfo.getInterfaceIndex());
564 
565         // Using the returned service info to resolve or register callback uses the interface index
566         client.resolveService(foundInfo, mock(ResolveListener.class));
567         client.registerServiceInfoCallback(foundInfo, Runnable::run,
568                 mock(ServiceInfoCallback.class));
569         waitForIdle();
570 
571         discManagerOrder.verify(mDiscoveryManager, times(2)).registerListener(any(), any(), argThat(
572                 o -> o.getNetwork() == null && o.getInterfaceIndex() == interfaceIdx));
573     }
574 
575     @Test
576     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
577     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testDiscoverOnBlackholeNetwork()578     public void testDiscoverOnBlackholeNetwork() throws Exception {
579         final NsdManager client = connectClient(mService);
580         final DiscoveryListener discListener = mock(DiscoveryListener.class);
581         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
582         waitForIdle();
583 
584         final IMDnsEventListener eventListener = getEventListener();
585         final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
586         verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE),
587                 eq(0) /* interfaceIdx */);
588         // NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
589         // this needs to use a timeout
590         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
591         final int discId = discIdCaptor.getValue();
592         verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId);
593 
594         final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
595                 discId,
596                 IMDnsEventListener.SERVICE_FOUND,
597                 SERVICE_NAME,
598                 SERVICE_TYPE,
599                 DOMAIN_NAME,
600                 123 /* interfaceIdx */,
601                 INetd.DUMMY_NET_ID); // netId of the blackhole network
602         eventListener.onServiceDiscoveryStatus(discoveryInfo);
603         waitForIdle();
604 
605         verify(discListener, never()).onServiceFound(any());
606     }
607 
608     @Test
609     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
610     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testServiceRegistrationSuccessfulAndFailed()611     public void testServiceRegistrationSuccessfulAndFailed() throws Exception {
612         final NsdManager client = connectClient(mService);
613         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
614         request.setPort(PORT);
615         final RegistrationListener regListener = mock(RegistrationListener.class);
616         client.registerService(request, PROTOCOL, regListener);
617         waitForIdle();
618 
619         final IMDnsEventListener eventListener = getEventListener();
620         final ArgumentCaptor<Integer> regIdCaptor = ArgumentCaptor.forClass(Integer.class);
621         verify(mMockMDnsM).registerService(regIdCaptor.capture(),
622                 eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
623 
624         // Register service successfully.
625         final int regId = regIdCaptor.getValue();
626         final RegistrationInfo registrationInfo = new RegistrationInfo(
627                 regId,
628                 IMDnsEventListener.SERVICE_REGISTERED,
629                 SERVICE_NAME,
630                 SERVICE_TYPE,
631                 PORT,
632                 new byte[0] /* txtRecord */,
633                 IFACE_IDX_ANY);
634         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
635         eventListener.onServiceRegistrationStatus(registrationInfo);
636 
637         final ArgumentCaptor<NsdServiceInfo> registeredInfoCaptor =
638                 ArgumentCaptor.forClass(NsdServiceInfo.class);
639         verify(regListener, timeout(TIMEOUT_MS))
640                 .onServiceRegistered(registeredInfoCaptor.capture());
641         final NsdServiceInfo registeredInfo = registeredInfoCaptor.getValue();
642         assertEquals(SERVICE_NAME, registeredInfo.getServiceName());
643         verify(mMetrics).reportServiceRegistrationSucceeded(
644                 true /* isLegacy */, regId, 10L /* durationMs */);
645 
646         // Fail to register service.
647         final RegistrationInfo registrationFailedInfo = new RegistrationInfo(
648                 regId,
649                 IMDnsEventListener.SERVICE_REGISTRATION_FAILED,
650                 null /* serviceName */,
651                 null /* registrationType */,
652                 0 /* port */,
653                 new byte[0] /* txtRecord */,
654                 IFACE_IDX_ANY);
655         doReturn(TEST_TIME_MS + 20L).when(mClock).elapsedRealtime();
656         eventListener.onServiceRegistrationStatus(registrationFailedInfo);
657         verify(regListener, timeout(TIMEOUT_MS))
658                 .onRegistrationFailed(any(), eq(FAILURE_INTERNAL_ERROR));
659         verify(mMetrics).reportServiceRegistrationFailed(
660                 true /* isLegacy */, regId, 20L /* durationMs */);
661     }
662 
663     @Test
664     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
665     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testServiceDiscoveryFailed()666     public void testServiceDiscoveryFailed() throws Exception {
667         final NsdManager client = connectClient(mService);
668         final DiscoveryListener discListener = mock(DiscoveryListener.class);
669         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
670         waitForIdle();
671 
672         final IMDnsEventListener eventListener = getEventListener();
673         final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
674         verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), eq(IFACE_IDX_ANY));
675         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
676         final int discId = discIdCaptor.getValue();
677         verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId);
678 
679         // Fail to discover service.
680         final DiscoveryInfo discoveryFailedInfo = new DiscoveryInfo(
681                 discId,
682                 IMDnsEventListener.SERVICE_DISCOVERY_FAILED,
683                 null /* serviceName */,
684                 null /* registrationType */,
685                 null /* domainName */,
686                 IFACE_IDX_ANY,
687                 0 /* netId */);
688         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
689         eventListener.onServiceDiscoveryStatus(discoveryFailedInfo);
690         verify(discListener, timeout(TIMEOUT_MS))
691                 .onStartDiscoveryFailed(SERVICE_TYPE, FAILURE_INTERNAL_ERROR);
692         verify(mMetrics).reportServiceDiscoveryFailed(
693                 true /* isLegacy */, discId, 10L /* durationMs */);
694     }
695 
696     @Test
697     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
698     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testServiceResolutionFailed()699     public void testServiceResolutionFailed() throws Exception {
700         final NsdManager client = connectClient(mService);
701         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
702         final ResolveListener resolveListener = mock(ResolveListener.class);
703         client.resolveService(request, resolveListener);
704         waitForIdle();
705 
706         final IMDnsEventListener eventListener = getEventListener();
707         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
708         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
709                 eq("local.") /* domain */, eq(IFACE_IDX_ANY));
710 
711         // Fail to resolve service.
712         final int resolvId = resolvIdCaptor.getValue();
713         final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
714                 resolvId,
715                 IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
716                 null /* serviceName */,
717                 null /* serviceType */,
718                 null /* domain */,
719                 null /* serviceFullName */,
720                 null /* domainName */,
721                 0 /* port */,
722                 new byte[0] /* txtRecord */,
723                 IFACE_IDX_ANY);
724         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
725         eventListener.onServiceResolutionStatus(resolutionFailedInfo);
726         verify(resolveListener, timeout(TIMEOUT_MS))
727                 .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
728         verify(mMetrics).reportServiceResolutionFailed(
729                 true /* isLegacy */, resolvId, 10L /* durationMs */);
730     }
731 
732     @Test
733     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
734     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testGettingAddressFailed()735     public void testGettingAddressFailed() throws Exception {
736         final NsdManager client = connectClient(mService);
737         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
738         final ResolveListener resolveListener = mock(ResolveListener.class);
739         client.resolveService(request, resolveListener);
740         waitForIdle();
741 
742         final IMDnsEventListener eventListener = getEventListener();
743         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
744         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
745                 eq("local.") /* domain */, eq(IFACE_IDX_ANY));
746 
747         // Resolve service successfully.
748         final ResolutionInfo resolutionInfo = new ResolutionInfo(
749                 resolvIdCaptor.getValue(),
750                 IMDnsEventListener.SERVICE_RESOLVED,
751                 null /* serviceName */,
752                 null /* serviceType */,
753                 null /* domain */,
754                 SERVICE_FULL_NAME,
755                 DOMAIN_NAME,
756                 PORT,
757                 new byte[0] /* txtRecord */,
758                 IFACE_IDX_ANY);
759         doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
760         eventListener.onServiceResolutionStatus(resolutionInfo);
761         waitForIdle();
762 
763         final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
764         verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
765                 eq(IFACE_IDX_ANY));
766 
767         // Fail to get service address.
768         final int getAddrId = getAddrIdCaptor.getValue();
769         final GetAddressInfo gettingAddrFailedInfo = new GetAddressInfo(
770                 getAddrId,
771                 IMDnsEventListener.SERVICE_GET_ADDR_FAILED,
772                 null /* hostname */,
773                 null /* address */,
774                 IFACE_IDX_ANY,
775                 0 /* netId */);
776         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
777         eventListener.onGettingServiceAddressStatus(gettingAddrFailedInfo);
778         verify(resolveListener, timeout(TIMEOUT_MS))
779                 .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
780         verify(mMetrics).reportServiceResolutionFailed(
781                 true /* isLegacy */, getAddrId, 10L /* durationMs */);
782     }
783 
784     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
785     @Test
testPerClientListenerLimit()786     public void testPerClientListenerLimit() throws Exception {
787         final NsdManager client1 = connectClient(mService);
788         final NsdManager client2 = connectClient(mService);
789 
790         final String testType1 = "_testtype1._tcp";
791         final NsdServiceInfo testServiceInfo1 = new NsdServiceInfo("MyTestService1", testType1);
792         testServiceInfo1.setPort(12345);
793         final String testType2 = "_testtype2._tcp";
794         final NsdServiceInfo testServiceInfo2 = new NsdServiceInfo("MyTestService2", testType2);
795         testServiceInfo2.setPort(12345);
796 
797         // Each client can register 200 requests (for example 100 discover and 100 register).
798         final int numEachListener = 100;
799         final ArrayList<DiscoveryListener> discListeners = new ArrayList<>(numEachListener);
800         final ArrayList<RegistrationListener> regListeners = new ArrayList<>(numEachListener);
801         for (int i = 0; i < numEachListener; i++) {
802             final DiscoveryListener discListener1 = mock(DiscoveryListener.class);
803             discListeners.add(discListener1);
804             final RegistrationListener regListener1 = mock(RegistrationListener.class);
805             regListeners.add(regListener1);
806             final DiscoveryListener discListener2 = mock(DiscoveryListener.class);
807             discListeners.add(discListener2);
808             final RegistrationListener regListener2 = mock(RegistrationListener.class);
809             regListeners.add(regListener2);
810             client1.discoverServices(testType1, NsdManager.PROTOCOL_DNS_SD,
811                     (Network) null, Runnable::run, discListener1);
812             client1.registerService(testServiceInfo1, NsdManager.PROTOCOL_DNS_SD, Runnable::run,
813                     regListener1);
814 
815             client2.registerService(testServiceInfo2, NsdManager.PROTOCOL_DNS_SD, Runnable::run,
816                     regListener2);
817             client2.discoverServices(testType2, NsdManager.PROTOCOL_DNS_SD,
818                     (Network) null, Runnable::run, discListener2);
819         }
820 
821         // Use a longer timeout than usual for the handler to process all the events. The
822         // registrations take about 1s on a high-end 2013 device.
823         HandlerUtils.waitForIdle(mHandler, 30_000L);
824         for (int i = 0; i < discListeners.size(); i++) {
825             // Callbacks are sent on the manager handler which is different from mHandler, so use
826             // a short timeout (each callback should come quickly after the previous one).
827             verify(discListeners.get(i), timeout(TEST_TIME_MS))
828                     .onDiscoveryStarted(i % 2 == 0 ? testType1 : testType2);
829 
830             // registerService does not get a callback before probing finishes (will not happen as
831             // this is mocked)
832             verifyNoMoreInteractions(regListeners.get(i));
833         }
834 
835         // The next registrations should fail
836         final DiscoveryListener failDiscListener1 = mock(DiscoveryListener.class);
837         final RegistrationListener failRegListener1 = mock(RegistrationListener.class);
838         final DiscoveryListener failDiscListener2 = mock(DiscoveryListener.class);
839         final RegistrationListener failRegListener2 = mock(RegistrationListener.class);
840 
841         client1.discoverServices(testType1, NsdManager.PROTOCOL_DNS_SD,
842                 (Network) null, Runnable::run, failDiscListener1);
843         verify(failDiscListener1, timeout(TEST_TIME_MS))
844                 .onStartDiscoveryFailed(testType1, FAILURE_MAX_LIMIT);
845 
846         client1.registerService(testServiceInfo1, NsdManager.PROTOCOL_DNS_SD, Runnable::run,
847                 failRegListener1);
848         verify(failRegListener1, timeout(TEST_TIME_MS)).onRegistrationFailed(
849                 argThat(a -> testServiceInfo1.getServiceName().equals(a.getServiceName())),
850                 eq(FAILURE_MAX_LIMIT));
851 
852         client1.discoverServices(testType2, NsdManager.PROTOCOL_DNS_SD,
853                 (Network) null, Runnable::run, failDiscListener2);
854         verify(failDiscListener2, timeout(TEST_TIME_MS))
855                 .onStartDiscoveryFailed(testType2, FAILURE_MAX_LIMIT);
856 
857         client1.registerService(testServiceInfo2, NsdManager.PROTOCOL_DNS_SD, Runnable::run,
858                 failRegListener2);
859         verify(failRegListener2, timeout(TEST_TIME_MS)).onRegistrationFailed(
860                 argThat(a -> testServiceInfo2.getServiceName().equals(a.getServiceName())),
861                 eq(FAILURE_MAX_LIMIT));
862     }
863 
864     @Test
865     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
866     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testNoCrashWhenProcessResolutionAfterBinderDied()867     public void testNoCrashWhenProcessResolutionAfterBinderDied() throws Exception {
868         final NsdManager client = connectClient(mService);
869         final INsdManagerCallback cb = getCallback();
870         final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb);
871         deathRecipient.binderDied();
872 
873         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
874         final ResolveListener resolveListener = mock(ResolveListener.class);
875         client.resolveService(request, resolveListener);
876         waitForIdle();
877 
878         verify(mMockMDnsM, never()).registerEventListener(any());
879         verify(mMockMDnsM, never()).startDaemon();
880         verify(mMockMDnsM, never()).resolve(anyInt() /* id */, anyString() /* serviceName */,
881                 anyString() /* registrationType */, anyString() /* domain */,
882                 anyInt()/* interfaceIdx */);
883     }
884 
885     @Test
886     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
887     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testStopServiceResolution()888     public void testStopServiceResolution() {
889         final NsdManager client = connectClient(mService);
890         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
891         final ResolveListener resolveListener = mock(ResolveListener.class);
892         client.resolveService(request, resolveListener);
893         waitForIdle();
894 
895         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
896         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
897                 eq("local.") /* domain */, eq(IFACE_IDX_ANY));
898 
899         final int resolveId = resolvIdCaptor.getValue();
900         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
901         client.stopServiceResolution(resolveListener);
902         waitForIdle();
903 
904         verify(mMockMDnsM).stopOperation(resolveId);
905         verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
906                 request.getServiceName().equals(ns.getServiceName())
907                         && request.getServiceType().equals(ns.getServiceType())));
908         verify(mMetrics).reportServiceResolutionStop(
909                 true /* isLegacy */, resolveId, 10L /* durationMs */, 0 /* sentQueryCount */);
910     }
911 
912     @Test
913     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
914     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testStopResolutionFailed()915     public void testStopResolutionFailed() {
916         final NsdManager client = connectClient(mService);
917         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
918         final ResolveListener resolveListener = mock(ResolveListener.class);
919         client.resolveService(request, resolveListener);
920         waitForIdle();
921 
922         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
923         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
924                 eq("local.") /* domain */, eq(IFACE_IDX_ANY));
925 
926         final int resolveId = resolvIdCaptor.getValue();
927         doReturn(false).when(mMockMDnsM).stopOperation(anyInt());
928         client.stopServiceResolution(resolveListener);
929         waitForIdle();
930 
931         verify(mMockMDnsM).stopOperation(resolveId);
932         verify(resolveListener, timeout(TIMEOUT_MS)).onStopResolutionFailed(argThat(ns ->
933                         request.getServiceName().equals(ns.getServiceName())
934                                 && request.getServiceType().equals(ns.getServiceType())),
935                 eq(FAILURE_OPERATION_NOT_RUNNING));
936     }
937 
938     @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
939     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
940     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testStopResolutionDuringGettingAddress()941     public void testStopResolutionDuringGettingAddress() throws RemoteException {
942         final NsdManager client = connectClient(mService);
943         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
944         final ResolveListener resolveListener = mock(ResolveListener.class);
945         client.resolveService(request, resolveListener);
946         waitForIdle();
947 
948         final IMDnsEventListener eventListener = getEventListener();
949         final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
950         verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
951                 eq("local.") /* domain */, eq(IFACE_IDX_ANY));
952 
953         // Resolve service successfully.
954         final ResolutionInfo resolutionInfo = new ResolutionInfo(
955                 resolvIdCaptor.getValue(),
956                 IMDnsEventListener.SERVICE_RESOLVED,
957                 null /* serviceName */,
958                 null /* serviceType */,
959                 null /* domain */,
960                 SERVICE_FULL_NAME,
961                 DOMAIN_NAME,
962                 PORT,
963                 new byte[0] /* txtRecord */,
964                 IFACE_IDX_ANY);
965         doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
966         eventListener.onServiceResolutionStatus(resolutionInfo);
967         waitForIdle();
968 
969         final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
970         verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
971                 eq(IFACE_IDX_ANY));
972 
973         final int getAddrId = getAddrIdCaptor.getValue();
974         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
975         client.stopServiceResolution(resolveListener);
976         waitForIdle();
977 
978         verify(mMockMDnsM).stopOperation(getAddrId);
979         verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
980                 request.getServiceName().equals(ns.getServiceName())
981                         && request.getServiceType().equals(ns.getServiceType())));
982         verify(mMetrics).reportServiceResolutionStop(
983                 true /* isLegacy */, getAddrId, 10L /* durationMs */,  0 /* sentQueryCount */);
984     }
985 
verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName, String serviceType, List<InetAddress> address, int port, int interfaceIndex, Network network)986     private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
987             String serviceType, List<InetAddress> address, int port, int interfaceIndex,
988             Network network) {
989         assertEquals(serviceName, info.getServiceName());
990         assertEquals(serviceType, info.getServiceType());
991         assertEquals(address, info.getHostAddresses());
992         assertEquals(port, info.getPort());
993         assertEquals(network, info.getNetwork());
994         assertEquals(interfaceIndex, info.getInterfaceIndex());
995     }
996 
997     @Test
testRegisterAndUnregisterServiceInfoCallback()998     public void testRegisterAndUnregisterServiceInfoCallback() {
999         final NsdManager client = connectClient(mService);
1000         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1001         final ServiceInfoCallback serviceInfoCallback = mock(
1002                 ServiceInfoCallback.class);
1003         final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1004         final Network network = new Network(999);
1005         request.setNetwork(network);
1006         client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
1007         waitForIdle();
1008         // Verify the registration callback start.
1009         final ArgumentCaptor<MdnsListener> listenerCaptor =
1010                 ArgumentCaptor.forClass(MdnsListener.class);
1011         verify(mSocketProvider).startMonitoringSockets();
1012         verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
1013                 listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
1014 
1015         final MdnsListener listener = listenerCaptor.getValue();
1016         final int servInfoId = listener.mTransactionId;
1017         // Verify the service info callback registered.
1018         verify(mMetrics).reportServiceInfoCallbackRegistered(servInfoId);
1019 
1020         final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
1021                 SERVICE_NAME,
1022                 serviceTypeWithLocalDomain.split("\\."),
1023                 List.of(), /* subtypes */
1024                 new String[]{"android", "local"}, /* hostName */
1025                 PORT,
1026                 List.of(IPV4_ADDRESS),
1027                 List.of(IPV6_ADDRESS),
1028                 List.of() /* textStrings */,
1029                 List.of() /* textEntries */,
1030                 1234,
1031                 network,
1032                 Instant.MAX /* expirationTime */);
1033 
1034         // Callbacks for query sent.
1035         listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
1036 
1037         // Verify onServiceFound callback
1038         listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */);
1039         final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
1040                 ArgumentCaptor.forClass(NsdServiceInfo.class);
1041         verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
1042                 .onServiceUpdated(updateInfoCaptor.capture());
1043         verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME,
1044                 SERVICE_TYPE,
1045                 List.of(parseNumericAddress(IPV4_ADDRESS), parseNumericAddress(IPV6_ADDRESS)),
1046                 PORT, IFACE_IDX_ANY, new Network(999));
1047 
1048         // Service addresses changed.
1049         final String v4Address = "192.0.2.1";
1050         final String v6Address = "2001:db8::1";
1051         final MdnsServiceInfo updatedServiceInfo = new MdnsServiceInfo(
1052                 SERVICE_NAME,
1053                 serviceTypeWithLocalDomain.split("\\."),
1054                 List.of(), /* subtypes */
1055                 new String[]{"android", "local"}, /* hostName */
1056                 PORT,
1057                 List.of(v4Address),
1058                 List.of(v6Address),
1059                 List.of() /* textStrings */,
1060                 List.of() /* textEntries */,
1061                 1234,
1062                 network,
1063                 Instant.MAX /* expirationTime */);
1064 
1065         // Verify onServiceUpdated callback.
1066         listener.onServiceUpdated(updatedServiceInfo);
1067         verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2))
1068                 .onServiceUpdated(updateInfoCaptor.capture());
1069         verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(2) /* info */, SERVICE_NAME,
1070                 SERVICE_TYPE,
1071                 List.of(parseNumericAddress(v4Address), parseNumericAddress(v6Address)),
1072                 PORT, IFACE_IDX_ANY, new Network(999));
1073 
1074         // Service lost then recovered.
1075         listener.onServiceRemoved(updatedServiceInfo);
1076         listener.onServiceFound(updatedServiceInfo, false /* isServiceFromCache */);
1077 
1078         // Verify service callback unregistration.
1079         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1080         client.unregisterServiceInfoCallback(serviceInfoCallback);
1081         waitForIdle();
1082         verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered();
1083         verify(mMetrics).reportServiceInfoCallbackUnregistered(servInfoId, 10L /* durationMs */,
1084                 3 /* updateCallbackCount */, 1 /* lostCallbackCount */,
1085                 true /* isServiceFromCache */, 1 /* sentQueryCount */);
1086     }
1087 
1088     @Test
testRegisterServiceCallbackFailed()1089     public void testRegisterServiceCallbackFailed() {
1090         final NsdManager client = connectClient(mService);
1091         final String invalidServiceType = "a_service";
1092         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, invalidServiceType);
1093         final ServiceInfoCallback serviceInfoCallback = mock(
1094                 ServiceInfoCallback.class);
1095         client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
1096         waitForIdle();
1097 
1098         // Fail to register service callback.
1099         verify(serviceInfoCallback, timeout(TIMEOUT_MS))
1100                 .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS));
1101         verify(mMetrics).reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
1102     }
1103 
1104     @Test
testUnregisterNotRegisteredCallback()1105     public void testUnregisterNotRegisteredCallback() {
1106         final NsdManager client = connectClient(mService);
1107         final ServiceInfoCallback serviceInfoCallback = mock(
1108                 ServiceInfoCallback.class);
1109 
1110         assertThrows(IllegalArgumentException.class, () ->
1111                 client.unregisterServiceInfoCallback(serviceInfoCallback));
1112     }
1113 
setMdnsDiscoveryManagerEnabled()1114     private void setMdnsDiscoveryManagerEnabled() {
1115         doReturn(true).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
1116     }
1117 
setMdnsAdvertiserEnabled()1118     private void setMdnsAdvertiserEnabled() {
1119         doReturn(true).when(mDeps).isMdnsAdvertiserEnabled(any(Context.class));
1120     }
1121 
1122     @Test
1123     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
1124     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testMdnsDiscoveryManagerFeature()1125     public void testMdnsDiscoveryManagerFeature() {
1126         // Create NsdService w/o feature enabled.
1127         final NsdManager client = connectClient(mService);
1128         final DiscoveryListener discListenerWithoutFeature = mock(DiscoveryListener.class);
1129         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListenerWithoutFeature);
1130         waitForIdle();
1131 
1132         final ArgumentCaptor<Integer> legacyIdCaptor = ArgumentCaptor.forClass(Integer.class);
1133         verify(mMockMDnsM).discover(legacyIdCaptor.capture(), any(), anyInt());
1134         verifyNoMoreInteractions(mDiscoveryManager);
1135 
1136         setMdnsDiscoveryManagerEnabled();
1137         final DiscoveryListener discListenerWithFeature = mock(DiscoveryListener.class);
1138         client.discoverServices(SERVICE_TYPE, PROTOCOL, discListenerWithFeature);
1139         waitForIdle();
1140 
1141         final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1142         final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
1143                 ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
1144         verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
1145                 listenerCaptor.capture(), any());
1146 
1147         client.stopServiceDiscovery(discListenerWithoutFeature);
1148         waitForIdle();
1149         verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue());
1150 
1151         client.stopServiceDiscovery(discListenerWithFeature);
1152         waitForIdle();
1153         verify(mDiscoveryManager).unregisterListener(serviceTypeWithLocalDomain,
1154                 listenerCaptor.getValue());
1155     }
1156 
1157     @Test
testDiscoveryWithMdnsDiscoveryManager()1158     public void testDiscoveryWithMdnsDiscoveryManager() {
1159         setMdnsDiscoveryManagerEnabled();
1160 
1161         final NsdManager client = connectClient(mService);
1162         final DiscoveryListener discListener = mock(DiscoveryListener.class);
1163         final Network network = new Network(999);
1164         final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1165         // Verify the discovery start / stop.
1166         final ArgumentCaptor<MdnsListener> listenerCaptor =
1167                 ArgumentCaptor.forClass(MdnsListener.class);
1168         client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener);
1169         waitForIdle();
1170         verify(mSocketProvider).startMonitoringSockets();
1171         verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
1172                 listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
1173         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
1174 
1175         final MdnsListener listener = listenerCaptor.getValue();
1176         final int discId = listener.mTransactionId;
1177         verify(mMetrics).reportServiceDiscoveryStarted(false /* isLegacy */, discId);
1178 
1179         // Callbacks for query sent.
1180         listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
1181         listener.onDiscoveryQuerySent(Collections.emptyList(), 2 /* transactionId */);
1182         listener.onDiscoveryQuerySent(Collections.emptyList(), 3 /* transactionId */);
1183 
1184         final MdnsServiceInfo foundInfo = new MdnsServiceInfo(
1185                 SERVICE_NAME, /* serviceInstanceName */
1186                 serviceTypeWithLocalDomain.split("\\."), /* serviceType */
1187                 List.of(), /* subtypes */
1188                 new String[] {"android", "local"}, /* hostName */
1189                 12345, /* port */
1190                 List.of(IPV4_ADDRESS),
1191                 List.of(IPV6_ADDRESS),
1192                 List.of(), /* textStrings */
1193                 List.of(), /* textEntries */
1194                 1234, /* interfaceIndex */
1195                 network,
1196                 Instant.MAX /* expirationTime */);
1197 
1198         // Verify onServiceNameDiscovered callback
1199         listener.onServiceNameDiscovered(foundInfo, true /* isServiceFromCache */);
1200         verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
1201                 info.getServiceName().equals(SERVICE_NAME)
1202                         // Service type in discovery callbacks has a dot at the end
1203                         && info.getServiceType().equals(SERVICE_TYPE + ".")
1204                         && info.getNetwork().equals(network)));
1205 
1206         final MdnsServiceInfo removedInfo = new MdnsServiceInfo(
1207                 SERVICE_NAME, /* serviceInstanceName */
1208                 serviceTypeWithLocalDomain.split("\\."), /* serviceType */
1209                 null, /* subtypes */
1210                 null, /* hostName */
1211                 0, /* port */
1212                 List.of(), /* ipv4Address */
1213                 List.of(), /* ipv6Address */
1214                 null, /* textStrings */
1215                 null, /* textEntries */
1216                 1234, /* interfaceIndex */
1217                 network,
1218                 Instant.MAX /* expirationTime */);
1219         // Verify onServiceNameRemoved callback
1220         listener.onServiceNameRemoved(removedInfo);
1221         verify(discListener, timeout(TIMEOUT_MS)).onServiceLost(argThat(info ->
1222                 info.getServiceName().equals(SERVICE_NAME)
1223                         // Service type in discovery callbacks has a dot at the end
1224                         && info.getServiceType().equals(SERVICE_TYPE + ".")
1225                         && info.getNetwork().equals(network)));
1226 
1227         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1228         client.stopServiceDiscovery(discListener);
1229         waitForIdle();
1230         verify(mDiscoveryManager).unregisterListener(eq(serviceTypeWithLocalDomain), any());
1231         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStopped(SERVICE_TYPE);
1232         verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
1233         verify(mMetrics).reportServiceDiscoveryStop(false /* isLegacy */, discId,
1234                 10L /* durationMs */, 1 /* foundCallbackCount */, 1 /* lostCallbackCount */,
1235                 1 /* servicesCount */, 3 /* sentQueryCount */, true /* isServiceFromCache */);
1236     }
1237 
1238     @Test
testDiscoveryWithMdnsDiscoveryManager_FailedWithInvalidServiceType()1239     public void testDiscoveryWithMdnsDiscoveryManager_FailedWithInvalidServiceType() {
1240         setMdnsDiscoveryManagerEnabled();
1241 
1242         final NsdManager client = connectClient(mService);
1243         final DiscoveryListener discListener = mock(DiscoveryListener.class);
1244         final Network network = new Network(999);
1245         final String invalidServiceType = "a_service";
1246         client.discoverServices(
1247                 invalidServiceType, PROTOCOL, network, r -> r.run(), discListener);
1248         waitForIdle();
1249         verify(discListener, timeout(TIMEOUT_MS))
1250                 .onStartDiscoveryFailed(invalidServiceType, FAILURE_INTERNAL_ERROR);
1251         verify(mMetrics, times(1)).reportServiceDiscoveryFailed(
1252                 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */);
1253 
1254         final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1255         client.discoverServices(
1256                 serviceTypeWithLocalDomain, PROTOCOL, network, r -> r.run(), discListener);
1257         waitForIdle();
1258         verify(discListener, timeout(TIMEOUT_MS))
1259                 .onStartDiscoveryFailed(serviceTypeWithLocalDomain, FAILURE_INTERNAL_ERROR);
1260         verify(mMetrics, times(2)).reportServiceDiscoveryFailed(
1261                 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */);
1262 
1263         final String serviceTypeWithoutTcpOrUdpEnding = "_test._com";
1264         client.discoverServices(
1265                 serviceTypeWithoutTcpOrUdpEnding, PROTOCOL, network, r -> r.run(), discListener);
1266         waitForIdle();
1267         verify(discListener, timeout(TIMEOUT_MS))
1268                 .onStartDiscoveryFailed(serviceTypeWithoutTcpOrUdpEnding, FAILURE_INTERNAL_ERROR);
1269         verify(mMetrics, times(3)).reportServiceDiscoveryFailed(
1270                 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */);
1271     }
1272 
1273     @Test
1274     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
testDiscoveryWithMdnsDiscoveryManager_UsesSubtypes()1275     public void testDiscoveryWithMdnsDiscoveryManager_UsesSubtypes() {
1276         final String typeWithSubtype = SERVICE_TYPE + ",_subtype";
1277         final NsdManager client = connectClient(mService);
1278         final NsdServiceInfo regInfo = new NsdServiceInfo("Instance", typeWithSubtype);
1279         final Network network = new Network(999);
1280         regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
1281         regInfo.setPort(12345);
1282         regInfo.setNetwork(network);
1283 
1284         final RegistrationListener regListener = mock(RegistrationListener.class);
1285         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1286         waitForIdle();
1287         verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(s ->
1288                 "Instance".equals(s.getServiceName())
1289                         && SERVICE_TYPE.equals(s.getServiceType())
1290                         && s.getSubtypes().equals(Set.of("_subtype"))), any(), anyInt());
1291 
1292         final DiscoveryListener discListener = mock(DiscoveryListener.class);
1293         client.discoverServices(typeWithSubtype, PROTOCOL, network, Runnable::run, discListener);
1294         waitForIdle();
1295         final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
1296                 ArgumentCaptor.forClass(MdnsSearchOptions.class);
1297         verify(mDiscoveryManager).registerListener(eq(SERVICE_TYPE + ".local"), any(),
1298                 optionsCaptor.capture());
1299         assertEquals(Collections.singletonList("subtype"), optionsCaptor.getValue().getSubtypes());
1300     }
1301 
1302     @Test
testResolutionWithMdnsDiscoveryManager()1303     public void testResolutionWithMdnsDiscoveryManager() throws UnknownHostException {
1304         setMdnsDiscoveryManagerEnabled();
1305 
1306         final NsdManager client = connectClient(mService);
1307         final ResolveListener resolveListener = mock(ResolveListener.class);
1308         final Network network = new Network(999);
1309         final String serviceType = "_nsd._service._tcp";
1310         final String constructedServiceType = "_service._tcp.local";
1311         final ArgumentCaptor<MdnsListener> listenerCaptor =
1312                 ArgumentCaptor.forClass(MdnsListener.class);
1313         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
1314         request.setNetwork(network);
1315         client.resolveService(request, resolveListener);
1316         waitForIdle();
1317         verify(mSocketProvider).startMonitoringSockets();
1318         final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
1319                 ArgumentCaptor.forClass(MdnsSearchOptions.class);
1320         verify(mDiscoveryManager).registerListener(eq(constructedServiceType),
1321                 listenerCaptor.capture(),
1322                 optionsCaptor.capture());
1323         assertEquals(network, optionsCaptor.getValue().getNetwork());
1324         // Subtypes are not used for resolution, only for discovery
1325         assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
1326 
1327         final MdnsListener listener = listenerCaptor.getValue();
1328         final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
1329                 SERVICE_NAME,
1330                 constructedServiceType.split("\\."),
1331                 List.of(), /* subtypes */
1332                 new String[]{"android", "local"}, /* hostName */
1333                 PORT,
1334                 List.of(IPV4_ADDRESS),
1335                 List.of("2001:db8::1", "2001:db8::2"),
1336                 List.of() /* textStrings */,
1337                 List.of(MdnsServiceInfo.TextEntry.fromBytes(new byte[]{
1338                         'k', 'e', 'y', '=', (byte) 0xFF, (byte) 0xFE})) /* textEntries */,
1339                 1234,
1340                 network,
1341                 Instant.ofEpochSecond(1000_000L) /* expirationTime */);
1342 
1343         // Verify onServiceFound callback
1344         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1345         listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */);
1346         final ArgumentCaptor<NsdServiceInfo> infoCaptor =
1347                 ArgumentCaptor.forClass(NsdServiceInfo.class);
1348         verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture());
1349         verify(mMetrics).reportServiceResolved(false /* isLegacy */, listener.mTransactionId,
1350                 10 /* durationMs */, true /* isServiceFromCache */, 0 /* sendQueryCount */);
1351 
1352         final NsdServiceInfo info = infoCaptor.getValue();
1353         assertEquals(SERVICE_NAME, info.getServiceName());
1354         assertEquals("._service._tcp", info.getServiceType());
1355         assertEquals(PORT, info.getPort());
1356         assertTrue(info.getAttributes().containsKey("key"));
1357         assertEquals(1, info.getAttributes().size());
1358         assertArrayEquals(new byte[]{(byte) 0xFF, (byte) 0xFE}, info.getAttributes().get("key"));
1359         assertEquals(parseNumericAddress(IPV4_ADDRESS), info.getHost());
1360         assertEquals(3, info.getHostAddresses().size());
1361         assertTrue(info.getHostAddresses().stream().anyMatch(
1362                 address -> address.equals(parseNumericAddress("2001:db8::1"))));
1363         assertTrue(info.getHostAddresses().stream().anyMatch(
1364                 address -> address.equals(parseNumericAddress("2001:db8::2"))));
1365         assertEquals(network, info.getNetwork());
1366         assertEquals(Instant.ofEpochSecond(1000_000L), info.getExpirationTime());
1367 
1368         // Verify the listener has been unregistered.
1369         verify(mDiscoveryManager, timeout(TIMEOUT_MS))
1370                 .unregisterListener(eq(constructedServiceType), any());
1371         verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
1372     }
1373 
1374     @Test
1375     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
1376     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testMdnsAdvertiserFeatureFlagging()1377     public void testMdnsAdvertiserFeatureFlagging() {
1378         // Create NsdService w/o feature enabled.
1379         final NsdManager client = connectClient(mService);
1380         final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1381         regInfo.setHost(parseNumericAddress("192.0.2.123"));
1382         regInfo.setPort(12345);
1383         final RegistrationListener regListenerWithoutFeature = mock(RegistrationListener.class);
1384         client.registerService(regInfo, PROTOCOL, regListenerWithoutFeature);
1385         waitForIdle();
1386 
1387         final ArgumentCaptor<Integer> legacyIdCaptor = ArgumentCaptor.forClass(Integer.class);
1388         verify(mMockMDnsM).registerService(legacyIdCaptor.capture(), any(), any(), anyInt(),
1389                 any(), anyInt());
1390         verifyNoMoreInteractions(mAdvertiser);
1391 
1392         setMdnsAdvertiserEnabled();
1393         final RegistrationListener regListenerWithFeature = mock(RegistrationListener.class);
1394         client.registerService(regInfo, PROTOCOL, regListenerWithFeature);
1395         waitForIdle();
1396 
1397         final ArgumentCaptor<Integer> serviceIdCaptor = ArgumentCaptor.forClass(Integer.class);
1398         verify(mAdvertiser).addOrUpdateService(serviceIdCaptor.capture(),
1399                 argThat(info -> matches(info, regInfo)), any(), anyInt());
1400 
1401         client.unregisterService(regListenerWithoutFeature);
1402         waitForIdle();
1403         verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue());
1404         verify(mAdvertiser, never()).removeService(anyInt());
1405 
1406         doReturn(mock(MdnsAdvertiser.AdvertiserMetrics.class))
1407                 .when(mAdvertiser).getAdvertiserMetrics(anyInt());
1408         client.unregisterService(regListenerWithFeature);
1409         waitForIdle();
1410         verify(mAdvertiser).removeService(serviceIdCaptor.getValue());
1411     }
1412 
1413     @Test
1414     @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
1415     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testTypeSpecificFeatureFlagging()1416     public void testTypeSpecificFeatureFlagging() {
1417         doReturn("_type1._tcp:flag1,_type2._tcp:flag2").when(mDeps).getTypeAllowlistFlags();
1418         doReturn(true).when(mDeps).isFeatureEnabled(any(),
1419                 eq("mdns_discovery_manager_allowlist_flag1_version"));
1420         doReturn(true).when(mDeps).isFeatureEnabled(any(),
1421                 eq("mdns_advertiser_allowlist_flag2_version"));
1422 
1423         final NsdManager client = connectClient(mService);
1424         final NsdServiceInfo service1 = new NsdServiceInfo(SERVICE_NAME, "_type1._tcp");
1425         service1.setHostAddresses(List.of(parseNumericAddress("2001:db8::123")));
1426         service1.setPort(1234);
1427         final NsdServiceInfo service2 = new NsdServiceInfo(SERVICE_NAME, "_type2._tcp");
1428         service1.setHostAddresses(List.of(parseNumericAddress("2001:db8::123")));
1429         service2.setPort(1234);
1430 
1431         client.discoverServices(service1.getServiceType(),
1432                 NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class));
1433         client.discoverServices(service2.getServiceType(),
1434                 NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class));
1435         waitForIdle();
1436 
1437         // The DiscoveryManager is enabled for _type1 but not _type2
1438         verify(mDiscoveryManager).registerListener(eq("_type1._tcp.local"), any(), any());
1439         verify(mDiscoveryManager, never()).registerListener(
1440                 eq("_type2._tcp.local"), any(), any());
1441 
1442         client.resolveService(service1, mock(ResolveListener.class));
1443         client.resolveService(service2, mock(ResolveListener.class));
1444         waitForIdle();
1445 
1446         // Same behavior for resolve
1447         verify(mDiscoveryManager, times(2)).registerListener(
1448                 eq("_type1._tcp.local"), any(), any());
1449         verify(mDiscoveryManager, never()).registerListener(
1450                 eq("_type2._tcp.local"), any(), any());
1451 
1452         client.registerService(service1, NsdManager.PROTOCOL_DNS_SD,
1453                 mock(RegistrationListener.class));
1454         client.registerService(service2, NsdManager.PROTOCOL_DNS_SD,
1455                 mock(RegistrationListener.class));
1456         waitForIdle();
1457 
1458         // The advertiser is enabled for _type2 but not _type1
1459         verify(mAdvertiser, never()).addOrUpdateService(anyInt(),
1460                 argThat(info -> matches(info, service1)), any(), anyInt());
1461         verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(info -> matches(info, service2)),
1462                 any(), anyInt());
1463     }
1464 
1465     @Test
testAdvertiseWithMdnsAdvertiser()1466     public void testAdvertiseWithMdnsAdvertiser() {
1467         setMdnsAdvertiserEnabled();
1468 
1469         final NsdManager client = connectClient(mService);
1470         final RegistrationListener regListener = mock(RegistrationListener.class);
1471         // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1472         final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
1473                 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
1474         verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
1475 
1476         final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1477         regInfo.setHost(parseNumericAddress("192.0.2.123"));
1478         regInfo.setPort(12345);
1479         regInfo.setAttribute("testattr", "testvalue");
1480         regInfo.setNetwork(new Network(999));
1481 
1482         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1483         waitForIdle();
1484         verify(mSocketProvider).startMonitoringSockets();
1485         final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
1486         verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), argThat(info ->
1487                 matches(info, regInfo)), any(), anyInt());
1488 
1489         // Verify onServiceRegistered callback
1490         final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
1491         final int regId = idCaptor.getValue();
1492         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1493         cb.onRegisterServiceSucceeded(regId, regInfo);
1494 
1495         verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(argThat(info -> matches(info,
1496                 new NsdServiceInfo(regInfo.getServiceName(), null))));
1497         verify(mMetrics).reportServiceRegistrationSucceeded(
1498                 false /* isLegacy */, regId, 10L /* durationMs */);
1499 
1500         final MdnsAdvertiser.AdvertiserMetrics metrics = new MdnsAdvertiser.AdvertiserMetrics(
1501                 50 /* repliedRequestCount */, 100 /* sentPacketCount */,
1502                 3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
1503         doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime();
1504         doReturn(metrics).when(mAdvertiser).getAdvertiserMetrics(regId);
1505         client.unregisterService(regListener);
1506         waitForIdle();
1507         verify(mAdvertiser).removeService(idCaptor.getValue());
1508         verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
1509                 argThat(info -> matches(info, regInfo)));
1510         verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive();
1511         verify(mMetrics).reportServiceUnregistration(false /* isLegacy */, regId,
1512                 100L /* durationMs */, 50 /* repliedRequestCount */, 100 /* sentPacketCount */,
1513                 3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
1514     }
1515 
1516     @Test
testAdvertiseWithMdnsAdvertiser_FailedWithInvalidServiceType()1517     public void testAdvertiseWithMdnsAdvertiser_FailedWithInvalidServiceType() {
1518         setMdnsAdvertiserEnabled();
1519 
1520         final NsdManager client = connectClient(mService);
1521         final RegistrationListener regListener = mock(RegistrationListener.class);
1522         // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1523         final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
1524                 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
1525         verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
1526 
1527         final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, "invalid_type");
1528         regInfo.setHost(parseNumericAddress("192.0.2.123"));
1529         regInfo.setPort(12345);
1530         regInfo.setAttribute("testattr", "testvalue");
1531         regInfo.setNetwork(new Network(999));
1532 
1533         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1534         waitForIdle();
1535         verify(mAdvertiser, never()).addOrUpdateService(anyInt(), any(), any(), anyInt());
1536 
1537         verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
1538                 argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
1539         verify(mMetrics).reportServiceRegistrationFailed(
1540                 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */);
1541     }
1542 
1543     @Test
testAdvertiseWithMdnsAdvertiser_LongServiceName()1544     public void testAdvertiseWithMdnsAdvertiser_LongServiceName() {
1545         setMdnsAdvertiserEnabled();
1546 
1547         final NsdManager client = connectClient(mService);
1548         final RegistrationListener regListener = mock(RegistrationListener.class);
1549         // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
1550         final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
1551                 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
1552         verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
1553 
1554         final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE);
1555         regInfo.setHost(parseNumericAddress("192.0.2.123"));
1556         regInfo.setPort(12345);
1557         regInfo.setAttribute("testattr", "testvalue");
1558         regInfo.setNetwork(new Network(999));
1559 
1560         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1561         waitForIdle();
1562         final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
1563         // Service name is truncated to 63 characters
1564         verify(mAdvertiser)
1565                 .addOrUpdateService(
1566                         idCaptor.capture(),
1567                         argThat(info -> info.getServiceName().equals("a".repeat(63))),
1568                         any(),
1569                         anyInt());
1570 
1571         // Verify onServiceRegistered callback
1572         final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
1573         final int regId = idCaptor.getValue();
1574         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1575         cb.onRegisterServiceSucceeded(regId, regInfo);
1576 
1577         verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(
1578                 argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null))));
1579         verify(mMetrics).reportServiceRegistrationSucceeded(
1580                 false /* isLegacy */, regId, 10L /* durationMs */);
1581     }
1582 
1583     @Test
testAdvertiseCustomTtl_validTtl_success()1584     public void testAdvertiseCustomTtl_validTtl_success() {
1585         runValidTtlAdvertisingTest(30L);
1586         runValidTtlAdvertisingTest(10 * 3600L);
1587     }
1588 
1589     @Test
testAdvertiseCustomTtl_ttlSmallerThan30SecondsButClientIsSystemServer_success()1590     public void testAdvertiseCustomTtl_ttlSmallerThan30SecondsButClientIsSystemServer_success() {
1591         when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID);
1592 
1593         runValidTtlAdvertisingTest(29L);
1594     }
1595 
1596     @Test
testAdvertiseCustomTtl_ttlLargerThan10HoursButClientIsSystemServer_success()1597     public void testAdvertiseCustomTtl_ttlLargerThan10HoursButClientIsSystemServer_success() {
1598         when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID);
1599 
1600         runValidTtlAdvertisingTest(10 * 3600L + 1);
1601         runValidTtlAdvertisingTest(0xffffffffL);
1602     }
1603 
runValidTtlAdvertisingTest(long validTtlSeconds)1604     private void runValidTtlAdvertisingTest(long validTtlSeconds) {
1605         setMdnsAdvertiserEnabled();
1606 
1607         final NsdManager client = connectClient(mService);
1608         final RegistrationListener regListener = mock(RegistrationListener.class);
1609         final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
1610                 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
1611         verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
1612 
1613         final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE);
1614         regInfo.setPort(1234);
1615         final AdvertisingRequest request =
1616                 new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD)
1617                     .setTtl(Duration.ofSeconds(validTtlSeconds)).build();
1618 
1619         client.registerService(request, Runnable::run, regListener);
1620         waitForIdle();
1621 
1622         final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
1623         final MdnsAdvertisingOptions expectedAdverstingOptions =
1624                 MdnsAdvertisingOptions.newBuilder().setTtl(request.getTtl()).build();
1625         verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), any(),
1626                 eq(expectedAdverstingOptions), anyInt());
1627 
1628         // Verify onServiceRegistered callback
1629         final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
1630         final int regId = idCaptor.getValue();
1631         cb.onRegisterServiceSucceeded(regId, regInfo);
1632 
1633         verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(
1634                 argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null))));
1635     }
1636 
1637     @Test
testAdvertiseCustomTtl_invalidTtl_FailsWithBadParameters()1638     public void testAdvertiseCustomTtl_invalidTtl_FailsWithBadParameters() {
1639         setMdnsAdvertiserEnabled();
1640         final long invalidTtlSeconds = 29L;
1641         final NsdManager client = connectClient(mService);
1642         final RegistrationListener regListener = mock(RegistrationListener.class);
1643         final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
1644                 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
1645         verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any());
1646 
1647         final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE);
1648         regInfo.setPort(1234);
1649         final AdvertisingRequest request =
1650                 new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD)
1651                     .setTtl(Duration.ofSeconds(invalidTtlSeconds)).build();
1652         client.registerService(request, Runnable::run, regListener);
1653         waitForIdle();
1654 
1655         verify(regListener, timeout(TIMEOUT_MS))
1656                 .onRegistrationFailed(any(), eq(FAILURE_BAD_PARAMETERS));
1657     }
1658 
1659     @Test
testStopServiceResolutionWithMdnsDiscoveryManager()1660     public void testStopServiceResolutionWithMdnsDiscoveryManager() {
1661         setMdnsDiscoveryManagerEnabled();
1662 
1663         final NsdManager client = connectClient(mService);
1664         final ResolveListener resolveListener = mock(ResolveListener.class);
1665         final Network network = new Network(999);
1666         final String serviceType = "_nsd._service._tcp";
1667         final String constructedServiceType = "_service._tcp.local";
1668         final ArgumentCaptor<MdnsListener> listenerCaptor =
1669                 ArgumentCaptor.forClass(MdnsListener.class);
1670         final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
1671         request.setNetwork(network);
1672         client.resolveService(request, resolveListener);
1673         waitForIdle();
1674         verify(mSocketProvider).startMonitoringSockets();
1675         final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
1676                 ArgumentCaptor.forClass(MdnsSearchOptions.class);
1677         verify(mDiscoveryManager).registerListener(eq(constructedServiceType),
1678                 listenerCaptor.capture(),
1679                 optionsCaptor.capture());
1680         assertEquals(network, optionsCaptor.getValue().getNetwork());
1681         // Subtypes are not used for resolution, only for discovery
1682         assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
1683 
1684         final MdnsListener listener = listenerCaptor.getValue();
1685         // Callbacks for query sent.
1686         listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
1687 
1688         doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
1689         client.stopServiceResolution(resolveListener);
1690         waitForIdle();
1691 
1692         // Verify the listener has been unregistered.
1693         verify(mDiscoveryManager, timeout(TIMEOUT_MS))
1694                 .unregisterListener(eq(constructedServiceType), eq(listener));
1695         verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
1696                 request.getServiceName().equals(ns.getServiceName())
1697                         && request.getServiceType().equals(ns.getServiceType())));
1698         verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
1699         verify(mMetrics).reportServiceResolutionStop(false /* isLegacy */, listener.mTransactionId,
1700                 10L /* durationMs */, 1 /* sentQueryCount */);
1701     }
1702 
1703     @Test
testParseTypeAndSubtype()1704     public void testParseTypeAndSubtype() {
1705         final String serviceType1 = "test._tcp";
1706         final String serviceType2 = "_test._quic";
1707         final String serviceType3 = "_test._quic,_test1,_test2";
1708         final String serviceType4 = "_123._udp.";
1709         final String serviceType5 = "_TEST._999._tcp.";
1710         final String serviceType6 = "_998._tcp.,_TEST";
1711         final String serviceType7 = "_997._tcp,_TEST";
1712         final String serviceType8 = "_997._tcp,_test1,_test2,_test3";
1713         final String serviceType9 = "_test4._997._tcp,_test1,_test2,_test3";
1714 
1715         assertNull(parseTypeAndSubtype(serviceType1));
1716         assertNull(parseTypeAndSubtype(serviceType2));
1717         assertNull(parseTypeAndSubtype(serviceType3));
1718         assertEquals(new Pair<>("_123._udp", Collections.emptyList()),
1719                 parseTypeAndSubtype(serviceType4));
1720         assertEquals(new Pair<>("_999._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType5));
1721         assertEquals(new Pair<>("_998._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType6));
1722         assertEquals(new Pair<>("_997._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType7));
1723 
1724         assertEquals(new Pair<>("_997._tcp", List.of("_test1", "_test2", "_test3")),
1725                 parseTypeAndSubtype(serviceType8));
1726         assertEquals(new Pair<>("_997._tcp", List.of("_test4")),
1727                 parseTypeAndSubtype(serviceType9));
1728     }
1729 
1730     @Test
TestCheckHostname()1731     public void TestCheckHostname() {
1732         // Valid cases
1733         assertTrue(checkHostname(null));
1734         assertTrue(checkHostname("a"));
1735         assertTrue(checkHostname("1"));
1736         assertTrue(checkHostname("a-1234-bbbb-cccc000"));
1737         assertTrue(checkHostname("A-1234-BBbb-CCCC000"));
1738         assertTrue(checkHostname("1234-bbbb-cccc000"));
1739         assertTrue(checkHostname("0123456789abcdef"
1740                                 + "0123456789abcdef"
1741                                 + "0123456789abcdef"
1742                                 + "0123456789abcde" // 63 characters
1743                         ));
1744 
1745         // Invalid cases
1746         assertFalse(checkHostname("?"));
1747         assertFalse(checkHostname("/"));
1748         assertFalse(checkHostname("a-"));
1749         assertFalse(checkHostname("B-"));
1750         assertFalse(checkHostname("-A"));
1751         assertFalse(checkHostname("-b"));
1752         assertFalse(checkHostname("-1-"));
1753         assertFalse(checkHostname("0123456789abcdef"
1754                                 + "0123456789abcdef"
1755                                 + "0123456789abcdef"
1756                                 + "0123456789abcdef" // 64 characters
1757                         ));
1758     }
1759 
1760     @Test
1761     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
testEnablePlatformMdnsBackend()1762     public void testEnablePlatformMdnsBackend() {
1763         final NsdManager client = connectClient(mService);
1764         final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE);
1765         final Network network = new Network(999);
1766         regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
1767         regInfo.setPort(12345);
1768         regInfo.setAttribute("testattr", "testvalue");
1769         regInfo.setNetwork(network);
1770 
1771         // Verify the registration uses MdnsAdvertiser
1772         final RegistrationListener regListener = mock(RegistrationListener.class);
1773         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1774         waitForIdle();
1775         verify(mSocketProvider).startMonitoringSockets();
1776         verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), anyInt());
1777 
1778         // Verify the discovery uses MdnsDiscoveryManager
1779         final DiscoveryListener discListener = mock(DiscoveryListener.class);
1780         client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener);
1781         waitForIdle();
1782         verify(mDiscoveryManager).registerListener(anyString(), any(), any());
1783 
1784         // Verify the discovery uses MdnsDiscoveryManager
1785         final ResolveListener resolveListener = mock(ResolveListener.class);
1786         client.resolveService(regInfo, r -> r.run(), resolveListener);
1787         waitForIdle();
1788         verify(mDiscoveryManager, times(2)).registerListener(anyString(), any(), any());
1789     }
1790 
1791     @Test
1792     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
testTakeMulticastLockOnBehalfOfClient_ForWifiNetworksOnly()1793     public void testTakeMulticastLockOnBehalfOfClient_ForWifiNetworksOnly() {
1794         // Test on one client in the foreground
1795         mUidImportanceListener.onUidImportance(123, IMPORTANCE_FOREGROUND);
1796         doReturn(123).when(mDeps).getCallingUid();
1797         final NsdManager client = connectClient(mService);
1798 
1799         final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1800         regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
1801         regInfo.setPort(12345);
1802         // File a request for all networks
1803         regInfo.setNetwork(null);
1804 
1805         final RegistrationListener regListener = mock(RegistrationListener.class);
1806         client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1807         waitForIdle();
1808         verify(mSocketProvider).startMonitoringSockets();
1809         verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), anyInt());
1810 
1811         final Network wifiNetwork1 = new Network(123);
1812         final Network wifiNetwork2 = new Network(124);
1813         final Network ethernetNetwork = new Network(125);
1814 
1815         final MdnsInterfaceSocket wifiNetworkSocket1 = mock(MdnsInterfaceSocket.class);
1816         final MdnsInterfaceSocket wifiNetworkSocket2 = mock(MdnsInterfaceSocket.class);
1817         final MdnsInterfaceSocket ethernetNetworkSocket = mock(MdnsInterfaceSocket.class);
1818 
1819         // Nothing happens for networks with no transports, no Wi-Fi transport, or VPN transport
1820         mHandler.post(() -> {
1821             mSocketRequestMonitor.onSocketRequestFulfilled(
1822                     new Network(125), mock(MdnsInterfaceSocket.class), new int[0]);
1823             mSocketRequestMonitor.onSocketRequestFulfilled(
1824                     ethernetNetwork, ethernetNetworkSocket,
1825                     new int[] { TRANSPORT_ETHERNET });
1826             mSocketRequestMonitor.onSocketRequestFulfilled(
1827                     new Network(127), mock(MdnsInterfaceSocket.class),
1828                     new int[] { TRANSPORT_WIFI, TRANSPORT_VPN });
1829         });
1830         waitForIdle();
1831         verify(mWifiManager, never()).createMulticastLock(any());
1832 
1833         // First Wi-Fi network
1834         mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
1835                 wifiNetwork1, wifiNetworkSocket1, new int[] { TRANSPORT_WIFI }));
1836         waitForIdle();
1837         verify(mWifiManager).createMulticastLock(any());
1838         verify(mMulticastLock).acquire();
1839 
1840         // Second Wi-Fi network
1841         mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
1842                 wifiNetwork2, wifiNetworkSocket2, new int[] { TRANSPORT_WIFI }));
1843         waitForIdle();
1844         verifyNoMoreInteractions(mMulticastLock);
1845 
1846         // One Wi-Fi network becomes unused, nothing happens
1847         mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
1848                 wifiNetwork1, wifiNetworkSocket1));
1849         waitForIdle();
1850         verifyNoMoreInteractions(mMulticastLock);
1851 
1852         // Ethernet network becomes unused, still nothing
1853         mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
1854                 ethernetNetwork, ethernetNetworkSocket));
1855         waitForIdle();
1856         verifyNoMoreInteractions(mMulticastLock);
1857 
1858         // The second Wi-Fi network becomes unused, the lock is released
1859         mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
1860                 wifiNetwork2, wifiNetworkSocket2));
1861         waitForIdle();
1862         verify(mMulticastLock).release();
1863     }
1864 
1865     @Test
1866     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
testTakeMulticastLockOnBehalfOfClient_ForForegroundAppsOnly()1867     public void testTakeMulticastLockOnBehalfOfClient_ForForegroundAppsOnly() {
1868         final int uid1 = 12;
1869         final int uid2 = 34;
1870         final int uid3 = 56;
1871         final int uid4 = 78;
1872         final InOrder lockOrder = inOrder(mMulticastLock);
1873         // Connect one client without any foreground info
1874         doReturn(uid1).when(mDeps).getCallingUid();
1875         final NsdManager client1 = connectClient(mService);
1876 
1877         // Connect client2 as visible, but not foreground
1878         mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_VISIBLE);
1879         waitForIdle();
1880         doReturn(uid2).when(mDeps).getCallingUid();
1881         final NsdManager client2 = connectClient(mService);
1882 
1883         // Connect client3, client4 as foreground
1884         mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_FOREGROUND);
1885         waitForIdle();
1886         doReturn(uid3).when(mDeps).getCallingUid();
1887         final NsdManager client3 = connectClient(mService);
1888 
1889         mUidImportanceListener.onUidImportance(uid4, IMPORTANCE_FOREGROUND);
1890         waitForIdle();
1891         doReturn(uid4).when(mDeps).getCallingUid();
1892         final NsdManager client4 = connectClient(mService);
1893 
1894         // First client advertises on any network
1895         final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1896         regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
1897         regInfo.setPort(12345);
1898         regInfo.setNetwork(null);
1899         final RegistrationListener regListener = mock(RegistrationListener.class);
1900         client1.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
1901         waitForIdle();
1902 
1903         final MdnsInterfaceSocket wifiSocket = mock(MdnsInterfaceSocket.class);
1904         final Network wifiNetwork = new Network(123);
1905 
1906         final MdnsInterfaceSocket ethSocket = mock(MdnsInterfaceSocket.class);
1907         final Network ethNetwork = new Network(234);
1908 
1909         mHandler.post(() -> {
1910             mSocketRequestMonitor.onSocketRequestFulfilled(
1911                     wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI });
1912             mSocketRequestMonitor.onSocketRequestFulfilled(
1913                     ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET });
1914         });
1915         waitForIdle();
1916 
1917         // No multicast lock since client1 has no foreground info
1918         lockOrder.verifyNoMoreInteractions();
1919 
1920         // Second client discovers specifically on the Wi-Fi network
1921         final DiscoveryListener discListener = mock(DiscoveryListener.class);
1922         client2.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, wifiNetwork,
1923                 Runnable::run, discListener);
1924         waitForIdle();
1925         mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
1926                 wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI }));
1927         waitForIdle();
1928         // No multicast lock since client2 is not visible enough
1929         lockOrder.verifyNoMoreInteractions();
1930 
1931         // Third client registers a callback on all networks
1932         final NsdServiceInfo cbInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1933         cbInfo.setNetwork(null);
1934         final ServiceInfoCallback infoCb = mock(ServiceInfoCallback.class);
1935         client3.registerServiceInfoCallback(cbInfo, Runnable::run, infoCb);
1936         waitForIdle();
1937         mHandler.post(() -> {
1938             mSocketRequestMonitor.onSocketRequestFulfilled(
1939                     wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI });
1940             mSocketRequestMonitor.onSocketRequestFulfilled(
1941                     ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET });
1942         });
1943         waitForIdle();
1944 
1945         // Multicast lock is taken for third client
1946         lockOrder.verify(mMulticastLock).acquire();
1947 
1948         // Client3 goes to the background
1949         mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_CACHED);
1950         waitForIdle();
1951         lockOrder.verify(mMulticastLock).release();
1952 
1953         // client4 resolves on a different network
1954         final ResolveListener resolveListener = mock(ResolveListener.class);
1955         final NsdServiceInfo resolveInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
1956         resolveInfo.setNetwork(ethNetwork);
1957         client4.resolveService(resolveInfo, Runnable::run, resolveListener);
1958         waitForIdle();
1959         mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
1960                 ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET }));
1961         waitForIdle();
1962 
1963         // client4 is foreground, but not Wi-Fi
1964         lockOrder.verifyNoMoreInteractions();
1965 
1966         // Second client becomes foreground
1967         mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_FOREGROUND);
1968         waitForIdle();
1969 
1970         lockOrder.verify(mMulticastLock).acquire();
1971 
1972         // Second client is lost
1973         mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_GONE);
1974         waitForIdle();
1975 
1976         lockOrder.verify(mMulticastLock).release();
1977     }
1978 
1979     @Test
testNullINsdManagerCallback()1980     public void testNullINsdManagerCallback() {
1981         final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) {
1982             @Override
1983             public INsdServiceConnector connect(INsdManagerCallback baseCb,
1984                     boolean runNewMdnsBackend) {
1985                 // Pass null INsdManagerCallback
1986                 return super.connect(null /* cb */, runNewMdnsBackend);
1987             }
1988         };
1989 
1990         assertThrows(IllegalArgumentException.class, () -> new NsdManager(mContext, service));
1991     }
1992 
1993     @Test
1994     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
1995     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
testRegisterOffloadEngine_checkPermission_V()1996     public void testRegisterOffloadEngine_checkPermission_V() {
1997         final NsdManager client = connectClient(mService);
1998         final OffloadEngine offloadEngine = mock(OffloadEngine.class);
1999         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK);
2000         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
2001                 PERMISSION_MAINLINE_NETWORK_STACK);
2002         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS);
2003         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
2004                 REGISTER_NSD_OFFLOAD_ENGINE);
2005 
2006         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
2007                 REGISTER_NSD_OFFLOAD_ENGINE);
2008         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER);
2009         assertThrows(SecurityException.class,
2010                 () -> client.registerOffloadEngine("iface1", OffloadEngine.OFFLOAD_TYPE_REPLY,
2011                         OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
2012                         offloadEngine));
2013         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
2014                 REGISTER_NSD_OFFLOAD_ENGINE);
2015         final OffloadEngine offloadEngine2 = mock(OffloadEngine.class);
2016         client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY,
2017                 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
2018                 offloadEngine2);
2019         client.unregisterOffloadEngine(offloadEngine2);
2020     }
2021 
2022     @Test
2023     @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
2024     @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
2025     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
testRegisterOffloadEngine_checkPermission_U()2026     public void testRegisterOffloadEngine_checkPermission_U() {
2027         final NsdManager client = connectClient(mService);
2028         final OffloadEngine offloadEngine = mock(OffloadEngine.class);
2029         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK);
2030         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
2031                 PERMISSION_MAINLINE_NETWORK_STACK);
2032         doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS);
2033         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
2034                 REGISTER_NSD_OFFLOAD_ENGINE);
2035 
2036         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER);
2037         client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY,
2038                 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run,
2039                 offloadEngine);
2040         client.unregisterOffloadEngine(offloadEngine);
2041     }
2042 
2043 
waitForIdle()2044     private void waitForIdle() {
2045         HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
2046     }
2047 
makeService()2048     NsdService makeService() {
2049         final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) {
2050             @Override
2051             public INsdServiceConnector connect(INsdManagerCallback baseCb,
2052                     boolean runNewMdnsBackend) {
2053                 // Wrap the callback in a transparent mock, to mock asBinder returning a
2054                 // LinkToDeathRecorder. This will allow recording the binder death recipient
2055                 // registered on the callback. Use a transparent mock and not a spy as the actual
2056                 // implementation class is not public and cannot be spied on by Mockito.
2057                 final INsdManagerCallback cb = mock(INsdManagerCallback.class,
2058                         AdditionalAnswers.delegatesTo(baseCb));
2059                 doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
2060                 mCreatedCallbacks.add(cb);
2061                 return super.connect(cb, runNewMdnsBackend);
2062             }
2063         };
2064         return service;
2065     }
2066 
getCallback()2067     private INsdManagerCallback getCallback() {
2068         return mCreatedCallbacks.remove();
2069     }
2070 
connectClient(NsdService service)2071     NsdManager connectClient(NsdService service) {
2072         final NsdManager nsdManager = new NsdManager(mContext, service);
2073         // Wait for client registration done.
2074         waitForIdle();
2075         return nsdManager;
2076     }
2077 
verifyDelayMaybeStopDaemon(long cleanupDelayMs)2078     void verifyDelayMaybeStopDaemon(long cleanupDelayMs) throws Exception {
2079         waitForIdle();
2080         // Stop daemon shouldn't be called immediately.
2081         verify(mMockMDnsM, never()).unregisterEventListener(any());
2082         verify(mMockMDnsM, never()).stopDaemon();
2083 
2084         // Clean up the daemon after CLEANUP_DELAY_MS.
2085         verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).unregisterEventListener(any());
2086         verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).stopDaemon();
2087     }
2088 
2089     /**
2090      * Return true if two service info are the same.
2091      *
2092      * Useful for argument matchers as {@link NsdServiceInfo} does not implement equals.
2093      */
matches(NsdServiceInfo a, NsdServiceInfo b)2094     private boolean matches(NsdServiceInfo a, NsdServiceInfo b) {
2095         return Objects.equals(a.getServiceName(), b.getServiceName())
2096                 && Objects.equals(a.getServiceType(), b.getServiceType())
2097                 && Objects.equals(a.getHost(), b.getHost())
2098                 && Objects.equals(a.getNetwork(), b.getNetwork())
2099                 && Objects.equals(a.getAttributes(), b.getAttributes());
2100     }
2101 
2102     public static class TestHandler extends Handler {
2103         public Message lastMessage;
2104 
TestHandler(Looper looper)2105         TestHandler(Looper looper) {
2106             super(looper);
2107         }
2108 
2109         @Override
handleMessage(Message msg)2110         public void handleMessage(Message msg) {
2111             lastMessage = obtainMessage();
2112             lastMessage.copyFrom(msg);
2113         }
2114     }
2115 }
2116