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