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 libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; 20 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.fail; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.reset; 29 import static org.mockito.Mockito.spy; 30 import static org.mockito.Mockito.timeout; 31 import static org.mockito.Mockito.verify; 32 33 import android.compat.testing.PlatformCompatChangeRule; 34 import android.content.Context; 35 import android.os.Build; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.Messenger; 41 42 import androidx.test.filters.SmallTest; 43 44 import com.android.internal.util.AsyncChannel; 45 import com.android.testutils.DevSdkIgnoreRule; 46 import com.android.testutils.DevSdkIgnoreRunner; 47 import com.android.testutils.HandlerUtils; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.rules.TestRule; 54 import org.junit.runner.RunWith; 55 import org.mockito.Mock; 56 import org.mockito.MockitoAnnotations; 57 58 @RunWith(DevSdkIgnoreRunner.class) 59 @SmallTest 60 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) 61 public class NsdManagerTest { 62 63 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; 64 65 @Rule 66 public TestRule compatChangeRule = new PlatformCompatChangeRule(); 67 68 @Mock Context mContext; 69 @Mock INsdManager mService; 70 MockServiceHandler mServiceHandler; 71 72 NsdManager mManager; 73 74 long mTimeoutMs = 200; // non-final so that tests can adjust the value. 75 76 @Before setUp()77 public void setUp() throws Exception { 78 MockitoAnnotations.initMocks(this); 79 80 mServiceHandler = spy(MockServiceHandler.create(mContext)); 81 doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger(); 82 } 83 84 @After tearDown()85 public void tearDown() throws Exception { 86 HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); 87 mServiceHandler.chan.disconnect(); 88 mServiceHandler.stop(); 89 if (mManager != null) { 90 mManager.disconnect(); 91 } 92 } 93 94 @Test 95 @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testResolveServiceS()96 public void testResolveServiceS() { 97 mManager = makeNsdManagerS(); 98 doTestResolveService(); 99 } 100 101 @Test 102 @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testResolveServicePreS()103 public void testResolveServicePreS() { 104 mManager = makeNsdManagerPreS(); 105 doTestResolveService(); 106 } 107 108 @Test 109 @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testDiscoverServiceS()110 public void testDiscoverServiceS() { 111 mManager = makeNsdManagerS(); 112 doTestDiscoverService(); 113 } 114 115 @Test 116 @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testDiscoverServicePreS()117 public void testDiscoverServicePreS() { 118 mManager = makeNsdManagerPreS(); 119 doTestDiscoverService(); 120 } 121 122 @Test 123 @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testParallelResolveServiceS()124 public void testParallelResolveServiceS() { 125 mManager = makeNsdManagerS(); 126 doTestParallelResolveService(); 127 } 128 129 @Test 130 @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testParallelResolveServicePreS()131 public void testParallelResolveServicePreS() { 132 mManager = makeNsdManagerPreS(); 133 doTestParallelResolveService(); 134 } 135 136 @Test 137 @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testInvalidCallsS()138 public void testInvalidCallsS() { 139 mManager = makeNsdManagerS(); 140 doTestInvalidCalls(); 141 } 142 143 @Test 144 @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testInvalidCallsPreS()145 public void testInvalidCallsPreS() { 146 mManager = makeNsdManagerPreS(); 147 doTestInvalidCalls(); 148 } 149 150 @Test 151 @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testRegisterServiceS()152 public void testRegisterServiceS() { 153 mManager = makeNsdManagerS(); 154 doTestRegisterService(); 155 } 156 157 @Test 158 @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) testRegisterServicePreS()159 public void testRegisterServicePreS() { 160 mManager = makeNsdManagerPreS(); 161 doTestRegisterService(); 162 } 163 doTestResolveService()164 public void doTestResolveService() { 165 NsdManager manager = mManager; 166 167 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 168 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); 169 NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); 170 171 manager.resolveService(request, listener); 172 int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); 173 int err = 33; 174 sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null); 175 verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); 176 177 manager.resolveService(request, listener); 178 int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); 179 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); 180 verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 181 } 182 doTestParallelResolveService()183 public void doTestParallelResolveService() { 184 NsdManager manager = mManager; 185 186 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 187 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); 188 189 NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class); 190 NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); 191 192 manager.resolveService(request, listener1); 193 int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); 194 195 manager.resolveService(request, listener2); 196 int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); 197 198 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); 199 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply); 200 201 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 202 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 203 } 204 doTestRegisterService()205 public void doTestRegisterService() { 206 NsdManager manager = mManager; 207 208 NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); 209 NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); 210 request1.setPort(2201); 211 request2.setPort(2202); 212 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 213 NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class); 214 215 // Register two services 216 manager.registerService(request1, PROTOCOL, listener1); 217 int key1 = verifyRequest(NsdManager.REGISTER_SERVICE); 218 219 manager.registerService(request2, PROTOCOL, listener2); 220 int key2 = verifyRequest(NsdManager.REGISTER_SERVICE); 221 222 // First reques fails, second request succeeds 223 sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2); 224 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); 225 226 int err = 1; 227 sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1); 228 verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); 229 230 // Client retries first request, it succeeds 231 manager.registerService(request1, PROTOCOL, listener1); 232 int key3 = verifyRequest(NsdManager.REGISTER_SERVICE); 233 234 sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1); 235 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); 236 237 // First request is unregistered, it succeeds 238 manager.unregisterService(listener1); 239 int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE); 240 assertEquals(key3, key3again); 241 242 sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null); 243 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); 244 245 // Second request is unregistered, it fails 246 manager.unregisterService(listener2); 247 int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE); 248 assertEquals(key2, key2again); 249 250 sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null); 251 verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); 252 253 // TODO: do not unregister listener until service is unregistered 254 // Client retries unregistration of second request, it succeeds 255 //manager.unregisterService(listener2); 256 //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE); 257 //assertEquals(key2, key2yetAgain); 258 259 //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null); 260 //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); 261 } 262 doTestDiscoverService()263 public void doTestDiscoverService() { 264 NsdManager manager = mManager; 265 266 NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); 267 NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); 268 NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type"); 269 270 NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class); 271 272 // Client registers for discovery, request fails 273 manager.discoverServices("a_type", PROTOCOL, listener); 274 int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES); 275 276 int err = 1; 277 sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null); 278 verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); 279 280 // Client retries, request succeeds 281 manager.discoverServices("a_type", PROTOCOL, listener); 282 int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES); 283 284 sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1); 285 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); 286 287 288 // mdns notifies about services 289 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1); 290 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); 291 292 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2); 293 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); 294 295 sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2); 296 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); 297 298 299 // Client unregisters its listener 300 manager.stopServiceDiscovery(listener); 301 int key2again = verifyRequest(NsdManager.STOP_DISCOVERY); 302 assertEquals(key2, key2again); 303 304 // TODO: unregister listener immediately and stop notifying it about services 305 // Notifications are still passed to the client's listener 306 sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1); 307 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); 308 309 // Client is notified of complete unregistration 310 sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type"); 311 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); 312 313 // Notifications are not passed to the client anymore 314 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3); 315 verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); 316 317 318 // Client registers for service discovery 319 reset(listener); 320 manager.discoverServices("a_type", PROTOCOL, listener); 321 int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES); 322 323 sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1); 324 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); 325 326 // Client unregisters immediately, it fails 327 manager.stopServiceDiscovery(listener); 328 int key3again = verifyRequest(NsdManager.STOP_DISCOVERY); 329 assertEquals(key3, key3again); 330 331 err = 2; 332 sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type"); 333 verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); 334 335 // New notifications are not passed to the client anymore 336 sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1); 337 verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); 338 } 339 doTestInvalidCalls()340 public void doTestInvalidCalls() { 341 NsdManager manager = mManager; 342 343 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 344 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); 345 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); 346 347 NsdServiceInfo invalidService = new NsdServiceInfo(null, null); 348 NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type"); 349 validService.setPort(2222); 350 351 // Service registration 352 // - invalid arguments 353 mustFail(() -> { manager.unregisterService(null); }); 354 mustFail(() -> { manager.registerService(null, -1, null); }); 355 mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); }); 356 mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); }); 357 mustFail(() -> { manager.registerService(validService, -1, listener1); }); 358 mustFail(() -> { manager.registerService(validService, PROTOCOL, null); }); 359 manager.registerService(validService, PROTOCOL, listener1); 360 // - listener already registered 361 mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); }); 362 manager.unregisterService(listener1); 363 // TODO: make listener immediately reusable 364 //mustFail(() -> { manager.unregisterService(listener1); }); 365 //manager.registerService(validService, PROTOCOL, listener1); 366 367 // Discover service 368 // - invalid arguments 369 mustFail(() -> { manager.stopServiceDiscovery(null); }); 370 mustFail(() -> { manager.discoverServices(null, -1, null); }); 371 mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); }); 372 mustFail(() -> { manager.discoverServices("a_service", -1, listener2); }); 373 mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); }); 374 manager.discoverServices("a_service", PROTOCOL, listener2); 375 // - listener already registered 376 mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); }); 377 manager.stopServiceDiscovery(listener2); 378 // TODO: make listener immediately reusable 379 //mustFail(() -> { manager.stopServiceDiscovery(listener2); }); 380 //manager.discoverServices("another_service", PROTOCOL, listener2); 381 382 // Resolver service 383 // - invalid arguments 384 mustFail(() -> { manager.resolveService(null, null); }); 385 mustFail(() -> { manager.resolveService(null, listener3); }); 386 mustFail(() -> { manager.resolveService(invalidService, listener3); }); 387 mustFail(() -> { manager.resolveService(validService, null); }); 388 manager.resolveService(validService, listener3); 389 // - listener already registered:w 390 mustFail(() -> { manager.resolveService(validService, listener3); }); 391 } 392 mustFail(Runnable fn)393 public void mustFail(Runnable fn) { 394 try { 395 fn.run(); 396 fail(); 397 } catch (Exception expected) { 398 } 399 } 400 makeNsdManagerS()401 NsdManager makeNsdManagerS() { 402 // Expect we'll get 2 AsyncChannel related msgs. 403 return makeManager(2); 404 } 405 makeNsdManagerPreS()406 NsdManager makeNsdManagerPreS() { 407 // Expect we'll get 3 msgs. 2 AsyncChannel related msgs + 1 additional daemon startup msg. 408 return makeManager(3); 409 } 410 makeManager(int expectedMsgCount)411 NsdManager makeManager(int expectedMsgCount) { 412 NsdManager manager = new NsdManager(mContext, mService); 413 // Acknowledge first two messages connecting the AsyncChannel. 414 verify(mServiceHandler, timeout(mTimeoutMs).times(expectedMsgCount)).handleMessage(any()); 415 416 reset(mServiceHandler); 417 assertNotNull(mServiceHandler.chan); 418 return manager; 419 } 420 verifyRequest(int expectedMessageType)421 int verifyRequest(int expectedMessageType) { 422 HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); 423 verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); 424 reset(mServiceHandler); 425 Message received = mServiceHandler.getLastMessage(); 426 assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what)); 427 return received.arg2; 428 } 429 sendResponse(int replyType, int arg, int key, Object obj)430 void sendResponse(int replyType, int arg, int key, Object obj) { 431 mServiceHandler.chan.sendMessage(replyType, arg, key, obj); 432 } 433 434 // Implements the server side of AsyncChannel connection protocol 435 public static class MockServiceHandler extends Handler { 436 public final Context context; 437 public AsyncChannel chan; 438 public Message lastMessage; 439 MockServiceHandler(Looper l, Context c)440 MockServiceHandler(Looper l, Context c) { 441 super(l); 442 context = c; 443 } 444 getLastMessage()445 synchronized Message getLastMessage() { 446 return lastMessage; 447 } 448 setLastMessage(Message msg)449 synchronized void setLastMessage(Message msg) { 450 lastMessage = obtainMessage(); 451 lastMessage.copyFrom(msg); 452 } 453 454 @Override handleMessage(Message msg)455 public void handleMessage(Message msg) { 456 setLastMessage(msg); 457 if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) { 458 chan = new AsyncChannel(); 459 chan.connect(context, this, msg.replyTo); 460 chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); 461 } 462 } 463 stop()464 void stop() { 465 getLooper().quitSafely(); 466 } 467 create(Context context)468 static MockServiceHandler create(Context context) { 469 HandlerThread t = new HandlerThread("mock-service-handler"); 470 t.start(); 471 return new MockServiceHandler(t.getLooper(), context); 472 } 473 } 474 } 475