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