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