Home | History | Annotate | Download | only in media
      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.app.Service;
     20 import android.content.Intent;
     21 import android.os.Handler;
     22 import android.os.IBinder;
     23 import android.os.IBinder.DeathRecipient;
     24 import android.os.Bundle;
     25 import android.os.DeadObjectException;
     26 import android.os.Message;
     27 import android.os.Messenger;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 import android.util.SparseArray;
     31 
     32 import java.lang.ref.WeakReference;
     33 import java.util.ArrayList;
     34 
     35 /**
     36  * Base class for media route provider services.
     37  * <p>
     38  * To implement your own media route provider service, extend this class and
     39  * override the {@link #onCreateMediaRouteProvider} method to return an
     40  * instance of your {@link MediaRouteProvider}.
     41  * </p><p>
     42  * Declare your media route provider service in your application manifest
     43  * like this:
     44  * </p>
     45  * <pre>
     46  *   &lt;service android:name=".MyMediaRouteProviderService"
     47  *           android:label="@string/my_media_route_provider_service">
     48  *       &lt;intent-filter>
     49  *           &lt;action android:name="android.media.MediaRouteProviderService" />
     50  *       &lt;/intent-filter>
     51  *   &lt;/service>
     52  * </pre>
     53  */
     54 public abstract class MediaRouteProviderService extends Service {
     55     private static final String TAG = "MediaRouteProviderSrv"; // max. 23 chars
     56     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     57 
     58     private final ArrayList<ClientRecord> mClients = new ArrayList<ClientRecord>();
     59     private final ReceiveHandler mReceiveHandler;
     60     private final Messenger mReceiveMessenger;
     61     private final PrivateHandler mPrivateHandler;
     62     private final ProviderCallback mProviderCallback;
     63 
     64     private MediaRouteProvider mProvider;
     65     private MediaRouteDiscoveryRequest mCompositeDiscoveryRequest;
     66 
     67     /**
     68      * The {@link Intent} that must be declared as handled by the service.
     69      * Put this in your manifest.
     70      */
     71     public static final String SERVICE_INTERFACE =
     72             "android.media.MediaRouteProviderService";
     73 
     74     /*
     75      * Messages sent from the client to the service.
     76      * DO NOT RENUMBER THESE!
     77      */
     78 
     79     /** (client v1)
     80      * Register client.
     81      * - replyTo : client messenger
     82      * - arg1    : request id
     83      * - arg2    : client version
     84      */
     85     static final int CLIENT_MSG_REGISTER = 1;
     86 
     87     /** (client v1)
     88      * Unregister client.
     89      * - replyTo : client messenger
     90      * - arg1    : request id
     91      */
     92     static final int CLIENT_MSG_UNREGISTER = 2;
     93 
     94     /** (client v1)
     95      * Create route controller.
     96      * - replyTo : client messenger
     97      * - arg1    : request id
     98      * - arg2    : route controller id
     99      * - CLIENT_DATA_ROUTE_ID : route id string
    100      */
    101     static final int CLIENT_MSG_CREATE_ROUTE_CONTROLLER = 3;
    102 
    103     /** (client v1)
    104      * Release route controller.
    105      * - replyTo : client messenger
    106      * - arg1    : request id
    107      * - arg2    : route controller id
    108      */
    109     static final int CLIENT_MSG_RELEASE_ROUTE_CONTROLLER = 4;
    110 
    111     /** (client v1)
    112      * Select route.
    113      * - replyTo : client messenger
    114      * - arg1    : request id
    115      * - arg2    : route controller id
    116      */
    117     static final int CLIENT_MSG_SELECT_ROUTE = 5;
    118 
    119     /** (client v1)
    120      * Unselect route.
    121      * - replyTo : client messenger
    122      * - arg1    : request id
    123      * - arg2    : route controller id
    124      */
    125     static final int CLIENT_MSG_UNSELECT_ROUTE = 6;
    126 
    127     /** (client v1)
    128      * Set route volume.
    129      * - replyTo : client messenger
    130      * - arg1    : request id
    131      * - arg2    : route controller id
    132      * - CLIENT_DATA_VOLUME : volume integer
    133      */
    134     static final int CLIENT_MSG_SET_ROUTE_VOLUME = 7;
    135 
    136     /** (client v1)
    137      * Update route volume.
    138      * - replyTo : client messenger
    139      * - arg1    : request id
    140      * - arg2    : route controller id
    141      * - CLIENT_DATA_VOLUME : volume delta integer
    142      */
    143     static final int CLIENT_MSG_UPDATE_ROUTE_VOLUME = 8;
    144 
    145     /** (client v1)
    146      * Route control request.
    147      * - replyTo : client messenger
    148      * - arg1    : request id
    149      * - arg2    : route controller id
    150      * - obj     : media control intent
    151      */
    152     static final int CLIENT_MSG_ROUTE_CONTROL_REQUEST = 9;
    153 
    154     /** (client v1)
    155      * Sets the discovery request.
    156      * - replyTo : client messenger
    157      * - arg1    : request id
    158      * - obj     : discovery request bundle, or null if none
    159      */
    160     static final int CLIENT_MSG_SET_DISCOVERY_REQUEST = 10;
    161 
    162     static final String CLIENT_DATA_ROUTE_ID = "routeId";
    163     static final String CLIENT_DATA_VOLUME = "volume";
    164 
    165     /*
    166      * Messages sent from the service to the client.
    167      * DO NOT RENUMBER THESE!
    168      */
    169 
    170     /** (service v1)
    171      * Generic failure sent in response to any unrecognized or malformed request.
    172      * - arg1    : request id
    173      */
    174     static final int SERVICE_MSG_GENERIC_FAILURE = 0;
    175 
    176     /** (service v1)
    177      * Generic failure sent in response to a successful message.
    178      * - arg1    : request id
    179      */
    180     static final int SERVICE_MSG_GENERIC_SUCCESS = 1;
    181 
    182     /** (service v1)
    183      * Registration succeeded.
    184      * - arg1    : request id
    185      * - arg2    : server version
    186      * - obj     : route provider descriptor bundle, or null
    187      */
    188     static final int SERVICE_MSG_REGISTERED = 2;
    189 
    190     /** (service v1)
    191      * Route control request success result.
    192      * - arg1    : request id
    193      * - obj     : result data bundle, or null
    194      */
    195     static final int SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED = 3;
    196 
    197     /** (service v1)
    198      * Route control request failure result.
    199      * - arg1    : request id
    200      * - obj     : result data bundle, or null
    201      * - SERVICE_DATA_ERROR: error message
    202      */
    203     static final int SERVICE_MSG_CONTROL_REQUEST_FAILED = 4;
    204 
    205     /** (service v1)
    206      * Route provider descriptor changed.  (unsolicited event)
    207      * - arg1    : reserved (0)
    208      * - obj     : route provider descriptor bundle, or null
    209      */
    210     static final int SERVICE_MSG_DESCRIPTOR_CHANGED = 5;
    211 
    212     static final String SERVICE_DATA_ERROR = "error";
    213 
    214     /*
    215      * Recognized client version numbers.  (Reserved for future use.)
    216      * DO NOT RENUMBER THESE!
    217      */
    218 
    219     static final int CLIENT_VERSION_1 = 1;
    220     static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_1;
    221 
    222     /*
    223      * Recognized server version numbers.  (Reserved for future use.)
    224      * DO NOT RENUMBER THESE!
    225      */
    226 
    227     static final int SERVICE_VERSION_1 = 1;
    228     static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1;
    229 
    230     /*
    231      * Private messages used internally.  (Yes, you can renumber these.)
    232      */
    233 
    234     private static final int PRIVATE_MSG_CLIENT_DIED = 1;
    235 
    236     /**
    237      * Creates a media route provider service.
    238      */
    239     public MediaRouteProviderService() {
    240         mReceiveHandler = new ReceiveHandler(this);
    241         mReceiveMessenger = new Messenger(mReceiveHandler);
    242         mPrivateHandler = new PrivateHandler();
    243         mProviderCallback = new ProviderCallback();
    244     }
    245 
    246     /**
    247      * Called by the system when it is time to create the media route provider.
    248      *
    249      * @return The media route provider offered by this service, or null if
    250      * this service has decided not to offer a media route provider.
    251      */
    252     public abstract MediaRouteProvider onCreateMediaRouteProvider();
    253 
    254     /**
    255      * Gets the media route provider offered by this service.
    256      *
    257      * @return The media route provider offered by this service, or null if
    258      * it has not yet been created.
    259      *
    260      * @see #onCreateMediaRouteProvider()
    261      */
    262     public MediaRouteProvider getMediaRouteProvider() {
    263         return mProvider;
    264     }
    265 
    266     @Override
    267     public IBinder onBind(Intent intent) {
    268         if (intent.getAction().equals(SERVICE_INTERFACE)) {
    269             if (mProvider == null) {
    270                 MediaRouteProvider provider = onCreateMediaRouteProvider();
    271                 if (provider != null) {
    272                     String providerPackage = provider.getMetadata().getPackageName();
    273                     if (!providerPackage.equals(getPackageName())) {
    274                         throw new IllegalStateException("onCreateMediaRouteProvider() returned "
    275                                 + "a provider whose package name does not match the package "
    276                                 + "name of the service.  A media route provider service can "
    277                                 + "only export its own media route providers.  "
    278                                 + "Provider package name: " + providerPackage
    279                                 + ".  Service package name: " + getPackageName() + ".");
    280                     }
    281                     mProvider = provider;
    282                     mProvider.setCallback(mProviderCallback);
    283                 }
    284             }
    285             if (mProvider != null) {
    286                 return mReceiveMessenger.getBinder();
    287             }
    288         }
    289         return null;
    290     }
    291 
    292     private boolean onRegisterClient(Messenger messenger, int requestId, int version) {
    293         if (version >= CLIENT_VERSION_1) {
    294             int index = findClient(messenger);
    295             if (index < 0) {
    296                 ClientRecord client = new ClientRecord(messenger, version);
    297                 if (client.register()) {
    298                     mClients.add(client);
    299                     if (DEBUG) {
    300                         Log.d(TAG, client + ": Registered, version=" + version);
    301                     }
    302                     if (requestId != 0) {
    303                         MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor();
    304                         sendReply(messenger, SERVICE_MSG_REGISTERED,
    305                                 requestId, SERVICE_VERSION_CURRENT,
    306                                 descriptor != null ? descriptor.asBundle() : null, null);
    307                     }
    308                     return true;
    309                 }
    310             }
    311         }
    312         return false;
    313     }
    314 
    315     private boolean onUnregisterClient(Messenger messenger, int requestId) {
    316         int index = findClient(messenger);
    317         if (index >= 0) {
    318             ClientRecord client = mClients.remove(index);
    319             if (DEBUG) {
    320                 Log.d(TAG, client + ": Unregistered");
    321             }
    322             client.dispose();
    323             sendGenericSuccess(messenger, requestId);
    324             return true;
    325         }
    326         return false;
    327     }
    328 
    329     private void onBinderDied(Messenger messenger) {
    330         int index = findClient(messenger);
    331         if (index >= 0) {
    332             ClientRecord client = mClients.remove(index);
    333             if (DEBUG) {
    334                 Log.d(TAG, client + ": Binder died");
    335             }
    336             client.dispose();
    337         }
    338     }
    339 
    340     private boolean onCreateRouteController(Messenger messenger, int requestId,
    341             int controllerId, String routeId) {
    342         ClientRecord client = getClient(messenger);
    343         if (client != null) {
    344             if (client.createRouteController(routeId, controllerId)) {
    345                 if (DEBUG) {
    346                     Log.d(TAG, client + ": Route controller created"
    347                             + ", controllerId=" + controllerId + ", routeId=" + routeId);
    348                 }
    349                 sendGenericSuccess(messenger, requestId);
    350                 return true;
    351             }
    352         }
    353         return false;
    354     }
    355 
    356     private boolean onReleaseRouteController(Messenger messenger, int requestId,
    357             int controllerId) {
    358         ClientRecord client = getClient(messenger);
    359         if (client != null) {
    360             if (client.releaseRouteController(controllerId)) {
    361                 if (DEBUG) {
    362                     Log.d(TAG, client + ": Route controller released"
    363                             + ", controllerId=" + controllerId);
    364                 }
    365                 sendGenericSuccess(messenger, requestId);
    366                 return true;
    367             }
    368         }
    369         return false;
    370     }
    371 
    372     private boolean onSelectRoute(Messenger messenger, int requestId,
    373             int controllerId) {
    374         ClientRecord client = getClient(messenger);
    375         if (client != null) {
    376             MediaRouteProvider.RouteController controller =
    377                     client.getRouteController(controllerId);
    378             if (controller != null) {
    379                 controller.onSelect();
    380                 if (DEBUG) {
    381                     Log.d(TAG, client + ": Route selected"
    382                             + ", controllerId=" + controllerId);
    383                 }
    384                 sendGenericSuccess(messenger, requestId);
    385                 return true;
    386             }
    387         }
    388         return false;
    389     }
    390 
    391     private boolean onUnselectRoute(Messenger messenger, int requestId,
    392             int controllerId) {
    393         ClientRecord client = getClient(messenger);
    394         if (client != null) {
    395             MediaRouteProvider.RouteController controller =
    396                     client.getRouteController(controllerId);
    397             if (controller != null) {
    398                 controller.onUnselect();
    399                 if (DEBUG) {
    400                     Log.d(TAG, client + ": Route unselected"
    401                             + ", controllerId=" + controllerId);
    402                 }
    403                 sendGenericSuccess(messenger, requestId);
    404                 return true;
    405             }
    406         }
    407         return false;
    408     }
    409 
    410     private boolean onSetRouteVolume(Messenger messenger, int requestId,
    411             int controllerId, int volume) {
    412         ClientRecord client = getClient(messenger);
    413         if (client != null) {
    414             MediaRouteProvider.RouteController controller =
    415                     client.getRouteController(controllerId);
    416             if (controller != null) {
    417                 controller.onSetVolume(volume);
    418                 if (DEBUG) {
    419                     Log.d(TAG, client + ": Route volume changed"
    420                             + ", controllerId=" + controllerId + ", volume=" + volume);
    421                 }
    422                 sendGenericSuccess(messenger, requestId);
    423                 return true;
    424             }
    425         }
    426         return false;
    427     }
    428 
    429     private boolean onUpdateRouteVolume(Messenger messenger, int requestId,
    430             int controllerId, int delta) {
    431         ClientRecord client = getClient(messenger);
    432         if (client != null) {
    433             MediaRouteProvider.RouteController controller =
    434                     client.getRouteController(controllerId);
    435             if (controller != null) {
    436                 controller.onUpdateVolume(delta);
    437                 if (DEBUG) {
    438                     Log.d(TAG, client + ": Route volume updated"
    439                             + ", controllerId=" + controllerId + ", delta=" + delta);
    440                 }
    441                 sendGenericSuccess(messenger, requestId);
    442                 return true;
    443             }
    444         }
    445         return false;
    446     }
    447 
    448     private boolean onRouteControlRequest(final Messenger messenger, final int requestId,
    449             final int controllerId, final Intent intent) {
    450         final ClientRecord client = getClient(messenger);
    451         if (client != null) {
    452             MediaRouteProvider.RouteController controller =
    453                     client.getRouteController(controllerId);
    454             if (controller != null) {
    455                 MediaRouter.ControlRequestCallback callback = null;
    456                 if (requestId != 0) {
    457                     callback = new MediaRouter.ControlRequestCallback() {
    458                         @Override
    459                         public void onResult(Bundle data) {
    460                             if (DEBUG) {
    461                                 Log.d(TAG, client + ": Route control request succeeded"
    462                                         + ", controllerId=" + controllerId
    463                                         + ", intent=" + intent
    464                                         + ", data=" + data);
    465                             }
    466                             if (findClient(messenger) >= 0) {
    467                                 sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED,
    468                                         requestId, 0, data, null);
    469                             }
    470                         }
    471 
    472                         @Override
    473                         public void onError(String error, Bundle data) {
    474                             if (DEBUG) {
    475                                 Log.d(TAG, client + ": Route control request failed"
    476                                         + ", controllerId=" + controllerId
    477                                         + ", intent=" + intent
    478                                         + ", error=" + error + ", data=" + data);
    479                             }
    480                             if (findClient(messenger) >= 0) {
    481                                 if (error != null) {
    482                                     Bundle bundle = new Bundle();
    483                                     bundle.putString(SERVICE_DATA_ERROR, error);
    484                                     sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
    485                                             requestId, 0, data, bundle);
    486                                 } else {
    487                                     sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
    488                                             requestId, 0, data, null);
    489                                 }
    490                             }
    491                         }
    492                     };
    493                 }
    494                 if (controller.onControlRequest(intent, callback)) {
    495                     if (DEBUG) {
    496                         Log.d(TAG, client + ": Route control request delivered"
    497                                 + ", controllerId=" + controllerId + ", intent=" + intent);
    498                     }
    499                     return true;
    500                 }
    501             }
    502         }
    503         return false;
    504     }
    505 
    506     private boolean onSetDiscoveryRequest(Messenger messenger, int requestId,
    507             MediaRouteDiscoveryRequest request) {
    508         ClientRecord client = getClient(messenger);
    509         if (client != null) {
    510             boolean actuallyChanged = client.setDiscoveryRequest(request);
    511             if (DEBUG) {
    512                 Log.d(TAG, client + ": Set discovery request, request=" + request
    513                         + ", actuallyChanged=" + actuallyChanged
    514                         + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest);
    515             }
    516             sendGenericSuccess(messenger, requestId);
    517             return true;
    518         }
    519         return false;
    520     }
    521 
    522     private void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) {
    523         Bundle descriptorBundle = descriptor != null ? descriptor.asBundle() : null;
    524         final int count = mClients.size();
    525         for (int i = 0; i < count; i++) {
    526             ClientRecord client = mClients.get(i);
    527             sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0,
    528                     descriptorBundle, null);
    529             if (DEBUG) {
    530                 Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor);
    531             }
    532         }
    533     }
    534 
    535     private boolean updateCompositeDiscoveryRequest() {
    536         MediaRouteDiscoveryRequest composite = null;
    537         MediaRouteSelector.Builder selectorBuilder = null;
    538         boolean activeScan = false;
    539         final int count = mClients.size();
    540         for (int i = 0; i < count; i++) {
    541             MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest;
    542             if (request != null
    543                     && (!request.getSelector().isEmpty() || request.isActiveScan())) {
    544                 activeScan |= request.isActiveScan();
    545                 if (composite == null) {
    546                     composite = request;
    547                 } else {
    548                     if (selectorBuilder == null) {
    549                         selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector());
    550                     }
    551                     selectorBuilder.addSelector(request.getSelector());
    552                 }
    553             }
    554         }
    555         if (selectorBuilder != null) {
    556             composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan);
    557         }
    558         if (mCompositeDiscoveryRequest != composite
    559                 && (mCompositeDiscoveryRequest == null
    560                         || !mCompositeDiscoveryRequest.equals(composite))) {
    561             mCompositeDiscoveryRequest = composite;
    562             mProvider.setDiscoveryRequest(composite);
    563             return true;
    564         }
    565         return false;
    566     }
    567 
    568     private ClientRecord getClient(Messenger messenger) {
    569         int index = findClient(messenger);
    570         return index >= 0 ? mClients.get(index) : null;
    571     }
    572 
    573     private int findClient(Messenger messenger) {
    574         final int count = mClients.size();
    575         for (int i = 0; i < count; i++) {
    576             ClientRecord client = mClients.get(i);
    577             if (client.hasMessenger(messenger)) {
    578                 return i;
    579             }
    580         }
    581         return -1;
    582     }
    583 
    584     private static void sendGenericFailure(Messenger messenger, int requestId) {
    585         if (requestId != 0) {
    586             sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null);
    587         }
    588     }
    589 
    590     private static void sendGenericSuccess(Messenger messenger, int requestId) {
    591         if (requestId != 0) {
    592             sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null);
    593         }
    594     }
    595 
    596     private static void sendReply(Messenger messenger, int what,
    597             int requestId, int arg, Object obj, Bundle data) {
    598         Message msg = Message.obtain();
    599         msg.what = what;
    600         msg.arg1 = requestId;
    601         msg.arg2 = arg;
    602         msg.obj = obj;
    603         msg.setData(data);
    604         try {
    605             messenger.send(msg);
    606         } catch (DeadObjectException ex) {
    607             // The client died.
    608         } catch (RemoteException ex) {
    609             Log.e(TAG, "Could not send message to " + getClientId(messenger), ex);
    610         }
    611     }
    612 
    613     private static String getClientId(Messenger messenger) {
    614         return "Client connection " + messenger.getBinder().toString();
    615     }
    616 
    617     /**
    618      * Returns true if the messenger object is valid.
    619      * <p>
    620      * The messenger constructor and unparceling code does not check whether the
    621      * provided IBinder is a valid IMessenger object.  As a result, it's possible
    622      * for a peer to send an invalid IBinder that will result in crashes downstream.
    623      * This method checks that the messenger is in a valid state.
    624      * </p>
    625      */
    626     static boolean isValidRemoteMessenger(Messenger messenger) {
    627         try {
    628             return messenger != null && messenger.getBinder() != null;
    629         } catch (NullPointerException ex) {
    630             // If the messenger was constructed with a binder interface other than
    631             // IMessenger then the call to getBinder() will crash with an NPE.
    632             return false;
    633         }
    634     }
    635 
    636     private final class PrivateHandler extends Handler {
    637         @Override
    638         public void handleMessage(Message msg) {
    639             switch (msg.what) {
    640                 case PRIVATE_MSG_CLIENT_DIED:
    641                     onBinderDied((Messenger)msg.obj);
    642                     break;
    643             }
    644         }
    645     }
    646 
    647     private final class ProviderCallback extends MediaRouteProvider.Callback {
    648         @Override
    649         public void onDescriptorChanged(MediaRouteProvider provider,
    650                 MediaRouteProviderDescriptor descriptor) {
    651             sendDescriptorChanged(descriptor);
    652         }
    653     }
    654 
    655     private final class ClientRecord implements DeathRecipient {
    656         public final Messenger mMessenger;
    657         public final int mVersion;
    658         public MediaRouteDiscoveryRequest mDiscoveryRequest;
    659 
    660         private final SparseArray<MediaRouteProvider.RouteController> mControllers =
    661                 new SparseArray<MediaRouteProvider.RouteController>();
    662 
    663         public ClientRecord(Messenger messenger, int version) {
    664             mMessenger = messenger;
    665             mVersion = version;
    666         }
    667 
    668         public boolean register() {
    669             try {
    670                 mMessenger.getBinder().linkToDeath(this, 0);
    671                 return true;
    672             } catch (RemoteException ex) {
    673                 binderDied();
    674             }
    675             return false;
    676         }
    677 
    678         public void dispose() {
    679             int count = mControllers.size();
    680             for (int i = 0; i < count; i++) {
    681                 mControllers.valueAt(i).onRelease();
    682             }
    683             mControllers.clear();
    684 
    685             mMessenger.getBinder().unlinkToDeath(this, 0);
    686 
    687             setDiscoveryRequest(null);
    688         }
    689 
    690         public boolean hasMessenger(Messenger other) {
    691             return mMessenger.getBinder() == other.getBinder();
    692         }
    693 
    694         public boolean createRouteController(String routeId, int controllerId) {
    695             if (mControllers.indexOfKey(controllerId) < 0) {
    696                 MediaRouteProvider.RouteController controller =
    697                         mProvider.onCreateRouteController(routeId);
    698                 if (controller != null) {
    699                     mControllers.put(controllerId, controller);
    700                     return true;
    701                 }
    702             }
    703             return false;
    704         }
    705 
    706         public boolean releaseRouteController(int controllerId) {
    707             MediaRouteProvider.RouteController controller = mControllers.get(controllerId);
    708             if (controller != null) {
    709                 mControllers.remove(controllerId);
    710                 controller.onRelease();
    711                 return true;
    712             }
    713             return false;
    714         }
    715 
    716         public MediaRouteProvider.RouteController getRouteController(int controllerId) {
    717             return mControllers.get(controllerId);
    718         }
    719 
    720         public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
    721             if (mDiscoveryRequest != request
    722                     && (mDiscoveryRequest == null || !mDiscoveryRequest.equals(request))) {
    723                 mDiscoveryRequest = request;
    724                 return updateCompositeDiscoveryRequest();
    725             }
    726             return false;
    727         }
    728 
    729         // Runs on a binder thread.
    730         @Override
    731         public void binderDied() {
    732             mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget();
    733         }
    734 
    735         @Override
    736         public String toString() {
    737             return getClientId(mMessenger);
    738         }
    739     }
    740 
    741     /**
    742      * Handler that receives messages from clients.
    743      * <p>
    744      * This inner class is static and only retains a weak reference to the service
    745      * to prevent the service from being leaked in case one of the clients is holding an
    746      * active reference to the server's messenger.
    747      * </p><p>
    748      * This handler should not be used to handle any messages other than those
    749      * that come from the client.
    750      * </p>
    751      */
    752     private static final class ReceiveHandler extends Handler {
    753         private final WeakReference<MediaRouteProviderService> mServiceRef;
    754 
    755         public ReceiveHandler(MediaRouteProviderService service) {
    756             mServiceRef = new WeakReference<MediaRouteProviderService>(service);
    757         }
    758 
    759         @Override
    760         public void handleMessage(Message msg) {
    761             final Messenger messenger = msg.replyTo;
    762             if (isValidRemoteMessenger(messenger)) {
    763                 final int what = msg.what;
    764                 final int requestId = msg.arg1;
    765                 final int arg = msg.arg2;
    766                 final Object obj = msg.obj;
    767                 final Bundle data = msg.peekData();
    768                 if (!processMessage(what, messenger, requestId, arg, obj, data)) {
    769                     if (DEBUG) {
    770                         Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what
    771                                 + ", requestId=" + requestId + ", arg=" + arg
    772                                 + ", obj=" + obj + ", data=" + data);
    773                     }
    774                     sendGenericFailure(messenger, requestId);
    775                 }
    776             } else {
    777                 if (DEBUG) {
    778                     Log.d(TAG, "Ignoring message without valid reply messenger.");
    779                 }
    780             }
    781         }
    782 
    783         private boolean processMessage(int what,
    784                 Messenger messenger, int requestId, int arg, Object obj, Bundle data) {
    785             MediaRouteProviderService service = mServiceRef.get();
    786             if (service != null) {
    787                 switch (what) {
    788                     case CLIENT_MSG_REGISTER:
    789                         return service.onRegisterClient(messenger, requestId, arg);
    790 
    791                     case CLIENT_MSG_UNREGISTER:
    792                         return service.onUnregisterClient(messenger, requestId);
    793 
    794                     case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: {
    795                         String routeId = data.getString(CLIENT_DATA_ROUTE_ID);
    796                         if (routeId != null) {
    797                             return service.onCreateRouteController(
    798                                     messenger, requestId, arg, routeId);
    799                         }
    800                         break;
    801                     }
    802 
    803                     case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER:
    804                         return service.onReleaseRouteController(messenger, requestId, arg);
    805 
    806                     case CLIENT_MSG_SELECT_ROUTE:
    807                         return service.onSelectRoute(messenger, requestId, arg);
    808 
    809                     case CLIENT_MSG_UNSELECT_ROUTE:
    810                         return service.onUnselectRoute(messenger, requestId, arg);
    811 
    812                     case CLIENT_MSG_SET_ROUTE_VOLUME: {
    813                         int volume = data.getInt(CLIENT_DATA_VOLUME, -1);
    814                         if (volume >= 0) {
    815                             return service.onSetRouteVolume(
    816                                     messenger, requestId, arg, volume);
    817                         }
    818                         break;
    819                     }
    820 
    821                     case CLIENT_MSG_UPDATE_ROUTE_VOLUME: {
    822                         int delta = data.getInt(CLIENT_DATA_VOLUME, 0);
    823                         if (delta != 0) {
    824                             return service.onUpdateRouteVolume(
    825                                     messenger, requestId, arg, delta);
    826                         }
    827                         break;
    828                     }
    829 
    830                     case CLIENT_MSG_ROUTE_CONTROL_REQUEST:
    831                         if (obj instanceof Intent) {
    832                             return service.onRouteControlRequest(
    833                                     messenger, requestId, arg, (Intent)obj);
    834                         }
    835                         break;
    836 
    837                     case CLIENT_MSG_SET_DISCOVERY_REQUEST: {
    838                         if (obj == null || obj instanceof Bundle) {
    839                             MediaRouteDiscoveryRequest request =
    840                                     MediaRouteDiscoveryRequest.fromBundle((Bundle)obj);
    841                             return service.onSetDiscoveryRequest(
    842                                     messenger, requestId,
    843                                     request != null && request.isValid() ? request : null);
    844                         }
    845                     }
    846                 }
    847             }
    848             return false;
    849         }
    850     }
    851 }
    852