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