SampleMediaRouteProvider.java revision e0189a39290c82f8bc1a37e6a52e959159c9a482
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            return true;
446        }
447
448        private boolean handleRemove(Intent intent, ControlRequestCallback callback) {
449            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
450            if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
451                return false;
452            }
453
454            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
455            PlaylistItem item = mSessionManager.remove(iid);
456            if (callback != null) {
457                if (item != null) {
458                    Bundle result = new Bundle();
459                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
460                            item.getStatus().asBundle());
461                    callback.onResult(result);
462                } else {
463                    callback.onError("Failed to remove" +
464                            ", sid=" + sid + ", iid=" + iid, null);
465                }
466            }
467            return (item != null);
468        }
469
470        private boolean handleSeek(Intent intent, ControlRequestCallback callback) {
471            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
472            if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
473                return false;
474            }
475
476            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
477            long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
478            Log.d(TAG, mRouteId + ": Received seek request, pos=" + pos);
479            PlaylistItem item = mSessionManager.seek(iid, pos);
480            if (callback != null) {
481                if (item != null) {
482                    Bundle result = new Bundle();
483                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
484                            item.getStatus().asBundle());
485                    callback.onResult(result);
486                } else {
487                    callback.onError("Failed to seek" +
488                            ", sid=" + sid + ", iid=" + iid + ", pos=" + pos, null);
489                }
490            }
491            return (item != null);
492        }
493
494        private boolean handleGetStatus(Intent intent, ControlRequestCallback callback) {
495            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
496            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
497            Log.d(TAG, mRouteId + ": Received getStatus request, sid=" + sid + ", iid=" + iid);
498            PlaylistItem item = mSessionManager.getStatus(iid);
499            if (callback != null) {
500                if (item != null) {
501                    Bundle result = new Bundle();
502                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
503                            item.getStatus().asBundle());
504                    callback.onResult(result);
505                } else {
506                    callback.onError("Failed to get status" +
507                            ", sid=" + sid + ", iid=" + iid, null);
508                }
509            }
510            return (item != null);
511        }
512
513        private boolean handlePause(Intent intent, ControlRequestCallback callback) {
514            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
515            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
516            mSessionManager.pause();
517            if (callback != null) {
518                if (success) {
519                    callback.onResult(new Bundle());
520                    handleSessionStatusChange(sid);
521                } else {
522                    callback.onError("Failed to pause, sid=" + sid, null);
523                }
524            }
525            return success;
526        }
527
528        private boolean handleResume(Intent intent, ControlRequestCallback callback) {
529            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
530            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
531            mSessionManager.resume();
532            if (callback != null) {
533                if (success) {
534                    callback.onResult(new Bundle());
535                    handleSessionStatusChange(sid);
536                } else {
537                    callback.onError("Failed to resume, sid=" + sid, null);
538                }
539            }
540            return success;
541        }
542
543        private boolean handleStop(Intent intent, ControlRequestCallback callback) {
544            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
545            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
546            mSessionManager.stop();
547            if (callback != null) {
548                if (success) {
549                    callback.onResult(new Bundle());
550                    handleSessionStatusChange(sid);
551                } else {
552                    callback.onError("Failed to stop, sid=" + sid, null);
553                }
554            }
555            return success;
556        }
557
558        private boolean handleStartSession(Intent intent, ControlRequestCallback callback) {
559            String sid = mSessionManager.startSession();
560            Log.d(TAG, "StartSession returns sessionId "+sid);
561            if (callback != null) {
562                if (sid != null) {
563                    Bundle result = new Bundle();
564                    result.putString(MediaControlIntent.EXTRA_SESSION_ID, sid);
565                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
566                            mSessionManager.getSessionStatus(sid).asBundle());
567                    callback.onResult(result);
568                    mSessionReceiver = (PendingIntent)intent.getParcelableExtra(
569                            MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER);
570                    handleSessionStatusChange(sid);
571                } else {
572                    callback.onError("Failed to start session.", null);
573                }
574            }
575            return (sid != null);
576        }
577
578        private boolean handleGetSessionStatus(Intent intent, ControlRequestCallback callback) {
579            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
580
581            MediaSessionStatus sessionStatus = mSessionManager.getSessionStatus(sid);
582            if (callback != null) {
583                if (sessionStatus != null) {
584                    Bundle result = new Bundle();
585                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
586                            mSessionManager.getSessionStatus(sid).asBundle());
587                    callback.onResult(result);
588                } else {
589                    callback.onError("Failed to get session status, sid=" + sid, null);
590                }
591            }
592            return (sessionStatus != null);
593        }
594
595        private boolean handleEndSession(Intent intent, ControlRequestCallback callback) {
596            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
597            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId())
598                    && mSessionManager.endSession();
599            if (callback != null) {
600                if (success) {
601                    Bundle result = new Bundle();
602                    MediaSessionStatus sessionStatus = new MediaSessionStatus.Builder(
603                            MediaSessionStatus.SESSION_STATE_ENDED).build();
604                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS, sessionStatus.asBundle());
605                    callback.onResult(result);
606                    handleSessionStatusChange(sid);
607                    mSessionReceiver = null;
608                } else {
609                    callback.onError("Failed to end session, sid=" + sid, null);
610                }
611            }
612            return success;
613        }
614
615        private void handleStatusChange(PlaylistItem item) {
616            if (item == null) {
617                item = mSessionManager.getCurrentItem();
618            }
619            if (item != null) {
620                PendingIntent receiver = item.getUpdateReceiver();
621                if (receiver != null) {
622                    Intent intent = new Intent();
623                    intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
624                    intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
625                    intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS,
626                            item.getStatus().asBundle());
627                    try {
628                        receiver.send(getContext(), 0, intent);
629                        Log.d(TAG, mRouteId + ": Sending status update from provider");
630                    } catch (PendingIntent.CanceledException e) {
631                        Log.d(TAG, mRouteId + ": Failed to send status update!");
632                    }
633                }
634            }
635        }
636
637        private void handleSessionStatusChange(String sid) {
638            if (mSessionReceiver != null) {
639                Intent intent = new Intent();
640                intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sid);
641                intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS,
642                        mSessionManager.getSessionStatus(sid).asBundle());
643                try {
644                    mSessionReceiver.send(getContext(), 0, intent);
645                    Log.d(TAG, mRouteId + ": Sending session status update from provider");
646                } catch (PendingIntent.CanceledException e) {
647                    Log.d(TAG, mRouteId + ": Failed to send session status update!");
648                }
649            }
650        }
651    }
652}
653