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