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