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