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