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