• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.net.nsd;
18 
19 import static android.net.InetAddresses.parseNumericAddress;
20 import static android.net.nsd.NsdManager.checkServiceInfoForRegistration;
21 
22 import static com.android.net.module.util.HexDump.hexStringToByteArray;
23 
24 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
25 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.fail;
29 import static org.mockito.ArgumentMatchers.anyBoolean;
30 import static org.mockito.Mockito.any;
31 import static org.mockito.Mockito.anyInt;
32 import static org.mockito.Mockito.doReturn;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.reset;
36 import static org.mockito.Mockito.timeout;
37 import static org.mockito.Mockito.times;
38 import static org.mockito.Mockito.verify;
39 
40 import android.compat.testing.PlatformCompatChangeRule;
41 import android.content.Context;
42 import android.net.connectivity.ConnectivityCompatChanges;
43 import android.os.Build;
44 
45 import androidx.test.filters.SmallTest;
46 
47 import com.android.modules.utils.build.SdkLevel;
48 import com.android.testutils.DevSdkIgnoreRule;
49 import com.android.testutils.DevSdkIgnoreRunner;
50 import com.android.testutils.FunctionalUtils.ThrowingConsumer;
51 
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.rules.TestRule;
56 import org.junit.runner.RunWith;
57 import org.mockito.ArgumentCaptor;
58 import org.mockito.Mock;
59 import org.mockito.MockitoAnnotations;
60 
61 import java.net.InetAddress;
62 import java.util.Collections;
63 import java.util.List;
64 import java.time.Duration;
65 
66 @DevSdkIgnoreRunner.MonitorThreadLeak
67 @RunWith(DevSdkIgnoreRunner.class)
68 @SmallTest
69 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
70 public class NsdManagerTest {
71 
72     static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
73 
74     @Rule
75     public TestRule compatChangeRule = new PlatformCompatChangeRule();
76 
77     @Mock Context mContext;
78     @Mock INsdManager mService;
79     @Mock INsdServiceConnector mServiceConn;
80 
81     NsdManager mManager;
82     INsdManagerCallback mCallback;
83 
84     long mTimeoutMs = 200; // non-final so that tests can adjust the value.
85 
86     @Before
setUp()87     public void setUp() throws Exception {
88         MockitoAnnotations.initMocks(this);
89 
90         doReturn(mServiceConn).when(mService).connect(any(), anyBoolean());
91         mManager = new NsdManager(mContext, mService);
92         final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
93                 INsdManagerCallback.class);
94         verify(mService).connect(cbCaptor.capture(), anyBoolean());
95         mCallback = cbCaptor.getValue();
96     }
97 
98     @Test
99     @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testResolveServiceS()100     public void testResolveServiceS() throws Exception {
101         verifyDaemonStarted(/* targetSdkPreS= */ false);
102         doTestResolveService();
103     }
104 
105     @Test
106     @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testResolveServicePreS()107     public void testResolveServicePreS() throws Exception {
108         verifyDaemonStarted(/* targetSdkPreS= */ true);
109         doTestResolveService();
110     }
111 
112     @Test
113     @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testDiscoverServiceS()114     public void testDiscoverServiceS() throws Exception {
115         verifyDaemonStarted(/* targetSdkPreS= */ false);
116         doTestDiscoverService();
117     }
118 
119     @Test
120     @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testDiscoverServicePreS()121     public void testDiscoverServicePreS() throws Exception {
122         verifyDaemonStarted(/* targetSdkPreS= */ true);
123         doTestDiscoverService();
124     }
125 
126     @Test
127     @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testParallelResolveServiceS()128     public void testParallelResolveServiceS() throws Exception {
129         verifyDaemonStarted(/* targetSdkPreS= */ false);
130         doTestParallelResolveService();
131     }
132 
133     @Test
134     @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testParallelResolveServicePreS()135     public void testParallelResolveServicePreS() throws Exception {
136         verifyDaemonStarted(/* targetSdkPreS= */ true);
137         doTestParallelResolveService();
138     }
139 
140     @Test
141     @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testInvalidCallsS()142     public void testInvalidCallsS() throws Exception {
143         verifyDaemonStarted(/* targetSdkPreS= */ false);
144         doTestInvalidCalls();
145     }
146 
147     @Test
148     @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testInvalidCallsPreS()149     public void testInvalidCallsPreS() throws Exception {
150         verifyDaemonStarted(/* targetSdkPreS= */ true);
151         doTestInvalidCalls();
152     }
153 
154     @Test
155     @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testRegisterServiceS()156     public void testRegisterServiceS() throws Exception {
157         verifyDaemonStarted(/* targetSdkPreS= */ false);
158         doTestRegisterService();
159     }
160 
161     @Test
162     @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
testRegisterServicePreS()163     public void testRegisterServicePreS() throws Exception {
164         verifyDaemonStarted(/* targetSdkPreS= */ true);
165         doTestRegisterService();
166     }
167 
verifyDaemonStarted(boolean targetSdkPreS)168     private void verifyDaemonStarted(boolean targetSdkPreS) throws Exception {
169         if (targetSdkPreS && !SdkLevel.isAtLeastV()) {
170             verify(mServiceConn).startDaemon();
171         } else {
172             verify(mServiceConn, never()).startDaemon();
173         }
174     }
175 
doTestResolveService()176     private void doTestResolveService() throws Exception {
177         NsdManager manager = mManager;
178 
179         NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
180         NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
181         NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
182 
183         manager.resolveService(request, listener);
184         int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
185         int err = 33;
186         mCallback.onResolveServiceFailed(key1, err);
187         verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
188 
189         manager.resolveService(request, listener);
190         int key2 = getRequestKey(req ->
191                 verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
192         mCallback.onResolveServiceSucceeded(key2, reply);
193         verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
194     }
195 
doTestParallelResolveService()196     private void doTestParallelResolveService() throws Exception {
197         NsdManager manager = mManager;
198 
199         NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
200         NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
201 
202         NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class);
203         NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
204 
205         manager.resolveService(request, listener1);
206         int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
207 
208         manager.resolveService(request, listener2);
209         int key2 = getRequestKey(req ->
210                 verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
211 
212         mCallback.onResolveServiceSucceeded(key2, reply);
213         mCallback.onResolveServiceSucceeded(key1, reply);
214 
215         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
216         verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
217     }
218 
219     @Test
testRegisterServiceWithAdvertisingRequest()220     public void testRegisterServiceWithAdvertisingRequest() throws Exception {
221         final NsdManager manager = mManager;
222         final NsdServiceInfo request = new NsdServiceInfo("another_name2", "another_type2");
223         request.setPort(2203);
224         final AdvertisingRequest advertisingRequest = new AdvertisingRequest.Builder(request,
225                 PROTOCOL).build();
226         final NsdManager.RegistrationListener listener = mock(
227                 NsdManager.RegistrationListener.class);
228 
229         manager.registerService(advertisingRequest, Runnable::run, listener);
230         int key4 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
231         mCallback.onRegisterServiceSucceeded(key4, request);
232         verify(listener, timeout(mTimeoutMs).times(1)).onServiceRegistered(request);
233     }
234 
235     @Test
testRegisterServiceWithCustomTtl()236     public void testRegisterServiceWithCustomTtl() throws Exception {
237         final NsdManager manager = mManager;
238         final NsdServiceInfo info = new NsdServiceInfo("another_name2", "another_type2");
239         info.setPort(2203);
240         final AdvertisingRequest request = new AdvertisingRequest.Builder(info, PROTOCOL)
241                 .setTtl(Duration.ofSeconds(30)).build();
242         final NsdManager.RegistrationListener listener = mock(
243                 NsdManager.RegistrationListener.class);
244 
245         manager.registerService(request, Runnable::run, listener);
246 
247         AdvertisingRequest capturedRequest = getAdvertisingRequest(
248                 req -> verify(mServiceConn).registerService(anyInt(), req.capture()));
249         assertEquals(request.getTtl(), capturedRequest.getTtl());
250     }
251 
doTestRegisterService()252     private void doTestRegisterService() throws Exception {
253         NsdManager manager = mManager;
254 
255         NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
256         NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type");
257         request1.setPort(2201);
258         request2.setPort(2202);
259         NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
260         NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class);
261 
262         // Register two services
263         manager.registerService(request1, PROTOCOL, listener1);
264         int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
265 
266         manager.registerService(request2, PROTOCOL, listener2);
267         int key2 = getRequestKey(req ->
268                 verify(mServiceConn, times(2)).registerService(req.capture(), any()));
269 
270         // First reques fails, second request succeeds
271         mCallback.onRegisterServiceSucceeded(key2, request2);
272         verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
273 
274         int err = 1;
275         mCallback.onRegisterServiceFailed(key1, err);
276         verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
277 
278         // Client retries first request, it succeeds
279         manager.registerService(request1, PROTOCOL, listener1);
280         int key3 = getRequestKey(req ->
281                 verify(mServiceConn, times(3)).registerService(req.capture(), any()));
282 
283         mCallback.onRegisterServiceSucceeded(key3, request1);
284         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
285 
286         // First request is unregistered, it succeeds
287         manager.unregisterService(listener1);
288         int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture()));
289         assertEquals(key3, key3again);
290 
291         mCallback.onUnregisterServiceSucceeded(key3again);
292         verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
293 
294         // Second request is unregistered, it fails
295         manager.unregisterService(listener2);
296         int key2again = getRequestKey(req ->
297                 verify(mServiceConn, times(2)).unregisterService(req.capture()));
298         assertEquals(key2, key2again);
299 
300         mCallback.onUnregisterServiceFailed(key2again, err);
301         verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
302 
303         // TODO: do not unregister listener until service is unregistered
304         // Client retries unregistration of second request, it succeeds
305         //manager.unregisterService(listener2);
306         //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE);
307         //assertEquals(key2, key2yetAgain);
308 
309         //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null);
310         //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
311     }
312 
doTestDiscoverService()313     private void doTestDiscoverService() throws Exception {
314         NsdManager manager = mManager;
315 
316         DiscoveryRequest request1 = new DiscoveryRequest.Builder("a_type").build();
317         NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
318         NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type");
319         NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type");
320 
321         NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class);
322 
323         // Client registers for discovery, request fails
324         manager.discoverServices("a_type", PROTOCOL, listener);
325         int key1 = getRequestKey(req ->
326                 verify(mServiceConn).discoverServices(req.capture(), any()));
327 
328         int err = 1;
329         mCallback.onDiscoverServicesFailed(key1, err);
330         verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
331 
332         // Client retries, request succeeds
333         manager.discoverServices("a_type", PROTOCOL, listener);
334         int key2 = getRequestKey(req ->
335                 verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
336 
337         mCallback.onDiscoverServicesStarted(key2, request1);
338         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
339 
340 
341         // mdns notifies about services
342         mCallback.onServiceFound(key2, reply1);
343         verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
344 
345         mCallback.onServiceFound(key2, reply2);
346         verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
347 
348         mCallback.onServiceLost(key2, reply2);
349         verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
350 
351 
352         // Client unregisters its listener
353         manager.stopServiceDiscovery(listener);
354         int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture()));
355         assertEquals(key2, key2again);
356 
357         // TODO: unregister listener immediately and stop notifying it about services
358         // Notifications are still passed to the client's listener
359         mCallback.onServiceLost(key2, reply1);
360         verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
361 
362         // Client is notified of complete unregistration
363         mCallback.onStopDiscoverySucceeded(key2again);
364         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
365 
366         // Notifications are not passed to the client anymore
367         mCallback.onServiceFound(key2, reply3);
368         verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
369 
370 
371         // Client registers for service discovery
372         reset(listener);
373         manager.discoverServices("a_type", PROTOCOL, listener);
374         int key3 = getRequestKey(req ->
375                 verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
376 
377         mCallback.onDiscoverServicesStarted(key3, request1);
378         verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
379 
380         // Client unregisters immediately, it fails
381         manager.stopServiceDiscovery(listener);
382         int key3again = getRequestKey(req ->
383                 verify(mServiceConn, times(2)).stopDiscovery(req.capture()));
384         assertEquals(key3, key3again);
385 
386         err = 2;
387         mCallback.onStopDiscoveryFailed(key3again, err);
388         verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
389 
390         // New notifications are not passed to the client anymore
391         mCallback.onServiceFound(key3, reply1);
392         verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
393     }
394 
doTestInvalidCalls()395     public void doTestInvalidCalls() {
396         NsdManager manager = mManager;
397 
398         NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
399         NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
400         NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
401         NsdManager.RegistrationListener listener4 = mock(NsdManager.RegistrationListener.class);
402         NsdManager.RegistrationListener listener5 = mock(NsdManager.RegistrationListener.class);
403         NsdManager.RegistrationListener listener6 = mock(NsdManager.RegistrationListener.class);
404         NsdManager.RegistrationListener listener7 = mock(NsdManager.RegistrationListener.class);
405 
406         NsdServiceInfo invalidService = new NsdServiceInfo(null, null);
407         NsdServiceInfo validService = new NsdServiceInfo("a_name", "_a_type._tcp");
408         NsdServiceInfo otherServiceWithSubtype = new NsdServiceInfo("b_name", "_a_type._tcp,_sub1");
409         NsdServiceInfo validServiceDuplicate = new NsdServiceInfo("a_name", "_a_type._tcp");
410         NsdServiceInfo validServiceSubtypeUpdate = new NsdServiceInfo("a_name",
411                 "_a_type._tcp,_sub1,_s2");
412         NsdServiceInfo otherSubtypeUpdate = new NsdServiceInfo("a_name", "_a_type._tcp,_sub1,_s3");
413         NsdServiceInfo dotSyntaxSubtypeUpdate = new NsdServiceInfo("a_name", "_sub1._a_type._tcp");
414 
415         validService.setPort(2222);
416         otherServiceWithSubtype.setPort(2222);
417         validServiceDuplicate.setPort(2222);
418         validServiceSubtypeUpdate.setPort(2222);
419         otherSubtypeUpdate.setPort(2222);
420         dotSyntaxSubtypeUpdate.setPort(2222);
421 
422         NsdServiceInfo invalidMissingHostnameWithAddresses = new NsdServiceInfo(null, null);
423         invalidMissingHostnameWithAddresses.setHostAddresses(
424                 List.of(
425                         InetAddress.parseNumericAddress("192.168.82.14"),
426                         InetAddress.parseNumericAddress("2001::1")));
427 
428         NsdServiceInfo validCustomHostWithAddresses = new NsdServiceInfo(null, null);
429         validCustomHostWithAddresses.setHostname("a_host");
430         validCustomHostWithAddresses.setHostAddresses(
431                 List.of(
432                         InetAddress.parseNumericAddress("192.168.82.14"),
433                         InetAddress.parseNumericAddress("2001::1")));
434 
435         NsdServiceInfo validServiceWithCustomHostAndAddresses =
436                 new NsdServiceInfo("a_name", "_a_type._tcp");
437         validServiceWithCustomHostAndAddresses.setPort(2222);
438         validServiceWithCustomHostAndAddresses.setHostname("a_host");
439         validServiceWithCustomHostAndAddresses.setHostAddresses(
440                 List.of(
441                         InetAddress.parseNumericAddress("192.168.82.14"),
442                         InetAddress.parseNumericAddress("2001::1")));
443 
444         NsdServiceInfo validServiceWithCustomHostNoAddresses =
445                 new NsdServiceInfo("a_name", "_a_type._tcp");
446         validServiceWithCustomHostNoAddresses.setPort(2222);
447         validServiceWithCustomHostNoAddresses.setHostname("a_host");
448 
449         NsdServiceInfo validServiceWithPublicKey = new NsdServiceInfo("a_name", "_a_type._tcp");
450         validServiceWithPublicKey.setPublicKey(
451                 hexStringToByteArray(
452                         "0201030dc141d0637960b98cbc12cfca"
453                                 + "221d2879dac26ee5b460e9007c992e19"
454                                 + "02d897c391b03764d448f7d0c772fdb0"
455                                 + "3b1d9d6d52ff8886769e8e2362513565"
456                                 + "270962d3"));
457 
458         NsdServiceInfo invalidServiceWithTooShortPublicKey =
459                 new NsdServiceInfo("a_name", "_a_type._tcp");
460         invalidServiceWithTooShortPublicKey.setPublicKey(hexStringToByteArray("0201"));
461 
462         // Service registration
463         //  - invalid arguments
464         mustFail(() -> { manager.unregisterService(null); });
465         mustFail(() -> { manager.registerService(null, -1, null); });
466         mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); });
467         mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); });
468         mustFail(() -> { manager.registerService(validService, -1, listener1); });
469         mustFail(() -> { manager.registerService(validService, PROTOCOL, null); });
470         mustFail(() -> {
471             manager.registerService(invalidMissingHostnameWithAddresses, PROTOCOL, listener1); });
472         mustFail(() -> {
473             manager.registerService(invalidServiceWithTooShortPublicKey, PROTOCOL, listener1); });
474         manager.registerService(validService, PROTOCOL, listener1);
475         //  - update without subtype is not allowed
476         mustFail(() -> { manager.registerService(validServiceDuplicate, PROTOCOL, listener1); });
477         //  - update with subtype is allowed
478         manager.registerService(validServiceSubtypeUpdate, PROTOCOL, listener1);
479         //  - re-updating to the same subtype is allowed
480         manager.registerService(validServiceSubtypeUpdate, PROTOCOL, listener1);
481         //  - updating to other subtypes is allowed
482         manager.registerService(otherSubtypeUpdate, PROTOCOL, listener1);
483         //  - update back to the service without subtype is allowed
484         manager.registerService(validService, PROTOCOL, listener1);
485         //  - updating to a subtype with _sub._type syntax is not allowed
486         mustFail(() -> { manager.registerService(dotSyntaxSubtypeUpdate, PROTOCOL, listener1); });
487         //  - updating to a different service name is not allowed
488         mustFail(() -> { manager.registerService(otherServiceWithSubtype, PROTOCOL, listener1); });
489         //  - listener already registered, and not using subtypes
490         mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); });
491         manager.unregisterService(listener1);
492         // TODO: make listener immediately reusable
493         //mustFail(() -> { manager.unregisterService(listener1); });
494         //manager.registerService(validService, PROTOCOL, listener1);
495         //  - registering a custom host without a service is valid
496         manager.registerService(validCustomHostWithAddresses, PROTOCOL, listener4);
497         manager.unregisterService(listener4);
498         //  - registering a service with a custom host is valid
499         manager.registerService(validServiceWithCustomHostAndAddresses, PROTOCOL, listener5);
500         manager.unregisterService(listener5);
501         //  - registering a service with a custom host with no addresses is valid
502         manager.registerService(validServiceWithCustomHostNoAddresses, PROTOCOL, listener6);
503         manager.unregisterService(listener6);
504         //  - registering a service with a public key is valid
505         manager.registerService(validServiceWithPublicKey, PROTOCOL, listener7);
506         manager.unregisterService(listener7);
507 
508         // Discover service
509         //  - invalid arguments
510         mustFail(() -> { manager.stopServiceDiscovery(null); });
511         mustFail(() -> { manager.discoverServices(null, -1, null); });
512         mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); });
513         mustFail(() -> { manager.discoverServices("a_service", -1, listener2); });
514         mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); });
515         manager.discoverServices("a_service", PROTOCOL, listener2);
516         //  - listener already registered
517         mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); });
518         manager.stopServiceDiscovery(listener2);
519         // TODO: make listener immediately reusable
520         //mustFail(() -> { manager.stopServiceDiscovery(listener2); });
521         //manager.discoverServices("another_service", PROTOCOL, listener2);
522 
523         // Resolver service
524         //  - invalid arguments
525         mustFail(() -> { manager.resolveService(null, null); });
526         mustFail(() -> { manager.resolveService(null, listener3); });
527         mustFail(() -> { manager.resolveService(invalidService, listener3); });
528         mustFail(() -> { manager.resolveService(validService, null); });
529         manager.resolveService(validService, listener3);
530         //  - listener already registered:w
531         mustFail(() -> { manager.resolveService(validService, listener3); });
532     }
533 
534     private static final class NsdServiceInfoBuilder {
535         private static final String SERVICE_NAME = "TestService";
536         private static final String SERVICE_TYPE = "_testservice._tcp";
537         private static final int SERVICE_PORT = 12345;
538         private static final String HOSTNAME = "TestHost";
539         private static final List<InetAddress> HOST_ADDRESSES =
540                 List.of(parseNumericAddress("192.168.2.23"), parseNumericAddress("2001:db8::3"));
541         private static final byte[] PUBLIC_KEY =
542                 hexStringToByteArray(
543                         "0201030dc141d0637960b98cbc12cfca"
544                                 + "221d2879dac26ee5b460e9007c992e19"
545                                 + "02d897c391b03764d448f7d0c772fdb0"
546                                 + "3b1d9d6d52ff8886769e8e2362513565"
547                                 + "270962d3");
548 
549         private final NsdServiceInfo mNsdServiceInfo = new NsdServiceInfo();
550 
build()551         NsdServiceInfo build() {
552             return mNsdServiceInfo;
553         }
554 
setNoService()555         NsdServiceInfoBuilder setNoService() {
556             mNsdServiceInfo.setServiceName(null);
557             mNsdServiceInfo.setServiceType(null);
558             mNsdServiceInfo.setPort(0);
559             return this;
560         }
561 
setService()562         NsdServiceInfoBuilder setService() {
563             mNsdServiceInfo.setServiceName(SERVICE_NAME);
564             mNsdServiceInfo.setServiceType(SERVICE_TYPE);
565             mNsdServiceInfo.setPort(SERVICE_PORT);
566             return this;
567         }
568 
setZeroPortService()569         NsdServiceInfoBuilder setZeroPortService() {
570             mNsdServiceInfo.setServiceName(SERVICE_NAME);
571             mNsdServiceInfo.setServiceType(SERVICE_TYPE);
572             mNsdServiceInfo.setPort(0);
573             return this;
574         }
575 
setInvalidService()576         NsdServiceInfoBuilder setInvalidService() {
577             mNsdServiceInfo.setServiceName(SERVICE_NAME);
578             mNsdServiceInfo.setServiceType(null);
579             mNsdServiceInfo.setPort(SERVICE_PORT);
580             return this;
581         }
582 
setDefaultHost()583         NsdServiceInfoBuilder setDefaultHost() {
584             mNsdServiceInfo.setHostname(null);
585             mNsdServiceInfo.setHostAddresses(Collections.emptyList());
586             return this;
587         }
588 
setCustomHost()589         NsdServiceInfoBuilder setCustomHost() {
590             mNsdServiceInfo.setHostname(HOSTNAME);
591             mNsdServiceInfo.setHostAddresses(HOST_ADDRESSES);
592             return this;
593         }
594 
setCustomHostNoAddress()595         NsdServiceInfoBuilder setCustomHostNoAddress() {
596             mNsdServiceInfo.setHostname(HOSTNAME);
597             mNsdServiceInfo.setHostAddresses(Collections.emptyList());
598             return this;
599         }
600 
setHostAddressesNoHostname()601         NsdServiceInfoBuilder setHostAddressesNoHostname() {
602             mNsdServiceInfo.setHostname(null);
603             mNsdServiceInfo.setHostAddresses(HOST_ADDRESSES);
604             return this;
605         }
606 
setNoPublicKey()607         NsdServiceInfoBuilder setNoPublicKey() {
608             mNsdServiceInfo.setPublicKey(null);
609             return this;
610         }
611 
setPublicKey()612         NsdServiceInfoBuilder setPublicKey() {
613             mNsdServiceInfo.setPublicKey(PUBLIC_KEY);
614             return this;
615         }
616 
setInvalidPublicKey()617         NsdServiceInfoBuilder setInvalidPublicKey() {
618             mNsdServiceInfo.setPublicKey(new byte[3]);
619             return this;
620         }
621     }
622 
623     @Test
testCheckServiceInfoForRegistration()624     public void testCheckServiceInfoForRegistration() {
625         // The service is invalid
626         mustFail(() -> checkServiceInfoForRegistration(
627                 new NsdServiceInfoBuilder()
628                         .setInvalidService()
629                         .setCustomHost()
630                         .setPublicKey().build()));
631         // Keep compatible with the legacy behavior: It's allowed to set host
632         // addresses for a service registration although the host addresses
633         // won't be registered. To register the addresses for a host, the
634         // hostname must be specified.
635         checkServiceInfoForRegistration(
636                 new NsdServiceInfoBuilder()
637                         .setService()
638                         .setHostAddressesNoHostname()
639                         .setPublicKey().build());
640         // The public key is invalid
641         mustFail(() -> checkServiceInfoForRegistration(
642                 new NsdServiceInfoBuilder()
643                         .setService()
644                         .setCustomHost()
645                         .setInvalidPublicKey().build()));
646         // Invalid combinations
647         // 1. (service, custom host, key): valid
648         checkServiceInfoForRegistration(
649                 new NsdServiceInfoBuilder()
650                         .setService()
651                         .setCustomHost()
652                         .setPublicKey().build());
653         // 2. (service, custom host, no key): valid
654         checkServiceInfoForRegistration(
655                 new NsdServiceInfoBuilder()
656                         .setService()
657                         .setCustomHost()
658                         .setNoPublicKey().build());
659         // 3. (service, no-address custom host, key): valid
660         checkServiceInfoForRegistration(
661                 new NsdServiceInfoBuilder()
662                         .setService()
663                         .setCustomHostNoAddress()
664                         .setPublicKey().build());
665         // 4. (service, no-address custom host, no key): valid
666         checkServiceInfoForRegistration(
667                 new NsdServiceInfoBuilder()
668                         .setService()
669                         .setCustomHostNoAddress()
670                         .setNoPublicKey().build());
671         // 5. (service, default host, key): valid
672         checkServiceInfoForRegistration(
673                 new NsdServiceInfoBuilder()
674                         .setService()
675                         .setDefaultHost()
676                         .setPublicKey().build());
677         // 6. (service, default host, no key): valid
678         checkServiceInfoForRegistration(
679                 new NsdServiceInfoBuilder()
680                         .setService()
681                         .setDefaultHost()
682                         .setNoPublicKey().build());
683         // 7. (0-port service, custom host, valid key): valid
684         checkServiceInfoForRegistration(
685                 new NsdServiceInfoBuilder()
686                         .setZeroPortService()
687                         .setCustomHost()
688                         .setPublicKey().build());
689         // 8. (0-port service, custom host, no key): invalid
690         mustFail(() -> checkServiceInfoForRegistration(
691                 new NsdServiceInfoBuilder()
692                         .setZeroPortService()
693                         .setCustomHost()
694                         .setNoPublicKey().build()));
695         // 9. (0-port service, no-address custom host, key): valid
696         checkServiceInfoForRegistration(
697                 new NsdServiceInfoBuilder()
698                         .setZeroPortService()
699                         .setCustomHostNoAddress()
700                         .setPublicKey().build());
701         // 10. (0-port service, no-address custom host, no key): invalid
702         mustFail(() -> checkServiceInfoForRegistration(
703                 new NsdServiceInfoBuilder()
704                         .setZeroPortService()
705                         .setCustomHostNoAddress()
706                         .setNoPublicKey().build()));
707         // 11. (0-port service, default host, key): valid
708         checkServiceInfoForRegistration(
709                 new NsdServiceInfoBuilder()
710                         .setZeroPortService()
711                         .setDefaultHost()
712                         .setPublicKey().build());
713         // 12. (0-port service, default host, no key): invalid
714         mustFail(() -> checkServiceInfoForRegistration(
715                 new NsdServiceInfoBuilder()
716                         .setZeroPortService()
717                         .setDefaultHost()
718                         .setNoPublicKey().build()));
719         // 13. (no service, custom host, key): valid
720         checkServiceInfoForRegistration(
721                 new NsdServiceInfoBuilder()
722                         .setNoService()
723                         .setCustomHost()
724                         .setPublicKey().build());
725         // 14. (no service, custom host, no key): valid
726         checkServiceInfoForRegistration(
727                 new NsdServiceInfoBuilder()
728                         .setNoService()
729                         .setCustomHost()
730                         .setNoPublicKey().build());
731         // 15. (no service, no-address custom host, key): valid
732         checkServiceInfoForRegistration(
733                 new NsdServiceInfoBuilder()
734                         .setNoService()
735                         .setCustomHostNoAddress()
736                         .setPublicKey().build());
737         // 16. (no service, no-address custom host, no key): invalid
738         mustFail(() -> checkServiceInfoForRegistration(
739                 new NsdServiceInfoBuilder()
740                         .setNoService()
741                         .setCustomHostNoAddress()
742                         .setNoPublicKey().build()));
743         // 17. (no service, default host, key): invalid
744         mustFail(() -> checkServiceInfoForRegistration(
745                 new NsdServiceInfoBuilder()
746                         .setNoService()
747                         .setDefaultHost()
748                         .setPublicKey().build()));
749         // 18. (no service, default host, no key): invalid
750         mustFail(() -> checkServiceInfoForRegistration(
751                 new NsdServiceInfoBuilder()
752                         .setNoService()
753                         .setDefaultHost()
754                         .setNoPublicKey().build()));
755     }
756 
mustFail(Runnable fn)757     public void mustFail(Runnable fn) {
758         try {
759             fn.run();
760             fail();
761         } catch (Exception expected) {
762         }
763     }
764 
getRequestKey(ThrowingConsumer<ArgumentCaptor<Integer>> verifier)765     int getRequestKey(ThrowingConsumer<ArgumentCaptor<Integer>> verifier)
766             throws Exception {
767         final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
768         verifier.accept(captor);
769         return captor.getValue();
770     }
771 
getAdvertisingRequest( ThrowingConsumer<ArgumentCaptor<AdvertisingRequest>> verifier)772     AdvertisingRequest getAdvertisingRequest(
773             ThrowingConsumer<ArgumentCaptor<AdvertisingRequest>> verifier) throws Exception {
774         final ArgumentCaptor<AdvertisingRequest> captor =
775                 ArgumentCaptor.forClass(AdvertisingRequest.class);
776         verifier.accept(captor);
777         return captor.getValue();
778     }
779 }
780