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