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