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