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 @Override 145 public boolean onUnbind(Intent intent) { 146 if (mProvider != null) { 147 mProvider.setCallback(null); 148 } 149 return super.onUnbind(intent); 150 } 151 152 private boolean onRegisterClient(Messenger messenger, int requestId, int version) { 153 if (version >= CLIENT_VERSION_1) { 154 int index = findClient(messenger); 155 if (index < 0) { 156 ClientRecord client = new ClientRecord(messenger, version); 157 if (client.register()) { 158 mClients.add(client); 159 if (DEBUG) { 160 Log.d(TAG, client + ": Registered, version=" + version); 161 } 162 if (requestId != 0) { 163 MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor(); 164 sendReply(messenger, SERVICE_MSG_REGISTERED, 165 requestId, SERVICE_VERSION_CURRENT, 166 descriptor != null ? descriptor.asBundle() : null, null); 167 } 168 return true; 169 } 170 } 171 } 172 return false; 173 } 174 175 private boolean onUnregisterClient(Messenger messenger, int requestId) { 176 int index = findClient(messenger); 177 if (index >= 0) { 178 ClientRecord client = mClients.remove(index); 179 if (DEBUG) { 180 Log.d(TAG, client + ": Unregistered"); 181 } 182 client.dispose(); 183 sendGenericSuccess(messenger, requestId); 184 return true; 185 } 186 return false; 187 } 188 189 private void onBinderDied(Messenger messenger) { 190 int index = findClient(messenger); 191 if (index >= 0) { 192 ClientRecord client = mClients.remove(index); 193 if (DEBUG) { 194 Log.d(TAG, client + ": Binder died"); 195 } 196 client.dispose(); 197 } 198 } 199 200 private boolean onCreateRouteController(Messenger messenger, int requestId, 201 int controllerId, String routeId) { 202 ClientRecord client = getClient(messenger); 203 if (client != null) { 204 if (client.createRouteController(routeId, controllerId)) { 205 if (DEBUG) { 206 Log.d(TAG, client + ": Route controller created" 207 + ", controllerId=" + controllerId + ", routeId=" + routeId); 208 } 209 sendGenericSuccess(messenger, requestId); 210 return true; 211 } 212 } 213 return false; 214 } 215 216 private boolean onReleaseRouteController(Messenger messenger, int requestId, 217 int controllerId) { 218 ClientRecord client = getClient(messenger); 219 if (client != null) { 220 if (client.releaseRouteController(controllerId)) { 221 if (DEBUG) { 222 Log.d(TAG, client + ": Route controller released" 223 + ", controllerId=" + controllerId); 224 } 225 sendGenericSuccess(messenger, requestId); 226 return true; 227 } 228 } 229 return false; 230 } 231 232 private boolean onSelectRoute(Messenger messenger, int requestId, 233 int controllerId) { 234 ClientRecord client = getClient(messenger); 235 if (client != null) { 236 MediaRouteProvider.RouteController controller = 237 client.getRouteController(controllerId); 238 if (controller != null) { 239 controller.onSelect(); 240 if (DEBUG) { 241 Log.d(TAG, client + ": Route selected" 242 + ", controllerId=" + controllerId); 243 } 244 sendGenericSuccess(messenger, requestId); 245 return true; 246 } 247 } 248 return false; 249 } 250 251 private boolean onUnselectRoute(Messenger messenger, int requestId, 252 int controllerId, int reason) { 253 ClientRecord client = getClient(messenger); 254 if (client != null) { 255 MediaRouteProvider.RouteController controller = 256 client.getRouteController(controllerId); 257 if (controller != null) { 258 controller.onUnselect(reason); 259 if (DEBUG) { 260 Log.d(TAG, client + ": Route unselected" 261 + ", controllerId=" + controllerId); 262 } 263 sendGenericSuccess(messenger, requestId); 264 return true; 265 } 266 } 267 return false; 268 } 269 270 private boolean onSetRouteVolume(Messenger messenger, int requestId, 271 int controllerId, int volume) { 272 ClientRecord client = getClient(messenger); 273 if (client != null) { 274 MediaRouteProvider.RouteController controller = 275 client.getRouteController(controllerId); 276 if (controller != null) { 277 controller.onSetVolume(volume); 278 if (DEBUG) { 279 Log.d(TAG, client + ": Route volume changed" 280 + ", controllerId=" + controllerId + ", volume=" + volume); 281 } 282 sendGenericSuccess(messenger, requestId); 283 return true; 284 } 285 } 286 return false; 287 } 288 289 private boolean onUpdateRouteVolume(Messenger messenger, int requestId, 290 int controllerId, int delta) { 291 ClientRecord client = getClient(messenger); 292 if (client != null) { 293 MediaRouteProvider.RouteController controller = 294 client.getRouteController(controllerId); 295 if (controller != null) { 296 controller.onUpdateVolume(delta); 297 if (DEBUG) { 298 Log.d(TAG, client + ": Route volume updated" 299 + ", controllerId=" + controllerId + ", delta=" + delta); 300 } 301 sendGenericSuccess(messenger, requestId); 302 return true; 303 } 304 } 305 return false; 306 } 307 308 private boolean onRouteControlRequest(final Messenger messenger, final int requestId, 309 final int controllerId, final Intent intent) { 310 final ClientRecord client = getClient(messenger); 311 if (client != null) { 312 MediaRouteProvider.RouteController controller = 313 client.getRouteController(controllerId); 314 if (controller != null) { 315 MediaRouter.ControlRequestCallback callback = null; 316 if (requestId != 0) { 317 callback = new MediaRouter.ControlRequestCallback() { 318 @Override 319 public void onResult(Bundle data) { 320 if (DEBUG) { 321 Log.d(TAG, client + ": Route control request succeeded" 322 + ", controllerId=" + controllerId 323 + ", intent=" + intent 324 + ", data=" + data); 325 } 326 if (findClient(messenger) >= 0) { 327 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED, 328 requestId, 0, data, null); 329 } 330 } 331 332 @Override 333 public void onError(String error, Bundle data) { 334 if (DEBUG) { 335 Log.d(TAG, client + ": Route control request failed" 336 + ", controllerId=" + controllerId 337 + ", intent=" + intent 338 + ", error=" + error + ", data=" + data); 339 } 340 if (findClient(messenger) >= 0) { 341 if (error != null) { 342 Bundle bundle = new Bundle(); 343 bundle.putString(SERVICE_DATA_ERROR, error); 344 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, 345 requestId, 0, data, bundle); 346 } else { 347 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, 348 requestId, 0, data, null); 349 } 350 } 351 } 352 }; 353 } 354 if (controller.onControlRequest(intent, callback)) { 355 if (DEBUG) { 356 Log.d(TAG, client + ": Route control request delivered" 357 + ", controllerId=" + controllerId + ", intent=" + intent); 358 } 359 return true; 360 } 361 } 362 } 363 return false; 364 } 365 366 private boolean onSetDiscoveryRequest(Messenger messenger, int requestId, 367 MediaRouteDiscoveryRequest request) { 368 ClientRecord client = getClient(messenger); 369 if (client != null) { 370 boolean actuallyChanged = client.setDiscoveryRequest(request); 371 if (DEBUG) { 372 Log.d(TAG, client + ": Set discovery request, request=" + request 373 + ", actuallyChanged=" + actuallyChanged 374 + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest); 375 } 376 sendGenericSuccess(messenger, requestId); 377 return true; 378 } 379 return false; 380 } 381 382 private void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) { 383 Bundle descriptorBundle = descriptor != null ? descriptor.asBundle() : null; 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 descriptorBundle, null); 389 if (DEBUG) { 390 Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor); 391 } 392 } 393 } 394 395 private boolean updateCompositeDiscoveryRequest() { 396 MediaRouteDiscoveryRequest composite = null; 397 MediaRouteSelector.Builder selectorBuilder = null; 398 boolean activeScan = false; 399 final int count = mClients.size(); 400 for (int i = 0; i < count; i++) { 401 MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest; 402 if (request != null 403 && (!request.getSelector().isEmpty() || request.isActiveScan())) { 404 activeScan |= request.isActiveScan(); 405 if (composite == null) { 406 composite = request; 407 } else { 408 if (selectorBuilder == null) { 409 selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector()); 410 } 411 selectorBuilder.addSelector(request.getSelector()); 412 } 413 } 414 } 415 if (selectorBuilder != null) { 416 composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan); 417 } 418 if (mCompositeDiscoveryRequest != composite 419 && (mCompositeDiscoveryRequest == null 420 || !mCompositeDiscoveryRequest.equals(composite))) { 421 mCompositeDiscoveryRequest = composite; 422 mProvider.setDiscoveryRequest(composite); 423 return true; 424 } 425 return false; 426 } 427 428 private ClientRecord getClient(Messenger messenger) { 429 int index = findClient(messenger); 430 return index >= 0 ? mClients.get(index) : null; 431 } 432 433 private int findClient(Messenger messenger) { 434 final int count = mClients.size(); 435 for (int i = 0; i < count; i++) { 436 ClientRecord client = mClients.get(i); 437 if (client.hasMessenger(messenger)) { 438 return i; 439 } 440 } 441 return -1; 442 } 443 444 private static void sendGenericFailure(Messenger messenger, int requestId) { 445 if (requestId != 0) { 446 sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null); 447 } 448 } 449 450 private static void sendGenericSuccess(Messenger messenger, int requestId) { 451 if (requestId != 0) { 452 sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null); 453 } 454 } 455 456 private static void sendReply(Messenger messenger, int what, 457 int requestId, int arg, Object obj, Bundle data) { 458 Message msg = Message.obtain(); 459 msg.what = what; 460 msg.arg1 = requestId; 461 msg.arg2 = arg; 462 msg.obj = obj; 463 msg.setData(data); 464 try { 465 messenger.send(msg); 466 } catch (DeadObjectException ex) { 467 // The client died. 468 } catch (RemoteException ex) { 469 Log.e(TAG, "Could not send message to " + getClientId(messenger), ex); 470 } 471 } 472 473 private static String getClientId(Messenger messenger) { 474 return "Client connection " + messenger.getBinder().toString(); 475 } 476 477 private final class PrivateHandler extends Handler { 478 @Override 479 public void handleMessage(Message msg) { 480 switch (msg.what) { 481 case PRIVATE_MSG_CLIENT_DIED: 482 onBinderDied((Messenger)msg.obj); 483 break; 484 } 485 } 486 } 487 488 private final class ProviderCallback extends MediaRouteProvider.Callback { 489 @Override 490 public void onDescriptorChanged(MediaRouteProvider provider, 491 MediaRouteProviderDescriptor descriptor) { 492 sendDescriptorChanged(descriptor); 493 } 494 } 495 496 private final class ClientRecord implements DeathRecipient { 497 public final Messenger mMessenger; 498 public final int mVersion; 499 public MediaRouteDiscoveryRequest mDiscoveryRequest; 500 501 private final SparseArray<MediaRouteProvider.RouteController> mControllers = 502 new SparseArray<MediaRouteProvider.RouteController>(); 503 504 public ClientRecord(Messenger messenger, int version) { 505 mMessenger = messenger; 506 mVersion = version; 507 } 508 509 public boolean register() { 510 try { 511 mMessenger.getBinder().linkToDeath(this, 0); 512 return true; 513 } catch (RemoteException ex) { 514 binderDied(); 515 } 516 return false; 517 } 518 519 public void dispose() { 520 int count = mControllers.size(); 521 for (int i = 0; i < count; i++) { 522 mControllers.valueAt(i).onRelease(); 523 } 524 mControllers.clear(); 525 526 mMessenger.getBinder().unlinkToDeath(this, 0); 527 528 setDiscoveryRequest(null); 529 } 530 531 public boolean hasMessenger(Messenger other) { 532 return mMessenger.getBinder() == other.getBinder(); 533 } 534 535 public boolean createRouteController(String routeId, int controllerId) { 536 if (mControllers.indexOfKey(controllerId) < 0) { 537 MediaRouteProvider.RouteController controller = 538 mProvider.onCreateRouteController(routeId); 539 if (controller != null) { 540 mControllers.put(controllerId, controller); 541 return true; 542 } 543 } 544 return false; 545 } 546 547 public boolean releaseRouteController(int controllerId) { 548 MediaRouteProvider.RouteController controller = mControllers.get(controllerId); 549 if (controller != null) { 550 mControllers.remove(controllerId); 551 controller.onRelease(); 552 return true; 553 } 554 return false; 555 } 556 557 public MediaRouteProvider.RouteController getRouteController(int controllerId) { 558 return mControllers.get(controllerId); 559 } 560 561 public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) { 562 if (mDiscoveryRequest != request 563 && (mDiscoveryRequest == null || !mDiscoveryRequest.equals(request))) { 564 mDiscoveryRequest = request; 565 return updateCompositeDiscoveryRequest(); 566 } 567 return false; 568 } 569 570 // Runs on a binder thread. 571 @Override 572 public void binderDied() { 573 mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget(); 574 } 575 576 @Override 577 public String toString() { 578 return getClientId(mMessenger); 579 } 580 } 581 582 /** 583 * Handler that receives messages from clients. 584 * <p> 585 * This inner class is static and only retains a weak reference to the service 586 * to prevent the service from being leaked in case one of the clients is holding an 587 * active reference to the server's messenger. 588 * </p><p> 589 * This handler should not be used to handle any messages other than those 590 * that come from the client. 591 * </p> 592 */ 593 private static final class ReceiveHandler extends Handler { 594 private final WeakReference<MediaRouteProviderService> mServiceRef; 595 596 public ReceiveHandler(MediaRouteProviderService service) { 597 mServiceRef = new WeakReference<MediaRouteProviderService>(service); 598 } 599 600 @Override 601 public void handleMessage(Message msg) { 602 final Messenger messenger = msg.replyTo; 603 if (isValidRemoteMessenger(messenger)) { 604 final int what = msg.what; 605 final int requestId = msg.arg1; 606 final int arg = msg.arg2; 607 final Object obj = msg.obj; 608 final Bundle data = msg.peekData(); 609 if (!processMessage(what, messenger, requestId, arg, obj, data)) { 610 if (DEBUG) { 611 Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what 612 + ", requestId=" + requestId + ", arg=" + arg 613 + ", obj=" + obj + ", data=" + data); 614 } 615 sendGenericFailure(messenger, requestId); 616 } 617 } else { 618 if (DEBUG) { 619 Log.d(TAG, "Ignoring message without valid reply messenger."); 620 } 621 } 622 } 623 624 private boolean processMessage(int what, 625 Messenger messenger, int requestId, int arg, Object obj, Bundle data) { 626 MediaRouteProviderService service = mServiceRef.get(); 627 if (service != null) { 628 switch (what) { 629 case CLIENT_MSG_REGISTER: 630 return service.onRegisterClient(messenger, requestId, arg); 631 632 case CLIENT_MSG_UNREGISTER: 633 return service.onUnregisterClient(messenger, requestId); 634 635 case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: { 636 String routeId = data.getString(CLIENT_DATA_ROUTE_ID); 637 if (routeId != null) { 638 return service.onCreateRouteController( 639 messenger, requestId, arg, routeId); 640 } 641 break; 642 } 643 644 case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER: 645 return service.onReleaseRouteController(messenger, requestId, arg); 646 647 case CLIENT_MSG_SELECT_ROUTE: 648 return service.onSelectRoute(messenger, requestId, arg); 649 650 case CLIENT_MSG_UNSELECT_ROUTE: 651 int reason = data == null ? 652 MediaRouter.UNSELECT_REASON_UNKNOWN 653 : data.getInt(CLIENT_DATA_UNSELECT_REASON, 654 MediaRouter.UNSELECT_REASON_UNKNOWN); 655 return service.onUnselectRoute(messenger, requestId, arg, reason); 656 657 case CLIENT_MSG_SET_ROUTE_VOLUME: { 658 int volume = data.getInt(CLIENT_DATA_VOLUME, -1); 659 if (volume >= 0) { 660 return service.onSetRouteVolume( 661 messenger, requestId, arg, volume); 662 } 663 break; 664 } 665 666 case CLIENT_MSG_UPDATE_ROUTE_VOLUME: { 667 int delta = data.getInt(CLIENT_DATA_VOLUME, 0); 668 if (delta != 0) { 669 return service.onUpdateRouteVolume( 670 messenger, requestId, arg, delta); 671 } 672 break; 673 } 674 675 case CLIENT_MSG_ROUTE_CONTROL_REQUEST: 676 if (obj instanceof Intent) { 677 return service.onRouteControlRequest( 678 messenger, requestId, arg, (Intent)obj); 679 } 680 break; 681 682 case CLIENT_MSG_SET_DISCOVERY_REQUEST: { 683 if (obj == null || obj instanceof Bundle) { 684 MediaRouteDiscoveryRequest request = 685 MediaRouteDiscoveryRequest.fromBundle((Bundle)obj); 686 return service.onSetDiscoveryRequest( 687 messenger, requestId, 688 request != null && request.isValid() ? request : null); 689 } 690 } 691 } 692 } 693 return false; 694 } 695 } 696} 697