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