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