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