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