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