[go: nahoru, domu]

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