1 /* 2 * Copyright (C) 2012 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.wifi.cts; 18 19 import android.content.Context; 20 import android.net.nsd.NsdManager; 21 import android.net.nsd.NsdServiceInfo; 22 import android.test.AndroidTestCase; 23 import android.util.Log; 24 25 import java.io.IOException; 26 import java.net.ServerSocket; 27 import java.util.Random; 28 import java.util.List; 29 import java.util.ArrayList; 30 31 public class NsdManagerTest extends AndroidTestCase { 32 33 private static final String TAG = "NsdManagerTest"; 34 private static final String SERVICE_TYPE = "_nmt._tcp"; 35 private static final int TIMEOUT = 2000; 36 37 private static final boolean DBG = false; 38 39 NsdManager mNsdManager; 40 41 NsdManager.RegistrationListener mRegistrationListener; 42 NsdManager.DiscoveryListener mDiscoveryListener; 43 NsdManager.ResolveListener mResolveListener; 44 NsdManagerTest()45 public NsdManagerTest() { 46 initRegistrationListener(); 47 initDiscoveryListener(); 48 initResolveListener(); 49 } 50 initRegistrationListener()51 private void initRegistrationListener() { 52 mRegistrationListener = new NsdManager.RegistrationListener() { 53 @Override 54 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 55 setEvent("onRegistrationFailed", errorCode); 56 } 57 58 @Override 59 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 60 setEvent("onUnregistrationFailed", errorCode); 61 } 62 63 @Override 64 public void onServiceRegistered(NsdServiceInfo serviceInfo) { 65 setEvent("onServiceRegistered", serviceInfo); 66 } 67 68 @Override 69 public void onServiceUnregistered(NsdServiceInfo serviceInfo) { 70 setEvent("onServiceUnregistered", serviceInfo); 71 } 72 }; 73 } 74 initDiscoveryListener()75 private void initDiscoveryListener() { 76 mDiscoveryListener = new NsdManager.DiscoveryListener() { 77 @Override 78 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 79 setEvent("onStartDiscoveryFailed", errorCode); 80 } 81 82 @Override 83 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 84 setEvent("onStopDiscoveryFailed", errorCode); 85 } 86 87 @Override 88 public void onDiscoveryStarted(String serviceType) { 89 NsdServiceInfo info = new NsdServiceInfo(); 90 info.setServiceType(serviceType); 91 setEvent("onDiscoveryStarted", info); 92 } 93 94 @Override 95 public void onDiscoveryStopped(String serviceType) { 96 NsdServiceInfo info = new NsdServiceInfo(); 97 info.setServiceType(serviceType); 98 setEvent("onDiscoveryStopped", info); 99 } 100 101 @Override 102 public void onServiceFound(NsdServiceInfo serviceInfo) { 103 setEvent("onServiceFound", serviceInfo); 104 } 105 106 @Override 107 public void onServiceLost(NsdServiceInfo serviceInfo) { 108 setEvent("onServiceLost", serviceInfo); 109 } 110 }; 111 } 112 initResolveListener()113 private void initResolveListener() { 114 mResolveListener = new NsdManager.ResolveListener() { 115 @Override 116 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { 117 setEvent("onResolveFailed", errorCode); 118 } 119 120 @Override 121 public void onServiceResolved(NsdServiceInfo serviceInfo) { 122 setEvent("onServiceResolved", serviceInfo); 123 } 124 }; 125 } 126 127 128 129 private final class EventData { EventData(String callbackName, NsdServiceInfo info)130 EventData(String callbackName, NsdServiceInfo info) { 131 mCallbackName = callbackName; 132 mSucceeded = true; 133 mErrorCode = 0; 134 mInfo = info; 135 } EventData(String callbackName, int errorCode)136 EventData(String callbackName, int errorCode) { 137 mCallbackName = callbackName; 138 mSucceeded = false; 139 mErrorCode = errorCode; 140 mInfo = null; 141 } 142 private final String mCallbackName; 143 private final boolean mSucceeded; 144 private final int mErrorCode; 145 private final NsdServiceInfo mInfo; 146 } 147 148 private final List<EventData> mEventCache = new ArrayList<EventData>(); 149 setEvent(String callbackName, int errorCode)150 private void setEvent(String callbackName, int errorCode) { 151 if (DBG) Log.d(TAG, callbackName + " failed with " + String.valueOf(errorCode)); 152 EventData eventData = new EventData(callbackName, errorCode); 153 synchronized (mEventCache) { 154 mEventCache.add(eventData); 155 mEventCache.notify(); 156 } 157 } 158 setEvent(String callbackName, NsdServiceInfo info)159 private void setEvent(String callbackName, NsdServiceInfo info) { 160 if (DBG) Log.d(TAG, "Received event " + callbackName + " for " + info.getServiceName()); 161 EventData eventData = new EventData(callbackName, info); 162 synchronized (mEventCache) { 163 mEventCache.add(eventData); 164 mEventCache.notify(); 165 } 166 } 167 clearEventCache()168 void clearEventCache() { 169 synchronized(mEventCache) { 170 mEventCache.clear(); 171 } 172 } 173 eventCacheSize()174 int eventCacheSize() { 175 synchronized(mEventCache) { 176 return mEventCache.size(); 177 } 178 } 179 180 private int mWaitId = 0; waitForCallback(String callbackName)181 private EventData waitForCallback(String callbackName) { 182 183 synchronized(mEventCache) { 184 185 mWaitId ++; 186 if (DBG) Log.d(TAG, "Waiting for " + callbackName + ", id=" + String.valueOf(mWaitId)); 187 188 try { 189 long startTime = android.os.SystemClock.uptimeMillis(); 190 long elapsedTime = 0; 191 int index = 0; 192 while (elapsedTime < TIMEOUT ) { 193 // first check if we've received that event 194 for (; index < mEventCache.size(); index++) { 195 EventData e = mEventCache.get(index); 196 if (e.mCallbackName.equals(callbackName)) { 197 if (DBG) Log.d(TAG, "exiting wait id=" + String.valueOf(mWaitId)); 198 return e; 199 } 200 } 201 202 // Not yet received, just wait 203 mEventCache.wait(TIMEOUT - elapsedTime); 204 elapsedTime = android.os.SystemClock.uptimeMillis() - startTime; 205 } 206 // we exited the loop because of TIMEOUT; fail the call 207 if (DBG) Log.d(TAG, "timed out waiting id=" + String.valueOf(mWaitId)); 208 return null; 209 } catch (InterruptedException e) { 210 return null; // wait timed out! 211 } 212 } 213 } 214 waitForNewEvents()215 private EventData waitForNewEvents() throws InterruptedException { 216 if (DBG) Log.d(TAG, "Waiting for a bit, id=" + String.valueOf(mWaitId)); 217 218 long startTime = android.os.SystemClock.uptimeMillis(); 219 long elapsedTime = 0; 220 synchronized (mEventCache) { 221 int index = mEventCache.size(); 222 while (elapsedTime < TIMEOUT ) { 223 // first check if we've received that event 224 for (; index < mEventCache.size(); index++) { 225 EventData e = mEventCache.get(index); 226 return e; 227 } 228 229 // Not yet received, just wait 230 mEventCache.wait(TIMEOUT - elapsedTime); 231 elapsedTime = android.os.SystemClock.uptimeMillis() - startTime; 232 } 233 } 234 235 return null; 236 } 237 238 private String mServiceName; 239 240 @Override setUp()241 public void setUp() { 242 if (DBG) Log.d(TAG, "Setup test ..."); 243 mNsdManager = (NsdManager) getContext().getSystemService(Context.NSD_SERVICE); 244 245 Random rand = new Random(); 246 mServiceName = new String("NsdTest"); 247 for (int i = 0; i < 4; i++) { 248 mServiceName = mServiceName + String.valueOf(rand.nextInt(10)); 249 } 250 } 251 252 @Override tearDown()253 public void tearDown() { 254 if (DBG) Log.d(TAG, "Tear down test ..."); 255 } 256 runTest()257 public void runTest() throws Exception { 258 NsdServiceInfo si = new NsdServiceInfo(); 259 si.setServiceType(SERVICE_TYPE); 260 si.setServiceName(mServiceName); 261 262 EventData lastEvent = null; 263 264 if (DBG) Log.d(TAG, "Starting test ..."); 265 266 ServerSocket socket; 267 int localPort; 268 269 try { 270 socket = new ServerSocket(0); 271 localPort = socket.getLocalPort(); 272 si.setPort(localPort); 273 } catch (IOException e) { 274 if (DBG) Log.d(TAG, "Could not open a local socket"); 275 assertTrue(false); 276 return; 277 } 278 279 if (DBG) Log.d(TAG, "Port = " + String.valueOf(localPort)); 280 281 clearEventCache(); 282 283 mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); 284 lastEvent = waitForCallback("onServiceRegistered"); // id = 1 285 assertTrue(lastEvent != null); 286 assertTrue(lastEvent.mSucceeded); 287 assertTrue(eventCacheSize() == 1); 288 289 // We may not always get the name that we tried to register; 290 // This events tells us the name that was registered. 291 String registeredName = lastEvent.mInfo.getServiceName(); 292 si.setServiceName(registeredName); 293 294 clearEventCache(); 295 296 mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, 297 mDiscoveryListener); 298 299 // Expect discovery started 300 lastEvent = waitForCallback("onDiscoveryStarted"); // id = 2 301 302 assertTrue(lastEvent != null); 303 assertTrue(lastEvent.mSucceeded); 304 305 // Remove this event, so accounting becomes easier later 306 synchronized (mEventCache) { 307 mEventCache.remove(lastEvent); 308 } 309 310 // Expect a service record to be discovered (and filter the ones 311 // that are unrelated to this test) 312 boolean found = false; 313 for (int i = 0; i < 32; i++) { 314 315 lastEvent = waitForCallback("onServiceFound"); // id = 3 316 if (lastEvent == null) { 317 // no more onServiceFound events are being reported! 318 break; 319 } 320 321 assertTrue(lastEvent.mSucceeded); 322 323 if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " + 324 lastEvent.mInfo.getServiceName()); 325 326 if (lastEvent.mInfo.getServiceName().equals(registeredName)) { 327 // Save it, as it will get overwritten with new serviceFound events 328 si = lastEvent.mInfo; 329 found = true; 330 } 331 332 // Remove this event from the event cache, so it won't be found by subsequent 333 // calls to waitForCallback 334 synchronized (mEventCache) { 335 mEventCache.remove(lastEvent); 336 } 337 } 338 339 assertTrue(found); 340 341 // We've removed all serviceFound events, and we've removed the discoveryStarted 342 // event as well, so now the event cache should be empty! 343 assertTrue(eventCacheSize() == 0); 344 345 // Resolve the service 346 clearEventCache(); 347 mNsdManager.resolveService(si, mResolveListener); 348 lastEvent = waitForCallback("onServiceResolved"); // id = 4 349 350 assertTrue(lastEvent != null); 351 assertTrue(lastEvent.mSucceeded); 352 353 if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": Port = " + 354 String.valueOf(lastEvent.mInfo.getPort())); 355 356 assertTrue(lastEvent.mInfo.getPort() == localPort); 357 assertTrue(eventCacheSize() == 1); 358 359 checkForAdditionalEvents(); 360 clearEventCache(); 361 362 // Unregister the service 363 mNsdManager.unregisterService(mRegistrationListener); 364 lastEvent = waitForCallback("onServiceUnregistered"); // id = 5 365 366 assertTrue(lastEvent != null); 367 assertTrue(lastEvent.mSucceeded); 368 369 // Expect a callback for service lost 370 lastEvent = waitForCallback("onServiceLost"); // id = 6 371 372 assertTrue(lastEvent != null); 373 assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName)); 374 375 // Register service again to see if we discover it 376 checkForAdditionalEvents(); 377 clearEventCache(); 378 379 si = new NsdServiceInfo(); 380 si.setServiceType(SERVICE_TYPE); 381 si.setServiceName(mServiceName); 382 si.setPort(localPort); 383 384 // Create a new registration listener and register same service again 385 initRegistrationListener(); 386 387 mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); 388 389 lastEvent = waitForCallback("onServiceRegistered"); // id = 7 390 391 assertTrue(lastEvent != null); 392 assertTrue(lastEvent.mSucceeded); 393 394 registeredName = lastEvent.mInfo.getServiceName(); 395 396 // Expect a record to be discovered 397 lastEvent = waitForCallback("onServiceFound"); // id = 8 398 399 assertTrue(lastEvent != null); 400 assertTrue(lastEvent.mSucceeded); 401 402 if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " + 403 lastEvent.mInfo.getServiceName()); 404 405 assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName)); 406 407 checkForAdditionalEvents(); 408 clearEventCache(); 409 410 mNsdManager.stopServiceDiscovery(mDiscoveryListener); 411 lastEvent = waitForCallback("onDiscoveryStopped"); // id = 9 412 assertTrue(lastEvent != null); 413 assertTrue(lastEvent.mSucceeded); 414 assertTrue(checkCacheSize(1)); 415 416 checkForAdditionalEvents(); 417 clearEventCache(); 418 419 mNsdManager.unregisterService(mRegistrationListener); 420 421 lastEvent = waitForCallback("onServiceUnregistered"); // id = 10 422 assertTrue(lastEvent != null); 423 assertTrue(lastEvent.mSucceeded); 424 assertTrue(checkCacheSize(1)); 425 } 426 checkCacheSize(int size)427 boolean checkCacheSize(int size) { 428 synchronized (mEventCache) { 429 int cacheSize = mEventCache.size(); 430 if (cacheSize != size) { 431 Log.d(TAG, "id = " + mWaitId + ": event cache size = " + cacheSize); 432 for (int i = 0; i < cacheSize; i++) { 433 EventData e = mEventCache.get(i); 434 String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : ""; 435 Log.d(TAG, "eventName is " + e.mCallbackName + sname); 436 } 437 } 438 return (cacheSize == size); 439 } 440 } 441 checkForAdditionalEvents()442 boolean checkForAdditionalEvents() { 443 try { 444 EventData e = waitForNewEvents(); 445 if (e != null) { 446 String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : ""; 447 Log.d(TAG, "ignoring unexpected event " + e.mCallbackName + sname); 448 } 449 return (e == null); 450 } 451 catch (InterruptedException ex) { 452 return false; 453 } 454 } 455 } 456 457