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