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