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