1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.support.v7.media;
18 
19 import android.app.Service;
20 import android.content.Intent;
21 import android.os.Handler;
22 import android.os.IBinder;
23 import android.os.IBinder.DeathRecipient;
24 import android.os.Bundle;
25 import android.os.DeadObjectException;
26 import android.os.Message;
27 import android.os.Messenger;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import java.lang.ref.WeakReference;
33 import java.util.ArrayList;
34 
35 import 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  */
63 public 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      */
MediaRouteProviderService()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      */
onCreateMediaRouteProvider()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      */
getMediaRouteProvider()114     public MediaRouteProvider getMediaRouteProvider() {
115         return mProvider;
116     }
117 
118     @Override
onBind(Intent intent)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
onUnbind(Intent intent)145     public boolean onUnbind(Intent intent) {
146         if (mProvider != null) {
147             mProvider.setCallback(null);
148         }
149         return super.onUnbind(intent);
150     }
151 
onRegisterClient(Messenger messenger, int requestId, int version)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 
onUnregisterClient(Messenger messenger, int requestId)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 
onBinderDied(Messenger messenger)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 
onCreateRouteController(Messenger messenger, int requestId, int controllerId, String routeId)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 
onReleaseRouteController(Messenger messenger, int requestId, int controllerId)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 
onSelectRoute(Messenger messenger, int requestId, int controllerId)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 
onUnselectRoute(Messenger messenger, int requestId, int controllerId, int reason)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 
onSetRouteVolume(Messenger messenger, int requestId, int controllerId, int volume)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 
onUpdateRouteVolume(Messenger messenger, int requestId, int controllerId, int delta)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 
onRouteControlRequest(final Messenger messenger, final int requestId, final int controllerId, final Intent intent)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 
onSetDiscoveryRequest(Messenger messenger, int requestId, MediaRouteDiscoveryRequest request)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 
sendDescriptorChanged(MediaRouteProviderDescriptor descriptor)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 
updateCompositeDiscoveryRequest()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 
getClient(Messenger messenger)428     private ClientRecord getClient(Messenger messenger) {
429         int index = findClient(messenger);
430         return index >= 0 ? mClients.get(index) : null;
431     }
432 
findClient(Messenger messenger)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 
sendGenericFailure(Messenger messenger, int requestId)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 
sendGenericSuccess(Messenger messenger, int requestId)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 
sendReply(Messenger messenger, int what, int requestId, int arg, Object obj, Bundle data)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 
getClientId(Messenger messenger)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
handleMessage(Message msg)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
onDescriptorChanged(MediaRouteProvider provider, MediaRouteProviderDescriptor descriptor)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 
ClientRecord(Messenger messenger, int version)504         public ClientRecord(Messenger messenger, int version) {
505             mMessenger = messenger;
506             mVersion = version;
507         }
508 
register()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 
dispose()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 
hasMessenger(Messenger other)531         public boolean hasMessenger(Messenger other) {
532             return mMessenger.getBinder() == other.getBinder();
533         }
534 
createRouteController(String routeId, int controllerId)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 
releaseRouteController(int controllerId)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 
getRouteController(int controllerId)557         public MediaRouteProvider.RouteController getRouteController(int controllerId) {
558             return mControllers.get(controllerId);
559         }
560 
setDiscoveryRequest(MediaRouteDiscoveryRequest request)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
binderDied()572         public void binderDied() {
573             mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget();
574         }
575 
576         @Override
toString()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 
ReceiveHandler(MediaRouteProviderService service)596         public ReceiveHandler(MediaRouteProviderService service) {
597             mServiceRef = new WeakReference<MediaRouteProviderService>(service);
598         }
599 
600         @Override
handleMessage(Message msg)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 
processMessage(int what, Messenger messenger, int requestId, int arg, Object obj, Bundle data)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