1/* 2 * Copyright (C) 2013 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.support.v7.media; 18 19import android.app.Service; 20import android.content.Intent; 21import android.os.Bundle; 22import android.os.DeadObjectException; 23import android.os.Handler; 24import android.os.IBinder; 25import android.os.IBinder.DeathRecipient; 26import android.os.Message; 27import android.os.Messenger; 28import android.os.RemoteException; 29import android.util.Log; 30import android.util.SparseArray; 31 32import java.lang.ref.WeakReference; 33import java.util.ArrayList; 34import java.util.List; 35 36import static android.support.v7.media.MediaRouteProviderProtocol.*; 37 38/** 39 * Base class for media route provider services. 40 * <p> 41 * A media router will bind to media route provider services when a callback is added via 42 * {@link MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} with a discovery 43 * flag: {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}, 44 * {@link MediaRouter#CALLBACK_FLAG_FORCE_DISCOVERY}, or 45 * {@link MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN}, and will unbind when the callback 46 * is removed via {@link MediaRouter#removeCallback(MediaRouter.Callback)}. 47 * </p><p> 48 * To implement your own media route provider service, extend this class and 49 * override the {@link #onCreateMediaRouteProvider} method to return an 50 * instance of your {@link MediaRouteProvider}. 51 * </p><p> 52 * Declare your media route provider service in your application manifest 53 * like this: 54 * </p> 55 * <pre> 56 * <service android:name=".MyMediaRouteProviderService" 57 * android:label="@string/my_media_route_provider_service"> 58 * <intent-filter> 59 * <action android:name="android.media.MediaRouteProviderService" /> 60 * </intent-filter> 61 * </service> 62 * </pre> 63 */ 64public abstract class MediaRouteProviderService extends Service { 65 static final String TAG = "MediaRouteProviderSrv"; // max. 23 chars 66 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 67 68 private final ArrayList<ClientRecord> mClients = new ArrayList<ClientRecord>(); 69 private final ReceiveHandler mReceiveHandler; 70 private final Messenger mReceiveMessenger; 71 final PrivateHandler mPrivateHandler; 72 private final ProviderCallback mProviderCallback; 73 74 MediaRouteProvider mProvider; 75 private MediaRouteDiscoveryRequest mCompositeDiscoveryRequest; 76 77 /** 78 * The {@link Intent} that must be declared as handled by the service. 79 * Put this in your manifest. 80 */ 81 public static final String SERVICE_INTERFACE = MediaRouteProviderProtocol.SERVICE_INTERFACE; 82 83 /* 84 * Private messages used internally. (Yes, you can renumber these.) 85 */ 86 87 static final int PRIVATE_MSG_CLIENT_DIED = 1; 88 89 /** 90 * Creates a media route provider service. 91 */ 92 public MediaRouteProviderService() { 93 mReceiveHandler = new ReceiveHandler(this); 94 mReceiveMessenger = new Messenger(mReceiveHandler); 95 mPrivateHandler = new PrivateHandler(); 96 mProviderCallback = new ProviderCallback(); 97 } 98 99 /** 100 * Called by the system when it is time to create the media route provider. 101 * 102 * @return The media route provider offered by this service, or null if 103 * this service has decided not to offer a media route provider. 104 */ 105 public abstract MediaRouteProvider onCreateMediaRouteProvider(); 106 107 /** 108 * Gets the media route provider offered by this service. 109 * 110 * @return The media route provider offered by this service, or null if 111 * it has not yet been created. 112 * 113 * @see #onCreateMediaRouteProvider() 114 */ 115 public MediaRouteProvider getMediaRouteProvider() { 116 return mProvider; 117 } 118 119 @Override 120 public IBinder onBind(Intent intent) { 121 if (intent.getAction().equals(SERVICE_INTERFACE)) { 122 if (mProvider == null) { 123 MediaRouteProvider provider = onCreateMediaRouteProvider(); 124 if (provider != null) { 125 String providerPackage = provider.getMetadata().getPackageName(); 126 if (!providerPackage.equals(getPackageName())) { 127 throw new IllegalStateException("onCreateMediaRouteProvider() returned " 128 + "a provider whose package name does not match the package " 129 + "name of the service. A media route provider service can " 130 + "only export its own media route providers. " 131 + "Provider package name: " + providerPackage 132 + ". Service package name: " + getPackageName() + "."); 133 } 134 mProvider = provider; 135 mProvider.setCallback(mProviderCallback); 136 } 137 } 138 if (mProvider != null) { 139 return mReceiveMessenger.getBinder(); 140 } 141 } 142 return null; 143 } 144 145 @Override 146 public boolean onUnbind(Intent intent) { 147 if (mProvider != null) { 148 mProvider.setCallback(null); 149 } 150 return super.onUnbind(intent); 151 } 152 153 boolean onRegisterClient(Messenger messenger, int requestId, int version) { 154 if (version >= CLIENT_VERSION_1) { 155 int index = findClient(messenger); 156 if (index < 0) { 157 ClientRecord client = new ClientRecord(messenger, version); 158 if (client.register()) { 159 mClients.add(client); 160 if (DEBUG) { 161 Log.d(TAG, client + ": Registered, version=" + version); 162 } 163 if (requestId != 0) { 164 MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor(); 165 sendReply(messenger, SERVICE_MSG_REGISTERED, 166 requestId, SERVICE_VERSION_CURRENT, 167 createDescriptorBundleForClient(descriptor, client), null); 168 } 169 return true; 170 } 171 } 172 } 173 return false; 174 } 175 176 boolean onUnregisterClient(Messenger messenger, int requestId) { 177 int index = findClient(messenger); 178 if (index >= 0) { 179 ClientRecord client = mClients.remove(index); 180 if (DEBUG) { 181 Log.d(TAG, client + ": Unregistered"); 182 } 183 client.dispose(); 184 sendGenericSuccess(messenger, requestId); 185 return true; 186 } 187 return false; 188 } 189 190 void onBinderDied(Messenger messenger) { 191 int index = findClient(messenger); 192 if (index >= 0) { 193 ClientRecord client = mClients.remove(index); 194 if (DEBUG) { 195 Log.d(TAG, client + ": Binder died"); 196 } 197 client.dispose(); 198 } 199 } 200 201 boolean onCreateRouteController(Messenger messenger, int requestId, 202 int controllerId, String routeId, String routeGroupId) { 203 ClientRecord client = getClient(messenger); 204 if (client != null) { 205 if (client.createRouteController(routeId, routeGroupId, controllerId)) { 206 if (DEBUG) { 207 Log.d(TAG, client + ": Route controller created, controllerId=" + controllerId 208 + ", routeId=" + routeId + ", routeGroupId=" + routeGroupId); 209 } 210 sendGenericSuccess(messenger, requestId); 211 return true; 212 } 213 } 214 return false; 215 } 216 217 boolean onReleaseRouteController(Messenger messenger, int requestId, 218 int controllerId) { 219 ClientRecord client = getClient(messenger); 220 if (client != null) { 221 if (client.releaseRouteController(controllerId)) { 222 if (DEBUG) { 223 Log.d(TAG, client + ": Route controller released" 224 + ", controllerId=" + controllerId); 225 } 226 sendGenericSuccess(messenger, requestId); 227 return true; 228 } 229 } 230 return false; 231 } 232 233 boolean onSelectRoute(Messenger messenger, int requestId, 234 int controllerId) { 235 ClientRecord client = getClient(messenger); 236 if (client != null) { 237 MediaRouteProvider.RouteController controller = 238 client.getRouteController(controllerId); 239 if (controller != null) { 240 controller.onSelect(); 241 if (DEBUG) { 242 Log.d(TAG, client + ": Route selected" 243 + ", controllerId=" + controllerId); 244 } 245 sendGenericSuccess(messenger, requestId); 246 return true; 247 } 248 } 249 return false; 250 } 251 252 boolean onUnselectRoute(Messenger messenger, int requestId, 253 int controllerId, int reason) { 254 ClientRecord client = getClient(messenger); 255 if (client != null) { 256 MediaRouteProvider.RouteController controller = 257 client.getRouteController(controllerId); 258 if (controller != null) { 259 controller.onUnselect(reason); 260 if (DEBUG) { 261 Log.d(TAG, client + ": Route unselected" 262 + ", controllerId=" + controllerId); 263 } 264 sendGenericSuccess(messenger, requestId); 265 return true; 266 } 267 } 268 return false; 269 } 270 271 boolean onSetRouteVolume(Messenger messenger, int requestId, 272 int controllerId, int volume) { 273 ClientRecord client = getClient(messenger); 274 if (client != null) { 275 MediaRouteProvider.RouteController controller = 276 client.getRouteController(controllerId); 277 if (controller != null) { 278 controller.onSetVolume(volume); 279 if (DEBUG) { 280 Log.d(TAG, client + ": Route volume changed" 281 + ", controllerId=" + controllerId + ", volume=" + volume); 282 } 283 sendGenericSuccess(messenger, requestId); 284 return true; 285 } 286 } 287 return false; 288 } 289 290 boolean onUpdateRouteVolume(Messenger messenger, int requestId, 291 int controllerId, int delta) { 292 ClientRecord client = getClient(messenger); 293 if (client != null) { 294 MediaRouteProvider.RouteController controller = 295 client.getRouteController(controllerId); 296 if (controller != null) { 297 controller.onUpdateVolume(delta); 298 if (DEBUG) { 299 Log.d(TAG, client + ": Route volume updated" 300 + ", controllerId=" + controllerId + ", delta=" + delta); 301 } 302 sendGenericSuccess(messenger, requestId); 303 return true; 304 } 305 } 306 return false; 307 } 308 309 boolean onRouteControlRequest(final Messenger messenger, final int requestId, 310 final int controllerId, final Intent intent) { 311 final ClientRecord client = getClient(messenger); 312 if (client != null) { 313 MediaRouteProvider.RouteController controller = 314 client.getRouteController(controllerId); 315 if (controller != null) { 316 MediaRouter.ControlRequestCallback callback = null; 317 if (requestId != 0) { 318 callback = new MediaRouter.ControlRequestCallback() { 319 @Override 320 public void onResult(Bundle data) { 321 if (DEBUG) { 322 Log.d(TAG, client + ": Route control request succeeded" 323 + ", controllerId=" + controllerId 324 + ", intent=" + intent 325 + ", data=" + data); 326 } 327 if (findClient(messenger) >= 0) { 328 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED, 329 requestId, 0, data, null); 330 } 331 } 332 333 @Override 334 public void onError(String error, Bundle data) { 335 if (DEBUG) { 336 Log.d(TAG, client + ": Route control request failed" 337 + ", controllerId=" + controllerId 338 + ", intent=" + intent 339 + ", error=" + error + ", data=" + data); 340 } 341 if (findClient(messenger) >= 0) { 342 if (error != null) { 343 Bundle bundle = new Bundle(); 344 bundle.putString(SERVICE_DATA_ERROR, error); 345 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, 346 requestId, 0, data, bundle); 347 } else { 348 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, 349 requestId, 0, data, null); 350 } 351 } 352 } 353 }; 354 } 355 if (controller.onControlRequest(intent, callback)) { 356 if (DEBUG) { 357 Log.d(TAG, client + ": Route control request delivered" 358 + ", controllerId=" + controllerId + ", intent=" + intent); 359 } 360 return true; 361 } 362 } 363 } 364 return false; 365 } 366 367 boolean onSetDiscoveryRequest(Messenger messenger, int requestId, 368 MediaRouteDiscoveryRequest request) { 369 ClientRecord client = getClient(messenger); 370 if (client != null) { 371 boolean actuallyChanged = client.setDiscoveryRequest(request); 372 if (DEBUG) { 373 Log.d(TAG, client + ": Set discovery request, request=" + request 374 + ", actuallyChanged=" + actuallyChanged 375 + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest); 376 } 377 sendGenericSuccess(messenger, requestId); 378 return true; 379 } 380 return false; 381 } 382 383 void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) { 384 final int count = mClients.size(); 385 for (int i = 0; i < count; i++) { 386 ClientRecord client = mClients.get(i); 387 sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0, 388 createDescriptorBundleForClient(descriptor, client), null); 389 if (DEBUG) { 390 Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor); 391 } 392 } 393 } 394 395 private Bundle createDescriptorBundleForClient(MediaRouteProviderDescriptor descriptor, 396 ClientRecord client) { 397 if (descriptor == null) { 398 return null; 399 } 400 List<MediaRouteDescriptor> routes = descriptor.getRoutes(); 401 for (int i = routes.size() - 1; i >= 0; i--) { 402 if (client.mVersion < routes.get(i).getMinClientVersion() 403 || client.mVersion > routes.get(i).getMaxClientVersion()) { 404 routes.remove(i); 405 } 406 } 407 408 // Keep the values of the bundle from descriptor excepts routes values. 409 Bundle bundle = descriptor.asBundle(); 410 bundle.remove(MediaRouteProviderDescriptor.KEY_ROUTES); 411 return new MediaRouteProviderDescriptor.Builder( 412 MediaRouteProviderDescriptor.fromBundle(bundle)) 413 .addRoutes(routes).build().asBundle(); 414 } 415 416 boolean updateCompositeDiscoveryRequest() { 417 MediaRouteDiscoveryRequest composite = null; 418 MediaRouteSelector.Builder selectorBuilder = null; 419 boolean activeScan = false; 420 final int count = mClients.size(); 421 for (int i = 0; i < count; i++) { 422 MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest; 423 if (request != null 424 && (!request.getSelector().isEmpty() || request.isActiveScan())) { 425 activeScan |= request.isActiveScan(); 426 if (composite == null) { 427 composite = request; 428 } else { 429 if (selectorBuilder == null) { 430 selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector()); 431 } 432 selectorBuilder.addSelector(request.getSelector()); 433 } 434 } 435 } 436 if (selectorBuilder != null) { 437 composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan); 438 } 439 if (mCompositeDiscoveryRequest != composite 440 && (mCompositeDiscoveryRequest == null 441 || !mCompositeDiscoveryRequest.equals(composite))) { 442 mCompositeDiscoveryRequest = composite; 443 mProvider.setDiscoveryRequest(composite); 444 return true; 445 } 446 return false; 447 } 448 449 private ClientRecord getClient(Messenger messenger) { 450 int index = findClient(messenger); 451 return index >= 0 ? mClients.get(index) : null; 452 } 453 454 int findClient(Messenger messenger) { 455 final int count = mClients.size(); 456 for (int i = 0; i < count; i++) { 457 ClientRecord client = mClients.get(i); 458 if (client.hasMessenger(messenger)) { 459 return i; 460 } 461 } 462 return -1; 463 } 464 465 static void sendGenericFailure(Messenger messenger, int requestId) { 466 if (requestId != 0) { 467 sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null); 468 } 469 } 470 471 private static void sendGenericSuccess(Messenger messenger, int requestId) { 472 if (requestId != 0) { 473 sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null); 474 } 475 } 476 477 static void sendReply(Messenger messenger, int what, 478 int requestId, int arg, Object obj, Bundle data) { 479 Message msg = Message.obtain(); 480 msg.what = what; 481 msg.arg1 = requestId; 482 msg.arg2 = arg; 483 msg.obj = obj; 484 msg.setData(data); 485 try { 486 messenger.send(msg); 487 } catch (DeadObjectException ex) { 488 // The client died. 489 } catch (RemoteException ex) { 490 Log.e(TAG, "Could not send message to " + getClientId(messenger), ex); 491 } 492 } 493 494 static String getClientId(Messenger messenger) { 495 return "Client connection " + messenger.getBinder().toString(); 496 } 497 498 private final class PrivateHandler extends Handler { 499 PrivateHandler() { 500 } 501 502 @Override 503 public void handleMessage(Message msg) { 504 switch (msg.what) { 505 case PRIVATE_MSG_CLIENT_DIED: 506 onBinderDied((Messenger)msg.obj); 507 break; 508 } 509 } 510 } 511 512 private final class ProviderCallback extends MediaRouteProvider.Callback { 513 ProviderCallback() { 514 } 515 516 @Override 517 public void onDescriptorChanged(MediaRouteProvider provider, 518 MediaRouteProviderDescriptor descriptor) { 519 sendDescriptorChanged(descriptor); 520 } 521 } 522 523 private final class ClientRecord implements DeathRecipient { 524 public final Messenger mMessenger; 525 public final int mVersion; 526 public MediaRouteDiscoveryRequest mDiscoveryRequest; 527 528 private final SparseArray<MediaRouteProvider.RouteController> mControllers = 529 new SparseArray<MediaRouteProvider.RouteController>(); 530 531 public ClientRecord(Messenger messenger, int version) { 532 mMessenger = messenger; 533 mVersion = version; 534 } 535 536 public boolean register() { 537 try { 538 mMessenger.getBinder().linkToDeath(this, 0); 539 return true; 540 } catch (RemoteException ex) { 541 binderDied(); 542 } 543 return false; 544 } 545 546 public void dispose() { 547 int count = mControllers.size(); 548 for (int i = 0; i < count; i++) { 549 mControllers.valueAt(i).onRelease(); 550 } 551 mControllers.clear(); 552 553 mMessenger.getBinder().unlinkToDeath(this, 0); 554 555 setDiscoveryRequest(null); 556 } 557 558 public boolean hasMessenger(Messenger other) { 559 return mMessenger.getBinder() == other.getBinder(); 560 } 561 562 public boolean createRouteController(String routeId, String routeGroupId, 563 int controllerId) { 564 if (mControllers.indexOfKey(controllerId) < 0) { 565 MediaRouteProvider.RouteController controller = routeGroupId == null 566 ? mProvider.onCreateRouteController(routeId) 567 : mProvider.onCreateRouteController(routeId, routeGroupId); 568 if (controller != null) { 569 mControllers.put(controllerId, controller); 570 return true; 571 } 572 } 573 return false; 574 } 575 576 public boolean releaseRouteController(int controllerId) { 577 MediaRouteProvider.RouteController controller = mControllers.get(controllerId); 578 if (controller != null) { 579 mControllers.remove(controllerId); 580 controller.onRelease(); 581 return true; 582 } 583 return false; 584 } 585 586 public MediaRouteProvider.RouteController getRouteController(int controllerId) { 587 return mControllers.get(controllerId); 588 } 589 590 public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) { 591 if (mDiscoveryRequest != request 592 && (mDiscoveryRequest == null || !mDiscoveryRequest.equals(request))) { 593 mDiscoveryRequest = request; 594 return updateCompositeDiscoveryRequest(); 595 } 596 return false; 597 } 598 599 // Runs on a binder thread. 600 @Override 601 public void binderDied() { 602 mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget(); 603 } 604 605 @Override 606 public String toString() { 607 return getClientId(mMessenger); 608 } 609 } 610 611 /** 612 * Handler that receives messages from clients. 613 * <p> 614 * This inner class is static and only retains a weak reference to the service 615 * to prevent the service from being leaked in case one of the clients is holding an 616 * active reference to the server's messenger. 617 * </p><p> 618 * This handler should not be used to handle any messages other than those 619 * that come from the client. 620 * </p> 621 */ 622 private static final class ReceiveHandler extends Handler { 623 private final WeakReference<MediaRouteProviderService> mServiceRef; 624 625 public ReceiveHandler(MediaRouteProviderService service) { 626 mServiceRef = new WeakReference<MediaRouteProviderService>(service); 627 } 628 629 @Override 630 public void handleMessage(Message msg) { 631 final Messenger messenger = msg.replyTo; 632 if (isValidRemoteMessenger(messenger)) { 633 final int what = msg.what; 634 final int requestId = msg.arg1; 635 final int arg = msg.arg2; 636 final Object obj = msg.obj; 637 final Bundle data = msg.peekData(); 638 if (!processMessage(what, messenger, requestId, arg, obj, data)) { 639 if (DEBUG) { 640 Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what 641 + ", requestId=" + requestId + ", arg=" + arg 642 + ", obj=" + obj + ", data=" + data); 643 } 644 sendGenericFailure(messenger, requestId); 645 } 646 } else { 647 if (DEBUG) { 648 Log.d(TAG, "Ignoring message without valid reply messenger."); 649 } 650 } 651 } 652 653 private boolean processMessage(int what, 654 Messenger messenger, int requestId, int arg, Object obj, Bundle data) { 655 MediaRouteProviderService service = mServiceRef.get(); 656 if (service != null) { 657 switch (what) { 658 case CLIENT_MSG_REGISTER: 659 return service.onRegisterClient(messenger, requestId, arg); 660 661 case CLIENT_MSG_UNREGISTER: 662 return service.onUnregisterClient(messenger, requestId); 663 664 case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: { 665 String routeId = data.getString(CLIENT_DATA_ROUTE_ID); 666 String routeGroupId = data.getString(CLIENT_DATA_ROUTE_GROUP_ID); 667 if (routeId != null) { 668 return service.onCreateRouteController( 669 messenger, requestId, arg, routeId, routeGroupId); 670 } 671 break; 672 } 673 674 case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER: 675 return service.onReleaseRouteController(messenger, requestId, arg); 676 677 case CLIENT_MSG_SELECT_ROUTE: 678 return service.onSelectRoute(messenger, requestId, arg); 679 680 case CLIENT_MSG_UNSELECT_ROUTE: 681 int reason = data == null ? 682 MediaRouter.UNSELECT_REASON_UNKNOWN 683 : data.getInt(CLIENT_DATA_UNSELECT_REASON, 684 MediaRouter.UNSELECT_REASON_UNKNOWN); 685 return service.onUnselectRoute(messenger, requestId, arg, reason); 686 687 case CLIENT_MSG_SET_ROUTE_VOLUME: { 688 int volume = data.getInt(CLIENT_DATA_VOLUME, -1); 689 if (volume >= 0) { 690 return service.onSetRouteVolume( 691 messenger, requestId, arg, volume); 692 } 693 break; 694 } 695 696 case CLIENT_MSG_UPDATE_ROUTE_VOLUME: { 697 int delta = data.getInt(CLIENT_DATA_VOLUME, 0); 698 if (delta != 0) { 699 return service.onUpdateRouteVolume( 700 messenger, requestId, arg, delta); 701 } 702 break; 703 } 704 705 case CLIENT_MSG_ROUTE_CONTROL_REQUEST: 706 if (obj instanceof Intent) { 707 return service.onRouteControlRequest( 708 messenger, requestId, arg, (Intent)obj); 709 } 710 break; 711 712 case CLIENT_MSG_SET_DISCOVERY_REQUEST: { 713 if (obj == null || obj instanceof Bundle) { 714 MediaRouteDiscoveryRequest request = 715 MediaRouteDiscoveryRequest.fromBundle((Bundle)obj); 716 return service.onSetDiscoveryRequest( 717 messenger, requestId, 718 request != null && request.isValid() ? request : null); 719 } 720 } 721 } 722 } 723 return false; 724 } 725 } 726} 727