SampleMediaRouteProvider.java revision 1636d4634299cba990966c01dbb577275ab8273c
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<IntentFilter>();
137        CONTROL_FILTERS_BASIC.add(f1);
138        CONTROL_FILTERS_BASIC.add(f2);
139        CONTROL_FILTERS_BASIC.add(f3);
140
141        CONTROL_FILTERS_QUEUING =
142                new ArrayList<IntentFilter>(CONTROL_FILTERS_BASIC);
143        CONTROL_FILTERS_QUEUING.add(f4);
144        CONTROL_FILTERS_QUEUING.add(f5);
145
146        CONTROL_FILTERS_SESSION =
147                new ArrayList<IntentFilter>(CONTROL_FILTERS_QUEUING);
148        CONTROL_FILTERS_SESSION.add(f6);
149    }
150
151    private static void addDataTypeUnchecked(IntentFilter filter, String type) {
152        try {
153            filter.addDataType(type);
154        } catch (MalformedMimeTypeException ex) {
155            throw new RuntimeException(ex);
156        }
157    }
158
159    private int mVolume = 5;
160    private int mEnqueueCount;
161
162    public SampleMediaRouteProvider(Context context) {
163        super(context);
164
165        publishRoutes();
166    }
167
168    @Override
169    public RouteController onCreateRouteController(String routeId) {
170        return new SampleRouteController(routeId);
171    }
172
173    private void publishRoutes() {
174        Resources r = getContext().getResources();
175        Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
176        settingsIntent.setClass(getContext(), SampleMediaRouteSettingsActivity.class);
177        IntentSender is = PendingIntent.getActivity(getContext(), 99, settingsIntent,
178                Intent.FLAG_ACTIVITY_NEW_TASK).getIntentSender();
179
180        MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder(
181                FIXED_VOLUME_ROUTE_ID,
182                r.getString(R.string.fixed_volume_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_FIXED)
188                .setVolume(VOLUME_MAX)
189                .setCanDisconnect(true)
190                .setSettingsActivity(is)
191                .build();
192
193        MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
194                VARIABLE_VOLUME_BASIC_ROUTE_ID,
195                r.getString(R.string.variable_volume_basic_route_name))
196                .setDescription(r.getString(R.string.sample_route_description))
197                .addControlFilters(CONTROL_FILTERS_BASIC)
198                .setPlaybackStream(AudioManager.STREAM_MUSIC)
199                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
200                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
201                .setVolumeMax(VOLUME_MAX)
202                .setVolume(mVolume)
203                .setSettingsActivity(is)
204                .build();
205
206        MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder(
207                VARIABLE_VOLUME_QUEUING_ROUTE_ID,
208                r.getString(R.string.variable_volume_queuing_route_name))
209                .setDescription(r.getString(R.string.sample_route_description))
210                .addControlFilters(CONTROL_FILTERS_QUEUING)
211                .setPlaybackStream(AudioManager.STREAM_MUSIC)
212                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
213                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
214                .setVolumeMax(VOLUME_MAX)
215                .setVolume(mVolume)
216                .setCanDisconnect(true)
217                .build();
218
219        MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder(
220                VARIABLE_VOLUME_SESSION_ROUTE_ID,
221                r.getString(R.string.variable_volume_session_route_name))
222                .setDescription(r.getString(R.string.sample_route_description))
223                .addControlFilters(CONTROL_FILTERS_SESSION)
224                .setPlaybackStream(AudioManager.STREAM_MUSIC)
225                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
226                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
227                .setVolumeMax(VOLUME_MAX)
228                .setVolume(mVolume)
229                .build();
230
231        MediaRouteDescriptor routeDescriptor5 = new MediaRouteDescriptor.Builder(
232                VARIABLE_VOLUME_ROUTE_GROUP_ID,
233                r.getString(R.string.variable_volume_route_group_name))
234                .addChildId(VARIABLE_VOLUME_BASIC_ROUTE_ID)
235                .addChildId(VARIABLE_VOLUME_QUEUING_ROUTE_ID)
236                .addChildId(VARIABLE_VOLUME_SESSION_ROUTE_ID)
237                .setDescription(r.getString(R.string.sample_route_description))
238                .addControlFilters(CONTROL_FILTERS_SESSION)
239                .setPlaybackStream(AudioManager.STREAM_MUSIC)
240                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
241                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
242                .setVolumeMax(VOLUME_MAX)
243                .setVolume(mVolume)
244                .build();
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                .addControlFilters(CONTROL_FILTERS_SESSION)
254                .setPlaybackStream(AudioManager.STREAM_MUSIC)
255                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
256                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
257                .setVolumeMax(VOLUME_MAX)
258                .setVolume(mVolume)
259                .build();
260
261        MediaRouteProviderDescriptor providerDescriptor = new MediaRouteProviderDescriptor.Builder()
262                .addRoute(routeDescriptor1)
263                .addRoute(routeDescriptor2)
264                .addRoute(routeDescriptor3)
265                .addRoute(routeDescriptor4)
266                .addRoute(routeDescriptor5)
267                .addRoute(routeDescriptor6)
268                .build();
269        setDescriptor(providerDescriptor);
270    }
271
272    private final class SampleRouteController extends MediaRouteProvider.RouteController {
273        private final String mRouteId;
274        private final SessionManager mSessionManager = new SessionManager("mrp");
275        private final Player mPlayer;
276        private PendingIntent mSessionReceiver;
277
278        public SampleRouteController(String routeId) {
279            mRouteId = routeId;
280            mPlayer = Player.create(getContext(), null, null);
281            mSessionManager.setPlayer(mPlayer);
282            mSessionManager.setCallback(new SessionManager.Callback() {
283                @Override
284                public void onStatusChanged() {
285                }
286
287                @Override
288                public void onItemChanged(PlaylistItem item) {
289                    handleStatusChange(item);
290                }
291            });
292            setVolumeInternal(mVolume);
293            Log.d(TAG, mRouteId + ": Controller created");
294        }
295
296        @Override
297        public void onRelease() {
298            Log.d(TAG, mRouteId + ": Controller released");
299            mPlayer.release();
300        }
301
302        @Override
303        public void onSelect() {
304            Log.d(TAG, mRouteId + ": Selected");
305            mPlayer.connect(null);
306        }
307
308        @Override
309        public void onUnselect() {
310            Log.d(TAG, mRouteId + ": Unselected");
311            mPlayer.release();
312        }
313
314        @Override
315        public void onSetVolume(int volume) {
316            Log.d(TAG, mRouteId + ": Set volume to " + volume);
317            if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
318                setVolumeInternal(volume);
319            }
320        }
321
322        @Override
323        public void onUpdateVolume(int delta) {
324            Log.d(TAG, mRouteId + ": Update volume by " + delta);
325            if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
326                setVolumeInternal(mVolume + delta);
327            }
328        }
329
330        @Override
331        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
332            Log.d(TAG, mRouteId + ": Received control request " + intent);
333            String action = intent.getAction();
334            if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
335                boolean success = false;
336                if (action.equals(MediaControlIntent.ACTION_PLAY)) {
337                    success = handlePlay(intent, callback);
338                } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
339                    success = handleEnqueue(intent, callback);
340                } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
341                    success = handleRemove(intent, callback);
342                } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
343                    success = handleSeek(intent, callback);
344                } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
345                    success = handleGetStatus(intent, callback);
346                } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
347                    success = handlePause(intent, callback);
348                } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
349                    success = handleResume(intent, callback);
350                } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
351                    success = handleStop(intent, callback);
352                } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
353                    success = handleStartSession(intent, callback);
354                } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
355                    success = handleGetSessionStatus(intent, callback);
356                } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
357                    success = handleEndSession(intent, callback);
358                }
359                Log.d(TAG, mSessionManager.toString());
360                return success;
361            }
362
363            if (action.equals(ACTION_GET_TRACK_INFO)
364                    && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) {
365                Bundle data = new Bundle();
366                PlaylistItem item = mSessionManager.getCurrentItem();
367                if (item != null) {
368                    data.putString(TRACK_INFO_DESC, item.toString());
369                    data.putParcelable(TRACK_INFO_SNAPSHOT, mPlayer.getSnapshot());
370                }
371                if (callback != null) {
372                    callback.onResult(data);
373                }
374                return true;
375            }
376            return false;
377        }
378
379        private void setVolumeInternal(int volume) {
380            if (volume >= 0 && volume <= VOLUME_MAX) {
381                mVolume = volume;
382                Log.d(TAG, mRouteId + ": New volume is " + mVolume);
383                AudioManager audioManager =
384                        (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
385                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
386                publishRoutes();
387            }
388        }
389
390        private boolean handlePlay(Intent intent, ControlRequestCallback callback) {
391            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
392            if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
393                Log.d(TAG, "handlePlay fails because of bad sid="+sid);
394                return false;
395            }
396            if (mSessionManager.hasSession()) {
397                mSessionManager.stop();
398            }
399            return handleEnqueue(intent, callback);
400        }
401
402        private boolean handleEnqueue(Intent intent, ControlRequestCallback callback) {
403            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
404            if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
405                Log.d(TAG, "handleEnqueue fails because of bad sid="+sid);
406                return false;
407            }
408
409            Uri uri = intent.getData();
410            if (uri == null) {
411                Log.d(TAG, "handleEnqueue fails because of bad uri="+uri);
412                return false;
413            }
414
415            boolean enqueue = intent.getAction().equals(MediaControlIntent.ACTION_ENQUEUE);
416            String mime = intent.getType();
417            long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
418            Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA);
419            Bundle headers = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS);
420            PendingIntent receiver = (PendingIntent)intent.getParcelableExtra(
421                    MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER);
422
423            Log.d(TAG, mRouteId + ": Received " + (enqueue?"enqueue":"play") + " request"
424                    + ", uri=" + uri
425                    + ", mime=" + mime
426                    + ", sid=" + sid
427                    + ", pos=" + pos
428                    + ", metadata=" + metadata
429                    + ", headers=" + headers
430                    + ", receiver=" + receiver);
431            PlaylistItem item = mSessionManager.add(uri, mime, receiver);
432            if (callback != null) {
433                if (item != null) {
434                    Bundle result = new Bundle();
435                    result.putString(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
436                    result.putString(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
437                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
438                            item.getStatus().asBundle());
439                    callback.onResult(result);
440                } else {
441                    callback.onError("Failed to open " + uri.toString(), null);
442                }
443            }
444            mEnqueueCount +=1;
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}