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