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