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