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 17package android.net.nsd; 18 19import static com.android.internal.util.Preconditions.checkArgument; 20import static com.android.internal.util.Preconditions.checkNotNull; 21import static com.android.internal.util.Preconditions.checkStringNotEmpty; 22 23import android.annotation.SdkConstant; 24import android.annotation.SystemService; 25import android.annotation.SdkConstant.SdkConstantType; 26import android.content.Context; 27import android.os.Handler; 28import android.os.HandlerThread; 29import android.os.Looper; 30import android.os.Message; 31import android.os.RemoteException; 32import android.os.Messenger; 33import android.text.TextUtils; 34import android.util.Log; 35import android.util.SparseArray; 36 37import java.util.concurrent.CountDownLatch; 38 39import com.android.internal.annotations.VisibleForTesting; 40import com.android.internal.util.AsyncChannel; 41import com.android.internal.util.Protocol; 42 43/** 44 * The Network Service Discovery Manager class provides the API to discover services 45 * on a network. As an example, if device A and device B are connected over a Wi-Fi 46 * network, a game registered on device A can be discovered by a game on device 47 * B. Another example use case is an application discovering printers on the network. 48 * 49 * <p> The API currently supports DNS based service discovery and discovery is currently 50 * limited to a local network over Multicast DNS. DNS service discovery is described at 51 * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt 52 * 53 * <p> The API is asynchronous and responses to requests from an application are on listener 54 * callbacks on a seperate internal thread. 55 * 56 * <p> There are three main operations the API supports - registration, discovery and resolution. 57 * <pre> 58 * Application start 59 * | 60 * | 61 * | onServiceRegistered() 62 * Register any local services / 63 * to be advertised with \ 64 * registerService() onRegistrationFailed() 65 * | 66 * | 67 * discoverServices() 68 * | 69 * Maintain a list to track 70 * discovered services 71 * | 72 * |---------> 73 * | | 74 * | onServiceFound() 75 * | | 76 * | add service to list 77 * | | 78 * |<---------- 79 * | 80 * |---------> 81 * | | 82 * | onServiceLost() 83 * | | 84 * | remove service from list 85 * | | 86 * |<---------- 87 * | 88 * | 89 * | Connect to a service 90 * | from list ? 91 * | 92 * resolveService() 93 * | 94 * onServiceResolved() 95 * | 96 * Establish connection to service 97 * with the host and port information 98 * 99 * </pre> 100 * An application that needs to advertise itself over a network for other applications to 101 * discover it can do so with a call to {@link #registerService}. If Example is a http based 102 * application that can provide HTML data to peer services, it can register a name "Example" 103 * with service type "_http._tcp". A successful registration is notified with a callback to 104 * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified 105 * over {@link RegistrationListener#onRegistrationFailed} 106 * 107 * <p> A peer application looking for http services can initiate a discovery for "_http._tcp" 108 * with a call to {@link #discoverServices}. A service found is notified with a callback 109 * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on 110 * {@link DiscoveryListener#onServiceLost}. 111 * 112 * <p> Once the peer application discovers the "Example" http service, and either needs to read the 113 * attributes of the service or wants to receive data from the "Example" application, it can 114 * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port 115 * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a 116 * failure is notified on {@link ResolveListener#onResolveFailed}. 117 * 118 * Applications can reserve for a service type at 119 * http://www.iana.org/form/ports-service. Existing services can be found at 120 * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml 121 * 122 * {@see NsdServiceInfo} 123 */ 124@SystemService(Context.NSD_SERVICE) 125public final class NsdManager { 126 private static final String TAG = NsdManager.class.getSimpleName(); 127 private static final boolean DBG = false; 128 129 /** 130 * Broadcast intent action to indicate whether network service discovery is 131 * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state 132 * information as int. 133 * 134 * @see #EXTRA_NSD_STATE 135 */ 136 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 137 public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; 138 139 /** 140 * The lookup key for an int that indicates whether network service discovery is enabled 141 * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. 142 * 143 * @see #NSD_STATE_DISABLED 144 * @see #NSD_STATE_ENABLED 145 */ 146 public static final String EXTRA_NSD_STATE = "nsd_state"; 147 148 /** 149 * Network service discovery is disabled 150 * 151 * @see #ACTION_NSD_STATE_CHANGED 152 */ 153 public static final int NSD_STATE_DISABLED = 1; 154 155 /** 156 * Network service discovery is enabled 157 * 158 * @see #ACTION_NSD_STATE_CHANGED 159 */ 160 public static final int NSD_STATE_ENABLED = 2; 161 162 private static final int BASE = Protocol.BASE_NSD_MANAGER; 163 164 /** @hide */ 165 public static final int DISCOVER_SERVICES = BASE + 1; 166 /** @hide */ 167 public static final int DISCOVER_SERVICES_STARTED = BASE + 2; 168 /** @hide */ 169 public static final int DISCOVER_SERVICES_FAILED = BASE + 3; 170 /** @hide */ 171 public static final int SERVICE_FOUND = BASE + 4; 172 /** @hide */ 173 public static final int SERVICE_LOST = BASE + 5; 174 175 /** @hide */ 176 public static final int STOP_DISCOVERY = BASE + 6; 177 /** @hide */ 178 public static final int STOP_DISCOVERY_FAILED = BASE + 7; 179 /** @hide */ 180 public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8; 181 182 /** @hide */ 183 public static final int REGISTER_SERVICE = BASE + 9; 184 /** @hide */ 185 public static final int REGISTER_SERVICE_FAILED = BASE + 10; 186 /** @hide */ 187 public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11; 188 189 /** @hide */ 190 public static final int UNREGISTER_SERVICE = BASE + 12; 191 /** @hide */ 192 public static final int UNREGISTER_SERVICE_FAILED = BASE + 13; 193 /** @hide */ 194 public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14; 195 196 /** @hide */ 197 public static final int RESOLVE_SERVICE = BASE + 18; 198 /** @hide */ 199 public static final int RESOLVE_SERVICE_FAILED = BASE + 19; 200 /** @hide */ 201 public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; 202 203 /** @hide */ 204 public static final int ENABLE = BASE + 24; 205 /** @hide */ 206 public static final int DISABLE = BASE + 25; 207 208 /** @hide */ 209 public static final int NATIVE_DAEMON_EVENT = BASE + 26; 210 211 /** Dns based service discovery protocol */ 212 public static final int PROTOCOL_DNS_SD = 0x0001; 213 214 private static final SparseArray<String> EVENT_NAMES = new SparseArray<>(); 215 static { 216 EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES"); 217 EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED"); 218 EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED"); 219 EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); 220 EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); 221 EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY"); 222 EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED"); 223 EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED"); 224 EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE"); 225 EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED"); 226 EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED"); 227 EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE"); 228 EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED"); 229 EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED"); 230 EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); 231 EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); 232 EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); 233 EVENT_NAMES.put(ENABLE, "ENABLE"); 234 EVENT_NAMES.put(DISABLE, "DISABLE"); 235 EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT"); 236 } 237 238 /** @hide */ 239 public static String nameOf(int event) { 240 String name = EVENT_NAMES.get(event); 241 if (name == null) { 242 return Integer.toString(event); 243 } 244 return name; 245 } 246 247 private static int FIRST_LISTENER_KEY = 1; 248 249 private final INsdManager mService; 250 private final Context mContext; 251 252 private int mListenerKey = FIRST_LISTENER_KEY; 253 private final SparseArray mListenerMap = new SparseArray(); 254 private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); 255 private final Object mMapLock = new Object(); 256 257 private final AsyncChannel mAsyncChannel = new AsyncChannel(); 258 private ServiceHandler mHandler; 259 private final CountDownLatch mConnected = new CountDownLatch(1); 260 261 /** 262 * Create a new Nsd instance. Applications use 263 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 264 * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}. 265 * @param service the Binder interface 266 * @hide - hide this because it takes in a parameter of type INsdManager, which 267 * is a system private class. 268 */ 269 public NsdManager(Context context, INsdManager service) { 270 mService = service; 271 mContext = context; 272 init(); 273 } 274 275 /** 276 * @hide 277 */ 278 @VisibleForTesting 279 public void disconnect() { 280 mAsyncChannel.disconnect(); 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 307 public void onStartDiscoveryFailed(String serviceType, int errorCode); 308 309 public void onStopDiscoveryFailed(String serviceType, int errorCode); 310 311 public void onDiscoveryStarted(String serviceType); 312 313 public void onDiscoveryStopped(String serviceType); 314 315 public void onServiceFound(NsdServiceInfo serviceInfo); 316 317 public void onServiceLost(NsdServiceInfo serviceInfo); 318 } 319 320 /** Interface for callback invocation for service registration */ 321 public interface RegistrationListener { 322 323 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 324 325 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 326 327 public void onServiceRegistered(NsdServiceInfo serviceInfo); 328 329 public void onServiceUnregistered(NsdServiceInfo serviceInfo); 330 } 331 332 /** Interface for callback invocation for service resolution */ 333 public interface ResolveListener { 334 335 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode); 336 337 public void onServiceResolved(NsdServiceInfo serviceInfo); 338 } 339 340 @VisibleForTesting 341 class ServiceHandler extends Handler { 342 ServiceHandler(Looper looper) { 343 super(looper); 344 } 345 346 @Override 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 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 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 456 private void removeListener(int key) { 457 synchronized (mMapLock) { 458 mListenerMap.remove(key); 459 mServiceMap.remove(key); 460 } 461 } 462 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 472 private static String getNsdServiceInfoType(NsdServiceInfo s) { 473 if (s == null) return "?"; 474 return s.getServiceType(); 475 } 476 477 /** 478 * Initialize AsyncChannel 479 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 635 private Messenger getMessenger() { 636 try { 637 return mService.getMessenger(); 638 } catch (RemoteException e) { 639 throw e.rethrowFromSystemServer(); 640 } 641 } 642 643 private static void checkListener(Object listener) { 644 checkNotNull(listener, "listener cannot be null"); 645 } 646 647 private static void checkProtocol(int protocolType) { 648 checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol"); 649 } 650 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