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