• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.example.android.supportv7.media;
18 
19 import android.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.IntentFilter.MalformedMimeTypeException;
24 import android.content.IntentSender;
25 import android.content.res.Resources;
26 import android.media.AudioManager;
27 import android.media.MediaRouter;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.support.v7.media.MediaControlIntent;
31 import android.support.v7.media.MediaRouteDescriptor;
32 import android.support.v7.media.MediaRouteProvider;
33 import android.support.v7.media.MediaRouteProviderDescriptor;
34 import android.support.v7.media.MediaRouter.ControlRequestCallback;
35 import android.support.v7.media.MediaSessionStatus;
36 import android.util.Log;
37 
38 import com.example.android.supportv7.R;
39 
40 import java.util.ArrayList;
41 
42 /**
43  * Demonstrates how to create a custom media route provider.
44  *
45  * @see SampleMediaRouteProviderService
46  */
47 final class SampleMediaRouteProvider extends MediaRouteProvider {
48     private static final String TAG = "SampleMediaRouteProvider";
49 
50     private static final String FIXED_VOLUME_ROUTE_ID = "fixed";
51     private static final String VARIABLE_VOLUME_BASIC_ROUTE_ID = "variable_basic";
52     private static final String VARIABLE_VOLUME_QUEUING_ROUTE_ID = "variable_queuing";
53     private static final String VARIABLE_VOLUME_SESSION_ROUTE_ID = "variable_session";
54 
55     private static final int VOLUME_MAX = 10;
56 
57     /**
58      * A custom media control intent category for special requests that are
59      * supported by this provider's routes.
60      */
61     public static final String CATEGORY_SAMPLE_ROUTE =
62             "com.example.android.supportv7.media.CATEGORY_SAMPLE_ROUTE";
63 
64     /**
65      * A custom media control intent action to take a snapshot.
66      * </p>
67      *
68      * @see #EXTRA_SNAPSHOT
69      */
70     public static final String ACTION_TAKE_SNAPSHOT =
71             "com.example.android.supportv7.media.action.TAKE_SNAPSHOT";
72 
73     /**
74      * {@link #ACTION_TAKE_SNAPSHOT} result data: a bitmap containing a snapshot
75      * of the currently playing media item
76      */
77     public static final String EXTRA_SNAPSHOT =
78             "com.example.android.supportv7.media.extra.SNAPSHOT";
79 
80     private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
81     private static final ArrayList<IntentFilter> CONTROL_FILTERS_QUEUING;
82     private static final ArrayList<IntentFilter> CONTROL_FILTERS_SESSION;
83 
84     static {
85         IntentFilter f1 = new IntentFilter();
86         f1.addCategory(CATEGORY_SAMPLE_ROUTE);
87         f1.addAction(ACTION_TAKE_SNAPSHOT);
88 
89         IntentFilter f2 = new IntentFilter();
90         f2.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
91         f2.addAction(MediaControlIntent.ACTION_PLAY);
92         f2.addDataScheme("http");
93         f2.addDataScheme("https");
94         f2.addDataScheme("rtsp");
95         f2.addDataScheme("file");
addDataTypeUnchecked(f2, "video/*")96         addDataTypeUnchecked(f2, "video/*");
97 
98         IntentFilter f3 = new IntentFilter();
99         f3.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
100         f3.addAction(MediaControlIntent.ACTION_SEEK);
101         f3.addAction(MediaControlIntent.ACTION_GET_STATUS);
102         f3.addAction(MediaControlIntent.ACTION_PAUSE);
103         f3.addAction(MediaControlIntent.ACTION_RESUME);
104         f3.addAction(MediaControlIntent.ACTION_STOP);
105 
106         IntentFilter f4 = new IntentFilter();
107         f4.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
108         f4.addAction(MediaControlIntent.ACTION_ENQUEUE);
109         f4.addDataScheme("http");
110         f4.addDataScheme("https");
111         f4.addDataScheme("rtsp");
112         f4.addDataScheme("file");
addDataTypeUnchecked(f4, "video/*")113         addDataTypeUnchecked(f4, "video/*");
114 
115         IntentFilter f5 = new IntentFilter();
116         f5.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
117         f5.addAction(MediaControlIntent.ACTION_REMOVE);
118 
119         IntentFilter f6 = new IntentFilter();
120         f6.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
121         f6.addAction(MediaControlIntent.ACTION_START_SESSION);
122         f6.addAction(MediaControlIntent.ACTION_GET_SESSION_STATUS);
123         f6.addAction(MediaControlIntent.ACTION_END_SESSION);
124 
125         CONTROL_FILTERS_BASIC = new ArrayList<>();
126         CONTROL_FILTERS_BASIC.add(f1);
127         CONTROL_FILTERS_BASIC.add(f2);
128         CONTROL_FILTERS_BASIC.add(f3);
129 
130         CONTROL_FILTERS_QUEUING = new ArrayList<>(CONTROL_FILTERS_BASIC);
131         CONTROL_FILTERS_QUEUING.add(f4);
132         CONTROL_FILTERS_QUEUING.add(f5);
133 
134         CONTROL_FILTERS_SESSION = new ArrayList<>(CONTROL_FILTERS_QUEUING);
135         CONTROL_FILTERS_SESSION.add(f6);
136     }
137 
addDataTypeUnchecked(IntentFilter filter, String type)138     private static void addDataTypeUnchecked(IntentFilter filter, String type) {
139         try {
140             filter.addDataType(type);
141         } catch (MalformedMimeTypeException ex) {
142             throw new RuntimeException(ex);
143         }
144     }
145 
146     private int mVolume = 5;
147 
SampleMediaRouteProvider(Context context)148     public SampleMediaRouteProvider(Context context) {
149         super(context);
150 
151         publishRoutes();
152     }
153 
154     @Override
onCreateRouteController(String routeId)155     public RouteController onCreateRouteController(String routeId) {
156         return new SampleRouteController(routeId);
157     }
158 
publishRoutes()159     private void publishRoutes() {
160         Resources r = getContext().getResources();
161         Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
162         settingsIntent.setClass(getContext(), SampleMediaRouteSettingsActivity.class);
163         IntentSender is = PendingIntent.getActivity(getContext(), 99, settingsIntent,
164                 Intent.FLAG_ACTIVITY_NEW_TASK).getIntentSender();
165 
166         MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder(
167                 FIXED_VOLUME_ROUTE_ID,
168                 r.getString(R.string.fixed_volume_route_name))
169                 .setDescription(r.getString(R.string.sample_route_description))
170                 .addControlFilters(CONTROL_FILTERS_BASIC)
171                 .setPlaybackStream(AudioManager.STREAM_MUSIC)
172                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
173                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
174                 .setVolume(VOLUME_MAX)
175                 .setCanDisconnect(true)
176                 .setSettingsActivity(is)
177                 .build();
178 
179         MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
180                 VARIABLE_VOLUME_BASIC_ROUTE_ID,
181                 r.getString(R.string.variable_volume_basic_route_name))
182                 .setDescription(r.getString(R.string.sample_route_description))
183                 .addControlFilters(CONTROL_FILTERS_BASIC)
184                 .setPlaybackStream(AudioManager.STREAM_MUSIC)
185                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
186                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
187                 .setVolumeMax(VOLUME_MAX)
188                 .setVolume(mVolume)
189                 .setSettingsActivity(is)
190                 .build();
191 
192         MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder(
193                 VARIABLE_VOLUME_QUEUING_ROUTE_ID,
194                 r.getString(R.string.variable_volume_queuing_route_name))
195                 .setDescription(r.getString(R.string.sample_route_description))
196                 .addControlFilters(CONTROL_FILTERS_QUEUING)
197                 .setPlaybackStream(AudioManager.STREAM_MUSIC)
198                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
199                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
200                 .setVolumeMax(VOLUME_MAX)
201                 .setVolume(mVolume)
202                 .setCanDisconnect(true)
203                 .build();
204 
205         Uri iconUri = Uri.parse("android.resource://com.example.android.supportv7/"
206                 + R.drawable.ic_android);
207         MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder(
208                 VARIABLE_VOLUME_SESSION_ROUTE_ID,
209                 r.getString(R.string.variable_volume_session_route_name))
210                 .setDescription(r.getString(R.string.sample_route_description))
211                 .addControlFilters(CONTROL_FILTERS_SESSION)
212                 .setPlaybackStream(AudioManager.STREAM_MUSIC)
213                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
214                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
215                 .setVolumeMax(VOLUME_MAX)
216                 .setVolume(mVolume)
217                 .setIconUri(iconUri)
218                 .build();
219 
220         MediaRouteProviderDescriptor providerDescriptor = new MediaRouteProviderDescriptor.Builder()
221                 .addRoute(routeDescriptor1)
222                 .addRoute(routeDescriptor2)
223                 .addRoute(routeDescriptor3)
224                 .addRoute(routeDescriptor4)
225                 .build();
226         setDescriptor(providerDescriptor);
227     }
228 
229     private final class SampleRouteController extends MediaRouteProvider.RouteController {
230         private final String mRouteId;
231         private final SessionManager mSessionManager = new SessionManager("mrp");
232         private final Player mPlayer;
233         private PendingIntent mSessionReceiver;
234 
SampleRouteController(String routeId)235         public SampleRouteController(String routeId) {
236             mRouteId = routeId;
237             mPlayer = Player.create(getContext(), null, null);
238             mSessionManager.setPlayer(mPlayer);
239             mSessionManager.setCallback(new SessionManager.Callback() {
240                 @Override
241                 public void onStatusChanged() {
242                 }
243 
244                 @Override
245                 public void onItemChanged(PlaylistItem item) {
246                     handleStatusChange(item);
247                 }
248             });
249             setVolumeInternal(mVolume);
250             Log.d(TAG, mRouteId + ": Controller created");
251         }
252 
253         @Override
onRelease()254         public void onRelease() {
255             Log.d(TAG, mRouteId + ": Controller released");
256             mPlayer.release();
257         }
258 
259         @Override
onSelect()260         public void onSelect() {
261             Log.d(TAG, mRouteId + ": Selected");
262             mPlayer.connect(null);
263         }
264 
265         @Override
onUnselect()266         public void onUnselect() {
267             Log.d(TAG, mRouteId + ": Unselected");
268             mPlayer.release();
269         }
270 
271         @Override
onSetVolume(int volume)272         public void onSetVolume(int volume) {
273             Log.d(TAG, mRouteId + ": Set volume to " + volume);
274             if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
275                 setVolumeInternal(volume);
276             }
277         }
278 
279         @Override
onUpdateVolume(int delta)280         public void onUpdateVolume(int delta) {
281             Log.d(TAG, mRouteId + ": Update volume by " + delta);
282             if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
283                 setVolumeInternal(mVolume + delta);
284             }
285         }
286 
287         @Override
onControlRequest(Intent intent, ControlRequestCallback callback)288         public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
289             Log.d(TAG, mRouteId + ": Received control request " + intent);
290             String action = intent.getAction();
291             if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
292                 boolean success = false;
293                 if (action.equals(MediaControlIntent.ACTION_PLAY)) {
294                     success = handlePlay(intent, callback);
295                 } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
296                     success = handleEnqueue(intent, callback);
297                 } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
298                     success = handleRemove(intent, callback);
299                 } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
300                     success = handleSeek(intent, callback);
301                 } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
302                     success = handleGetStatus(intent, callback);
303                 } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
304                     success = handlePause(intent, callback);
305                 } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
306                     success = handleResume(intent, callback);
307                 } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
308                     success = handleStop(intent, callback);
309                 } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
310                     success = handleStartSession(intent, callback);
311                 } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
312                     success = handleGetSessionStatus(intent, callback);
313                 } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
314                     success = handleEndSession(intent, callback);
315                 }
316                 Log.d(TAG, mSessionManager.toString());
317                 return success;
318             }
319 
320             if (callback != null && action.equals(ACTION_TAKE_SNAPSHOT)
321                     && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) {
322                 if (mSessionManager.getCurrentItem() != null) {
323                     Bundle data = new Bundle();
324                     data.putParcelable(EXTRA_SNAPSHOT, mPlayer.getSnapshot());
325                     callback.onResult(data);
326                 } else {
327                     callback.onError("Failed to take a snapshot", null);
328                 }
329                 return true;
330             }
331             return false;
332         }
333 
setVolumeInternal(int volume)334         private void setVolumeInternal(int volume) {
335             if (volume >= 0 && volume <= VOLUME_MAX) {
336                 mVolume = volume;
337                 Log.d(TAG, mRouteId + ": New volume is " + mVolume);
338                 AudioManager audioManager =
339                         (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
340                 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
341                 publishRoutes();
342             }
343         }
344 
handlePlay(Intent intent, ControlRequestCallback callback)345         private boolean handlePlay(Intent intent, ControlRequestCallback callback) {
346             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
347             if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
348                 Log.d(TAG, "handlePlay fails because of bad sid="+sid);
349                 return false;
350             }
351             if (mSessionManager.hasSession()) {
352                 mSessionManager.stop();
353             }
354             return handleEnqueue(intent, callback);
355         }
356 
handleEnqueue(Intent intent, ControlRequestCallback callback)357         private boolean handleEnqueue(Intent intent, ControlRequestCallback callback) {
358             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
359             if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
360                 Log.d(TAG, "handleEnqueue fails because of bad sid="+sid);
361                 return false;
362             }
363 
364             Uri uri = intent.getData();
365             if (uri == null) {
366                 Log.d(TAG, "handleEnqueue fails because of bad uri="+uri);
367                 return false;
368             }
369 
370             boolean enqueue = intent.getAction().equals(MediaControlIntent.ACTION_ENQUEUE);
371             String mime = intent.getType();
372             long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
373             Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA);
374             Bundle headers = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS);
375             PendingIntent receiver = (PendingIntent)intent.getParcelableExtra(
376                     MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER);
377 
378             Log.d(TAG, mRouteId + ": Received " + (enqueue?"enqueue":"play") + " request"
379                     + ", uri=" + uri
380                     + ", mime=" + mime
381                     + ", sid=" + sid
382                     + ", pos=" + pos
383                     + ", metadata=" + metadata
384                     + ", headers=" + headers
385                     + ", receiver=" + receiver);
386             PlaylistItem item = mSessionManager.add(null, uri, mime, receiver);
387             if (callback != null) {
388                 if (item != null) {
389                     Bundle result = new Bundle();
390                     result.putString(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
391                     result.putString(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
392                     result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
393                             item.getStatus().asBundle());
394                     callback.onResult(result);
395                 } else {
396                     callback.onError("Failed to open " + uri.toString(), null);
397                 }
398             }
399             return true;
400         }
401 
handleRemove(Intent intent, ControlRequestCallback callback)402         private boolean handleRemove(Intent intent, ControlRequestCallback callback) {
403             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
404             if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
405                 return false;
406             }
407 
408             String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
409             PlaylistItem item = mSessionManager.remove(iid);
410             if (callback != null) {
411                 if (item != null) {
412                     Bundle result = new Bundle();
413                     result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
414                             item.getStatus().asBundle());
415                     callback.onResult(result);
416                 } else {
417                     callback.onError("Failed to remove" +
418                             ", sid=" + sid + ", iid=" + iid, null);
419                 }
420             }
421             return (item != null);
422         }
423 
handleSeek(Intent intent, ControlRequestCallback callback)424         private boolean handleSeek(Intent intent, ControlRequestCallback callback) {
425             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
426             if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
427                 return false;
428             }
429 
430             String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
431             long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
432             Log.d(TAG, mRouteId + ": Received seek request, pos=" + pos);
433             PlaylistItem item = mSessionManager.seek(iid, pos);
434             if (callback != null) {
435                 if (item != null) {
436                     Bundle result = new Bundle();
437                     result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
438                             item.getStatus().asBundle());
439                     callback.onResult(result);
440                 } else {
441                     callback.onError("Failed to seek" +
442                             ", sid=" + sid + ", iid=" + iid + ", pos=" + pos, null);
443                 }
444             }
445             return (item != null);
446         }
447 
handleGetStatus(Intent intent, ControlRequestCallback callback)448         private boolean handleGetStatus(Intent intent, ControlRequestCallback callback) {
449             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
450             String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
451             Log.d(TAG, mRouteId + ": Received getStatus request, sid=" + sid + ", iid=" + iid);
452             PlaylistItem item = mSessionManager.getStatus(iid);
453             if (callback != null) {
454                 if (item != null) {
455                     Bundle result = new Bundle();
456                     result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
457                             item.getStatus().asBundle());
458                     callback.onResult(result);
459                 } else {
460                     callback.onError("Failed to get status" +
461                             ", sid=" + sid + ", iid=" + iid, null);
462                 }
463             }
464             return (item != null);
465         }
466 
handlePause(Intent intent, ControlRequestCallback callback)467         private boolean handlePause(Intent intent, ControlRequestCallback callback) {
468             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
469             boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
470             mSessionManager.pause();
471             if (callback != null) {
472                 if (success) {
473                     callback.onResult(new Bundle());
474                     handleSessionStatusChange(sid);
475                 } else {
476                     callback.onError("Failed to pause, sid=" + sid, null);
477                 }
478             }
479             return success;
480         }
481 
handleResume(Intent intent, ControlRequestCallback callback)482         private boolean handleResume(Intent intent, ControlRequestCallback callback) {
483             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
484             boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
485             mSessionManager.resume();
486             if (callback != null) {
487                 if (success) {
488                     callback.onResult(new Bundle());
489                     handleSessionStatusChange(sid);
490                 } else {
491                     callback.onError("Failed to resume, sid=" + sid, null);
492                 }
493             }
494             return success;
495         }
496 
handleStop(Intent intent, ControlRequestCallback callback)497         private boolean handleStop(Intent intent, ControlRequestCallback callback) {
498             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
499             boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
500             mSessionManager.stop();
501             if (callback != null) {
502                 if (success) {
503                     callback.onResult(new Bundle());
504                     handleSessionStatusChange(sid);
505                 } else {
506                     callback.onError("Failed to stop, sid=" + sid, null);
507                 }
508             }
509             return success;
510         }
511 
handleStartSession(Intent intent, ControlRequestCallback callback)512         private boolean handleStartSession(Intent intent, ControlRequestCallback callback) {
513             String sid = mSessionManager.startSession();
514             Log.d(TAG, "StartSession returns sessionId "+sid);
515             if (callback != null) {
516                 if (sid != null) {
517                     Bundle result = new Bundle();
518                     result.putString(MediaControlIntent.EXTRA_SESSION_ID, sid);
519                     result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
520                             mSessionManager.getSessionStatus(sid).asBundle());
521                     callback.onResult(result);
522                     mSessionReceiver = (PendingIntent)intent.getParcelableExtra(
523                             MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER);
524                     handleSessionStatusChange(sid);
525                 } else {
526                     callback.onError("Failed to start session.", null);
527                 }
528             }
529             return (sid != null);
530         }
531 
handleGetSessionStatus(Intent intent, ControlRequestCallback callback)532         private boolean handleGetSessionStatus(Intent intent, ControlRequestCallback callback) {
533             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
534 
535             MediaSessionStatus sessionStatus = mSessionManager.getSessionStatus(sid);
536             if (callback != null) {
537                 if (sessionStatus != null) {
538                     Bundle result = new Bundle();
539                     result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
540                             mSessionManager.getSessionStatus(sid).asBundle());
541                     callback.onResult(result);
542                 } else {
543                     callback.onError("Failed to get session status, sid=" + sid, null);
544                 }
545             }
546             return (sessionStatus != null);
547         }
548 
handleEndSession(Intent intent, ControlRequestCallback callback)549         private boolean handleEndSession(Intent intent, ControlRequestCallback callback) {
550             String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
551             boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId())
552                     && mSessionManager.endSession();
553             if (callback != null) {
554                 if (success) {
555                     Bundle result = new Bundle();
556                     MediaSessionStatus sessionStatus = new MediaSessionStatus.Builder(
557                             MediaSessionStatus.SESSION_STATE_ENDED).build();
558                     result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS, sessionStatus.asBundle());
559                     callback.onResult(result);
560                     handleSessionStatusChange(sid);
561                     mSessionReceiver = null;
562                 } else {
563                     callback.onError("Failed to end session, sid=" + sid, null);
564                 }
565             }
566             return success;
567         }
568 
handleStatusChange(PlaylistItem item)569         private void handleStatusChange(PlaylistItem item) {
570             if (item == null) {
571                 item = mSessionManager.getCurrentItem();
572             }
573             if (item != null) {
574                 PendingIntent receiver = item.getUpdateReceiver();
575                 if (receiver != null) {
576                     Intent intent = new Intent();
577                     intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
578                     intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
579                     intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS,
580                             item.getStatus().asBundle());
581                     try {
582                         receiver.send(getContext(), 0, intent);
583                         Log.d(TAG, mRouteId + ": Sending status update from provider");
584                     } catch (PendingIntent.CanceledException e) {
585                         Log.d(TAG, mRouteId + ": Failed to send status update!");
586                     }
587                 }
588             }
589         }
590 
handleSessionStatusChange(String sid)591         private void handleSessionStatusChange(String sid) {
592             if (mSessionReceiver != null) {
593                 Intent intent = new Intent();
594                 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sid);
595                 intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS,
596                         mSessionManager.getSessionStatus(sid).asBundle());
597                 try {
598                     mSessionReceiver.send(getContext(), 0, intent);
599                     Log.d(TAG, mRouteId + ": Sending session status update from provider");
600                 } catch (PendingIntent.CanceledException e) {
601                     Log.d(TAG, mRouteId + ": Failed to send session status update!");
602                 }
603             }
604         }
605     }
606 }
607