1/* 2 * Copyright 2018 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 com.android.support.mediarouter.media; 18 19import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID; 20import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 21 .CLIENT_DATA_ROUTE_LIBRARY_GROUP; 22import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 23 .CLIENT_DATA_UNSELECT_REASON; 24import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME; 25import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 26 .CLIENT_MSG_CREATE_ROUTE_CONTROLLER; 27import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER; 28import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 29 .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER; 30import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 31 .CLIENT_MSG_ROUTE_CONTROL_REQUEST; 32import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE; 33import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 34 .CLIENT_MSG_SET_DISCOVERY_REQUEST; 35import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 36 .CLIENT_MSG_SET_ROUTE_VOLUME; 37import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER; 38import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 39 .CLIENT_MSG_UNSELECT_ROUTE; 40import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 41 .CLIENT_MSG_UPDATE_ROUTE_VOLUME; 42import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_CURRENT; 43import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR; 44import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 45 .SERVICE_MSG_CONTROL_REQUEST_FAILED; 46import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 47 .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED; 48import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 49 .SERVICE_MSG_DESCRIPTOR_CHANGED; 50import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 51 .SERVICE_MSG_GENERIC_FAILURE; 52import static com.android.support.mediarouter.media.MediaRouteProviderProtocol 53 .SERVICE_MSG_GENERIC_SUCCESS; 54import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED; 55import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1; 56import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger; 57 58import android.annotation.NonNull; 59import android.content.ComponentName; 60import android.content.Context; 61import android.content.Intent; 62import android.content.ServiceConnection; 63import android.os.Bundle; 64import android.os.DeadObjectException; 65import android.os.Handler; 66import android.os.IBinder; 67import android.os.IBinder.DeathRecipient; 68import android.os.Message; 69import android.os.Messenger; 70import android.os.RemoteException; 71import android.util.Log; 72import android.util.SparseArray; 73 74import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback; 75 76import java.lang.ref.WeakReference; 77import java.util.ArrayList; 78import java.util.List; 79 80/** 81 * Maintains a connection to a particular media route provider service. 82 */ 83final class RegisteredMediaRouteProvider extends MediaRouteProvider 84 implements ServiceConnection { 85 static final String TAG = "MediaRouteProviderProxy"; // max. 23 chars 86 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 87 88 private final ComponentName mComponentName; 89 final PrivateHandler mPrivateHandler; 90 private final ArrayList<Controller> mControllers = new ArrayList<Controller>(); 91 92 private boolean mStarted; 93 private boolean mBound; 94 private Connection mActiveConnection; 95 private boolean mConnectionReady; 96 97 public RegisteredMediaRouteProvider(Context context, ComponentName componentName) { 98 super(context, new ProviderMetadata(componentName)); 99 100 mComponentName = componentName; 101 mPrivateHandler = new PrivateHandler(); 102 } 103 104 @Override 105 public RouteController onCreateRouteController(@NonNull String routeId) { 106 if (routeId == null) { 107 throw new IllegalArgumentException("routeId cannot be null"); 108 } 109 return createRouteController(routeId, null); 110 } 111 112 @Override 113 public RouteController onCreateRouteController( 114 @NonNull String routeId, @NonNull String routeGroupId) { 115 if (routeId == null) { 116 throw new IllegalArgumentException("routeId cannot be null"); 117 } 118 if (routeGroupId == null) { 119 throw new IllegalArgumentException("routeGroupId cannot be null"); 120 } 121 return createRouteController(routeId, routeGroupId); 122 } 123 124 @Override 125 public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { 126 if (mConnectionReady) { 127 mActiveConnection.setDiscoveryRequest(request); 128 } 129 updateBinding(); 130 } 131 132 @Override 133 public void onServiceConnected(ComponentName name, IBinder service) { 134 if (DEBUG) { 135 Log.d(TAG, this + ": Connected"); 136 } 137 138 if (mBound) { 139 disconnect(); 140 141 Messenger messenger = (service != null ? new Messenger(service) : null); 142 if (isValidRemoteMessenger(messenger)) { 143 Connection connection = new Connection(messenger); 144 if (connection.register()) { 145 mActiveConnection = connection; 146 } else { 147 if (DEBUG) { 148 Log.d(TAG, this + ": Registration failed"); 149 } 150 } 151 } else { 152 Log.e(TAG, this + ": Service returned invalid messenger binder"); 153 } 154 } 155 } 156 157 @Override 158 public void onServiceDisconnected(ComponentName name) { 159 if (DEBUG) { 160 Log.d(TAG, this + ": Service disconnected"); 161 } 162 disconnect(); 163 } 164 165 @Override 166 public String toString() { 167 return "Service connection " + mComponentName.flattenToShortString(); 168 } 169 170 public boolean hasComponentName(String packageName, String className) { 171 return mComponentName.getPackageName().equals(packageName) 172 && mComponentName.getClassName().equals(className); 173 } 174 175 public void start() { 176 if (!mStarted) { 177 if (DEBUG) { 178 Log.d(TAG, this + ": Starting"); 179 } 180 181 mStarted = true; 182 updateBinding(); 183 } 184 } 185 186 public void stop() { 187 if (mStarted) { 188 if (DEBUG) { 189 Log.d(TAG, this + ": Stopping"); 190 } 191 192 mStarted = false; 193 updateBinding(); 194 } 195 } 196 197 public void rebindIfDisconnected() { 198 if (mActiveConnection == null && shouldBind()) { 199 unbind(); 200 bind(); 201 } 202 } 203 204 private void updateBinding() { 205 if (shouldBind()) { 206 bind(); 207 } else { 208 unbind(); 209 } 210 } 211 212 private boolean shouldBind() { 213 if (mStarted) { 214 // Bind whenever there is a discovery request. 215 if (getDiscoveryRequest() != null) { 216 return true; 217 } 218 219 // Bind whenever the application has an active route controller. 220 // This means that one of this provider's routes is selected. 221 if (!mControllers.isEmpty()) { 222 return true; 223 } 224 } 225 return false; 226 } 227 228 private void bind() { 229 if (!mBound) { 230 if (DEBUG) { 231 Log.d(TAG, this + ": Binding"); 232 } 233 234 Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE); 235 service.setComponent(mComponentName); 236 try { 237 mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE); 238 if (!mBound && DEBUG) { 239 Log.d(TAG, this + ": Bind failed"); 240 } 241 } catch (SecurityException ex) { 242 if (DEBUG) { 243 Log.d(TAG, this + ": Bind failed", ex); 244 } 245 } 246 } 247 } 248 249 private void unbind() { 250 if (mBound) { 251 if (DEBUG) { 252 Log.d(TAG, this + ": Unbinding"); 253 } 254 255 mBound = false; 256 disconnect(); 257 getContext().unbindService(this); 258 } 259 } 260 261 private RouteController createRouteController(String routeId, String routeGroupId) { 262 MediaRouteProviderDescriptor descriptor = getDescriptor(); 263 if (descriptor != null) { 264 List<MediaRouteDescriptor> routes = descriptor.getRoutes(); 265 final int count = routes.size(); 266 for (int i = 0; i < count; i++) { 267 final MediaRouteDescriptor route = routes.get(i); 268 if (route.getId().equals(routeId)) { 269 Controller controller = new Controller(routeId, routeGroupId); 270 mControllers.add(controller); 271 if (mConnectionReady) { 272 controller.attachConnection(mActiveConnection); 273 } 274 updateBinding(); 275 return controller; 276 } 277 } 278 } 279 return null; 280 } 281 282 void onConnectionReady(Connection connection) { 283 if (mActiveConnection == connection) { 284 mConnectionReady = true; 285 attachControllersToConnection(); 286 287 MediaRouteDiscoveryRequest request = getDiscoveryRequest(); 288 if (request != null) { 289 mActiveConnection.setDiscoveryRequest(request); 290 } 291 } 292 } 293 294 void onConnectionDied(Connection connection) { 295 if (mActiveConnection == connection) { 296 if (DEBUG) { 297 Log.d(TAG, this + ": Service connection died"); 298 } 299 disconnect(); 300 } 301 } 302 303 void onConnectionError(Connection connection, String error) { 304 if (mActiveConnection == connection) { 305 if (DEBUG) { 306 Log.d(TAG, this + ": Service connection error - " + error); 307 } 308 unbind(); 309 } 310 } 311 312 void onConnectionDescriptorChanged(Connection connection, 313 MediaRouteProviderDescriptor descriptor) { 314 if (mActiveConnection == connection) { 315 if (DEBUG) { 316 Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor); 317 } 318 setDescriptor(descriptor); 319 } 320 } 321 322 private void disconnect() { 323 if (mActiveConnection != null) { 324 setDescriptor(null); 325 mConnectionReady = false; 326 detachControllersFromConnection(); 327 mActiveConnection.dispose(); 328 mActiveConnection = null; 329 } 330 } 331 332 void onControllerReleased(Controller controller) { 333 mControllers.remove(controller); 334 controller.detachConnection(); 335 updateBinding(); 336 } 337 338 private void attachControllersToConnection() { 339 int count = mControllers.size(); 340 for (int i = 0; i < count; i++) { 341 mControllers.get(i).attachConnection(mActiveConnection); 342 } 343 } 344 345 private void detachControllersFromConnection() { 346 int count = mControllers.size(); 347 for (int i = 0; i < count; i++) { 348 mControllers.get(i).detachConnection(); 349 } 350 } 351 352 private final class Controller extends RouteController { 353 private final String mRouteId; 354 private final String mRouteGroupId; 355 356 private boolean mSelected; 357 private int mPendingSetVolume = -1; 358 private int mPendingUpdateVolumeDelta; 359 360 private Connection mConnection; 361 private int mControllerId; 362 363 public Controller(String routeId, String routeGroupId) { 364 mRouteId = routeId; 365 mRouteGroupId = routeGroupId; 366 } 367 368 public void attachConnection(Connection connection) { 369 mConnection = connection; 370 mControllerId = connection.createRouteController(mRouteId, mRouteGroupId); 371 if (mSelected) { 372 connection.selectRoute(mControllerId); 373 if (mPendingSetVolume >= 0) { 374 connection.setVolume(mControllerId, mPendingSetVolume); 375 mPendingSetVolume = -1; 376 } 377 if (mPendingUpdateVolumeDelta != 0) { 378 connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta); 379 mPendingUpdateVolumeDelta = 0; 380 } 381 } 382 } 383 384 public void detachConnection() { 385 if (mConnection != null) { 386 mConnection.releaseRouteController(mControllerId); 387 mConnection = null; 388 mControllerId = 0; 389 } 390 } 391 392 @Override 393 public void onRelease() { 394 onControllerReleased(this); 395 } 396 397 @Override 398 public void onSelect() { 399 mSelected = true; 400 if (mConnection != null) { 401 mConnection.selectRoute(mControllerId); 402 } 403 } 404 405 @Override 406 public void onUnselect() { 407 onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN); 408 } 409 410 @Override 411 public void onUnselect(int reason) { 412 mSelected = false; 413 if (mConnection != null) { 414 mConnection.unselectRoute(mControllerId, reason); 415 } 416 } 417 418 @Override 419 public void onSetVolume(int volume) { 420 if (mConnection != null) { 421 mConnection.setVolume(mControllerId, volume); 422 } else { 423 mPendingSetVolume = volume; 424 mPendingUpdateVolumeDelta = 0; 425 } 426 } 427 428 @Override 429 public void onUpdateVolume(int delta) { 430 if (mConnection != null) { 431 mConnection.updateVolume(mControllerId, delta); 432 } else { 433 mPendingUpdateVolumeDelta += delta; 434 } 435 } 436 437 @Override 438 public boolean onControlRequest(Intent intent, ControlRequestCallback callback) { 439 if (mConnection != null) { 440 return mConnection.sendControlRequest(mControllerId, intent, callback); 441 } 442 return false; 443 } 444 } 445 446 private final class Connection implements DeathRecipient { 447 private final Messenger mServiceMessenger; 448 private final ReceiveHandler mReceiveHandler; 449 private final Messenger mReceiveMessenger; 450 451 private int mNextRequestId = 1; 452 private int mNextControllerId = 1; 453 private int mServiceVersion; // non-zero when registration complete 454 455 private int mPendingRegisterRequestId; 456 private final SparseArray<ControlRequestCallback> mPendingCallbacks = 457 new SparseArray<ControlRequestCallback>(); 458 459 public Connection(Messenger serviceMessenger) { 460 mServiceMessenger = serviceMessenger; 461 mReceiveHandler = new ReceiveHandler(this); 462 mReceiveMessenger = new Messenger(mReceiveHandler); 463 } 464 465 public boolean register() { 466 mPendingRegisterRequestId = mNextRequestId++; 467 if (!sendRequest(CLIENT_MSG_REGISTER, 468 mPendingRegisterRequestId, 469 CLIENT_VERSION_CURRENT, null, null)) { 470 return false; 471 } 472 473 try { 474 mServiceMessenger.getBinder().linkToDeath(this, 0); 475 return true; 476 } catch (RemoteException ex) { 477 binderDied(); 478 } 479 return false; 480 } 481 482 public void dispose() { 483 sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null); 484 mReceiveHandler.dispose(); 485 mServiceMessenger.getBinder().unlinkToDeath(this, 0); 486 487 mPrivateHandler.post(new Runnable() { 488 @Override 489 public void run() { 490 failPendingCallbacks(); 491 } 492 }); 493 } 494 495 void failPendingCallbacks() { 496 int count = 0; 497 for (int i = 0; i < mPendingCallbacks.size(); i++) { 498 mPendingCallbacks.valueAt(i).onError(null, null); 499 } 500 mPendingCallbacks.clear(); 501 } 502 503 public boolean onGenericFailure(int requestId) { 504 if (requestId == mPendingRegisterRequestId) { 505 mPendingRegisterRequestId = 0; 506 onConnectionError(this, "Registration failed"); 507 } 508 ControlRequestCallback callback = mPendingCallbacks.get(requestId); 509 if (callback != null) { 510 mPendingCallbacks.remove(requestId); 511 callback.onError(null, null); 512 } 513 return true; 514 } 515 516 public boolean onGenericSuccess(int requestId) { 517 return true; 518 } 519 520 public boolean onRegistered(int requestId, int serviceVersion, 521 Bundle descriptorBundle) { 522 if (mServiceVersion == 0 523 && requestId == mPendingRegisterRequestId 524 && serviceVersion >= SERVICE_VERSION_1) { 525 mPendingRegisterRequestId = 0; 526 mServiceVersion = serviceVersion; 527 onConnectionDescriptorChanged(this, 528 MediaRouteProviderDescriptor.fromBundle(descriptorBundle)); 529 onConnectionReady(this); 530 return true; 531 } 532 return false; 533 } 534 535 public boolean onDescriptorChanged(Bundle descriptorBundle) { 536 if (mServiceVersion != 0) { 537 onConnectionDescriptorChanged(this, 538 MediaRouteProviderDescriptor.fromBundle(descriptorBundle)); 539 return true; 540 } 541 return false; 542 } 543 544 public boolean onControlRequestSucceeded(int requestId, Bundle data) { 545 ControlRequestCallback callback = mPendingCallbacks.get(requestId); 546 if (callback != null) { 547 mPendingCallbacks.remove(requestId); 548 callback.onResult(data); 549 return true; 550 } 551 return false; 552 } 553 554 public boolean onControlRequestFailed(int requestId, String error, Bundle data) { 555 ControlRequestCallback callback = mPendingCallbacks.get(requestId); 556 if (callback != null) { 557 mPendingCallbacks.remove(requestId); 558 callback.onError(error, data); 559 return true; 560 } 561 return false; 562 } 563 564 @Override 565 public void binderDied() { 566 mPrivateHandler.post(new Runnable() { 567 @Override 568 public void run() { 569 onConnectionDied(Connection.this); 570 } 571 }); 572 } 573 574 public int createRouteController(String routeId, String routeGroupId) { 575 int controllerId = mNextControllerId++; 576 Bundle data = new Bundle(); 577 data.putString(CLIENT_DATA_ROUTE_ID, routeId); 578 data.putString(CLIENT_DATA_ROUTE_LIBRARY_GROUP, routeGroupId); 579 sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER, 580 mNextRequestId++, controllerId, null, data); 581 return controllerId; 582 } 583 584 public void releaseRouteController(int controllerId) { 585 sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER, 586 mNextRequestId++, controllerId, null, null); 587 } 588 589 public void selectRoute(int controllerId) { 590 sendRequest(CLIENT_MSG_SELECT_ROUTE, 591 mNextRequestId++, controllerId, null, null); 592 } 593 594 public void unselectRoute(int controllerId, int reason) { 595 Bundle extras = new Bundle(); 596 extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason); 597 sendRequest(CLIENT_MSG_UNSELECT_ROUTE, 598 mNextRequestId++, controllerId, null, extras); 599 } 600 601 public void setVolume(int controllerId, int volume) { 602 Bundle data = new Bundle(); 603 data.putInt(CLIENT_DATA_VOLUME, volume); 604 sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME, 605 mNextRequestId++, controllerId, null, data); 606 } 607 608 public void updateVolume(int controllerId, int delta) { 609 Bundle data = new Bundle(); 610 data.putInt(CLIENT_DATA_VOLUME, delta); 611 sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME, 612 mNextRequestId++, controllerId, null, data); 613 } 614 615 public boolean sendControlRequest(int controllerId, Intent intent, 616 ControlRequestCallback callback) { 617 int requestId = mNextRequestId++; 618 if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST, 619 requestId, controllerId, intent, null)) { 620 if (callback != null) { 621 mPendingCallbacks.put(requestId, callback); 622 } 623 return true; 624 } 625 return false; 626 } 627 628 public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) { 629 sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST, 630 mNextRequestId++, 0, request != null ? request.asBundle() : null, null); 631 } 632 633 private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) { 634 Message msg = Message.obtain(); 635 msg.what = what; 636 msg.arg1 = requestId; 637 msg.arg2 = arg; 638 msg.obj = obj; 639 msg.setData(data); 640 msg.replyTo = mReceiveMessenger; 641 try { 642 mServiceMessenger.send(msg); 643 return true; 644 } catch (DeadObjectException ex) { 645 // The service died. 646 } catch (RemoteException ex) { 647 if (what != CLIENT_MSG_UNREGISTER) { 648 Log.e(TAG, "Could not send message to service.", ex); 649 } 650 } 651 return false; 652 } 653 } 654 655 private static final class PrivateHandler extends Handler { 656 PrivateHandler() { 657 } 658 } 659 660 /** 661 * Handler that receives messages from the server. 662 * <p> 663 * This inner class is static and only retains a weak reference to the connection 664 * to prevent the client from being leaked in case the service is holding an 665 * active reference to the client's messenger. 666 * </p><p> 667 * This handler should not be used to handle any messages other than those 668 * that come from the service. 669 * </p> 670 */ 671 private static final class ReceiveHandler extends Handler { 672 private final WeakReference<Connection> mConnectionRef; 673 674 public ReceiveHandler(Connection connection) { 675 mConnectionRef = new WeakReference<Connection>(connection); 676 } 677 678 public void dispose() { 679 mConnectionRef.clear(); 680 } 681 682 @Override 683 public void handleMessage(Message msg) { 684 Connection connection = mConnectionRef.get(); 685 if (connection != null) { 686 final int what = msg.what; 687 final int requestId = msg.arg1; 688 final int arg = msg.arg2; 689 final Object obj = msg.obj; 690 final Bundle data = msg.peekData(); 691 if (!processMessage(connection, what, requestId, arg, obj, data)) { 692 if (DEBUG) { 693 Log.d(TAG, "Unhandled message from server: " + msg); 694 } 695 } 696 } 697 } 698 699 private boolean processMessage(Connection connection, 700 int what, int requestId, int arg, Object obj, Bundle data) { 701 switch (what) { 702 case SERVICE_MSG_GENERIC_FAILURE: 703 connection.onGenericFailure(requestId); 704 return true; 705 706 case SERVICE_MSG_GENERIC_SUCCESS: 707 connection.onGenericSuccess(requestId); 708 return true; 709 710 case SERVICE_MSG_REGISTERED: 711 if (obj == null || obj instanceof Bundle) { 712 return connection.onRegistered(requestId, arg, (Bundle)obj); 713 } 714 break; 715 716 case SERVICE_MSG_DESCRIPTOR_CHANGED: 717 if (obj == null || obj instanceof Bundle) { 718 return connection.onDescriptorChanged((Bundle)obj); 719 } 720 break; 721 722 case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED: 723 if (obj == null || obj instanceof Bundle) { 724 return connection.onControlRequestSucceeded( 725 requestId, (Bundle)obj); 726 } 727 break; 728 729 case SERVICE_MSG_CONTROL_REQUEST_FAILED: 730 if (obj == null || obj instanceof Bundle) { 731 String error = (data == null ? null : 732 data.getString(SERVICE_DATA_ERROR)); 733 return connection.onControlRequestFailed( 734 requestId, error, (Bundle)obj); 735 } 736 break; 737 } 738 return false; 739 } 740 } 741} 742