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