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