Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2018 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 android.media;
     18 
     19 import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
     20 
     21 import android.annotation.CallbackExecutor;
     22 import android.annotation.NonNull;
     23 import android.annotation.Nullable;
     24 import android.app.PendingIntent;
     25 import android.content.Context;
     26 import android.media.MediaPlaylistAgent.RepeatMode;
     27 import android.media.MediaPlaylistAgent.ShuffleMode;
     28 import android.media.MediaSession2.CommandButton;
     29 import android.media.MediaSession2.ControllerInfo;
     30 import android.media.MediaSession2.ErrorCode;
     31 import android.media.session.MediaSessionManager;
     32 import android.media.update.ApiLoader;
     33 import android.media.update.MediaController2Provider;
     34 import android.media.update.MediaController2Provider.PlaybackInfoProvider;
     35 import android.net.Uri;
     36 import android.os.Bundle;
     37 import android.os.ResultReceiver;
     38 
     39 import java.util.List;
     40 import java.util.concurrent.Executor;
     41 
     42 /**
     43  * @hide
     44  * Allows an app to interact with an active {@link MediaSession2} or a
     45  * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
     46  * the session.
     47  * <p>
     48  * When you're done, use {@link #close()} to clean up resources. This also helps session service
     49  * to be destroyed when there's no controller associated with it.
     50  * <p>
     51  * When controlling {@link MediaSession2}, the controller will be available immediately after
     52  * the creation.
     53  * <p>
     54  * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
     55  * available only if the session service allows this controller by
     56  * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
     57  * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
     58  * {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
     59  * <p>
     60  * A controller can be created through token from {@link MediaSessionManager} if you hold the
     61  * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
     62  * an enabled notification listener or by getting a {@link SessionToken2} directly the
     63  * the session owner.
     64  * <p>
     65  * MediaController2 objects are thread-safe.
     66  * <p>
     67  * @see MediaSession2
     68  * @see MediaSessionService2
     69  */
     70 public class MediaController2 implements AutoCloseable {
     71     /**
     72      * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
     73      * active if and only if it has set a player.
     74      */
     75     public abstract static class ControllerCallback {
     76         /**
     77          * Called when the controller is successfully connected to the session. The controller
     78          * becomes available afterwards.
     79          *
     80          * @param controller the controller for this event
     81          * @param allowedCommands commands that's allowed by the session.
     82          */
     83         public void onConnected(@NonNull MediaController2 controller,
     84                 @NonNull SessionCommandGroup2 allowedCommands) { }
     85 
     86         /**
     87          * Called when the session refuses the controller or the controller is disconnected from
     88          * the session. The controller becomes unavailable afterwards and the callback wouldn't
     89          * be called.
     90          * <p>
     91          * It will be also called after the {@link #close()}, so you can put clean up code here.
     92          * You don't need to call {@link #close()} after this.
     93          *
     94          * @param controller the controller for this event
     95          * @param controller controller for this event
     96          */
     97         public void onDisconnected(@NonNull MediaController2 controller) { }
     98 
     99         /**
    100          * Called when the session set the custom layout through the
    101          * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
    102          * <p>
    103          * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
    104          * called.
    105          *
    106          * @param controller the controller for this event
    107          * @param layout
    108          */
    109         public void onCustomLayoutChanged(@NonNull MediaController2 controller,
    110                 @NonNull List<CommandButton> layout) { }
    111 
    112         /**
    113          * Called when the session has changed anything related with the {@link PlaybackInfo}.
    114          *
    115          * @param controller the controller for this event
    116          * @param info new playback info
    117          */
    118         public void onPlaybackInfoChanged(@NonNull MediaController2 controller,
    119                 @NonNull PlaybackInfo info) { }
    120 
    121         /**
    122          * Called when the allowed commands are changed by session.
    123          *
    124          * @param controller the controller for this event
    125          * @param commands newly allowed commands
    126          */
    127         public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
    128                 @NonNull SessionCommandGroup2 commands) { }
    129 
    130         /**
    131          * Called when the session sent a custom command.
    132          *
    133          * @param controller the controller for this event
    134          * @param command
    135          * @param args
    136          * @param receiver
    137          */
    138         public void onCustomCommand(@NonNull MediaController2 controller,
    139                 @NonNull SessionCommand2 command, @Nullable Bundle args,
    140                 @Nullable ResultReceiver receiver) { }
    141 
    142         /**
    143          * Called when the player state is changed.
    144          *
    145          * @param controller the controller for this event
    146          * @param state
    147          */
    148         public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
    149 
    150         /**
    151          * Called when playback speed is changed.
    152          *
    153          * @param controller the controller for this event
    154          * @param speed speed
    155          */
    156         public void onPlaybackSpeedChanged(@NonNull MediaController2 controller,
    157                 float speed) { }
    158 
    159         /**
    160          * Called to report buffering events for a data source.
    161          * <p>
    162          * Use {@link #getBufferedPosition()} for current buffering position.
    163          *
    164          * @param controller the controller for this event
    165          * @param item the media item for which buffering is happening.
    166          * @param state the new buffering state.
    167          */
    168         public void onBufferingStateChanged(@NonNull MediaController2 controller,
    169                 @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
    170 
    171         /**
    172          * Called to indicate that seeking is completed.
    173          *
    174          * @param controller the controller for this event.
    175          * @param position the previous seeking request.
    176          */
    177         public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
    178 
    179         /**
    180          * Called when a error from
    181          *
    182          * @param controller the controller for this event
    183          * @param errorCode error code
    184          * @param extras extra information
    185          */
    186         public void onError(@NonNull MediaController2 controller, @ErrorCode int errorCode,
    187                 @Nullable Bundle extras) { }
    188 
    189         /**
    190          * Called when the player's currently playing item is changed
    191          * <p>
    192          * When it's called, you should invalidate previous playback information and wait for later
    193          * callbacks.
    194          *
    195          * @param controller the controller for this event
    196          * @param item new item
    197          * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
    198          */
    199         // TODO(jaewan): Use this (b/74316764)
    200         public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
    201                 @NonNull MediaItem2 item) { }
    202 
    203         /**
    204          * Called when a playlist is changed.
    205          *
    206          * @param controller the controller for this event
    207          * @param list new playlist
    208          * @param metadata new metadata
    209          */
    210         public void onPlaylistChanged(@NonNull MediaController2 controller,
    211                 @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
    212 
    213         /**
    214          * Called when a playlist metadata is changed.
    215          *
    216          * @param controller the controller for this event
    217          * @param metadata new metadata
    218          */
    219         public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
    220                 @Nullable MediaMetadata2 metadata) { }
    221 
    222         /**
    223          * Called when the shuffle mode is changed.
    224          *
    225          * @param controller the controller for this event
    226          * @param shuffleMode repeat mode
    227          * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    228          * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    229          * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    230          */
    231         public void onShuffleModeChanged(@NonNull MediaController2 controller,
    232                 @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
    233 
    234         /**
    235          * Called when the repeat mode is changed.
    236          *
    237          * @param controller the controller for this event
    238          * @param repeatMode repeat mode
    239          * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    240          * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    241          * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    242          * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    243          */
    244         public void onRepeatModeChanged(@NonNull MediaController2 controller,
    245                 @MediaPlaylistAgent.RepeatMode int repeatMode) { }
    246     }
    247 
    248     /**
    249      * Holds information about the current playback and how audio is handled for
    250      * this session.
    251      */
    252     // The same as MediaController.PlaybackInfo
    253     public static final class PlaybackInfo {
    254         /**
    255          * The session uses remote playback.
    256          */
    257         public static final int PLAYBACK_TYPE_REMOTE = 2;
    258         /**
    259          * The session uses local playback.
    260          */
    261         public static final int PLAYBACK_TYPE_LOCAL = 1;
    262 
    263         private final PlaybackInfoProvider mProvider;
    264 
    265         /**
    266          * @hide
    267          */
    268         public PlaybackInfo(PlaybackInfoProvider provider) {
    269             mProvider = provider;
    270         }
    271 
    272         /**
    273          * @hide
    274          */
    275         public PlaybackInfoProvider getProvider() {
    276             return mProvider;
    277         }
    278 
    279         /**
    280          * Get the type of playback which affects volume handling. One of:
    281          * <ul>
    282          * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
    283          * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
    284          * </ul>
    285          *
    286          * @return The type of playback this session is using.
    287          */
    288         public int getPlaybackType() {
    289             return mProvider.getPlaybackType_impl();
    290         }
    291 
    292         /**
    293          * Get the audio attributes for this session. The attributes will affect
    294          * volume handling for the session. When the volume type is
    295          * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
    296          * remote volume handler.
    297          *
    298          * @return The attributes for this session.
    299          */
    300         public AudioAttributes getAudioAttributes() {
    301             return mProvider.getAudioAttributes_impl();
    302         }
    303 
    304         /**
    305          * Get the type of volume control that can be used. One of:
    306          * <ul>
    307          * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
    308          * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
    309          * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
    310          * </ul>
    311          *
    312          * @return The type of volume control that may be used with this session.
    313          */
    314         public int getControlType() {
    315             return mProvider.getControlType_impl();
    316         }
    317 
    318         /**
    319          * Get the maximum volume that may be set for this session.
    320          *
    321          * @return The maximum allowed volume where this session is playing.
    322          */
    323         public int getMaxVolume() {
    324             return mProvider.getMaxVolume_impl();
    325         }
    326 
    327         /**
    328          * Get the current volume for this session.
    329          *
    330          * @return The current volume where this session is playing.
    331          */
    332         public int getCurrentVolume() {
    333             return mProvider.getCurrentVolume_impl();
    334         }
    335     }
    336 
    337     private final MediaController2Provider mProvider;
    338 
    339     /**
    340      * Create a {@link MediaController2} from the {@link SessionToken2}.
    341      * This connects to the session and may wake up the service if it's not available.
    342      *
    343      * @param context Context
    344      * @param token token to connect to
    345      * @param executor executor to run callbacks on.
    346      * @param callback controller callback to receive changes in
    347      */
    348     public MediaController2(@NonNull Context context, @NonNull SessionToken2 token,
    349             @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) {
    350         super();
    351 
    352         mProvider = createProvider(context, token, executor, callback);
    353         // This also connects to the token.
    354         // Explicit connect() isn't added on purpose because retrying connect() is impossible with
    355         // session whose session binder is only valid while it's active.
    356         // prevent a controller from reusable after the
    357         // session is released and recreated.
    358         mProvider.initialize();
    359     }
    360 
    361     MediaController2Provider createProvider(@NonNull Context context,
    362             @NonNull SessionToken2 token, @NonNull Executor executor,
    363             @NonNull ControllerCallback callback) {
    364         return ApiLoader.getProvider().createMediaController2(
    365                 context, this, token, executor, callback);
    366     }
    367 
    368     /**
    369      * Release this object, and disconnect from the session. After this, callbacks wouldn't be
    370      * received.
    371      */
    372     @Override
    373     public void close() {
    374         mProvider.close_impl();
    375     }
    376 
    377     /**
    378      * @hide
    379      */
    380     public MediaController2Provider getProvider() {
    381         return mProvider;
    382     }
    383 
    384     /**
    385      * @return token
    386      */
    387     public @NonNull SessionToken2 getSessionToken() {
    388         return mProvider.getSessionToken_impl();
    389     }
    390 
    391     /**
    392      * Returns whether this class is connected to active {@link MediaSession2} or not.
    393      */
    394     public boolean isConnected() {
    395         return mProvider.isConnected_impl();
    396     }
    397 
    398     public void play() {
    399         mProvider.play_impl();
    400     }
    401 
    402     public void pause() {
    403         mProvider.pause_impl();
    404     }
    405 
    406     public void stop() {
    407         mProvider.stop_impl();
    408     }
    409 
    410     /**
    411      * Request that the player prepare its playback. In other words, other sessions can continue
    412      * to play during the preparation of this session. This method can be used to speed up the
    413      * start of the playback. Once the preparation is done, the session will change its playback
    414      * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
    415      * to start playback.
    416      */
    417     public void prepare() {
    418         mProvider.prepare_impl();
    419     }
    420 
    421     /**
    422      * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
    423      */
    424     public void fastForward() {
    425         mProvider.fastForward_impl();
    426     }
    427 
    428     /**
    429      * Rewinds playback. If playback is already rewinding this may increase the rate.
    430      */
    431     public void rewind() {
    432         mProvider.rewind_impl();
    433     }
    434 
    435     /**
    436      * Move to a new location in the media stream.
    437      *
    438      * @param pos Position to move to, in milliseconds.
    439      */
    440     public void seekTo(long pos) {
    441         mProvider.seekTo_impl(pos);
    442     }
    443 
    444     /**
    445      * Revisit this API later.
    446      * @hide
    447      */
    448     public void skipForward() {
    449         // TODO(jaewan): (Post-P) Discuss this API later.
    450         // To match with KEYCODE_MEDIA_SKIP_FORWARD
    451     }
    452 
    453     /**
    454      * @hide
    455      */
    456     public void skipBackward() {
    457         // TODO(jaewan): (Post-P) Discuss this API later.
    458         // To match with KEYCODE_MEDIA_SKIP_BACKWARD
    459     }
    460 
    461     /**
    462      * Request that the player start playback for a specific media id.
    463      *
    464      * @param mediaId The id of the requested media.
    465      * @param extras Optional extras that can include extra information about the media item
    466      *               to be played.
    467      */
    468     public void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
    469         mProvider.playFromMediaId_impl(mediaId, extras);
    470     }
    471 
    472     /**
    473      * Request that the player start playback for a specific search query.
    474      *
    475      * @param query The search query. Should not be an empty string.
    476      * @param extras Optional extras that can include extra information about the query.
    477      */
    478     public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
    479         mProvider.playFromSearch_impl(query, extras);
    480     }
    481 
    482     /**
    483      * Request that the player start playback for a specific {@link Uri}.
    484      *
    485      * @param uri The URI of the requested media.
    486      * @param extras Optional extras that can include extra information about the media item
    487      *               to be played.
    488      */
    489     public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
    490         mProvider.playFromUri_impl(uri, extras);
    491     }
    492 
    493     /**
    494      * Request that the player prepare playback for a specific media id. In other words, other
    495      * sessions can continue to play during the preparation of this session. This method can be
    496      * used to speed up the start of the playback. Once the preparation is done, the session
    497      * will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
    498      * {@link #play} can be called to start playback. If the preparation is not needed,
    499      * {@link #playFromMediaId} can be directly called without this method.
    500      *
    501      * @param mediaId The id of the requested media.
    502      * @param extras Optional extras that can include extra information about the media item
    503      *               to be prepared.
    504      */
    505     public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
    506         mProvider.prepareFromMediaId_impl(mediaId, extras);
    507     }
    508 
    509     /**
    510      * Request that the player prepare playback for a specific search query.
    511      * In other words, other sessions can continue to play during the preparation of this session.
    512      * This method can be used to speed up the start of the playback.
    513      * Once the preparation is done, the session will change its playback state to
    514      * {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards,
    515      * {@link #play} can be called to start playback. If the preparation is not needed,
    516      * {@link #playFromSearch} can be directly called without this method.
    517      *
    518      * @param query The search query. Should not be an empty string.
    519      * @param extras Optional extras that can include extra information about the query.
    520      */
    521     public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
    522         mProvider.prepareFromSearch_impl(query, extras);
    523     }
    524 
    525     /**
    526      * Request that the player prepare playback for a specific {@link Uri}. In other words,
    527      * other sessions can continue to play during the preparation of this session. This method
    528      * can be used to speed up the start of the playback. Once the preparation is done, the
    529      * session will change its playback state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}.
    530      * Afterwards, {@link #play} can be called to start playback. If the preparation is not needed,
    531      * {@link #playFromUri} can be directly called without this method.
    532      *
    533      * @param uri The URI of the requested media.
    534      * @param extras Optional extras that can include extra information about the media item
    535      *               to be prepared.
    536      */
    537     public void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
    538         mProvider.prepareFromUri_impl(uri, extras);
    539     }
    540 
    541     /**
    542      * Set the volume of the output this session is playing on. The command will be ignored if it
    543      * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
    544      * <p>
    545      * If the session is local playback, this changes the device's volume with the stream that
    546      * session's player is using. Flags will be specified for the {@link AudioManager}.
    547      * <p>
    548      * If the session is remote player (i.e. session has set volume provider), its volume provider
    549      * will receive this request instead.
    550      *
    551      * @see #getPlaybackInfo()
    552      * @param value The value to set it to, between 0 and the reported max.
    553      * @param flags flags from {@link AudioManager} to include with the volume request for local
    554      *              playback
    555      */
    556     public void setVolumeTo(int value, int flags) {
    557         mProvider.setVolumeTo_impl(value, flags);
    558     }
    559 
    560     /**
    561      * Adjust the volume of the output this session is playing on. The direction
    562      * must be one of {@link AudioManager#ADJUST_LOWER},
    563      * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
    564      * The command will be ignored if the session does not support
    565      * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
    566      * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
    567      * <p>
    568      * If the session is local playback, this changes the device's volume with the stream that
    569      * session's player is using. Flags will be specified for the {@link AudioManager}.
    570      * <p>
    571      * If the session is remote player (i.e. session has set volume provider), its volume provider
    572      * will receive this request instead.
    573      *
    574      * @see #getPlaybackInfo()
    575      * @param direction The direction to adjust the volume in.
    576      * @param flags flags from {@link AudioManager} to include with the volume request for local
    577      *              playback
    578      */
    579     public void adjustVolume(int direction, int flags) {
    580         mProvider.adjustVolume_impl(direction, flags);
    581     }
    582 
    583     /**
    584      * Get an intent for launching UI associated with this session if one exists.
    585      *
    586      * @return A {@link PendingIntent} to launch UI or null.
    587      */
    588     public @Nullable PendingIntent getSessionActivity() {
    589         return mProvider.getSessionActivity_impl();
    590     }
    591 
    592     /**
    593      * Get the lastly cached player state from
    594      * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
    595      *
    596      * @return player state
    597      */
    598     public int getPlayerState() {
    599         return mProvider.getPlayerState_impl();
    600     }
    601 
    602     /**
    603      * Gets the current playback position.
    604      * <p>
    605      * This returns the calculated value of the position, based on the difference between the
    606      * update time and current time.
    607      *
    608      * @return position
    609      */
    610     public long getCurrentPosition() {
    611         return mProvider.getCurrentPosition_impl();
    612     }
    613 
    614     /**
    615      * Get the lastly cached playback speed from
    616      * {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}.
    617      *
    618      * @return speed
    619      */
    620     public float getPlaybackSpeed() {
    621         return mProvider.getPlaybackSpeed_impl();
    622     }
    623 
    624     /**
    625      * Set the playback speed.
    626      */
    627     public void setPlaybackSpeed(float speed) {
    628         // TODO(jaewan): implement this (b/74093080)
    629     }
    630 
    631 
    632     /**
    633      * Gets the current buffering state of the player.
    634      * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
    635      * buffered.
    636      * @return the buffering state.
    637      */
    638     public @MediaPlayerBase.BuffState int getBufferingState() {
    639         // TODO(jaewan): Implement.
    640         return BUFFERING_STATE_UNKNOWN;
    641     }
    642 
    643     /**
    644      * Gets the lastly cached buffered position from the session when
    645      * {@link ControllerCallback#onBufferingStateChanged(MediaController2, MediaItem2, int)} is
    646      * called.
    647      *
    648      * @return buffering position in millis
    649      */
    650     public long getBufferedPosition() {
    651         return mProvider.getBufferedPosition_impl();
    652     }
    653 
    654     /**
    655      * Get the current playback info for this session.
    656      *
    657      * @return The current playback info or null.
    658      */
    659     public @Nullable PlaybackInfo getPlaybackInfo() {
    660         return mProvider.getPlaybackInfo_impl();
    661     }
    662 
    663     /**
    664      * Rate the media. This will cause the rating to be set for the current user.
    665      * The rating style must follow the user rating style from the session.
    666      * You can get the rating style from the session through the
    667      * {@link MediaMetadata#getRating(String)} with the key
    668      * {@link MediaMetadata#METADATA_KEY_USER_RATING}.
    669      * <p>
    670      * If the user rating was {@code null}, the media item does not accept setting user rating.
    671      *
    672      * @param mediaId The id of the media
    673      * @param rating The rating to set
    674      */
    675     public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
    676         mProvider.setRating_impl(mediaId, rating);
    677     }
    678 
    679     /**
    680      * Send custom command to the session
    681      *
    682      * @param command custom command
    683      * @param args optional argument
    684      * @param cb optional result receiver
    685      */
    686     public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
    687             @Nullable ResultReceiver cb) {
    688         mProvider.sendCustomCommand_impl(command, args, cb);
    689     }
    690 
    691     /**
    692      * Returns the cached playlist from
    693      * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
    694      * <p>
    695      * This list may differ with the list that was specified with
    696      * {@link #setPlaylist(List, MediaMetadata2)} depending on the session implementation. Use media
    697      * items returned here for other playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
    698      *
    699      * @return The playlist. Can be {@code null} if the controller doesn't have enough permission or
    700      *         the session hasn't set any playlist.
    701      */
    702     public @Nullable List<MediaItem2> getPlaylist() {
    703         return mProvider.getPlaylist_impl();
    704     }
    705 
    706     /**
    707      * Sets the playlist.
    708      * <p>
    709      * Even when the playlist is successfully set, use the playlist returned from
    710      * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
    711      * Otherwise the session in the remote process can't distinguish between media items.
    712      *
    713      * @param list playlist
    714      * @param metadata metadata of the playlist
    715      * @see #getPlaylist()
    716      * @see ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)
    717      */
    718     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
    719         mProvider.setPlaylist_impl(list, metadata);
    720     }
    721 
    722     /**
    723      * Updates the playlist metadata
    724      *
    725      * @param metadata metadata of the playlist
    726      */
    727     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
    728         mProvider.updatePlaylistMetadata_impl(metadata);
    729     }
    730 
    731     /**
    732      * Gets the lastly cached playlist playlist metadata either from
    733      * {@link ControllerCallback#onPlaylistMetadataChanged(MediaController2,  MediaMetadata2)} or
    734      * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
    735      *
    736      * @return metadata metadata of the playlist, or null if none is set
    737      */
    738     public @Nullable MediaMetadata2 getPlaylistMetadata() {
    739         return mProvider.getPlaylistMetadata_impl();
    740     }
    741 
    742 
    743     /**
    744      * Adds the media item to the playlist at position index. Index equals or greater than
    745      * the current playlist size will add the item at the end of the playlist.
    746      * <p>
    747      * This will not change the currently playing media item.
    748      * If index is less than or equal to the current index of the playlist,
    749      * the current index of the playlist will be incremented correspondingly.
    750      *
    751      * @param index the index you want to add
    752      * @param item the media item you want to add
    753      */
    754     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
    755         mProvider.addPlaylistItem_impl(index, item);
    756     }
    757 
    758     /**
    759      * Removes the media item at index in the playlist.
    760      *<p>
    761      * If the item is the currently playing item of the playlist, current playback
    762      * will be stopped and playback moves to next source in the list.
    763      *
    764      * @param item the media item you want to add
    765      */
    766     public void removePlaylistItem(@NonNull MediaItem2 item) {
    767         mProvider.removePlaylistItem_impl(item);
    768     }
    769 
    770     /**
    771      * Replace the media item at index in the playlist. This can be also used to update metadata of
    772      * an item.
    773      *
    774      * @param index the index of the item to replace
    775      * @param item the new item
    776      */
    777     public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
    778         mProvider.replacePlaylistItem_impl(index, item);
    779     }
    780 
    781     /**
    782      * Get the lastly cached current item from
    783      * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
    784      *
    785      * @return index of the current item
    786      */
    787     public MediaItem2 getCurrentMediaItem() {
    788         return mProvider.getCurrentMediaItem_impl();
    789     }
    790 
    791     /**
    792      * Skips to the previous item in the playlist.
    793      * <p>
    794      * This calls {@link MediaSession2#skipToPreviousItem()} if the session allows.
    795      */
    796      public void skipToPreviousItem() {
    797          mProvider.skipToPreviousItem_impl();
    798      }
    799 
    800     /**
    801      * Skips to the next item in the playlist.
    802      * <p>
    803      * This calls {@link MediaSession2#skipToNextItem()} if the session allows.
    804      */
    805     public void skipToNextItem() {
    806         mProvider.skipToNextItem_impl();
    807     }
    808 
    809     /**
    810      * Skips to the item in the playlist.
    811      * <p>
    812      * This calls {@link MediaSession2#skipToPlaylistItem(MediaItem2)} if the session allows.
    813      *
    814      * @param item The item in the playlist you want to play
    815      */
    816     public void skipToPlaylistItem(@NonNull MediaItem2 item) {
    817         mProvider.skipToPlaylistItem_impl(item);
    818     }
    819 
    820     /**
    821      * Gets the cached repeat mode from the {@link ControllerCallback#onRepeatModeChanged(
    822      * MediaController2, int)}.
    823      *
    824      * @return repeat mode
    825      * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    826      * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    827      * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    828      * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    829      */
    830     public @RepeatMode int getRepeatMode() {
    831         return mProvider.getRepeatMode_impl();
    832     }
    833 
    834     /**
    835      * Sets the repeat mode.
    836      *
    837      * @param repeatMode repeat mode
    838      * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    839      * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    840      * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    841      * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    842      */
    843     public void setRepeatMode(@RepeatMode int repeatMode) {
    844         mProvider.setRepeatMode_impl(repeatMode);
    845     }
    846 
    847     /**
    848      * Gets the cached shuffle mode from the {@link ControllerCallback#onShuffleModeChanged(
    849      * MediaController2, int)}.
    850      *
    851      * @return The shuffle mode
    852      * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    853      * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    854      * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    855      */
    856     public @ShuffleMode int getShuffleMode() {
    857         return mProvider.getShuffleMode_impl();
    858     }
    859 
    860     /**
    861      * Sets the shuffle mode.
    862      *
    863      * @param shuffleMode The shuffle mode
    864      * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    865      * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    866      * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    867      */
    868     public void setShuffleMode(@ShuffleMode int shuffleMode) {
    869         mProvider.setShuffleMode_impl(shuffleMode);
    870     }
    871 }
    872