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