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.nsd; 18 19 import static com.android.internal.util.Preconditions.checkArgument; 20 import static com.android.internal.util.Preconditions.checkNotNull; 21 import static com.android.internal.util.Preconditions.checkStringNotEmpty; 22 23 import android.annotation.SdkConstant; 24 import android.annotation.SdkConstant.SdkConstantType; 25 import android.annotation.SystemService; 26 import android.content.Context; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.Messenger; 32 import android.os.RemoteException; 33 import android.util.Log; 34 import android.util.SparseArray; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.util.AsyncChannel; 38 import com.android.internal.util.Protocol; 39 40 import java.util.concurrent.CountDownLatch; 41 42 /** 43 * The Network Service Discovery Manager class provides the API to discover services 44 * on a network. As an example, if device A and device B are connected over a Wi-Fi 45 * network, a game registered on device A can be discovered by a game on device 46 * B. Another example use case is an application discovering printers on the network. 47 * 48 * <p> The API currently supports DNS based service discovery and discovery is currently 49 * limited to a local network over Multicast DNS. DNS service discovery is described at 50 * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt 51 * 52 * <p> The API is asynchronous, and responses to requests from an application are on listener 53 * callbacks on a separate internal thread. 54 * 55 * <p> There are three main operations the API supports - registration, discovery and resolution. 56 * <pre> 57 * Application start 58 * | 59 * | 60 * | onServiceRegistered() 61 * Register any local services / 62 * to be advertised with \ 63 * registerService() onRegistrationFailed() 64 * | 65 * | 66 * discoverServices() 67 * | 68 * Maintain a list to track 69 * discovered services 70 * | 71 * |---------> 72 * | | 73 * | onServiceFound() 74 * | | 75 * | add service to list 76 * | | 77 * |<---------- 78 * | 79 * |---------> 80 * | | 81 * | onServiceLost() 82 * | | 83 * | remove service from list 84 * | | 85 * |<---------- 86 * | 87 * | 88 * | Connect to a service 89 * | from list ? 90 * | 91 * resolveService() 92 * | 93 * onServiceResolved() 94 * | 95 * Establish connection to service 96 * with the host and port information 97 * 98 * </pre> 99 * An application that needs to advertise itself over a network for other applications to 100 * discover it can do so with a call to {@link #registerService}. If Example is a http based 101 * application that can provide HTML data to peer services, it can register a name "Example" 102 * with service type "_http._tcp". A successful registration is notified with a callback to 103 * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified 104 * over {@link RegistrationListener#onRegistrationFailed} 105 * 106 * <p> A peer application looking for http services can initiate a discovery for "_http._tcp" 107 * with a call to {@link #discoverServices}. A service found is notified with a callback 108 * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on 109 * {@link DiscoveryListener#onServiceLost}. 110 * 111 * <p> Once the peer application discovers the "Example" http service, and either needs to read the 112 * attributes of the service or wants to receive data from the "Example" application, it can 113 * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port 114 * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a 115 * failure is notified on {@link ResolveListener#onResolveFailed}. 116 * 117 * Applications can reserve for a service type at 118 * http://www.iana.org/form/ports-service. Existing services can be found at 119 * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml 120 * 121 * {@see NsdServiceInfo} 122 */ 123 @SystemService(Context.NSD_SERVICE) 124 public final class NsdManager { 125 private static final String TAG = NsdManager.class.getSimpleName(); 126 private static final boolean DBG = false; 127 128 /** 129 * Broadcast intent action to indicate whether network service discovery is 130 * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state 131 * information as int. 132 * 133 * @see #EXTRA_NSD_STATE 134 */ 135 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 136 public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; 137 138 /** 139 * The lookup key for an int that indicates whether network service discovery is enabled 140 * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. 141 * 142 * @see #NSD_STATE_DISABLED 143 * @see #NSD_STATE_ENABLED 144 */ 145 public static final String EXTRA_NSD_STATE = "nsd_state"; 146 147 /** 148 * Network service discovery is disabled 149 * 150 * @see #ACTION_NSD_STATE_CHANGED 151 */ 152 public static final int NSD_STATE_DISABLED = 1; 153 154 /** 155 * Network service discovery is enabled 156 * 157 * @see #ACTION_NSD_STATE_CHANGED 158 */ 159 public static final int NSD_STATE_ENABLED = 2; 160 161 private static final int BASE = Protocol.BASE_NSD_MANAGER; 162 163 /** @hide */ 164 public static final int DISCOVER_SERVICES = BASE + 1; 165 /** @hide */ 166 public static final int DISCOVER_SERVICES_STARTED = BASE + 2; 167 /** @hide */ 168 public static final int DISCOVER_SERVICES_FAILED = BASE + 3; 169 /** @hide */ 170 public static final int SERVICE_FOUND = BASE + 4; 171 /** @hide */ 172 public static final int SERVICE_LOST = BASE + 5; 173 174 /** @hide */ 175 public static final int STOP_DISCOVERY = BASE + 6; 176 /** @hide */ 177 public static final int STOP_DISCOVERY_FAILED = BASE + 7; 178 /** @hide */ 179 public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8; 180 181 /** @hide */ 182 public static final int REGISTER_SERVICE = BASE + 9; 183 /** @hide */ 184 public static final int REGISTER_SERVICE_FAILED = BASE + 10; 185 /** @hide */ 186 public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11; 187 188 /** @hide */ 189 public static final int UNREGISTER_SERVICE = BASE + 12; 190 /** @hide */ 191 public static final int UNREGISTER_SERVICE_FAILED = BASE + 13; 192 /** @hide */ 193 public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14; 194 195 /** @hide */ 196 public static final int RESOLVE_SERVICE = BASE + 18; 197 /** @hide */ 198 public static final int RESOLVE_SERVICE_FAILED = BASE + 19; 199 /** @hide */ 200 public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; 201 202 /** @hide */ 203 public static final int ENABLE = BASE + 24; 204 /** @hide */ 205 public static final int DISABLE = BASE + 25; 206 207 /** @hide */ 208 public static final int NATIVE_DAEMON_EVENT = BASE + 26; 209 210 /** Dns based service discovery protocol */ 211 public static final int PROTOCOL_DNS_SD = 0x0001; 212 213 private static final SparseArray<String> EVENT_NAMES = new SparseArray<>(); 214 static { EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES")215 EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES"); EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED")216 EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED"); EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED")217 EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED"); EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND")218 EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST")219 EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY")220 EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY"); EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED")221 EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED"); EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED")222 EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED"); EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE")223 EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE"); EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED")224 EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED"); EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED")225 EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE")226 EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE"); EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED")227 EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED"); EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED")228 EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE")229 EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED")230 EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED")231 EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); EVENT_NAMES.put(ENABLE, "ENABLE")232 EVENT_NAMES.put(ENABLE, "ENABLE"); EVENT_NAMES.put(DISABLE, "DISABLE")233 EVENT_NAMES.put(DISABLE, "DISABLE"); EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT")234 EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT"); 235 } 236 237 /** @hide */ nameOf(int event)238 public static String nameOf(int event) { 239 String name = EVENT_NAMES.get(event); 240 if (name == null) { 241 return Integer.toString(event); 242 } 243 return name; 244 } 245 246 private static final int FIRST_LISTENER_KEY = 1; 247 248 private final INsdManager mService; 249 private final Context mContext; 250 251 private int mListenerKey = FIRST_LISTENER_KEY; 252 private final SparseArray mListenerMap = new SparseArray(); 253 private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); 254 private final Object mMapLock = new Object(); 255 256 private final AsyncChannel mAsyncChannel = new AsyncChannel(); 257 private ServiceHandler mHandler; 258 private final CountDownLatch mConnected = new CountDownLatch(1); 259 260 /** 261 * Create a new Nsd instance. Applications use 262 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 263 * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}. 264 * @param service the Binder interface 265 * @hide - hide this because it takes in a parameter of type INsdManager, which 266 * is a system private class. 267 */ NsdManager(Context context, INsdManager service)268 public NsdManager(Context context, INsdManager service) { 269 mService = service; 270 mContext = context; 271 init(); 272 } 273 274 /** 275 * @hide 276 */ 277 @VisibleForTesting disconnect()278 public void disconnect() { 279 mAsyncChannel.disconnect(); 280 mHandler.getLooper().quitSafely(); 281 } 282 283 /** 284 * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, 285 * {@link RegistrationListener#onUnregistrationFailed}, 286 * {@link DiscoveryListener#onStartDiscoveryFailed}, 287 * {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}. 288 * 289 * Indicates that the operation failed due to an internal error. 290 */ 291 public static final int FAILURE_INTERNAL_ERROR = 0; 292 293 /** 294 * Indicates that the operation failed because it is already active. 295 */ 296 public static final int FAILURE_ALREADY_ACTIVE = 3; 297 298 /** 299 * Indicates that the operation failed because the maximum outstanding 300 * requests from the applications have reached. 301 */ 302 public static final int FAILURE_MAX_LIMIT = 4; 303 304 /** Interface for callback invocation for service discovery */ 305 public interface DiscoveryListener { 306 onStartDiscoveryFailed(String serviceType, int errorCode)307 public void onStartDiscoveryFailed(String serviceType, int errorCode); 308 onStopDiscoveryFailed(String serviceType, int errorCode)309 public void onStopDiscoveryFailed(String serviceType, int errorCode); 310 onDiscoveryStarted(String serviceType)311 public void onDiscoveryStarted(String serviceType); 312 onDiscoveryStopped(String serviceType)313 public void onDiscoveryStopped(String serviceType); 314 onServiceFound(NsdServiceInfo serviceInfo)315 public void onServiceFound(NsdServiceInfo serviceInfo); 316 onServiceLost(NsdServiceInfo serviceInfo)317 public void onServiceLost(NsdServiceInfo serviceInfo); 318 } 319 320 /** Interface for callback invocation for service registration */ 321 public interface RegistrationListener { 322 onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)323 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 324 onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)325 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 326 onServiceRegistered(NsdServiceInfo serviceInfo)327 public void onServiceRegistered(NsdServiceInfo serviceInfo); 328 onServiceUnregistered(NsdServiceInfo serviceInfo)329 public void onServiceUnregistered(NsdServiceInfo serviceInfo); 330 } 331 332 /** Interface for callback invocation for service resolution */ 333 public interface ResolveListener { 334 onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)335 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode); 336 onServiceResolved(NsdServiceInfo serviceInfo)337 public void onServiceResolved(NsdServiceInfo serviceInfo); 338 } 339 340 @VisibleForTesting 341 class ServiceHandler extends Handler { ServiceHandler(Looper looper)342 ServiceHandler(Looper looper) { 343 super(looper); 344 } 345 346 @Override handleMessage(Message message)347 public void handleMessage(Message message) { 348 final int what = message.what; 349 final int key = message.arg2; 350 switch (what) { 351 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 352 mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 353 return; 354 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: 355 mConnected.countDown(); 356 return; 357 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 358 Log.e(TAG, "Channel lost"); 359 return; 360 default: 361 break; 362 } 363 final Object listener; 364 final NsdServiceInfo ns; 365 synchronized (mMapLock) { 366 listener = mListenerMap.get(key); 367 ns = mServiceMap.get(key); 368 } 369 if (listener == null) { 370 Log.d(TAG, "Stale key " + message.arg2); 371 return; 372 } 373 if (DBG) { 374 Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns); 375 } 376 switch (what) { 377 case DISCOVER_SERVICES_STARTED: 378 String s = getNsdServiceInfoType((NsdServiceInfo) message.obj); 379 ((DiscoveryListener) listener).onDiscoveryStarted(s); 380 break; 381 case DISCOVER_SERVICES_FAILED: 382 removeListener(key); 383 ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns), 384 message.arg1); 385 break; 386 case SERVICE_FOUND: 387 ((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj); 388 break; 389 case SERVICE_LOST: 390 ((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj); 391 break; 392 case STOP_DISCOVERY_FAILED: 393 // TODO: failure to stop discovery should be internal and retried internally, as 394 // the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED 395 removeListener(key); 396 ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns), 397 message.arg1); 398 break; 399 case STOP_DISCOVERY_SUCCEEDED: 400 removeListener(key); 401 ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns)); 402 break; 403 case REGISTER_SERVICE_FAILED: 404 removeListener(key); 405 ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1); 406 break; 407 case REGISTER_SERVICE_SUCCEEDED: 408 ((RegistrationListener) listener).onServiceRegistered( 409 (NsdServiceInfo) message.obj); 410 break; 411 case UNREGISTER_SERVICE_FAILED: 412 removeListener(key); 413 ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1); 414 break; 415 case UNREGISTER_SERVICE_SUCCEEDED: 416 // TODO: do not unregister listener until service is unregistered, or provide 417 // alternative way for unregistering ? 418 removeListener(message.arg2); 419 ((RegistrationListener) listener).onServiceUnregistered(ns); 420 break; 421 case RESOLVE_SERVICE_FAILED: 422 removeListener(key); 423 ((ResolveListener) listener).onResolveFailed(ns, message.arg1); 424 break; 425 case RESOLVE_SERVICE_SUCCEEDED: 426 removeListener(key); 427 ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj); 428 break; 429 default: 430 Log.d(TAG, "Ignored " + message); 431 break; 432 } 433 } 434 } 435 nextListenerKey()436 private int nextListenerKey() { 437 // Ensure mListenerKey >= FIRST_LISTENER_KEY; 438 mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1); 439 return mListenerKey; 440 } 441 442 // Assert that the listener is not in the map, then add it and returns its key putListener(Object listener, NsdServiceInfo s)443 private int putListener(Object listener, NsdServiceInfo s) { 444 checkListener(listener); 445 final int key; 446 synchronized (mMapLock) { 447 int valueIndex = mListenerMap.indexOfValue(listener); 448 checkArgument(valueIndex == -1, "listener already in use"); 449 key = nextListenerKey(); 450 mListenerMap.put(key, listener); 451 mServiceMap.put(key, s); 452 } 453 return key; 454 } 455 removeListener(int key)456 private void removeListener(int key) { 457 synchronized (mMapLock) { 458 mListenerMap.remove(key); 459 mServiceMap.remove(key); 460 } 461 } 462 getListenerKey(Object listener)463 private int getListenerKey(Object listener) { 464 checkListener(listener); 465 synchronized (mMapLock) { 466 int valueIndex = mListenerMap.indexOfValue(listener); 467 checkArgument(valueIndex != -1, "listener not registered"); 468 return mListenerMap.keyAt(valueIndex); 469 } 470 } 471 getNsdServiceInfoType(NsdServiceInfo s)472 private static String getNsdServiceInfoType(NsdServiceInfo s) { 473 if (s == null) return "?"; 474 return s.getServiceType(); 475 } 476 477 /** 478 * Initialize AsyncChannel 479 */ init()480 private void init() { 481 final Messenger messenger = getMessenger(); 482 if (messenger == null) { 483 fatal("Failed to obtain service Messenger"); 484 } 485 HandlerThread t = new HandlerThread("NsdManager"); 486 t.start(); 487 mHandler = new ServiceHandler(t.getLooper()); 488 mAsyncChannel.connect(mContext, mHandler, messenger); 489 try { 490 mConnected.await(); 491 } catch (InterruptedException e) { 492 fatal("Interrupted wait at init"); 493 } 494 } 495 fatal(String msg)496 private static void fatal(String msg) { 497 Log.e(TAG, msg); 498 throw new RuntimeException(msg); 499 } 500 501 /** 502 * Register a service to be discovered by other services. 503 * 504 * <p> The function call immediately returns after sending a request to register service 505 * to the framework. The application is notified of a successful registration 506 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 507 * through {@link RegistrationListener#onRegistrationFailed}. 508 * 509 * <p> The application should call {@link #unregisterService} when the service 510 * registration is no longer required, and/or whenever the application is stopped. 511 * 512 * @param serviceInfo The service being registered 513 * @param protocolType The service discovery protocol 514 * @param listener The listener notifies of a successful registration and is used to 515 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 516 * Cannot be in use for an active service registration. 517 */ registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener)518 public void registerService(NsdServiceInfo serviceInfo, int protocolType, 519 RegistrationListener listener) { 520 checkArgument(serviceInfo.getPort() > 0, "Invalid port number"); 521 checkServiceInfo(serviceInfo); 522 checkProtocol(protocolType); 523 int key = putListener(listener, serviceInfo); 524 mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); 525 } 526 527 /** 528 * Unregister a service registered through {@link #registerService}. A successful 529 * unregister is notified to the application with a call to 530 * {@link RegistrationListener#onServiceUnregistered}. 531 * 532 * @param listener This should be the listener object that was passed to 533 * {@link #registerService}. It identifies the service that should be unregistered 534 * and notifies of a successful or unsuccessful unregistration via the listener 535 * callbacks. In API versions 20 and above, the listener object may be used for 536 * another service registration once the callback has been called. In API versions <= 19, 537 * there is no entirely reliable way to know when a listener may be re-used, and a new 538 * listener should be created for each service registration request. 539 */ unregisterService(RegistrationListener listener)540 public void unregisterService(RegistrationListener listener) { 541 int id = getListenerKey(listener); 542 mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); 543 } 544 545 /** 546 * Initiate service discovery to browse for instances of a service type. Service discovery 547 * consumes network bandwidth and will continue until the application calls 548 * {@link #stopServiceDiscovery}. 549 * 550 * <p> The function call immediately returns after sending a request to start service 551 * discovery to the framework. The application is notified of a success to initiate 552 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 553 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 554 * 555 * <p> Upon successful start, application is notified when a service is found with 556 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 557 * {@link DiscoveryListener#onServiceLost}. 558 * 559 * <p> Upon failure to start, service discovery is not active and application does 560 * not need to invoke {@link #stopServiceDiscovery} 561 * 562 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 563 * service type is no longer required, and/or whenever the application is paused or 564 * stopped. 565 * 566 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 567 * http services or "_ipp._tcp" for printers 568 * @param protocolType The service discovery protocol 569 * @param listener The listener notifies of a successful discovery and is used 570 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 571 * Cannot be null. Cannot be in use for an active service discovery. 572 */ discoverServices(String serviceType, int protocolType, DiscoveryListener listener)573 public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { 574 checkStringNotEmpty(serviceType, "Service type cannot be empty"); 575 checkProtocol(protocolType); 576 577 NsdServiceInfo s = new NsdServiceInfo(); 578 s.setServiceType(serviceType); 579 580 int key = putListener(listener, s); 581 mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); 582 } 583 584 /** 585 * Stop service discovery initiated with {@link #discoverServices}. An active service 586 * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} 587 * and it stays active until the application invokes a stop service discovery. A successful 588 * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}. 589 * 590 * <p> Upon failure to stop service discovery, application is notified through 591 * {@link DiscoveryListener#onStopDiscoveryFailed}. 592 * 593 * @param listener This should be the listener object that was passed to {@link #discoverServices}. 594 * It identifies the discovery that should be stopped and notifies of a successful or 595 * unsuccessful stop. In API versions 20 and above, the listener object may be used for 596 * another service discovery once the callback has been called. In API versions <= 19, 597 * there is no entirely reliable way to know when a listener may be re-used, and a new 598 * listener should be created for each service discovery request. 599 */ stopServiceDiscovery(DiscoveryListener listener)600 public void stopServiceDiscovery(DiscoveryListener listener) { 601 int id = getListenerKey(listener); 602 mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); 603 } 604 605 /** 606 * Resolve a discovered service. An application can resolve a service right before 607 * establishing a connection to fetch the IP and port details on which to setup 608 * the connection. 609 * 610 * @param serviceInfo service to be resolved 611 * @param listener to receive callback upon success or failure. Cannot be null. 612 * Cannot be in use for an active service resolution. 613 */ resolveService(NsdServiceInfo serviceInfo, ResolveListener listener)614 public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { 615 checkServiceInfo(serviceInfo); 616 int key = putListener(listener, serviceInfo); 617 mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); 618 } 619 620 /** Internal use only @hide */ setEnabled(boolean enabled)621 public void setEnabled(boolean enabled) { 622 try { 623 mService.setEnabled(enabled); 624 } catch (RemoteException e) { 625 throw e.rethrowFromSystemServer(); 626 } 627 } 628 629 /** 630 * Get a reference to NsdService handler. This is used to establish 631 * an AsyncChannel communication with the service 632 * 633 * @return Messenger pointing to the NsdService handler 634 */ getMessenger()635 private Messenger getMessenger() { 636 try { 637 return mService.getMessenger(); 638 } catch (RemoteException e) { 639 throw e.rethrowFromSystemServer(); 640 } 641 } 642 checkListener(Object listener)643 private static void checkListener(Object listener) { 644 checkNotNull(listener, "listener cannot be null"); 645 } 646 checkProtocol(int protocolType)647 private static void checkProtocol(int protocolType) { 648 checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol"); 649 } 650 checkServiceInfo(NsdServiceInfo serviceInfo)651 private static void checkServiceInfo(NsdServiceInfo serviceInfo) { 652 checkNotNull(serviceInfo, "NsdServiceInfo cannot be null"); 653 checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty"); 654 checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty"); 655 } 656 } 657