Home | History | Annotate | Download | only in session
      1 /*
      2  * Copyright (C) 2014 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.support.v4.media.session;
     18 
     19 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
     20 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     21 import static androidx.media.MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
     22 
     23 import android.app.Activity;
     24 import android.app.PendingIntent;
     25 import android.content.BroadcastReceiver;
     26 import android.content.ComponentName;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.graphics.Bitmap;
     30 import android.media.AudioManager;
     31 import android.media.MediaMetadataEditor;
     32 import android.media.MediaMetadataRetriever;
     33 import android.media.Rating;
     34 import android.media.RemoteControlClient;
     35 import android.media.session.MediaSession;
     36 import android.net.Uri;
     37 import android.os.BadParcelableException;
     38 import android.os.Binder;
     39 import android.os.Build;
     40 import android.os.Bundle;
     41 import android.os.Handler;
     42 import android.os.IBinder;
     43 import android.os.Looper;
     44 import android.os.Message;
     45 import android.os.Parcel;
     46 import android.os.Parcelable;
     47 import android.os.RemoteCallbackList;
     48 import android.os.RemoteException;
     49 import android.os.ResultReceiver;
     50 import android.os.SystemClock;
     51 import android.support.v4.media.MediaDescriptionCompat;
     52 import android.support.v4.media.MediaMetadataCompat;
     53 import android.support.v4.media.RatingCompat;
     54 import android.text.TextUtils;
     55 import android.util.Log;
     56 import android.util.TypedValue;
     57 import android.view.KeyEvent;
     58 import android.view.ViewConfiguration;
     59 
     60 import androidx.annotation.IntDef;
     61 import androidx.annotation.NonNull;
     62 import androidx.annotation.RequiresApi;
     63 import androidx.annotation.RestrictTo;
     64 import androidx.core.app.BundleCompat;
     65 import androidx.media.MediaSessionManager;
     66 import androidx.media.MediaSessionManager.RemoteUserInfo;
     67 import androidx.media.VolumeProviderCompat;
     68 import androidx.media.session.MediaButtonReceiver;
     69 
     70 import java.lang.annotation.Retention;
     71 import java.lang.annotation.RetentionPolicy;
     72 import java.lang.ref.WeakReference;
     73 import java.util.ArrayList;
     74 import java.util.List;
     75 
     76 /**
     77  * Allows interaction with media controllers, volume keys, media buttons, and
     78  * transport controls.
     79  * <p>
     80  * A MediaSession should be created when an app wants to publish media playback
     81  * information or handle media keys. In general an app only needs one session
     82  * for all playback, though multiple sessions can be created to provide finer
     83  * grain controls of media.
     84  * <p>
     85  * Once a session is created the owner of the session may pass its
     86  * {@link #getSessionToken() session token} to other processes to allow them to
     87  * create a {@link MediaControllerCompat} to interact with the session.
     88  * <p>
     89  * To receive commands, media keys, and other events a {@link Callback} must be
     90  * set with {@link #setCallback(Callback)}.
     91  * <p>
     92  * When an app is finished performing playback it must call {@link #release()}
     93  * to clean up the session and notify any controllers.
     94  * <p>
     95  * MediaSessionCompat objects are not thread safe and all calls should be made
     96  * from the same thread.
     97  * <p>
     98  * This is a helper for accessing features in
     99  * {@link android.media.session.MediaSession} introduced after API level 4 in a
    100  * backwards compatible fashion.
    101  *
    102  * <div class="special reference">
    103  * <h3>Developer Guides</h3>
    104  * <p>For information about building your media application, read the
    105  * <a href="{@docRoot}guide/topics/media-apps/index.html">Media Apps</a> developer guide.</p>
    106  * </div>
    107  */
    108 public class MediaSessionCompat {
    109     static final String TAG = "MediaSessionCompat";
    110 
    111     private final MediaSessionImpl mImpl;
    112     private final MediaControllerCompat mController;
    113     private final ArrayList<OnActiveChangeListener> mActiveListeners = new ArrayList<>();
    114 
    115     /**
    116      * @hide
    117      */
    118     @RestrictTo(LIBRARY_GROUP)
    119     @IntDef(flag=true, value={
    120             FLAG_HANDLES_MEDIA_BUTTONS,
    121             FLAG_HANDLES_TRANSPORT_CONTROLS,
    122             FLAG_HANDLES_QUEUE_COMMANDS })
    123     @Retention(RetentionPolicy.SOURCE)
    124     public @interface SessionFlags {}
    125 
    126     /**
    127      * Sets this flag on the session to indicate that it can handle media button
    128      * events.
    129      */
    130     public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
    131 
    132     /**
    133      * Sets this flag on the session to indicate that it handles transport
    134      * control commands through its {@link Callback}.
    135      */
    136     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
    137 
    138     /**
    139      * Sets this flag on the session to indicate that it handles queue
    140      * management commands through its {@link Callback}.
    141      */
    142     public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
    143 
    144     /**
    145      * Predefined custom action to flag the media that is currently playing as inappropriate.
    146      *
    147      * @see Callback#onCustomAction
    148      */
    149     public static final String ACTION_FLAG_AS_INAPPROPRIATE =
    150             "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
    151 
    152     /**
    153      * Predefined custom action to skip the advertisement that is currently playing.
    154      *
    155      * @see Callback#onCustomAction
    156      */
    157     public static final String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
    158 
    159     /**
    160      * Predefined custom action to follow an artist, album, or playlist. The extra bundle must have
    161      * {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the follow action. The
    162      * bundle can also have an optional string argument,
    163      * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to follow (e.g., the
    164      * name of the artist to follow). If this argument is omitted, the currently playing media will
    165      * be the target of the action. Thus, the session must perform the follow action with the
    166      * current metadata. If there's no specified attribute in the current metadata, the controller
    167      * must not omit this argument.
    168      *
    169      * @see #ARGUMENT_MEDIA_ATTRIBUTE
    170      * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
    171      * @see Callback#onCustomAction
    172      */
    173     public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
    174 
    175     /**
    176      * Predefined custom action to unfollow an artist, album, or playlist. The extra bundle must
    177      * have {@link #ARGUMENT_MEDIA_ATTRIBUTE} to indicate the type of the unfollow action.
    178      * The bundle can also have an optional string argument,
    179      * {@link #ARGUMENT_MEDIA_ATTRIBUTE_VALUE}, to specify the target to unfollow (e.g., the
    180      * name of the artist to unfollow). If this argument is omitted, the currently playing media
    181      * will be the target of the action. Thus, the session must perform the unfollow action with the
    182      * current metadata. If there's no specified attribute in the current metadata, the controller
    183      * must not omit this argument.
    184      *
    185      * @see #ARGUMENT_MEDIA_ATTRIBUTE
    186      * @see #ARGUMENT_MEDIA_ATTRIBUTE_VALUE
    187      * @see Callback#onCustomAction
    188      */
    189     public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
    190 
    191     /**
    192      * Argument to indicate the media attribute. It should be one of the following:
    193      * <ul>
    194      * <li>{@link #MEDIA_ATTRIBUTE_ARTIST}</li>
    195      * <li>{@link #MEDIA_ATTRIBUTE_PLAYLIST}</li>
    196      * <li>{@link #MEDIA_ATTRIBUTE_ALBUM}</li>
    197      * </ul>
    198      */
    199     public static final String ARGUMENT_MEDIA_ATTRIBUTE =
    200             "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
    201 
    202     /**
    203      * String argument to indicate the value of the media attribute (e.g., the name of the artist).
    204      */
    205     public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE =
    206             "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
    207 
    208     /**
    209      * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the artist.
    210      *
    211      * @see #ARGUMENT_MEDIA_ATTRIBUTE
    212      */
    213     public static final int MEDIA_ATTRIBUTE_ARTIST = 0;
    214 
    215     /**
    216      * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the album.
    217      *
    218      * @see #ARGUMENT_MEDIA_ATTRIBUTE
    219      */
    220     public static final int MEDIA_ATTRIBUTE_ALBUM = 1;
    221 
    222     /**
    223      * The value of {@link #ARGUMENT_MEDIA_ATTRIBUTE} indicating the playlist.
    224      *
    225      * @see #ARGUMENT_MEDIA_ATTRIBUTE
    226      */
    227     public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2;
    228 
    229     /**
    230      * Custom action to invoke playFromUri() for the forward compatibility.
    231      *
    232      * @hide
    233      */
    234     @RestrictTo(LIBRARY)
    235     public static final String ACTION_PLAY_FROM_URI =
    236             "android.support.v4.media.session.action.PLAY_FROM_URI";
    237 
    238     /**
    239      * Custom action to invoke prepare() for the forward compatibility.
    240      *
    241      * @hide
    242      */
    243     @RestrictTo(LIBRARY)
    244     public static final String ACTION_PREPARE = "android.support.v4.media.session.action.PREPARE";
    245 
    246     /**
    247      * Custom action to invoke prepareFromMediaId() for the forward compatibility.
    248      *
    249      * @hide
    250      */
    251     @RestrictTo(LIBRARY)
    252     public static final String ACTION_PREPARE_FROM_MEDIA_ID =
    253             "android.support.v4.media.session.action.PREPARE_FROM_MEDIA_ID";
    254 
    255     /**
    256      * Custom action to invoke prepareFromSearch() for the forward compatibility.
    257      *
    258      * @hide
    259      */
    260     @RestrictTo(LIBRARY)
    261     public static final String ACTION_PREPARE_FROM_SEARCH =
    262             "android.support.v4.media.session.action.PREPARE_FROM_SEARCH";
    263 
    264     /**
    265      * Custom action to invoke prepareFromUri() for the forward compatibility.
    266      *
    267      * @hide
    268      */
    269     @RestrictTo(LIBRARY)
    270     public static final String ACTION_PREPARE_FROM_URI =
    271             "android.support.v4.media.session.action.PREPARE_FROM_URI";
    272 
    273     /**
    274      * Custom action to invoke setCaptioningEnabled() for the forward compatibility.
    275      *
    276      * @hide
    277      */
    278     @RestrictTo(LIBRARY)
    279     public static final String ACTION_SET_CAPTIONING_ENABLED =
    280             "android.support.v4.media.session.action.SET_CAPTIONING_ENABLED";
    281 
    282     /**
    283      * Custom action to invoke setRepeatMode() for the forward compatibility.
    284      *
    285      * @hide
    286      */
    287     @RestrictTo(LIBRARY)
    288     public static final String ACTION_SET_REPEAT_MODE =
    289             "android.support.v4.media.session.action.SET_REPEAT_MODE";
    290 
    291     /**
    292      * Custom action to invoke setShuffleMode() for the forward compatibility.
    293      *
    294      * @hide
    295      */
    296     @RestrictTo(LIBRARY)
    297     public static final String ACTION_SET_SHUFFLE_MODE =
    298             "android.support.v4.media.session.action.SET_SHUFFLE_MODE";
    299 
    300     /**
    301      * Custom action to invoke setRating() with extra fields.
    302      *
    303      * @hide
    304      */
    305     @RestrictTo(LIBRARY)
    306     public static final String ACTION_SET_RATING =
    307             "android.support.v4.media.session.action.SET_RATING";
    308 
    309     /**
    310      * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play.
    311      *
    312      * @hide
    313      */
    314     @RestrictTo(LIBRARY)
    315     public static final String ACTION_ARGUMENT_MEDIA_ID =
    316             "android.support.v4.media.session.action.ARGUMENT_MEDIA_ID";
    317 
    318     /**
    319      * Argument for use with {@link #ACTION_PREPARE_FROM_SEARCH} indicating search query.
    320      *
    321      * @hide
    322      */
    323     @RestrictTo(LIBRARY)
    324     public static final String ACTION_ARGUMENT_QUERY =
    325             "android.support.v4.media.session.action.ARGUMENT_QUERY";
    326 
    327     /**
    328      * Argument for use with {@link #ACTION_PREPARE_FROM_URI} and {@link #ACTION_PLAY_FROM_URI}
    329      * indicating URI to play.
    330      *
    331      * @hide
    332      */
    333     @RestrictTo(LIBRARY)
    334     public static final String ACTION_ARGUMENT_URI =
    335             "android.support.v4.media.session.action.ARGUMENT_URI";
    336 
    337     /**
    338      * Argument for use with {@link #ACTION_SET_RATING} indicating the rate to be set.
    339      *
    340      * @hide
    341      */
    342     @RestrictTo(LIBRARY)
    343     public static final String ACTION_ARGUMENT_RATING =
    344             "android.support.v4.media.session.action.ARGUMENT_RATING";
    345 
    346     /**
    347      * Argument for use with various actions indicating extra bundle.
    348      *
    349      * @hide
    350      */
    351     @RestrictTo(LIBRARY)
    352     public static final String ACTION_ARGUMENT_EXTRAS =
    353             "android.support.v4.media.session.action.ARGUMENT_EXTRAS";
    354 
    355     /**
    356      * Argument for use with {@link #ACTION_SET_CAPTIONING_ENABLED} indicating whether captioning is
    357      * enabled.
    358      *
    359      * @hide
    360      */
    361     @RestrictTo(LIBRARY)
    362     public static final String ACTION_ARGUMENT_CAPTIONING_ENABLED =
    363             "android.support.v4.media.session.action.ARGUMENT_CAPTIONING_ENABLED";
    364 
    365     /**
    366      * Argument for use with {@link #ACTION_SET_REPEAT_MODE} indicating repeat mode.
    367      *
    368      * @hide
    369      */
    370     @RestrictTo(LIBRARY)
    371     public static final String ACTION_ARGUMENT_REPEAT_MODE =
    372             "android.support.v4.media.session.action.ARGUMENT_REPEAT_MODE";
    373 
    374     /**
    375      * Argument for use with {@link #ACTION_SET_SHUFFLE_MODE} indicating shuffle mode.
    376      *
    377      * @hide
    378      */
    379     @RestrictTo(LIBRARY)
    380     public static final String ACTION_ARGUMENT_SHUFFLE_MODE =
    381             "android.support.v4.media.session.action.ARGUMENT_SHUFFLE_MODE";
    382 
    383     /**
    384      * @hide
    385      */
    386     @RestrictTo(LIBRARY)
    387     public static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER";
    388 
    389     // Maximum size of the bitmap in dp.
    390     private static final int MAX_BITMAP_SIZE_IN_DP = 320;
    391 
    392     private static final String DATA_CALLING_PACKAGE = "data_calling_pkg";
    393     private static final String DATA_CALLING_PID = "data_calling_pid";
    394     private static final String DATA_CALLING_UID = "data_calling_uid";
    395     private static final String DATA_EXTRAS = "data_extras";
    396 
    397     // Maximum size of the bitmap in px. It shouldn't be changed.
    398     static int sMaxBitmapSize;
    399 
    400     /**
    401      * Creates a new session. You must call {@link #release()} when finished with the session.
    402      * <p>
    403      * The session will automatically be registered with the system but will not be published
    404      * until {@link #setActive(boolean) setActive(true)} is called.
    405      * </p><p>
    406      * For API 20 or earlier, note that a media button receiver is required for handling
    407      * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate
    408      * {@link BroadcastReceiver} from your manifest. See {@link MediaButtonReceiver} for more
    409      * details.
    410      * </p>
    411      * @param context The context to use to create the session.
    412      * @param tag A short name for debugging purposes.
    413      */
    414     public MediaSessionCompat(Context context, String tag) {
    415         this(context, tag, null, null);
    416     }
    417 
    418     /**
    419      * Creates a new session with a specified media button receiver (a component name and/or
    420      * a pending intent). You must call {@link #release()} when finished with the session.
    421      * <p>
    422      * The session will automatically be registered with the system but will not be published
    423      * until {@link #setActive(boolean) setActive(true)} is called.
    424      * </p><p>
    425      * For API 20 or earlier, note that a media button receiver is required for handling
    426      * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate
    427      * {@link BroadcastReceiver} from your manifest if it's not specified. See
    428      * {@link MediaButtonReceiver} for more details.
    429      * </p>
    430      * @param context The context to use to create the session.
    431      * @param tag A short name for debugging purposes.
    432      * @param mbrComponent The component name for your media button receiver.
    433      * @param mbrIntent The PendingIntent for your receiver component that handles
    434      *            media button events. This is optional and will be used on between
    435      *            {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} and
    436      *            {@link android.os.Build.VERSION_CODES#KITKAT_WATCH} instead of the
    437      *            component name.
    438      */
    439     public MediaSessionCompat(Context context, String tag, ComponentName mbrComponent,
    440             PendingIntent mbrIntent) {
    441         if (context == null) {
    442             throw new IllegalArgumentException("context must not be null");
    443         }
    444         if (TextUtils.isEmpty(tag)) {
    445             throw new IllegalArgumentException("tag must not be null or empty");
    446         }
    447 
    448         if (mbrComponent == null) {
    449             mbrComponent = MediaButtonReceiver.getMediaButtonReceiverComponent(context);
    450             if (mbrComponent == null) {
    451                 Log.w(TAG, "Couldn't find a unique registered media button receiver in the "
    452                         + "given context.");
    453             }
    454         }
    455         if (mbrComponent != null && mbrIntent == null) {
    456             // construct a PendingIntent for the media button
    457             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    458             // the associated intent will be handled by the component being registered
    459             mediaButtonIntent.setComponent(mbrComponent);
    460             mbrIntent = PendingIntent.getBroadcast(context,
    461                     0/* requestCode, ignored */, mediaButtonIntent, 0/* flags */);
    462         }
    463         if (android.os.Build.VERSION.SDK_INT >= 28) {
    464             mImpl = new MediaSessionImplApi28(context, tag);
    465             // Set default callback to respond to controllers' extra binder requests.
    466             setCallback(new Callback() {});
    467         } else if (android.os.Build.VERSION.SDK_INT >= 21) {
    468             mImpl = new MediaSessionImplApi21(context, tag);
    469             // Set default callback to respond to controllers' extra binder requests.
    470             setCallback(new Callback() {});
    471             mImpl.setMediaButtonReceiver(mbrIntent);
    472         } else if (android.os.Build.VERSION.SDK_INT >= 19) {
    473             mImpl = new MediaSessionImplApi19(context, tag, mbrComponent, mbrIntent);
    474         } else if (android.os.Build.VERSION.SDK_INT >= 18) {
    475             mImpl = new MediaSessionImplApi18(context, tag, mbrComponent, mbrIntent);
    476         } else {
    477             mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent);
    478         }
    479         mController = new MediaControllerCompat(context, this);
    480 
    481         if (sMaxBitmapSize == 0) {
    482             sMaxBitmapSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
    483                     MAX_BITMAP_SIZE_IN_DP, context.getResources().getDisplayMetrics());
    484         }
    485     }
    486 
    487     private MediaSessionCompat(Context context, MediaSessionImpl impl) {
    488         mImpl = impl;
    489         if (android.os.Build.VERSION.SDK_INT >= 21
    490                 && !MediaSessionCompatApi21.hasCallback(impl.getMediaSession())) {
    491             // Set default callback to respond to controllers' extra binder requests.
    492             setCallback(new Callback() {});
    493         }
    494         mController = new MediaControllerCompat(context, this);
    495     }
    496 
    497     /**
    498      * Adds a callback to receive updates on for the MediaSession. This includes
    499      * media button and volume events. The caller's thread will be used to post
    500      * events.
    501      *
    502      * @param callback The callback object
    503      */
    504     public void setCallback(Callback callback) {
    505         setCallback(callback, null);
    506     }
    507 
    508     /**
    509      * Sets the callback to receive updates for the MediaSession. This includes
    510      * media button and volume events. Set the callback to null to stop
    511      * receiving events.
    512      *
    513      * @param callback The callback to receive updates on.
    514      * @param handler The handler that events should be posted on.
    515      */
    516     public void setCallback(Callback callback, Handler handler) {
    517         mImpl.setCallback(callback, handler != null ? handler : new Handler());
    518     }
    519 
    520     /**
    521      * Sets an intent for launching UI for this Session. This can be used as a
    522      * quick link to an ongoing media screen. The intent should be for an
    523      * activity that may be started using
    524      * {@link Activity#startActivity(Intent)}.
    525      *
    526      * @param pi The intent to launch to show UI for this Session.
    527      */
    528     public void setSessionActivity(PendingIntent pi) {
    529         mImpl.setSessionActivity(pi);
    530     }
    531 
    532     /**
    533      * Sets a pending intent for your media button receiver to allow restarting
    534      * playback after the session has been stopped. If your app is started in
    535      * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
    536      * the pending intent.
    537      * <p>
    538      * This method will only work on
    539      * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. Earlier
    540      * platform versions must include the media button receiver in the
    541      * constructor.
    542      *
    543      * @param mbr The {@link PendingIntent} to send the media button event to.
    544      */
    545     public void setMediaButtonReceiver(PendingIntent mbr) {
    546         mImpl.setMediaButtonReceiver(mbr);
    547     }
    548 
    549     /**
    550      * Sets any flags for the session.
    551      *
    552      * @param flags The flags to set for this session.
    553      */
    554     public void setFlags(@SessionFlags int flags) {
    555         mImpl.setFlags(flags);
    556     }
    557 
    558     /**
    559      * Sets the stream this session is playing on. This will affect the system's
    560      * volume handling for this session. If {@link #setPlaybackToRemote} was
    561      * previously called it will stop receiving volume commands and the system
    562      * will begin sending volume changes to the appropriate stream.
    563      * <p>
    564      * By default sessions are on {@link AudioManager#STREAM_MUSIC}.
    565      *
    566      * @param stream The {@link AudioManager} stream this session is playing on.
    567      */
    568     public void setPlaybackToLocal(int stream) {
    569         mImpl.setPlaybackToLocal(stream);
    570     }
    571 
    572     /**
    573      * Configures this session to use remote volume handling. This must be called
    574      * to receive volume button events, otherwise the system will adjust the
    575      * current stream volume for this session. If {@link #setPlaybackToLocal}
    576      * was previously called that stream will stop receiving volume changes for
    577      * this session.
    578      * <p>
    579      * On platforms earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}
    580      * this will only allow an app to handle volume commands sent directly to
    581      * the session by a {@link MediaControllerCompat}. System routing of volume
    582      * keys will not use the volume provider.
    583      *
    584      * @param volumeProvider The provider that will handle volume changes. May
    585      *            not be null.
    586      */
    587     public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) {
    588         if (volumeProvider == null) {
    589             throw new IllegalArgumentException("volumeProvider may not be null!");
    590         }
    591         mImpl.setPlaybackToRemote(volumeProvider);
    592     }
    593 
    594     /**
    595      * Sets if this session is currently active and ready to receive commands. If
    596      * set to false your session's controller may not be discoverable. You must
    597      * set the session to active before it can start receiving media button
    598      * events or transport commands.
    599      * <p>
    600      * On platforms earlier than
    601      * {@link android.os.Build.VERSION_CODES#LOLLIPOP},
    602      * a media button event receiver should be set via the constructor to
    603      * receive media button events.
    604      *
    605      * @param active Whether this session is active or not.
    606      */
    607     public void setActive(boolean active) {
    608         mImpl.setActive(active);
    609         for (OnActiveChangeListener listener : mActiveListeners) {
    610             listener.onActiveChanged();
    611         }
    612     }
    613 
    614     /**
    615      * Gets the current active state of this session.
    616      *
    617      * @return True if the session is active, false otherwise.
    618      */
    619     public boolean isActive() {
    620         return mImpl.isActive();
    621     }
    622 
    623     /**
    624      * Sends a proprietary event to all MediaControllers listening to this
    625      * Session. It's up to the Controller/Session owner to determine the meaning
    626      * of any events.
    627      *
    628      * @param event The name of the event to send
    629      * @param extras Any extras included with the event
    630      */
    631     public void sendSessionEvent(String event, Bundle extras) {
    632         if (TextUtils.isEmpty(event)) {
    633             throw new IllegalArgumentException("event cannot be null or empty");
    634         }
    635         mImpl.sendSessionEvent(event, extras);
    636     }
    637 
    638     /**
    639      * This must be called when an app has finished performing playback. If
    640      * playback is expected to start again shortly the session can be left open,
    641      * but it must be released if your activity or service is being destroyed.
    642      */
    643     public void release() {
    644         mImpl.release();
    645     }
    646 
    647     /**
    648      * Retrieves a token object that can be used by apps to create a
    649      * {@link MediaControllerCompat} for interacting with this session. The
    650      * owner of the session is responsible for deciding how to distribute these
    651      * tokens.
    652      * <p>
    653      * On platform versions before
    654      * {@link android.os.Build.VERSION_CODES#LOLLIPOP} this token may only be
    655      * used within your app as there is no way to guarantee other apps are using
    656      * the same version of the support library.
    657      *
    658      * @return A token that can be used to create a media controller for this
    659      *         session.
    660      */
    661     public Token getSessionToken() {
    662         return mImpl.getSessionToken();
    663     }
    664 
    665     /**
    666      * Gets a controller for this session. This is a convenience method to avoid
    667      * having to cache your own controller in process.
    668      *
    669      * @return A controller for this session.
    670      */
    671     public MediaControllerCompat getController() {
    672         return mController;
    673     }
    674 
    675     /**
    676      * Updates the current playback state.
    677      *
    678      * @param state The current state of playback
    679      */
    680     public void setPlaybackState(PlaybackStateCompat state) {
    681         mImpl.setPlaybackState(state);
    682     }
    683 
    684     /**
    685      * Updates the current metadata. New metadata can be created using
    686      * {@link android.support.v4.media.MediaMetadataCompat.Builder}. This operation may take time
    687      * proportional to the size of the bitmap to replace large bitmaps with a scaled down copy.
    688      *
    689      * @param metadata The new metadata
    690      * @see android.support.v4.media.MediaMetadataCompat.Builder#putBitmap
    691      */
    692     public void setMetadata(MediaMetadataCompat metadata) {
    693         mImpl.setMetadata(metadata);
    694     }
    695 
    696     /**
    697      * Updates the list of items in the play queue. It is an ordered list and
    698      * should contain the current item, and previous or upcoming items if they
    699      * exist. Specify null if there is no current play queue.
    700      * <p>
    701      * The queue should be of reasonable size. If the play queue is unbounded
    702      * within your app, it is better to send a reasonable amount in a sliding
    703      * window instead.
    704      *
    705      * @param queue A list of items in the play queue.
    706      */
    707     public void setQueue(List<QueueItem> queue) {
    708         mImpl.setQueue(queue);
    709     }
    710 
    711     /**
    712      * Sets the title of the play queue. The UI should display this title along
    713      * with the play queue itself. e.g. "Play Queue", "Now Playing", or an album
    714      * name.
    715      *
    716      * @param title The title of the play queue.
    717      */
    718     public void setQueueTitle(CharSequence title) {
    719         mImpl.setQueueTitle(title);
    720     }
    721 
    722     /**
    723      * Sets the style of rating used by this session. Apps trying to set the
    724      * rating should use this style. Must be one of the following:
    725      * <ul>
    726      * <li>{@link RatingCompat#RATING_NONE}</li>
    727      * <li>{@link RatingCompat#RATING_3_STARS}</li>
    728      * <li>{@link RatingCompat#RATING_4_STARS}</li>
    729      * <li>{@link RatingCompat#RATING_5_STARS}</li>
    730      * <li>{@link RatingCompat#RATING_HEART}</li>
    731      * <li>{@link RatingCompat#RATING_PERCENTAGE}</li>
    732      * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li>
    733      * </ul>
    734      */
    735     public void setRatingType(@RatingCompat.Style int type) {
    736         mImpl.setRatingType(type);
    737     }
    738 
    739     /**
    740      * Enables/disables captioning for this session.
    741      *
    742      * @param enabled {@code true} to enable captioning, {@code false} to disable.
    743      */
    744     public void setCaptioningEnabled(boolean enabled) {
    745         mImpl.setCaptioningEnabled(enabled);
    746     }
    747 
    748     /**
    749      * Sets the repeat mode for this session.
    750      * <p>
    751      * Note that if this method is not called before, {@link MediaControllerCompat#getRepeatMode}
    752      * will return {@link PlaybackStateCompat#REPEAT_MODE_NONE}.
    753      *
    754      * @param repeatMode The repeat mode. Must be one of the followings:
    755      *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
    756      *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
    757      *            {@link PlaybackStateCompat#REPEAT_MODE_ALL},
    758      *            {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
    759      */
    760     public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
    761         mImpl.setRepeatMode(repeatMode);
    762     }
    763 
    764     /**
    765      * Sets the shuffle mode for this session.
    766      * <p>
    767      * Note that if this method is not called before, {@link MediaControllerCompat#getShuffleMode}
    768      * will return {@link PlaybackStateCompat#SHUFFLE_MODE_NONE}.
    769      *
    770      * @param shuffleMode The shuffle mode. Must be one of the followings:
    771      *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
    772      *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
    773      *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
    774      */
    775     public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
    776         mImpl.setShuffleMode(shuffleMode);
    777     }
    778 
    779     /**
    780      * Sets some extras that can be associated with the
    781      * {@link MediaSessionCompat}. No assumptions should be made as to how a
    782      * {@link MediaControllerCompat} will handle these extras. Keys should be
    783      * fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
    784      *
    785      * @param extras The extras associated with the session.
    786      */
    787     public void setExtras(Bundle extras) {
    788         mImpl.setExtras(extras);
    789     }
    790 
    791     /**
    792      * Gets the underlying framework {@link android.media.session.MediaSession}
    793      * object.
    794      * <p>
    795      * This method is only supported on API 21+.
    796      * </p>
    797      *
    798      * @return The underlying {@link android.media.session.MediaSession} object,
    799      *         or null if none.
    800      */
    801     public Object getMediaSession() {
    802         return mImpl.getMediaSession();
    803     }
    804 
    805     /**
    806      * Gets the underlying framework {@link android.media.RemoteControlClient}
    807      * object.
    808      * <p>
    809      * This method is only supported on APIs 14-20. On API 21+
    810      * {@link #getMediaSession()} should be used instead.
    811      *
    812      * @return The underlying {@link android.media.RemoteControlClient} object,
    813      *         or null if none.
    814      */
    815     public Object getRemoteControlClient() {
    816         return mImpl.getRemoteControlClient();
    817     }
    818 
    819     /**
    820      * Gets the controller information who sent the current request.
    821      * <p>
    822      * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
    823      *
    824      * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
    825      * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
    826      */
    827     public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
    828         return mImpl.getCurrentControllerInfo();
    829     }
    830 
    831     /**
    832      * Returns the name of the package that sent the last media button, transport control, or
    833      * command from controllers and the system. This is only valid while in a request callback, such
    834      * as {@link Callback#onPlay}. This method is not available and returns null on pre-N devices.
    835      *
    836      * @hide
    837      */
    838     @RestrictTo(LIBRARY_GROUP)
    839     public String getCallingPackage() {
    840         return mImpl.getCallingPackage();
    841     }
    842 
    843     /**
    844      * Adds a listener to be notified when the active status of this session
    845      * changes. This is primarily used by the support library and should not be
    846      * needed by apps.
    847      *
    848      * @param listener The listener to add.
    849      */
    850     public void addOnActiveChangeListener(OnActiveChangeListener listener) {
    851         if (listener == null) {
    852             throw new IllegalArgumentException("Listener may not be null");
    853         }
    854         mActiveListeners.add(listener);
    855     }
    856 
    857     /**
    858      * Stops the listener from being notified when the active status of this
    859      * session changes.
    860      *
    861      * @param listener The listener to remove.
    862      */
    863     public void removeOnActiveChangeListener(OnActiveChangeListener listener) {
    864         if (listener == null) {
    865             throw new IllegalArgumentException("Listener may not be null");
    866         }
    867         mActiveListeners.remove(listener);
    868     }
    869 
    870     /**
    871      * Creates an instance from a framework {@link android.media.session.MediaSession} object.
    872      * <p>
    873      * This method is only supported on API 21+. On API 20 and below, it returns null.
    874      * </p>
    875      *
    876      * @param context The context to use to create the session.
    877      * @param mediaSession A {@link android.media.session.MediaSession} object.
    878      * @return An equivalent {@link MediaSessionCompat} object, or null if none.
    879      */
    880     public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) {
    881         if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) {
    882             return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession));
    883         }
    884         return null;
    885     }
    886 
    887     private static PlaybackStateCompat getStateWithUpdatedPosition(
    888             PlaybackStateCompat state, MediaMetadataCompat metadata) {
    889         if (state == null || state.getPosition() == PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN) {
    890             return state;
    891         }
    892 
    893         if (state.getState() == PlaybackStateCompat.STATE_PLAYING
    894                 || state.getState() == PlaybackStateCompat.STATE_FAST_FORWARDING
    895                 || state.getState() == PlaybackStateCompat.STATE_REWINDING) {
    896             long updateTime = state.getLastPositionUpdateTime();
    897             if (updateTime > 0) {
    898                 long currentTime = SystemClock.elapsedRealtime();
    899                 long position = (long) (state.getPlaybackSpeed() * (currentTime - updateTime))
    900                         + state.getPosition();
    901                 long duration = -1;
    902                 if (metadata != null && metadata.containsKey(
    903                         MediaMetadataCompat.METADATA_KEY_DURATION)) {
    904                     duration = metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
    905                 }
    906 
    907                 if (duration >= 0 && position > duration) {
    908                     position = duration;
    909                 } else if (position < 0) {
    910                     position = 0;
    911                 }
    912                 return new PlaybackStateCompat.Builder(state)
    913                         .setState(state.getState(), position, state.getPlaybackSpeed(), currentTime)
    914                         .build();
    915             }
    916         }
    917         return state;
    918     }
    919 
    920     /**
    921      * Receives transport controls, media buttons, and commands from controllers
    922      * and the system. The callback may be set using {@link #setCallback}.
    923      */
    924     public abstract static class Callback {
    925         final Object mCallbackObj;
    926         private WeakReference<MediaSessionImpl> mSessionImpl;
    927         private CallbackHandler mCallbackHandler = null;
    928         private boolean mMediaPlayPauseKeyPending;
    929 
    930         public Callback() {
    931             if (android.os.Build.VERSION.SDK_INT >= 24) {
    932                 mCallbackObj = MediaSessionCompatApi24.createCallback(new StubApi24());
    933             } else if (android.os.Build.VERSION.SDK_INT >= 23) {
    934                 mCallbackObj = MediaSessionCompatApi23.createCallback(new StubApi23());
    935             } else if (android.os.Build.VERSION.SDK_INT >= 21) {
    936                 mCallbackObj = MediaSessionCompatApi21.createCallback(new StubApi21());
    937             } else {
    938                 mCallbackObj = null;
    939             }
    940         }
    941 
    942         private void setSessionImpl(MediaSessionImpl impl, Handler handler) {
    943             mSessionImpl = new WeakReference<MediaSessionImpl>(impl);
    944             if (mCallbackHandler != null) {
    945                 mCallbackHandler.removeCallbacksAndMessages(null);
    946             }
    947             mCallbackHandler = new CallbackHandler(handler.getLooper());
    948         }
    949 
    950         /**
    951          * Called when a controller has sent a custom command to this session.
    952          * The owner of the session may handle custom commands but is not
    953          * required to.
    954          *
    955          * @param command The command name.
    956          * @param extras Optional parameters for the command, may be null.
    957          * @param cb A result receiver to which a result may be sent by the command, may be null.
    958          */
    959         public void onCommand(String command, Bundle extras, ResultReceiver cb) {
    960         }
    961 
    962         /**
    963          * Override to handle media button events.
    964          * <p>
    965          * The double tap of {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} or {@link
    966          * KeyEvent#KEYCODE_HEADSETHOOK} will call the {@link #onSkipToNext} by default.
    967          *
    968          * @param mediaButtonEvent The media button event intent.
    969          * @return True if the event was handled, false otherwise.
    970          */
    971         public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
    972             MediaSessionImpl impl = mSessionImpl.get();
    973             if (impl == null || mCallbackHandler == null) {
    974                 return false;
    975             }
    976             KeyEvent keyEvent = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
    977             if (keyEvent == null || keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
    978                 return false;
    979             }
    980             int keyCode = keyEvent.getKeyCode();
    981             switch (keyCode) {
    982                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    983                 case KeyEvent.KEYCODE_HEADSETHOOK:
    984                     if (keyEvent.getRepeatCount() > 0) {
    985                         // Consider long-press as a single tap.
    986                         handleMediaPlayPauseKeySingleTapIfPending();
    987                     } else if (mMediaPlayPauseKeyPending) {
    988                         mCallbackHandler.removeMessages(
    989                                 CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
    990                         mMediaPlayPauseKeyPending = false;
    991                         PlaybackStateCompat state = impl.getPlaybackState();
    992                         long validActions = state == null ? 0 : state.getActions();
    993                         // Consider double tap as the next.
    994                         if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
    995                             onSkipToNext();
    996                         }
    997                     } else {
    998                         mMediaPlayPauseKeyPending = true;
    999                         mCallbackHandler.sendEmptyMessageDelayed(
   1000                                 CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
   1001                                 ViewConfiguration.getDoubleTapTimeout());
   1002                     }
   1003                     return true;
   1004                 default:
   1005                     // If another key is pressed within double tap timeout, consider the pending
   1006                     // pending play/pause as a single tap to handle media keys in order.
   1007                     handleMediaPlayPauseKeySingleTapIfPending();
   1008                     break;
   1009             }
   1010             return false;
   1011         }
   1012 
   1013         private void handleMediaPlayPauseKeySingleTapIfPending() {
   1014             if (!mMediaPlayPauseKeyPending) {
   1015                 return;
   1016             }
   1017             mMediaPlayPauseKeyPending = false;
   1018             mCallbackHandler.removeMessages(
   1019                     CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
   1020             MediaSessionImpl impl = mSessionImpl.get();
   1021             if (impl == null) {
   1022                 return;
   1023             }
   1024             PlaybackStateCompat state = impl.getPlaybackState();
   1025             long validActions = state == null ? 0 : state.getActions();
   1026             boolean isPlaying = state != null
   1027                     && state.getState() == PlaybackStateCompat.STATE_PLAYING;
   1028             boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
   1029                         | PlaybackStateCompat.ACTION_PLAY)) != 0;
   1030             boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
   1031                         | PlaybackStateCompat.ACTION_PAUSE)) != 0;
   1032             if (isPlaying && canPause) {
   1033                 onPause();
   1034             } else if (!isPlaying && canPlay) {
   1035                 onPlay();
   1036             }
   1037         }
   1038 
   1039         /**
   1040          * Override to handle requests to prepare playback. Override {@link #onPlay} to handle
   1041          * requests for starting playback.
   1042          */
   1043         public void onPrepare() {
   1044         }
   1045 
   1046         /**
   1047          * Override to handle requests to prepare for playing a specific mediaId that was provided
   1048          * by your app. Override {@link #onPlayFromMediaId} to handle requests for starting
   1049          * playback.
   1050          */
   1051         public void onPrepareFromMediaId(String mediaId, Bundle extras) {
   1052         }
   1053 
   1054         /**
   1055          * Override to handle requests to prepare playback from a search query. An
   1056          * empty query indicates that the app may prepare any music. The
   1057          * implementation should attempt to make a smart choice about what to play.
   1058          * Override {@link #onPlayFromSearch} to handle requests
   1059          * for starting playback.
   1060          */
   1061         public void onPrepareFromSearch(String query, Bundle extras) {
   1062         }
   1063 
   1064         /**
   1065          * Override to handle requests to prepare a specific media item represented by a URI.
   1066          * Override {@link #onPlayFromUri} to handle requests
   1067          * for starting playback.
   1068          */
   1069         public void onPrepareFromUri(Uri uri, Bundle extras) {
   1070         }
   1071 
   1072         /**
   1073          * Override to handle requests to begin playback.
   1074          */
   1075         public void onPlay() {
   1076         }
   1077 
   1078         /**
   1079          * Override to handle requests to play a specific mediaId that was
   1080          * provided by your app.
   1081          */
   1082         public void onPlayFromMediaId(String mediaId, Bundle extras) {
   1083         }
   1084 
   1085         /**
   1086          * Override to handle requests to begin playback from a search query. An
   1087          * empty query indicates that the app may play any music. The
   1088          * implementation should attempt to make a smart choice about what to
   1089          * play.
   1090          */
   1091         public void onPlayFromSearch(String query, Bundle extras) {
   1092         }
   1093 
   1094         /**
   1095          * Override to handle requests to play a specific media item represented by a URI.
   1096          */
   1097         public void onPlayFromUri(Uri uri, Bundle extras) {
   1098         }
   1099 
   1100         /**
   1101          * Override to handle requests to play an item with a given id from the
   1102          * play queue.
   1103          */
   1104         public void onSkipToQueueItem(long id) {
   1105         }
   1106 
   1107         /**
   1108          * Override to handle requests to pause playback.
   1109          */
   1110         public void onPause() {
   1111         }
   1112 
   1113         /**
   1114          * Override to handle requests to skip to the next media item.
   1115          */
   1116         public void onSkipToNext() {
   1117         }
   1118 
   1119         /**
   1120          * Override to handle requests to skip to the previous media item.
   1121          */
   1122         public void onSkipToPrevious() {
   1123         }
   1124 
   1125         /**
   1126          * Override to handle requests to fast forward.
   1127          */
   1128         public void onFastForward() {
   1129         }
   1130 
   1131         /**
   1132          * Override to handle requests to rewind.
   1133          */
   1134         public void onRewind() {
   1135         }
   1136 
   1137         /**
   1138          * Override to handle requests to stop playback.
   1139          */
   1140         public void onStop() {
   1141         }
   1142 
   1143         /**
   1144          * Override to handle requests to seek to a specific position in ms.
   1145          *
   1146          * @param pos New position to move to, in milliseconds.
   1147          */
   1148         public void onSeekTo(long pos) {
   1149         }
   1150 
   1151         /**
   1152          * Override to handle the item being rated.
   1153          *
   1154          * @param rating The rating being set.
   1155          */
   1156         public void onSetRating(RatingCompat rating) {
   1157         }
   1158 
   1159         /**
   1160          * Override to handle the item being rated.
   1161          *
   1162          * @param rating The rating being set.
   1163          * @param extras The extras can include information about the media item being rated.
   1164          */
   1165         public void onSetRating(RatingCompat rating, Bundle extras) {
   1166         }
   1167 
   1168         /**
   1169          * Override to handle requests to enable/disable captioning.
   1170          *
   1171          * @param enabled {@code true} to enable captioning, {@code false} to disable.
   1172          */
   1173         public void onSetCaptioningEnabled(boolean enabled) {
   1174         }
   1175 
   1176         /**
   1177          * Override to handle the setting of the repeat mode.
   1178          * <p>
   1179          * You should call {@link #setRepeatMode} before end of this method in order to notify
   1180          * the change to the {@link MediaControllerCompat}, or
   1181          * {@link MediaControllerCompat#getRepeatMode} could return an invalid value.
   1182          *
   1183          * @param repeatMode The repeat mode which is one of followings:
   1184          *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
   1185          *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
   1186          *            {@link PlaybackStateCompat#REPEAT_MODE_ALL},
   1187          *            {@link PlaybackStateCompat#REPEAT_MODE_GROUP}
   1188          */
   1189         public void onSetRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
   1190         }
   1191 
   1192         /**
   1193          * Override to handle the setting of the shuffle mode.
   1194          * <p>
   1195          * You should call {@link #setShuffleMode} before the end of this method in order to
   1196          * notify the change to the {@link MediaControllerCompat}, or
   1197          * {@link MediaControllerCompat#getShuffleMode} could return an invalid value.
   1198          *
   1199          * @param shuffleMode The shuffle mode which is one of followings:
   1200          *                    {@link PlaybackStateCompat#SHUFFLE_MODE_NONE},
   1201          *                    {@link PlaybackStateCompat#SHUFFLE_MODE_ALL},
   1202          *                    {@link PlaybackStateCompat#SHUFFLE_MODE_GROUP}
   1203          */
   1204         public void onSetShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
   1205         }
   1206 
   1207         /**
   1208          * Called when a {@link MediaControllerCompat} wants a
   1209          * {@link PlaybackStateCompat.CustomAction} to be performed.
   1210          *
   1211          * @param action The action that was originally sent in the
   1212          *            {@link PlaybackStateCompat.CustomAction}.
   1213          * @param extras Optional extras specified by the
   1214          *            {@link MediaControllerCompat}.
   1215          * @see #ACTION_FLAG_AS_INAPPROPRIATE
   1216          * @see #ACTION_SKIP_AD
   1217          * @see #ACTION_FOLLOW
   1218          * @see #ACTION_UNFOLLOW
   1219          */
   1220         public void onCustomAction(String action, Bundle extras) {
   1221         }
   1222 
   1223         /**
   1224          * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem}
   1225          * with the given {@link MediaDescriptionCompat description} at the end of the play queue.
   1226          *
   1227          * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem}
   1228          *            to be inserted.
   1229          */
   1230         public void onAddQueueItem(MediaDescriptionCompat description) {
   1231         }
   1232 
   1233         /**
   1234          * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem}
   1235          * with the given {@link MediaDescriptionCompat description} at the specified position
   1236          * in the play queue.
   1237          *
   1238          * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem}
   1239          *            to be inserted.
   1240          * @param index The index at which the created {@link QueueItem} is to be inserted.
   1241          */
   1242         public void onAddQueueItem(MediaDescriptionCompat description, int index) {
   1243         }
   1244 
   1245         /**
   1246          * Called when a {@link MediaControllerCompat} wants to remove the first occurrence of the
   1247          * specified {@link QueueItem} with the given {@link MediaDescriptionCompat description}
   1248          * in the play queue.
   1249          *
   1250          * @param description The {@link MediaDescriptionCompat} for denoting the {@link QueueItem}
   1251          *            to be removed.
   1252          */
   1253         public void onRemoveQueueItem(MediaDescriptionCompat description) {
   1254         }
   1255 
   1256         /**
   1257          * Called when a {@link MediaControllerCompat} wants to remove a {@link QueueItem} at the
   1258          * specified position in the play queue.
   1259          *
   1260          * @param index The index of the element to be removed.
   1261          * @deprecated {@link #onRemoveQueueItem} will be called instead.
   1262          */
   1263         @Deprecated
   1264         public void onRemoveQueueItemAt(int index) {
   1265         }
   1266 
   1267         private class CallbackHandler extends Handler {
   1268             private static final int MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 1;
   1269 
   1270             CallbackHandler(Looper looper) {
   1271                 super(looper);
   1272             }
   1273 
   1274             @Override
   1275             public void handleMessage(Message msg) {
   1276                 if (msg.what == MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT) {
   1277                     handleMediaPlayPauseKeySingleTapIfPending();
   1278                 }
   1279             }
   1280         }
   1281 
   1282         @RequiresApi(21)
   1283         private class StubApi21 implements MediaSessionCompatApi21.Callback {
   1284 
   1285             StubApi21() {
   1286             }
   1287 
   1288             @Override
   1289             public void onCommand(String command, Bundle extras, ResultReceiver cb) {
   1290                 try {
   1291                     if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) {
   1292                         MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
   1293                         if (impl != null) {
   1294                             Bundle result = new Bundle();
   1295                             IMediaSession extraBinder = impl.getSessionToken().getExtraBinder();
   1296                             BundleCompat.putBinder(result, EXTRA_BINDER,
   1297                                     extraBinder == null ? null : extraBinder.asBinder());
   1298                             cb.send(0, result);
   1299                         }
   1300                     } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) {
   1301                         extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
   1302                         Callback.this.onAddQueueItem(
   1303                                 (MediaDescriptionCompat) extras.getParcelable(
   1304                                         MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
   1305                     } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) {
   1306                         extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
   1307                         Callback.this.onAddQueueItem(
   1308                                 (MediaDescriptionCompat) extras.getParcelable(
   1309                                         MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION),
   1310                                 extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
   1311                     } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) {
   1312                         extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
   1313                         Callback.this.onRemoveQueueItem(
   1314                                 (MediaDescriptionCompat) extras.getParcelable(
   1315                                         MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
   1316                     } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) {
   1317                         MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
   1318                         if (impl != null && impl.mQueue != null) {
   1319                             int index =
   1320                                     extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX, -1);
   1321                             QueueItem item = (index >= 0 && index < impl.mQueue.size())
   1322                                     ? impl.mQueue.get(index) : null;
   1323                             if (item != null) {
   1324                                 Callback.this.onRemoveQueueItem(item.getDescription());
   1325                             }
   1326                         }
   1327                     } else {
   1328                         Callback.this.onCommand(command, extras, cb);
   1329                     }
   1330                 } catch (BadParcelableException e) {
   1331                     // Do not print the exception here, since it is already done by the Parcel
   1332                     // class.
   1333                     Log.e(TAG, "Could not unparcel the extra data.");
   1334                 }
   1335             }
   1336 
   1337             @Override
   1338             public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
   1339                 return Callback.this.onMediaButtonEvent(mediaButtonIntent);
   1340             }
   1341 
   1342             @Override
   1343             public void onPlay() {
   1344                 Callback.this.onPlay();
   1345             }
   1346 
   1347             @Override
   1348             public void onPlayFromMediaId(String mediaId, Bundle extras) {
   1349                 Callback.this.onPlayFromMediaId(mediaId, extras);
   1350             }
   1351 
   1352             @Override
   1353             public void onPlayFromSearch(String search, Bundle extras) {
   1354                 Callback.this.onPlayFromSearch(search, extras);
   1355             }
   1356 
   1357             @Override
   1358             public void onSkipToQueueItem(long id) {
   1359                 Callback.this.onSkipToQueueItem(id);
   1360             }
   1361 
   1362             @Override
   1363             public void onPause() {
   1364                 Callback.this.onPause();
   1365             }
   1366 
   1367             @Override
   1368             public void onSkipToNext() {
   1369                 Callback.this.onSkipToNext();
   1370             }
   1371 
   1372             @Override
   1373             public void onSkipToPrevious() {
   1374                 Callback.this.onSkipToPrevious();
   1375             }
   1376 
   1377             @Override
   1378             public void onFastForward() {
   1379                 Callback.this.onFastForward();
   1380             }
   1381 
   1382             @Override
   1383             public void onRewind() {
   1384                 Callback.this.onRewind();
   1385             }
   1386 
   1387             @Override
   1388             public void onStop() {
   1389                 Callback.this.onStop();
   1390             }
   1391 
   1392             @Override
   1393             public void onSeekTo(long pos) {
   1394                 Callback.this.onSeekTo(pos);
   1395             }
   1396 
   1397             @Override
   1398             public void onSetRating(Object ratingObj) {
   1399                 Callback.this.onSetRating(RatingCompat.fromRating(ratingObj));
   1400             }
   1401 
   1402             @Override
   1403             public void onSetRating(Object ratingObj, Bundle extras) {
   1404                 Callback.this.onSetRating(RatingCompat.fromRating(ratingObj), extras);
   1405             }
   1406 
   1407             @Override
   1408             public void onCustomAction(String action, Bundle extras) {
   1409                 if (action.equals(ACTION_PLAY_FROM_URI)) {
   1410                     Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI);
   1411                     Bundle bundle = extras.getParcelable(ACTION_ARGUMENT_EXTRAS);
   1412                     Callback.this.onPlayFromUri(uri, bundle);
   1413                 } else if (action.equals(ACTION_PREPARE)) {
   1414                     Callback.this.onPrepare();
   1415                 } else if (action.equals(ACTION_PREPARE_FROM_MEDIA_ID)) {
   1416                     String mediaId = extras.getString(ACTION_ARGUMENT_MEDIA_ID);
   1417                     Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
   1418                     Callback.this.onPrepareFromMediaId(mediaId, bundle);
   1419                 } else if (action.equals(ACTION_PREPARE_FROM_SEARCH)) {
   1420                     String query = extras.getString(ACTION_ARGUMENT_QUERY);
   1421                     Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
   1422                     Callback.this.onPrepareFromSearch(query, bundle);
   1423                 } else if (action.equals(ACTION_PREPARE_FROM_URI)) {
   1424                     Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI);
   1425                     Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
   1426                     Callback.this.onPrepareFromUri(uri, bundle);
   1427                 } else if (action.equals(ACTION_SET_CAPTIONING_ENABLED)) {
   1428                     boolean enabled = extras.getBoolean(ACTION_ARGUMENT_CAPTIONING_ENABLED);
   1429                     Callback.this.onSetCaptioningEnabled(enabled);
   1430                 } else if (action.equals(ACTION_SET_REPEAT_MODE)) {
   1431                     int repeatMode = extras.getInt(ACTION_ARGUMENT_REPEAT_MODE);
   1432                     Callback.this.onSetRepeatMode(repeatMode);
   1433                 } else if (action.equals(ACTION_SET_SHUFFLE_MODE)) {
   1434                     int shuffleMode = extras.getInt(ACTION_ARGUMENT_SHUFFLE_MODE);
   1435                     Callback.this.onSetShuffleMode(shuffleMode);
   1436                 } else if (action.equals(ACTION_SET_RATING)) {
   1437                     extras.setClassLoader(RatingCompat.class.getClassLoader());
   1438                     RatingCompat rating = extras.getParcelable(ACTION_ARGUMENT_RATING);
   1439                     Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS);
   1440                     Callback.this.onSetRating(rating, bundle);
   1441                 } else {
   1442                     Callback.this.onCustomAction(action, extras);
   1443                 }
   1444             }
   1445         }
   1446 
   1447         @RequiresApi(23)
   1448         private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback {
   1449 
   1450             StubApi23() {
   1451             }
   1452 
   1453             @Override
   1454             public void onPlayFromUri(Uri uri, Bundle extras) {
   1455                 Callback.this.onPlayFromUri(uri, extras);
   1456             }
   1457         }
   1458 
   1459         @RequiresApi(24)
   1460         private class StubApi24 extends StubApi23 implements MediaSessionCompatApi24.Callback {
   1461 
   1462             StubApi24() {
   1463             }
   1464 
   1465             @Override
   1466             public void onPrepare() {
   1467                 Callback.this.onPrepare();
   1468             }
   1469 
   1470             @Override
   1471             public void onPrepareFromMediaId(String mediaId, Bundle extras) {
   1472                 Callback.this.onPrepareFromMediaId(mediaId, extras);
   1473             }
   1474 
   1475             @Override
   1476             public void onPrepareFromSearch(String query, Bundle extras) {
   1477                 Callback.this.onPrepareFromSearch(query, extras);
   1478             }
   1479 
   1480             @Override
   1481             public void onPrepareFromUri(Uri uri, Bundle extras) {
   1482                 Callback.this.onPrepareFromUri(uri, extras);
   1483             }
   1484         }
   1485     }
   1486 
   1487     /**
   1488      * Represents an ongoing session. This may be passed to apps by the session
   1489      * owner to allow them to create a {@link MediaControllerCompat} to communicate with
   1490      * the session.
   1491      */
   1492     public static final class Token implements Parcelable {
   1493         private final Object mInner;
   1494         private final IMediaSession mExtraBinder;
   1495 
   1496         Token(Object inner) {
   1497             this(inner, null);
   1498         }
   1499 
   1500         Token(Object inner, IMediaSession extraBinder) {
   1501             mInner = inner;
   1502             mExtraBinder = extraBinder;
   1503         }
   1504 
   1505         /**
   1506          * Creates a compat Token from a framework
   1507          * {@link android.media.session.MediaSession.Token} object.
   1508          * <p>
   1509          * This method is only supported on
   1510          * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
   1511          * </p>
   1512          *
   1513          * @param token The framework token object.
   1514          * @return A compat Token for use with {@link MediaControllerCompat}.
   1515          */
   1516         public static Token fromToken(Object token) {
   1517             return fromToken(token, null);
   1518         }
   1519 
   1520         /**
   1521          * Creates a compat Token from a framework
   1522          * {@link android.media.session.MediaSession.Token} object, and the extra binder.
   1523          * <p>
   1524          * This method is only supported on
   1525          * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
   1526          * </p>
   1527          *
   1528          * @param token The framework token object.
   1529          * @param extraBinder The extra binder.
   1530          * @return A compat Token for use with {@link MediaControllerCompat}.
   1531          * @hide
   1532          */
   1533         @RestrictTo(LIBRARY_GROUP)
   1534         public static Token fromToken(Object token, IMediaSession extraBinder) {
   1535             if (token != null && android.os.Build.VERSION.SDK_INT >= 21) {
   1536                 return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder);
   1537             }
   1538             return null;
   1539         }
   1540 
   1541         @Override
   1542         public int describeContents() {
   1543             return 0;
   1544         }
   1545 
   1546         @Override
   1547         public void writeToParcel(Parcel dest, int flags) {
   1548             if (android.os.Build.VERSION.SDK_INT >= 21) {
   1549                 dest.writeParcelable((Parcelable) mInner, flags);
   1550             } else {
   1551                 dest.writeStrongBinder((IBinder) mInner);
   1552             }
   1553         }
   1554 
   1555         @Override
   1556         public int hashCode() {
   1557             if (mInner == null) {
   1558                 return 0;
   1559             }
   1560             return mInner.hashCode();
   1561         }
   1562 
   1563         @Override
   1564         public boolean equals(Object obj) {
   1565             if (this == obj) {
   1566                 return true;
   1567             }
   1568             if (!(obj instanceof Token)) {
   1569                 return false;
   1570             }
   1571 
   1572             Token other = (Token) obj;
   1573             if (mInner == null) {
   1574                 return other.mInner == null;
   1575             }
   1576             if (other.mInner == null) {
   1577                 return false;
   1578             }
   1579             return mInner.equals(other.mInner);
   1580         }
   1581 
   1582         /**
   1583          * Gets the underlying framework {@link android.media.session.MediaSession.Token} object.
   1584          * <p>
   1585          * This method is only supported on API 21+.
   1586          * </p>
   1587          *
   1588          * @return The underlying {@link android.media.session.MediaSession.Token} object,
   1589          * or null if none.
   1590          */
   1591         public Object getToken() {
   1592             return mInner;
   1593         }
   1594 
   1595         /**
   1596          * @hide
   1597          */
   1598         @RestrictTo(LIBRARY_GROUP)
   1599         public IMediaSession getExtraBinder() {
   1600             return mExtraBinder;
   1601         }
   1602 
   1603         public static final Parcelable.Creator<Token> CREATOR
   1604                 = new Parcelable.Creator<Token>() {
   1605                     @Override
   1606                     public Token createFromParcel(Parcel in) {
   1607                         Object inner;
   1608                         if (android.os.Build.VERSION.SDK_INT >= 21) {
   1609                             inner = in.readParcelable(null);
   1610                         } else {
   1611                             inner = in.readStrongBinder();
   1612                         }
   1613                         return new Token(inner);
   1614                     }
   1615 
   1616                     @Override
   1617                     public Token[] newArray(int size) {
   1618                         return new Token[size];
   1619                     }
   1620         };
   1621     }
   1622 
   1623     /**
   1624      * A single item that is part of the play queue. It contains a description
   1625      * of the item and its id in the queue.
   1626      */
   1627     public static final class QueueItem implements Parcelable {
   1628         /**
   1629          * This id is reserved. No items can be explicitly assigned this id.
   1630          */
   1631         public static final int UNKNOWN_ID = -1;
   1632 
   1633         private final MediaDescriptionCompat mDescription;
   1634         private final long mId;
   1635 
   1636         private Object mItem;
   1637 
   1638         /**
   1639          * Creates a new {@link MediaSessionCompat.QueueItem}.
   1640          *
   1641          * @param description The {@link MediaDescriptionCompat} for this item.
   1642          * @param id An identifier for this item. It must be unique within the
   1643          *            play queue and cannot be {@link #UNKNOWN_ID}.
   1644          */
   1645         public QueueItem(MediaDescriptionCompat description, long id) {
   1646             this(null, description, id);
   1647         }
   1648 
   1649         private QueueItem(Object queueItem, MediaDescriptionCompat description, long id) {
   1650             if (description == null) {
   1651                 throw new IllegalArgumentException("Description cannot be null.");
   1652             }
   1653             if (id == UNKNOWN_ID) {
   1654                 throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
   1655             }
   1656             mDescription = description;
   1657             mId = id;
   1658             mItem = queueItem;
   1659         }
   1660 
   1661         QueueItem(Parcel in) {
   1662             mDescription = MediaDescriptionCompat.CREATOR.createFromParcel(in);
   1663             mId = in.readLong();
   1664         }
   1665 
   1666         /**
   1667          * Gets the description for this item.
   1668          */
   1669         public MediaDescriptionCompat getDescription() {
   1670             return mDescription;
   1671         }
   1672 
   1673         /**
   1674          * Gets the queue id for this item.
   1675          */
   1676         public long getQueueId() {
   1677             return mId;
   1678         }
   1679 
   1680         @Override
   1681         public void writeToParcel(Parcel dest, int flags) {
   1682             mDescription.writeToParcel(dest, flags);
   1683             dest.writeLong(mId);
   1684         }
   1685 
   1686         @Override
   1687         public int describeContents() {
   1688             return 0;
   1689         }
   1690 
   1691         /**
   1692          * Gets the underlying
   1693          * {@link android.media.session.MediaSession.QueueItem}.
   1694          * <p>
   1695          * On builds before {@link android.os.Build.VERSION_CODES#LOLLIPOP} null
   1696          * is returned.
   1697          *
   1698          * @return The underlying
   1699          *         {@link android.media.session.MediaSession.QueueItem} or null.
   1700          */
   1701         public Object getQueueItem() {
   1702             if (mItem != null || android.os.Build.VERSION.SDK_INT < 21) {
   1703                 return mItem;
   1704             }
   1705             mItem = MediaSessionCompatApi21.QueueItem.createItem(mDescription.getMediaDescription(),
   1706                     mId);
   1707             return mItem;
   1708         }
   1709 
   1710         /**
   1711          * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem}
   1712          * object.
   1713          * <p>
   1714          * This method is only supported on API 21+. On API 20 and below, it returns null.
   1715          * </p>
   1716          *
   1717          * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object.
   1718          * @return An equivalent {@link QueueItem} object, or null if none.
   1719          */
   1720         public static QueueItem fromQueueItem(Object queueItem) {
   1721             if (queueItem == null || Build.VERSION.SDK_INT < 21) {
   1722                 return null;
   1723             }
   1724             Object descriptionObj = MediaSessionCompatApi21.QueueItem.getDescription(queueItem);
   1725             MediaDescriptionCompat description = MediaDescriptionCompat.fromMediaDescription(
   1726                     descriptionObj);
   1727             long id = MediaSessionCompatApi21.QueueItem.getQueueId(queueItem);
   1728             return new QueueItem(queueItem, description, id);
   1729         }
   1730 
   1731         /**
   1732          * Creates a list of {@link QueueItem} objects from a framework
   1733          * {@link android.media.session.MediaSession.QueueItem} object list.
   1734          * <p>
   1735          * This method is only supported on API 21+. On API 20 and below, it returns null.
   1736          * </p>
   1737          *
   1738          * @param itemList A list of {@link android.media.session.MediaSession.QueueItem} objects.
   1739          * @return An equivalent list of {@link QueueItem} objects, or null if none.
   1740          */
   1741         public static List<QueueItem> fromQueueItemList(List<?> itemList) {
   1742             if (itemList == null || Build.VERSION.SDK_INT < 21) {
   1743                 return null;
   1744             }
   1745             List<QueueItem> items = new ArrayList<>();
   1746             for (Object itemObj : itemList) {
   1747                 items.add(fromQueueItem(itemObj));
   1748             }
   1749             return items;
   1750         }
   1751 
   1752         public static final Creator<MediaSessionCompat.QueueItem> CREATOR
   1753                 = new Creator<MediaSessionCompat.QueueItem>() {
   1754 
   1755             @Override
   1756             public MediaSessionCompat.QueueItem createFromParcel(Parcel p) {
   1757                 return new MediaSessionCompat.QueueItem(p);
   1758             }
   1759 
   1760             @Override
   1761             public MediaSessionCompat.QueueItem[] newArray(int size) {
   1762                 return new MediaSessionCompat.QueueItem[size];
   1763             }
   1764         };
   1765 
   1766         @Override
   1767         public String toString() {
   1768             return "MediaSession.QueueItem {" +
   1769                     "Description=" + mDescription +
   1770                     ", Id=" + mId + " }";
   1771         }
   1772     }
   1773 
   1774     /**
   1775      * This is a wrapper for {@link ResultReceiver} for sending over aidl
   1776      * interfaces. The framework version was not exposed to aidls until
   1777      * {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
   1778      *
   1779      * @hide
   1780      */
   1781     @RestrictTo(LIBRARY)
   1782     public static final class ResultReceiverWrapper implements Parcelable {
   1783         private ResultReceiver mResultReceiver;
   1784 
   1785         public ResultReceiverWrapper(ResultReceiver resultReceiver) {
   1786             mResultReceiver = resultReceiver;
   1787         }
   1788 
   1789         ResultReceiverWrapper(Parcel in) {
   1790             mResultReceiver = ResultReceiver.CREATOR.createFromParcel(in);
   1791         }
   1792 
   1793         public static final Creator<ResultReceiverWrapper>
   1794                 CREATOR = new Creator<ResultReceiverWrapper>() {
   1795             @Override
   1796             public ResultReceiverWrapper createFromParcel(Parcel p) {
   1797                 return new ResultReceiverWrapper(p);
   1798             }
   1799 
   1800             @Override
   1801             public ResultReceiverWrapper[] newArray(int size) {
   1802                 return new ResultReceiverWrapper[size];
   1803             }
   1804         };
   1805 
   1806         @Override
   1807         public int describeContents() {
   1808             return 0;
   1809         }
   1810 
   1811         @Override
   1812         public void writeToParcel(Parcel dest, int flags) {
   1813             mResultReceiver.writeToParcel(dest, flags);
   1814         }
   1815     }
   1816 
   1817     public interface OnActiveChangeListener {
   1818         void onActiveChanged();
   1819     }
   1820 
   1821     interface MediaSessionImpl {
   1822         void setCallback(Callback callback, Handler handler);
   1823         void setFlags(@SessionFlags int flags);
   1824         void setPlaybackToLocal(int stream);
   1825         void setPlaybackToRemote(VolumeProviderCompat volumeProvider);
   1826         void setActive(boolean active);
   1827         boolean isActive();
   1828         void sendSessionEvent(String event, Bundle extras);
   1829         void release();
   1830         Token getSessionToken();
   1831         void setPlaybackState(PlaybackStateCompat state);
   1832         PlaybackStateCompat getPlaybackState();
   1833         void setMetadata(MediaMetadataCompat metadata);
   1834 
   1835         void setSessionActivity(PendingIntent pi);
   1836 
   1837         void setMediaButtonReceiver(PendingIntent mbr);
   1838         void setQueue(List<QueueItem> queue);
   1839         void setQueueTitle(CharSequence title);
   1840 
   1841         void setRatingType(@RatingCompat.Style int type);
   1842         void setCaptioningEnabled(boolean enabled);
   1843         void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode);
   1844         void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode);
   1845         void setExtras(Bundle extras);
   1846 
   1847         Object getMediaSession();
   1848 
   1849         Object getRemoteControlClient();
   1850 
   1851         String getCallingPackage();
   1852         RemoteUserInfo getCurrentControllerInfo();
   1853     }
   1854 
   1855     static class MediaSessionImplBase implements MediaSessionImpl {
   1856         /***** RemoteControlClient States, we only need none as the others were public *******/
   1857         static final int RCC_PLAYSTATE_NONE = 0;
   1858 
   1859         private final Context mContext;
   1860         private final ComponentName mMediaButtonReceiverComponentName;
   1861         private final PendingIntent mMediaButtonReceiverIntent;
   1862         private final MediaSessionStub mStub;
   1863         private final Token mToken;
   1864         final String mPackageName;
   1865         final String mTag;
   1866         final AudioManager mAudioManager;
   1867         final RemoteControlClient mRcc;
   1868 
   1869         final Object mLock = new Object();
   1870         final RemoteCallbackList<IMediaControllerCallback> mControllerCallbacks
   1871                 = new RemoteCallbackList<>();
   1872 
   1873         private MessageHandler mHandler;
   1874         boolean mDestroyed = false;
   1875         boolean mIsActive = false;
   1876         private boolean mIsMbrRegistered = false;
   1877         private boolean mIsRccRegistered = false;
   1878         volatile Callback mCallback;
   1879 
   1880         @SessionFlags int mFlags;
   1881 
   1882         MediaMetadataCompat mMetadata;
   1883         PlaybackStateCompat mState;
   1884         PendingIntent mSessionActivity;
   1885         List<QueueItem> mQueue;
   1886         CharSequence mQueueTitle;
   1887         @RatingCompat.Style int mRatingType;
   1888         boolean mCaptioningEnabled;
   1889         @PlaybackStateCompat.RepeatMode int mRepeatMode;
   1890         @PlaybackStateCompat.ShuffleMode int mShuffleMode;
   1891         Bundle mExtras;
   1892 
   1893         int mVolumeType;
   1894         int mLocalStream;
   1895         VolumeProviderCompat mVolumeProvider;
   1896 
   1897         private VolumeProviderCompat.Callback mVolumeCallback
   1898                 = new VolumeProviderCompat.Callback() {
   1899             @Override
   1900             public void onVolumeChanged(VolumeProviderCompat volumeProvider) {
   1901                 if (mVolumeProvider != volumeProvider) {
   1902                     return;
   1903                 }
   1904                 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream,
   1905                         volumeProvider.getVolumeControl(), volumeProvider.getMaxVolume(),
   1906                         volumeProvider.getCurrentVolume());
   1907                 sendVolumeInfoChanged(info);
   1908             }
   1909         };
   1910 
   1911         public MediaSessionImplBase(Context context, String tag, ComponentName mbrComponent,
   1912                 PendingIntent mbrIntent) {
   1913             if (mbrComponent == null) {
   1914                 throw new IllegalArgumentException(
   1915                         "MediaButtonReceiver component may not be null.");
   1916             }
   1917             mContext = context;
   1918             mPackageName = context.getPackageName();
   1919             mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
   1920             mTag = tag;
   1921             mMediaButtonReceiverComponentName = mbrComponent;
   1922             mMediaButtonReceiverIntent = mbrIntent;
   1923             mStub = new MediaSessionStub();
   1924             mToken = new Token(mStub);
   1925 
   1926             mRatingType = RatingCompat.RATING_NONE;
   1927             mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
   1928             mLocalStream = AudioManager.STREAM_MUSIC;
   1929             mRcc = new RemoteControlClient(mbrIntent);
   1930         }
   1931 
   1932         @Override
   1933         public void setCallback(Callback callback, Handler handler) {
   1934             mCallback = callback;
   1935             if (callback != null) {
   1936                 if (handler == null) {
   1937                     handler = new Handler();
   1938                 }
   1939                 synchronized (mLock) {
   1940                     if (mHandler != null) {
   1941                         mHandler.removeCallbacksAndMessages(null);
   1942                     }
   1943                     mHandler = new MessageHandler(handler.getLooper());
   1944                     mCallback.setSessionImpl(this, handler);
   1945                 }
   1946             }
   1947         }
   1948 
   1949         void postToHandler(int what, int arg1, int arg2, Object obj, Bundle extras) {
   1950             synchronized (mLock) {
   1951                 if (mHandler != null) {
   1952                     Message msg = mHandler.obtainMessage(what, arg1, arg2, obj);
   1953                     Bundle data = new Bundle();
   1954                     data.putString(DATA_CALLING_PACKAGE, LEGACY_CONTROLLER);
   1955                     data.putInt(DATA_CALLING_PID, Binder.getCallingPid());
   1956                     data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
   1957                     if (extras != null) {
   1958                         data.putBundle(DATA_EXTRAS, extras);
   1959                     }
   1960                     msg.setData(data);
   1961                     msg.sendToTarget();
   1962                 }
   1963             }
   1964         }
   1965 
   1966         @Override
   1967         public void setFlags(@SessionFlags int flags) {
   1968             synchronized (mLock) {
   1969                 mFlags = flags;
   1970             }
   1971             update();
   1972         }
   1973 
   1974         @Override
   1975         public void setPlaybackToLocal(int stream) {
   1976             if (mVolumeProvider != null) {
   1977                 mVolumeProvider.setCallback(null);
   1978             }
   1979             mLocalStream = stream;
   1980             mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
   1981             ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream,
   1982                     VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE,
   1983                     mAudioManager.getStreamMaxVolume(mLocalStream),
   1984                     mAudioManager.getStreamVolume(mLocalStream));
   1985             sendVolumeInfoChanged(info);
   1986         }
   1987 
   1988         @Override
   1989         public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) {
   1990             if (volumeProvider == null) {
   1991                 throw new IllegalArgumentException("volumeProvider may not be null");
   1992             }
   1993             if (mVolumeProvider != null) {
   1994                 mVolumeProvider.setCallback(null);
   1995             }
   1996             mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE;
   1997             mVolumeProvider = volumeProvider;
   1998             ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream,
   1999                     mVolumeProvider.getVolumeControl(), mVolumeProvider.getMaxVolume(),
   2000                     mVolumeProvider.getCurrentVolume());
   2001             sendVolumeInfoChanged(info);
   2002 
   2003             volumeProvider.setCallback(mVolumeCallback);
   2004         }
   2005 
   2006         @Override
   2007         public void setActive(boolean active) {
   2008             if (active == mIsActive) {
   2009                 return;
   2010             }
   2011             mIsActive = active;
   2012             if (update()) {
   2013                 setMetadata(mMetadata);
   2014                 setPlaybackState(mState);
   2015             }
   2016         }
   2017 
   2018         @Override
   2019         public boolean isActive() {
   2020             return mIsActive;
   2021         }
   2022 
   2023         @Override
   2024         public void sendSessionEvent(String event, Bundle extras) {
   2025             sendEvent(event, extras);
   2026         }
   2027 
   2028         @Override
   2029         public void release() {
   2030             mIsActive = false;
   2031             mDestroyed = true;
   2032             update();
   2033             sendSessionDestroyed();
   2034         }
   2035 
   2036         @Override
   2037         public Token getSessionToken() {
   2038             return mToken;
   2039         }
   2040 
   2041         @Override
   2042         public void setPlaybackState(PlaybackStateCompat state) {
   2043             synchronized (mLock) {
   2044                 mState = state;
   2045             }
   2046             sendState(state);
   2047             if (!mIsActive) {
   2048                 // Don't set the state until after the RCC is registered
   2049                 return;
   2050             }
   2051             if (state == null) {
   2052                 mRcc.setPlaybackState(0);
   2053                 mRcc.setTransportControlFlags(0);
   2054             } else {
   2055                 // Set state
   2056                 setRccState(state);
   2057 
   2058                 // Set transport control flags
   2059                 mRcc.setTransportControlFlags(
   2060                         getRccTransportControlFlagsFromActions(state.getActions()));
   2061             }
   2062         }
   2063 
   2064         @Override
   2065         public PlaybackStateCompat getPlaybackState() {
   2066             synchronized (mLock) {
   2067                 return mState;
   2068             }
   2069         }
   2070 
   2071         void setRccState(PlaybackStateCompat state) {
   2072             mRcc.setPlaybackState(getRccStateFromState(state.getState()));
   2073         }
   2074 
   2075         int getRccStateFromState(int state) {
   2076             switch (state) {
   2077                 case PlaybackStateCompat.STATE_CONNECTING:
   2078                 case PlaybackStateCompat.STATE_BUFFERING:
   2079                     return RemoteControlClient.PLAYSTATE_BUFFERING;
   2080                 case PlaybackStateCompat.STATE_ERROR:
   2081                     return RemoteControlClient.PLAYSTATE_ERROR;
   2082                 case PlaybackStateCompat.STATE_FAST_FORWARDING:
   2083                     return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
   2084                 case PlaybackStateCompat.STATE_NONE:
   2085                     return RCC_PLAYSTATE_NONE;
   2086                 case PlaybackStateCompat.STATE_PAUSED:
   2087                     return RemoteControlClient.PLAYSTATE_PAUSED;
   2088                 case PlaybackStateCompat.STATE_PLAYING:
   2089                     return RemoteControlClient.PLAYSTATE_PLAYING;
   2090                 case PlaybackStateCompat.STATE_REWINDING:
   2091                     return RemoteControlClient.PLAYSTATE_REWINDING;
   2092                 case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS:
   2093                     return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
   2094                 case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT:
   2095                 case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM:
   2096                     return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
   2097                 case PlaybackStateCompat.STATE_STOPPED:
   2098                     return RemoteControlClient.PLAYSTATE_STOPPED;
   2099                 default:
   2100                     return -1;
   2101             }
   2102         }
   2103 
   2104         int getRccTransportControlFlagsFromActions(long actions) {
   2105             int transportControlFlags = 0;
   2106             if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) {
   2107                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP;
   2108             }
   2109             if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
   2110                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
   2111             }
   2112             if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) {
   2113                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
   2114             }
   2115             if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
   2116                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
   2117             }
   2118             if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
   2119                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
   2120             }
   2121             if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
   2122                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
   2123             }
   2124             if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
   2125                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
   2126             }
   2127             if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
   2128                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
   2129             }
   2130             return transportControlFlags;
   2131         }
   2132 
   2133         @Override
   2134         public void setMetadata(MediaMetadataCompat metadata) {
   2135             if (metadata != null) {
   2136                 // Clones {@link MediaMetadataCompat} and scales down bitmaps if they are large.
   2137                 metadata = new MediaMetadataCompat.Builder(metadata, sMaxBitmapSize).build();
   2138             }
   2139 
   2140             synchronized (mLock) {
   2141                 mMetadata = metadata;
   2142             }
   2143             sendMetadata(metadata);
   2144             if (!mIsActive) {
   2145                 // Don't set metadata until after the rcc has been registered
   2146                 return;
   2147             }
   2148             RemoteControlClient.MetadataEditor editor = buildRccMetadata(
   2149                     metadata == null ? null : metadata.getBundle());
   2150             editor.apply();
   2151         }
   2152 
   2153         RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
   2154             RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true);
   2155             if (metadata == null) {
   2156                 return editor;
   2157             }
   2158             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) {
   2159                 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART);
   2160                 if (art != null) {
   2161                     // Clone the bitmap to prevent it from being recycled by RCC.
   2162                     art = art.copy(art.getConfig(), false);
   2163                 }
   2164                 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
   2165             } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
   2166                 // Fall back to album art if the track art wasn't available
   2167                 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
   2168                 if (art != null) {
   2169                     // Clone the bitmap to prevent it from being recycled by RCC.
   2170                     art = art.copy(art.getConfig(), false);
   2171                 }
   2172                 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
   2173             }
   2174             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) {
   2175                 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
   2176                         metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
   2177             }
   2178             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) {
   2179                 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
   2180                         metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST));
   2181             }
   2182             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) {
   2183                 editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
   2184                         metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
   2185             }
   2186             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) {
   2187                 editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR,
   2188                         metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR));
   2189             }
   2190             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) {
   2191                 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
   2192                         metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION));
   2193             }
   2194             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) {
   2195                 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER,
   2196                         metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER));
   2197             }
   2198             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) {
   2199                 editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE,
   2200                         metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE));
   2201             }
   2202             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) {
   2203                 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
   2204                         metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER));
   2205             }
   2206             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) {
   2207                 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
   2208                         metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
   2209             }
   2210             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) {
   2211                 editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE,
   2212                         metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
   2213             }
   2214             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
   2215                 editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
   2216                         metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
   2217             }
   2218             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) {
   2219                 editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
   2220                         metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
   2221             }
   2222             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) {
   2223                 editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER,
   2224                         metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER));
   2225             }
   2226             return editor;
   2227         }
   2228 
   2229         @Override
   2230         public void setSessionActivity(PendingIntent pi) {
   2231             synchronized (mLock) {
   2232                 mSessionActivity = pi;
   2233             }
   2234         }
   2235 
   2236         @Override
   2237         public void setMediaButtonReceiver(PendingIntent mbr) {
   2238             // Do nothing, changing this is not supported before API 21.
   2239         }
   2240 
   2241         @Override
   2242         public void setQueue(List<QueueItem> queue) {
   2243             mQueue = queue;
   2244             sendQueue(queue);
   2245         }
   2246 
   2247         @Override
   2248         public void setQueueTitle(CharSequence title) {
   2249             mQueueTitle = title;
   2250             sendQueueTitle(title);
   2251         }
   2252 
   2253         @Override
   2254         public Object getMediaSession() {
   2255             return null;
   2256         }
   2257 
   2258         @Override
   2259         public Object getRemoteControlClient() {
   2260             return null;
   2261         }
   2262 
   2263         @Override
   2264         public String getCallingPackage() {
   2265             return null;
   2266         }
   2267 
   2268         @Override
   2269         public void setRatingType(@RatingCompat.Style int type) {
   2270             mRatingType = type;
   2271         }
   2272 
   2273         @Override
   2274         public void setCaptioningEnabled(boolean enabled) {
   2275             if (mCaptioningEnabled != enabled) {
   2276                 mCaptioningEnabled = enabled;
   2277                 sendCaptioningEnabled(enabled);
   2278             }
   2279         }
   2280 
   2281         @Override
   2282         public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
   2283             if (mRepeatMode != repeatMode) {
   2284                 mRepeatMode = repeatMode;
   2285                 sendRepeatMode(repeatMode);
   2286             }
   2287         }
   2288 
   2289         @Override
   2290         public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
   2291             if (mShuffleMode != shuffleMode) {
   2292                 mShuffleMode = shuffleMode;
   2293                 sendShuffleMode(shuffleMode);
   2294             }
   2295         }
   2296 
   2297         @Override
   2298         public void setExtras(Bundle extras) {
   2299             mExtras = extras;
   2300             sendExtras(extras);
   2301         }
   2302 
   2303         @Override
   2304         public RemoteUserInfo getCurrentControllerInfo() {
   2305             synchronized (mLock) {
   2306                 if (mHandler != null) {
   2307                     return mHandler.getRemoteUserInfo();
   2308                 }
   2309             }
   2310             return null;
   2311         }
   2312 
   2313         // Registers/unregisters components as needed.
   2314         boolean update() {
   2315             boolean registeredRcc = false;
   2316             if (mIsActive) {
   2317                 // Register a MBR if it's supported, unregister it if support was removed.
   2318                 if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) {
   2319                     registerMediaButtonEventReceiver(mMediaButtonReceiverIntent,
   2320                             mMediaButtonReceiverComponentName);
   2321                     mIsMbrRegistered = true;
   2322                 } else if (mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) == 0) {
   2323                     unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent,
   2324                             mMediaButtonReceiverComponentName);
   2325                     mIsMbrRegistered = false;
   2326                 }
   2327                 // Register a RCC if it's supported, unregister it if support was removed.
   2328                 if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
   2329                     mAudioManager.registerRemoteControlClient(mRcc);
   2330                     mIsRccRegistered = true;
   2331                     registeredRcc = true;
   2332                 } else if (mIsRccRegistered
   2333                         && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
   2334                     // RCC keeps the state while the system resets its state internally when
   2335                     // we register RCC. Reset the state so that the states in RCC and the system
   2336                     // are in sync when we re-register the RCC.
   2337                     mRcc.setPlaybackState(0);
   2338                     mAudioManager.unregisterRemoteControlClient(mRcc);
   2339                     mIsRccRegistered = false;
   2340                 }
   2341             } else {
   2342                 // When inactive remove any registered components.
   2343                 if (mIsMbrRegistered) {
   2344                     unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent,
   2345                             mMediaButtonReceiverComponentName);
   2346                     mIsMbrRegistered = false;
   2347                 }
   2348                 if (mIsRccRegistered) {
   2349                     // RCC keeps the state while the system resets its state internally when
   2350                     // we register RCC. Reset the state so that the states in RCC and the system
   2351                     // are in sync when we re-register the RCC.
   2352                     mRcc.setPlaybackState(0);
   2353                     mAudioManager.unregisterRemoteControlClient(mRcc);
   2354                     mIsRccRegistered = false;
   2355                 }
   2356             }
   2357             return registeredRcc;
   2358         }
   2359 
   2360         void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) {
   2361             mAudioManager.registerMediaButtonEventReceiver(mbrComponent);
   2362         }
   2363 
   2364         void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent,
   2365                 ComponentName mbrComponent) {
   2366             mAudioManager.unregisterMediaButtonEventReceiver(mbrComponent);
   2367         }
   2368 
   2369         void adjustVolume(int direction, int flags) {
   2370             if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
   2371                 if (mVolumeProvider != null) {
   2372                     mVolumeProvider.onAdjustVolume(direction);
   2373                 }
   2374             } else {
   2375                 mAudioManager.adjustStreamVolume(mLocalStream, direction, flags);
   2376             }
   2377         }
   2378 
   2379         void setVolumeTo(int value, int flags) {
   2380             if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
   2381                 if (mVolumeProvider != null) {
   2382                     mVolumeProvider.onSetVolumeTo(value);
   2383                 }
   2384             } else {
   2385                 mAudioManager.setStreamVolume(mLocalStream, value, flags);
   2386             }
   2387         }
   2388 
   2389         void sendVolumeInfoChanged(ParcelableVolumeInfo info) {
   2390             int size = mControllerCallbacks.beginBroadcast();
   2391             for (int i = size - 1; i >= 0; i--) {
   2392                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2393                 try {
   2394                     cb.onVolumeInfoChanged(info);
   2395                 } catch (RemoteException e) {
   2396                 }
   2397             }
   2398             mControllerCallbacks.finishBroadcast();
   2399         }
   2400 
   2401         private void sendSessionDestroyed() {
   2402             int size = mControllerCallbacks.beginBroadcast();
   2403             for (int i = size - 1; i >= 0; i--) {
   2404                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2405                 try {
   2406                     cb.onSessionDestroyed();
   2407                 } catch (RemoteException e) {
   2408                 }
   2409             }
   2410             mControllerCallbacks.finishBroadcast();
   2411             mControllerCallbacks.kill();
   2412         }
   2413 
   2414         private void sendEvent(String event, Bundle extras) {
   2415             int size = mControllerCallbacks.beginBroadcast();
   2416             for (int i = size - 1; i >= 0; i--) {
   2417                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2418                 try {
   2419                     cb.onEvent(event, extras);
   2420                 } catch (RemoteException e) {
   2421                 }
   2422             }
   2423             mControllerCallbacks.finishBroadcast();
   2424         }
   2425 
   2426         private void sendState(PlaybackStateCompat state) {
   2427             int size = mControllerCallbacks.beginBroadcast();
   2428             for (int i = size - 1; i >= 0; i--) {
   2429                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2430                 try {
   2431                     cb.onPlaybackStateChanged(state);
   2432                 } catch (RemoteException e) {
   2433                 }
   2434             }
   2435             mControllerCallbacks.finishBroadcast();
   2436         }
   2437 
   2438         private void sendMetadata(MediaMetadataCompat metadata) {
   2439             int size = mControllerCallbacks.beginBroadcast();
   2440             for (int i = size - 1; i >= 0; i--) {
   2441                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2442                 try {
   2443                     cb.onMetadataChanged(metadata);
   2444                 } catch (RemoteException e) {
   2445                 }
   2446             }
   2447             mControllerCallbacks.finishBroadcast();
   2448         }
   2449 
   2450         private void sendQueue(List<QueueItem> queue) {
   2451             int size = mControllerCallbacks.beginBroadcast();
   2452             for (int i = size - 1; i >= 0; i--) {
   2453                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2454                 try {
   2455                     cb.onQueueChanged(queue);
   2456                 } catch (RemoteException e) {
   2457                 }
   2458             }
   2459             mControllerCallbacks.finishBroadcast();
   2460         }
   2461 
   2462         private void sendQueueTitle(CharSequence queueTitle) {
   2463             int size = mControllerCallbacks.beginBroadcast();
   2464             for (int i = size - 1; i >= 0; i--) {
   2465                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2466                 try {
   2467                     cb.onQueueTitleChanged(queueTitle);
   2468                 } catch (RemoteException e) {
   2469                 }
   2470             }
   2471             mControllerCallbacks.finishBroadcast();
   2472         }
   2473 
   2474         private void sendCaptioningEnabled(boolean enabled) {
   2475             int size = mControllerCallbacks.beginBroadcast();
   2476             for (int i = size - 1; i >= 0; i--) {
   2477                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2478                 try {
   2479                     cb.onCaptioningEnabledChanged(enabled);
   2480                 } catch (RemoteException e) {
   2481                 }
   2482             }
   2483             mControllerCallbacks.finishBroadcast();
   2484         }
   2485 
   2486         private void sendRepeatMode(int repeatMode) {
   2487             int size = mControllerCallbacks.beginBroadcast();
   2488             for (int i = size - 1; i >= 0; i--) {
   2489                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2490                 try {
   2491                     cb.onRepeatModeChanged(repeatMode);
   2492                 } catch (RemoteException e) {
   2493                 }
   2494             }
   2495             mControllerCallbacks.finishBroadcast();
   2496         }
   2497 
   2498         private void sendShuffleMode(int shuffleMode) {
   2499             int size = mControllerCallbacks.beginBroadcast();
   2500             for (int i = size - 1; i >= 0; i--) {
   2501                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2502                 try {
   2503                     cb.onShuffleModeChanged(shuffleMode);
   2504                 } catch (RemoteException e) {
   2505                 }
   2506             }
   2507             mControllerCallbacks.finishBroadcast();
   2508         }
   2509 
   2510         private void sendExtras(Bundle extras) {
   2511             int size = mControllerCallbacks.beginBroadcast();
   2512             for (int i = size - 1; i >= 0; i--) {
   2513                 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
   2514                 try {
   2515                     cb.onExtrasChanged(extras);
   2516                 } catch (RemoteException e) {
   2517                 }
   2518             }
   2519             mControllerCallbacks.finishBroadcast();
   2520         }
   2521 
   2522         class MediaSessionStub extends IMediaSession.Stub {
   2523             @Override
   2524             public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) {
   2525                 postToHandler(MessageHandler.MSG_COMMAND,
   2526                         new Command(command, args, cb.mResultReceiver));
   2527             }
   2528 
   2529             @Override
   2530             public boolean sendMediaButton(KeyEvent mediaButton) {
   2531                 boolean handlesMediaButtons =
   2532                         (mFlags & MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS) != 0;
   2533                 if (handlesMediaButtons) {
   2534                     postToHandler(MessageHandler.MSG_MEDIA_BUTTON, mediaButton);
   2535                 }
   2536                 return handlesMediaButtons;
   2537             }
   2538 
   2539             @Override
   2540             public void registerCallbackListener(IMediaControllerCallback cb) {
   2541                 // If this session is already destroyed tell the caller and
   2542                 // don't add them.
   2543                 if (mDestroyed) {
   2544                     try {
   2545                         cb.onSessionDestroyed();
   2546                     } catch (Exception e) {
   2547                         // ignored
   2548                     }
   2549                     return;
   2550                 }
   2551                 mControllerCallbacks.register(cb);
   2552             }
   2553 
   2554             @Override
   2555             public void unregisterCallbackListener(IMediaControllerCallback cb) {
   2556                 mControllerCallbacks.unregister(cb);
   2557             }
   2558 
   2559             @Override
   2560             public String getPackageName() {
   2561                 // mPackageName is final so doesn't need synchronize block
   2562                 return mPackageName;
   2563             }
   2564 
   2565             @Override
   2566             public String getTag() {
   2567                 // mTag is final so doesn't need synchronize block
   2568                 return mTag;
   2569             }
   2570 
   2571             @Override
   2572             public PendingIntent getLaunchPendingIntent() {
   2573                 synchronized (mLock) {
   2574                     return mSessionActivity;
   2575                 }
   2576             }
   2577 
   2578             @Override
   2579             @SessionFlags
   2580             public long getFlags() {
   2581                 synchronized (mLock) {
   2582                     return mFlags;
   2583                 }
   2584             }
   2585 
   2586             @Override
   2587             public ParcelableVolumeInfo getVolumeAttributes() {
   2588                 int controlType;
   2589                 int max;
   2590                 int current;
   2591                 int stream;
   2592                 int volumeType;
   2593                 synchronized (mLock) {
   2594                     volumeType = mVolumeType;
   2595                     stream = mLocalStream;
   2596                     VolumeProviderCompat vp = mVolumeProvider;
   2597                     if (volumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
   2598                         controlType = vp.getVolumeControl();
   2599                         max = vp.getMaxVolume();
   2600                         current = vp.getCurrentVolume();
   2601                     } else {
   2602                         controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
   2603                         max = mAudioManager.getStreamMaxVolume(stream);
   2604                         current = mAudioManager.getStreamVolume(stream);
   2605                     }
   2606                 }
   2607                 return new ParcelableVolumeInfo(volumeType, stream, controlType, max, current);
   2608             }
   2609 
   2610             @Override
   2611             public void adjustVolume(int direction, int flags, String packageName) {
   2612                 MediaSessionImplBase.this.adjustVolume(direction, flags);
   2613             }
   2614 
   2615             @Override
   2616             public void setVolumeTo(int value, int flags, String packageName) {
   2617                 MediaSessionImplBase.this.setVolumeTo(value, flags);
   2618             }
   2619 
   2620             @Override
   2621             public void prepare() throws RemoteException {
   2622                 postToHandler(MessageHandler.MSG_PREPARE);
   2623             }
   2624 
   2625             @Override
   2626             public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException {
   2627                 postToHandler(MessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
   2628             }
   2629 
   2630             @Override
   2631             public void prepareFromSearch(String query, Bundle extras) throws RemoteException {
   2632                 postToHandler(MessageHandler.MSG_PREPARE_SEARCH, query, extras);
   2633             }
   2634 
   2635             @Override
   2636             public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException {
   2637                 postToHandler(MessageHandler.MSG_PREPARE_URI, uri, extras);
   2638             }
   2639 
   2640             @Override
   2641             public void play() throws RemoteException {
   2642                 postToHandler(MessageHandler.MSG_PLAY);
   2643             }
   2644 
   2645             @Override
   2646             public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
   2647                 postToHandler(MessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
   2648             }
   2649 
   2650             @Override
   2651             public void playFromSearch(String query, Bundle extras) throws RemoteException {
   2652                 postToHandler(MessageHandler.MSG_PLAY_SEARCH, query, extras);
   2653             }
   2654 
   2655             @Override
   2656             public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
   2657                 postToHandler(MessageHandler.MSG_PLAY_URI, uri, extras);
   2658             }
   2659 
   2660             @Override
   2661             public void skipToQueueItem(long id) {
   2662                 postToHandler(MessageHandler.MSG_SKIP_TO_ITEM, id);
   2663             }
   2664 
   2665             @Override
   2666             public void pause() throws RemoteException {
   2667                 postToHandler(MessageHandler.MSG_PAUSE);
   2668             }
   2669 
   2670             @Override
   2671             public void stop() throws RemoteException {
   2672                 postToHandler(MessageHandler.MSG_STOP);
   2673             }
   2674 
   2675             @Override
   2676             public void next() throws RemoteException {
   2677                 postToHandler(MessageHandler.MSG_NEXT);
   2678             }
   2679 
   2680             @Override
   2681             public void previous() throws RemoteException {
   2682                 postToHandler(MessageHandler.MSG_PREVIOUS);
   2683             }
   2684 
   2685             @Override
   2686             public void fastForward() throws RemoteException {
   2687                 postToHandler(MessageHandler.MSG_FAST_FORWARD);
   2688             }
   2689 
   2690             @Override
   2691             public void rewind() throws RemoteException {
   2692                 postToHandler(MessageHandler.MSG_REWIND);
   2693             }
   2694 
   2695             @Override
   2696             public void seekTo(long pos) throws RemoteException {
   2697                 postToHandler(MessageHandler.MSG_SEEK_TO, pos);
   2698             }
   2699 
   2700             @Override
   2701             public void rate(RatingCompat rating) throws RemoteException {
   2702                 postToHandler(MessageHandler.MSG_RATE, rating);
   2703             }
   2704 
   2705             @Override
   2706             public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
   2707                 postToHandler(MessageHandler.MSG_RATE_EXTRA, rating, extras);
   2708             }
   2709 
   2710             @Override
   2711             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
   2712                 postToHandler(MessageHandler.MSG_SET_CAPTIONING_ENABLED, enabled);
   2713             }
   2714 
   2715             @Override
   2716             public void setRepeatMode(int repeatMode) throws RemoteException {
   2717                 postToHandler(MessageHandler.MSG_SET_REPEAT_MODE, repeatMode);
   2718             }
   2719 
   2720             @Override
   2721             public void setShuffleModeEnabledRemoved(boolean enabled) throws RemoteException {
   2722                 // Do nothing.
   2723             }
   2724 
   2725             @Override
   2726             public void setShuffleMode(int shuffleMode) throws RemoteException {
   2727                 postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE, shuffleMode);
   2728             }
   2729 
   2730             @Override
   2731             public void sendCustomAction(String action, Bundle args)
   2732                     throws RemoteException {
   2733                 postToHandler(MessageHandler.MSG_CUSTOM_ACTION, action, args);
   2734             }
   2735 
   2736             @Override
   2737             public MediaMetadataCompat getMetadata() {
   2738                 return mMetadata;
   2739             }
   2740 
   2741             @Override
   2742             public PlaybackStateCompat getPlaybackState() {
   2743                 PlaybackStateCompat state;
   2744                 MediaMetadataCompat metadata;
   2745                 synchronized (mLock) {
   2746                     state = mState;
   2747                     metadata = mMetadata;
   2748                 }
   2749                 return getStateWithUpdatedPosition(state, metadata);
   2750             }
   2751 
   2752             @Override
   2753             public List<QueueItem> getQueue() {
   2754                 synchronized (mLock) {
   2755                     return mQueue;
   2756                 }
   2757             }
   2758 
   2759             @Override
   2760             public void addQueueItem(MediaDescriptionCompat description) {
   2761                 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM, description);
   2762             }
   2763 
   2764             @Override
   2765             public void addQueueItemAt(MediaDescriptionCompat description, int index) {
   2766                 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index);
   2767             }
   2768 
   2769             @Override
   2770             public void removeQueueItem(MediaDescriptionCompat description) {
   2771                 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM, description);
   2772             }
   2773 
   2774             @Override
   2775             public void removeQueueItemAt(int index) {
   2776                 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index);
   2777             }
   2778 
   2779             @Override
   2780             public CharSequence getQueueTitle() {
   2781                 return mQueueTitle;
   2782             }
   2783 
   2784             @Override
   2785             public Bundle getExtras() {
   2786                 synchronized (mLock) {
   2787                     return mExtras;
   2788                 }
   2789             }
   2790 
   2791             @Override
   2792             @RatingCompat.Style
   2793             public int getRatingType() {
   2794                 return mRatingType;
   2795             }
   2796 
   2797             @Override
   2798             public boolean isCaptioningEnabled() {
   2799                 return mCaptioningEnabled;
   2800             }
   2801 
   2802             @Override
   2803             @PlaybackStateCompat.RepeatMode
   2804             public int getRepeatMode() {
   2805                 return mRepeatMode;
   2806             }
   2807 
   2808             @Override
   2809             public boolean isShuffleModeEnabledRemoved() {
   2810                 return false;
   2811             }
   2812 
   2813             @Override
   2814             @PlaybackStateCompat.ShuffleMode
   2815             public int getShuffleMode() {
   2816                 return mShuffleMode;
   2817             }
   2818 
   2819             @Override
   2820             public boolean isTransportControlEnabled() {
   2821                 return (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0;
   2822             }
   2823 
   2824             void postToHandler(int what) {
   2825                 MediaSessionImplBase.this.postToHandler(what, 0, 0, null, null);
   2826             }
   2827 
   2828             void postToHandler(int what, int arg1) {
   2829                 MediaSessionImplBase.this.postToHandler(what, arg1, 0, null, null);
   2830             }
   2831 
   2832             void postToHandler(int what, Object obj) {
   2833                 MediaSessionImplBase.this.postToHandler(what, 0, 0, obj, null);
   2834             }
   2835 
   2836             void postToHandler(int what, Object obj, int arg1) {
   2837                 MediaSessionImplBase.this.postToHandler(what, arg1, 0, obj, null);
   2838             }
   2839 
   2840             void postToHandler(int what, Object obj, Bundle extras) {
   2841                 MediaSessionImplBase.this.postToHandler(what, 0, 0, obj, extras);
   2842             }
   2843         }
   2844 
   2845         private static final class Command {
   2846             public final String command;
   2847             public final Bundle extras;
   2848             public final ResultReceiver stub;
   2849 
   2850             public Command(String command, Bundle extras, ResultReceiver stub) {
   2851                 this.command = command;
   2852                 this.extras = extras;
   2853                 this.stub = stub;
   2854             }
   2855         }
   2856 
   2857         class MessageHandler extends Handler {
   2858 
   2859             private static final int MSG_COMMAND = 1;
   2860             private static final int MSG_ADJUST_VOLUME = 2;
   2861             private static final int MSG_PREPARE = 3;
   2862             private static final int MSG_PREPARE_MEDIA_ID = 4;
   2863             private static final int MSG_PREPARE_SEARCH = 5;
   2864             private static final int MSG_PREPARE_URI = 6;
   2865             private static final int MSG_PLAY = 7;
   2866             private static final int MSG_PLAY_MEDIA_ID = 8;
   2867             private static final int MSG_PLAY_SEARCH = 9;
   2868             private static final int MSG_PLAY_URI = 10;
   2869             private static final int MSG_SKIP_TO_ITEM = 11;
   2870             private static final int MSG_PAUSE = 12;
   2871             private static final int MSG_STOP = 13;
   2872             private static final int MSG_NEXT = 14;
   2873             private static final int MSG_PREVIOUS = 15;
   2874             private static final int MSG_FAST_FORWARD = 16;
   2875             private static final int MSG_REWIND = 17;
   2876             private static final int MSG_SEEK_TO = 18;
   2877             private static final int MSG_RATE = 19;
   2878             private static final int MSG_RATE_EXTRA = 31;
   2879             private static final int MSG_CUSTOM_ACTION = 20;
   2880             private static final int MSG_MEDIA_BUTTON = 21;
   2881             private static final int MSG_SET_VOLUME = 22;
   2882             private static final int MSG_SET_REPEAT_MODE = 23;
   2883             private static final int MSG_ADD_QUEUE_ITEM = 25;
   2884             private static final int MSG_ADD_QUEUE_ITEM_AT = 26;
   2885             private static final int MSG_REMOVE_QUEUE_ITEM = 27;
   2886             private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
   2887             private static final int MSG_SET_CAPTIONING_ENABLED = 29;
   2888             private static final int MSG_SET_SHUFFLE_MODE = 30;
   2889 
   2890             // KeyEvent constants only available on API 11+
   2891             private static final int KEYCODE_MEDIA_PAUSE = 127;
   2892             private static final int KEYCODE_MEDIA_PLAY = 126;
   2893 
   2894             private RemoteUserInfo mRemoteUserInfo;
   2895 
   2896             public MessageHandler(Looper looper) {
   2897                 super(looper);
   2898             }
   2899 
   2900             @Override
   2901             public void handleMessage(Message msg) {
   2902                 MediaSessionCompat.Callback cb = mCallback;
   2903                 if (cb == null) {
   2904                     return;
   2905                 }
   2906 
   2907                 Bundle data = msg.getData();
   2908                 mRemoteUserInfo = new RemoteUserInfo(data.getString(DATA_CALLING_PACKAGE),
   2909                         data.getInt(DATA_CALLING_PID), data.getInt(DATA_CALLING_UID));
   2910                 data = data.getBundle(DATA_EXTRAS);
   2911 
   2912                 try {
   2913                     switch (msg.what) {
   2914                         case MSG_COMMAND:
   2915                             Command cmd = (Command) msg.obj;
   2916                             cb.onCommand(cmd.command, cmd.extras, cmd.stub);
   2917                             break;
   2918                         case MSG_MEDIA_BUTTON:
   2919                             KeyEvent keyEvent = (KeyEvent) msg.obj;
   2920                             Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   2921                             intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   2922                             // Let the Callback handle events first before using the default
   2923                             // behavior
   2924                             if (!cb.onMediaButtonEvent(intent)) {
   2925                                 onMediaButtonEvent(keyEvent, cb);
   2926                             }
   2927                             break;
   2928                         case MSG_PREPARE:
   2929                             cb.onPrepare();
   2930                             break;
   2931                         case MSG_PREPARE_MEDIA_ID:
   2932                             cb.onPrepareFromMediaId((String) msg.obj, data);
   2933                             break;
   2934                         case MSG_PREPARE_SEARCH:
   2935                             cb.onPrepareFromSearch((String) msg.obj, data);
   2936                             break;
   2937                         case MSG_PREPARE_URI:
   2938                             cb.onPrepareFromUri((Uri) msg.obj, data);
   2939                             break;
   2940                         case MSG_PLAY:
   2941                             cb.onPlay();
   2942                             break;
   2943                         case MSG_PLAY_MEDIA_ID:
   2944                             cb.onPlayFromMediaId((String) msg.obj, data);
   2945                             break;
   2946                         case MSG_PLAY_SEARCH:
   2947                             cb.onPlayFromSearch((String) msg.obj, data);
   2948                             break;
   2949                         case MSG_PLAY_URI:
   2950                             cb.onPlayFromUri((Uri) msg.obj, data);
   2951                             break;
   2952                         case MSG_SKIP_TO_ITEM:
   2953                             cb.onSkipToQueueItem((Long) msg.obj);
   2954                             break;
   2955                         case MSG_PAUSE:
   2956                             cb.onPause();
   2957                             break;
   2958                         case MSG_STOP:
   2959                             cb.onStop();
   2960                             break;
   2961                         case MSG_NEXT:
   2962                             cb.onSkipToNext();
   2963                             break;
   2964                         case MSG_PREVIOUS:
   2965                             cb.onSkipToPrevious();
   2966                             break;
   2967                         case MSG_FAST_FORWARD:
   2968                             cb.onFastForward();
   2969                             break;
   2970                         case MSG_REWIND:
   2971                             cb.onRewind();
   2972                             break;
   2973                         case MSG_SEEK_TO:
   2974                             cb.onSeekTo((Long) msg.obj);
   2975                             break;
   2976                         case MSG_RATE:
   2977                             cb.onSetRating((RatingCompat) msg.obj);
   2978                             break;
   2979                         case MSG_RATE_EXTRA:
   2980                             cb.onSetRating((RatingCompat) msg.obj, data);
   2981                             break;
   2982                         case MSG_CUSTOM_ACTION:
   2983                             cb.onCustomAction((String) msg.obj, data);
   2984                             break;
   2985                         case MSG_ADD_QUEUE_ITEM:
   2986                             cb.onAddQueueItem((MediaDescriptionCompat) msg.obj);
   2987                             break;
   2988                         case MSG_ADD_QUEUE_ITEM_AT:
   2989                             cb.onAddQueueItem((MediaDescriptionCompat) msg.obj, msg.arg1);
   2990                             break;
   2991                         case MSG_REMOVE_QUEUE_ITEM:
   2992                             cb.onRemoveQueueItem((MediaDescriptionCompat) msg.obj);
   2993                             break;
   2994                         case MSG_REMOVE_QUEUE_ITEM_AT:
   2995                             if (mQueue != null) {
   2996                                 QueueItem item = (msg.arg1 >= 0 && msg.arg1 < mQueue.size())
   2997                                         ? mQueue.get(msg.arg1) : null;
   2998                                 if (item != null) {
   2999                                     cb.onRemoveQueueItem(item.getDescription());
   3000                                 }
   3001                             }
   3002                             break;
   3003                         case MSG_ADJUST_VOLUME:
   3004                             adjustVolume(msg.arg1, 0);
   3005                             break;
   3006                         case MSG_SET_VOLUME:
   3007                             setVolumeTo(msg.arg1, 0);
   3008                             break;
   3009                         case MSG_SET_CAPTIONING_ENABLED:
   3010                             cb.onSetCaptioningEnabled((boolean) msg.obj);
   3011                             break;
   3012                         case MSG_SET_REPEAT_MODE:
   3013                             cb.onSetRepeatMode(msg.arg1);
   3014                             break;
   3015                         case MSG_SET_SHUFFLE_MODE:
   3016                             cb.onSetShuffleMode(msg.arg1);
   3017                             break;
   3018                     }
   3019                 } finally {
   3020                     mRemoteUserInfo = null;
   3021                 }
   3022             }
   3023 
   3024             private void onMediaButtonEvent(KeyEvent ke, MediaSessionCompat.Callback cb) {
   3025                 if (ke == null || ke.getAction() != KeyEvent.ACTION_DOWN) {
   3026                     return;
   3027                 }
   3028                 long validActions = mState == null ? 0 : mState.getActions();
   3029                 switch (ke.getKeyCode()) {
   3030                     // Note KeyEvent.KEYCODE_MEDIA_PLAY is API 11+
   3031                     case KEYCODE_MEDIA_PLAY:
   3032                         if ((validActions & PlaybackStateCompat.ACTION_PLAY) != 0) {
   3033                             cb.onPlay();
   3034                         }
   3035                         break;
   3036                     // Note KeyEvent.KEYCODE_MEDIA_PAUSE is API 11+
   3037                     case KEYCODE_MEDIA_PAUSE:
   3038                         if ((validActions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
   3039                             cb.onPause();
   3040                         }
   3041                         break;
   3042                     case KeyEvent.KEYCODE_MEDIA_NEXT:
   3043                         if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
   3044                             cb.onSkipToNext();
   3045                         }
   3046                         break;
   3047                     case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
   3048                         if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
   3049                             cb.onSkipToPrevious();
   3050                         }
   3051                         break;
   3052                     case KeyEvent.KEYCODE_MEDIA_STOP:
   3053                         if ((validActions & PlaybackStateCompat.ACTION_STOP) != 0) {
   3054                             cb.onStop();
   3055                         }
   3056                         break;
   3057                     case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
   3058                         if ((validActions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
   3059                             cb.onFastForward();
   3060                         }
   3061                         break;
   3062                     case KeyEvent.KEYCODE_MEDIA_REWIND:
   3063                         if ((validActions & PlaybackStateCompat.ACTION_REWIND) != 0) {
   3064                             cb.onRewind();
   3065                         }
   3066                         break;
   3067                     case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
   3068                     case KeyEvent.KEYCODE_HEADSETHOOK:
   3069                         Log.w(TAG, "KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK are handled"
   3070                                 + " already");
   3071                         break;
   3072                 }
   3073             }
   3074 
   3075             RemoteUserInfo getRemoteUserInfo() {
   3076                 return mRemoteUserInfo;
   3077             }
   3078         }
   3079     }
   3080 
   3081     @RequiresApi(18)
   3082     static class MediaSessionImplApi18 extends MediaSessionImplBase {
   3083         private static boolean sIsMbrPendingIntentSupported = true;
   3084 
   3085         MediaSessionImplApi18(Context context, String tag, ComponentName mbrComponent,
   3086                 PendingIntent mbrIntent) {
   3087             super(context, tag, mbrComponent, mbrIntent);
   3088         }
   3089 
   3090         @Override
   3091         public void setCallback(Callback callback, Handler handler) {
   3092             super.setCallback(callback, handler);
   3093             if (callback == null) {
   3094                 mRcc.setPlaybackPositionUpdateListener(null);
   3095             } else {
   3096                 RemoteControlClient.OnPlaybackPositionUpdateListener listener =
   3097                         new RemoteControlClient.OnPlaybackPositionUpdateListener() {
   3098                             @Override
   3099                             public void onPlaybackPositionUpdate(long newPositionMs) {
   3100                                 postToHandler(
   3101                                         MessageHandler.MSG_SEEK_TO, -1, -1, newPositionMs, null);
   3102                             }
   3103                         };
   3104                 mRcc.setPlaybackPositionUpdateListener(listener);
   3105             }
   3106         }
   3107 
   3108         @Override
   3109         void setRccState(PlaybackStateCompat state) {
   3110             long position = state.getPosition();
   3111             float speed = state.getPlaybackSpeed();
   3112             long updateTime = state.getLastPositionUpdateTime();
   3113             long currTime = SystemClock.elapsedRealtime();
   3114             if (state.getState() == PlaybackStateCompat.STATE_PLAYING && position > 0) {
   3115                 long diff = 0;
   3116                 if (updateTime > 0) {
   3117                     diff = currTime - updateTime;
   3118                     if (speed > 0 && speed != 1f) {
   3119                         diff = (long) (diff * speed);
   3120                     }
   3121                 }
   3122                 position += diff;
   3123             }
   3124             mRcc.setPlaybackState(getRccStateFromState(state.getState()), position, speed);
   3125         }
   3126 
   3127         @Override
   3128         int getRccTransportControlFlagsFromActions(long actions) {
   3129             int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions);
   3130             if ((actions & PlaybackStateCompat.ACTION_SEEK_TO) != 0) {
   3131                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
   3132             }
   3133             return transportControlFlags;
   3134         }
   3135 
   3136         @Override
   3137         void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) {
   3138             // Some Android implementations are not able to register a media button event receiver
   3139             // using a PendingIntent but need a ComponentName instead. These will raise a
   3140             // NullPointerException.
   3141             if (sIsMbrPendingIntentSupported) {
   3142                 try {
   3143                     mAudioManager.registerMediaButtonEventReceiver(mbrIntent);
   3144                 } catch (NullPointerException e) {
   3145                     Log.w(TAG, "Unable to register media button event receiver with "
   3146                             + "PendingIntent, falling back to ComponentName.");
   3147                     sIsMbrPendingIntentSupported = false;
   3148                 }
   3149             }
   3150 
   3151             if (!sIsMbrPendingIntentSupported) {
   3152                 super.registerMediaButtonEventReceiver(mbrIntent, mbrComponent);
   3153             }
   3154         }
   3155 
   3156         @Override
   3157         void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent,
   3158                 ComponentName mbrComponent) {
   3159             if (sIsMbrPendingIntentSupported) {
   3160                 mAudioManager.unregisterMediaButtonEventReceiver(mbrIntent);
   3161             } else {
   3162                 super.unregisterMediaButtonEventReceiver(mbrIntent, mbrComponent);
   3163             }
   3164         }
   3165     }
   3166 
   3167     @RequiresApi(19)
   3168     static class MediaSessionImplApi19 extends MediaSessionImplApi18 {
   3169         MediaSessionImplApi19(Context context, String tag, ComponentName mbrComponent,
   3170                 PendingIntent mbrIntent) {
   3171             super(context, tag, mbrComponent, mbrIntent);
   3172         }
   3173 
   3174         @Override
   3175         public void setCallback(Callback callback, Handler handler) {
   3176             super.setCallback(callback, handler);
   3177             if (callback == null) {
   3178                 mRcc.setMetadataUpdateListener(null);
   3179             } else {
   3180                 RemoteControlClient.OnMetadataUpdateListener listener =
   3181                         new RemoteControlClient.OnMetadataUpdateListener() {
   3182                             @Override
   3183                             public void onMetadataUpdate(int key, Object newValue) {
   3184                                 if (key == MediaMetadataEditor.RATING_KEY_BY_USER
   3185                                         && newValue instanceof Rating) {
   3186                                     postToHandler(MessageHandler.MSG_RATE, -1, -1,
   3187                                             RatingCompat.fromRating(newValue), null);
   3188                                 }
   3189                             }
   3190                         };
   3191                 mRcc.setMetadataUpdateListener(listener);
   3192             }
   3193         }
   3194 
   3195         @Override
   3196         int getRccTransportControlFlagsFromActions(long actions) {
   3197             int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions);
   3198             if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) {
   3199                 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_RATING;
   3200             }
   3201             return transportControlFlags;
   3202         }
   3203 
   3204         @Override
   3205         RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
   3206             RemoteControlClient.MetadataEditor editor = super.buildRccMetadata(metadata);
   3207             long actions = mState == null ? 0 : mState.getActions();
   3208             if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) {
   3209                 editor.addEditableKey(RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER);
   3210             }
   3211 
   3212             if (metadata == null) {
   3213                 return editor;
   3214             }
   3215             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) {
   3216                 editor.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR,
   3217                         metadata.getLong(MediaMetadataCompat.METADATA_KEY_YEAR));
   3218             }
   3219             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_RATING)) {
   3220                 // Do not remove casting here. Without this, a crash will happen in API 19.
   3221                 ((MediaMetadataEditor) editor).putObject(MediaMetadataEditor.RATING_KEY_BY_OTHERS,
   3222                         metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_RATING));
   3223             }
   3224             if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_USER_RATING)) {
   3225                 // Do not remove casting here. Without this, a crash will happen in API 19.
   3226                 ((MediaMetadataEditor) editor).putObject(MediaMetadataEditor.RATING_KEY_BY_USER,
   3227                         metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_USER_RATING));
   3228             }
   3229             return editor;
   3230         }
   3231     }
   3232 
   3233     @RequiresApi(21)
   3234     static class MediaSessionImplApi21 implements MediaSessionImpl {
   3235         private final Object mSessionObj;
   3236         private final Token mToken;
   3237 
   3238         private boolean mDestroyed = false;
   3239         private final RemoteCallbackList<IMediaControllerCallback> mExtraControllerCallbacks =
   3240                 new RemoteCallbackList<>();
   3241 
   3242         private PlaybackStateCompat mPlaybackState;
   3243         private List<QueueItem> mQueue;
   3244         private MediaMetadataCompat mMetadata;
   3245         @RatingCompat.Style int mRatingType;
   3246         boolean mCaptioningEnabled;
   3247         @PlaybackStateCompat.RepeatMode int mRepeatMode;
   3248         @PlaybackStateCompat.ShuffleMode int mShuffleMode;
   3249 
   3250         public MediaSessionImplApi21(Context context, String tag) {
   3251             mSessionObj = MediaSessionCompatApi21.createSession(context, tag);
   3252             mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj),
   3253                     new ExtraSession());
   3254         }
   3255 
   3256         public MediaSessionImplApi21(Object mediaSession) {
   3257             mSessionObj = MediaSessionCompatApi21.verifySession(mediaSession);
   3258             mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj),
   3259                     new ExtraSession());
   3260         }
   3261 
   3262         @Override
   3263         public void setCallback(Callback callback, Handler handler) {
   3264             MediaSessionCompatApi21.setCallback(mSessionObj,
   3265                     callback == null ? null : callback.mCallbackObj, handler);
   3266             if (callback != null) {
   3267                 callback.setSessionImpl(this, handler);
   3268             }
   3269         }
   3270 
   3271         @Override
   3272         public void setFlags(@SessionFlags int flags) {
   3273             MediaSessionCompatApi21.setFlags(mSessionObj, flags);
   3274         }
   3275 
   3276         @Override
   3277         public void setPlaybackToLocal(int stream) {
   3278             MediaSessionCompatApi21.setPlaybackToLocal(mSessionObj, stream);
   3279         }
   3280 
   3281         @Override
   3282         public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) {
   3283             MediaSessionCompatApi21.setPlaybackToRemote(mSessionObj,
   3284                     volumeProvider.getVolumeProvider());
   3285         }
   3286 
   3287         @Override
   3288         public void setActive(boolean active) {
   3289             MediaSessionCompatApi21.setActive(mSessionObj, active);
   3290         }
   3291 
   3292         @Override
   3293         public boolean isActive() {
   3294             return MediaSessionCompatApi21.isActive(mSessionObj);
   3295         }
   3296 
   3297         @Override
   3298         public void sendSessionEvent(String event, Bundle extras) {
   3299             if (android.os.Build.VERSION.SDK_INT < 23) {
   3300                 int size = mExtraControllerCallbacks.beginBroadcast();
   3301                 for (int i = size - 1; i >= 0; i--) {
   3302                     IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
   3303                     try {
   3304                         cb.onEvent(event, extras);
   3305                     } catch (RemoteException e) {
   3306                     }
   3307                 }
   3308                 mExtraControllerCallbacks.finishBroadcast();
   3309             }
   3310             MediaSessionCompatApi21.sendSessionEvent(mSessionObj, event, extras);
   3311         }
   3312 
   3313         @Override
   3314         public void release() {
   3315             mDestroyed = true;
   3316             MediaSessionCompatApi21.release(mSessionObj);
   3317         }
   3318 
   3319         @Override
   3320         public Token getSessionToken() {
   3321             return mToken;
   3322         }
   3323 
   3324         @Override
   3325         public void setPlaybackState(PlaybackStateCompat state) {
   3326             mPlaybackState = state;
   3327             int size = mExtraControllerCallbacks.beginBroadcast();
   3328             for (int i = size - 1; i >= 0; i--) {
   3329                 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
   3330                 try {
   3331                     cb.onPlaybackStateChanged(state);
   3332                 } catch (RemoteException e) {
   3333                 }
   3334             }
   3335             mExtraControllerCallbacks.finishBroadcast();
   3336             MediaSessionCompatApi21.setPlaybackState(mSessionObj,
   3337                     state == null ? null : state.getPlaybackState());
   3338         }
   3339 
   3340         @Override
   3341         public PlaybackStateCompat getPlaybackState() {
   3342             return mPlaybackState;
   3343         }
   3344 
   3345         @Override
   3346         public void setMetadata(MediaMetadataCompat metadata) {
   3347             mMetadata = metadata;
   3348             MediaSessionCompatApi21.setMetadata(mSessionObj,
   3349                     metadata == null ? null : metadata.getMediaMetadata());
   3350         }
   3351 
   3352         @Override
   3353         public void setSessionActivity(PendingIntent pi) {
   3354             MediaSessionCompatApi21.setSessionActivity(mSessionObj, pi);
   3355         }
   3356 
   3357         @Override
   3358         public void setMediaButtonReceiver(PendingIntent mbr) {
   3359             MediaSessionCompatApi21.setMediaButtonReceiver(mSessionObj, mbr);
   3360         }
   3361 
   3362         @Override
   3363         public void setQueue(List<QueueItem> queue) {
   3364             mQueue = queue;
   3365             List<Object> queueObjs = null;
   3366             if (queue != null) {
   3367                 queueObjs = new ArrayList<>();
   3368                 for (QueueItem item : queue) {
   3369                     queueObjs.add(item.getQueueItem());
   3370                 }
   3371             }
   3372             MediaSessionCompatApi21.setQueue(mSessionObj, queueObjs);
   3373         }
   3374 
   3375         @Override
   3376         public void setQueueTitle(CharSequence title) {
   3377             MediaSessionCompatApi21.setQueueTitle(mSessionObj, title);
   3378         }
   3379 
   3380         @Override
   3381         public void setRatingType(@RatingCompat.Style int type) {
   3382             if (android.os.Build.VERSION.SDK_INT < 22) {
   3383                 mRatingType = type;
   3384             } else {
   3385                 MediaSessionCompatApi22.setRatingType(mSessionObj, type);
   3386             }
   3387         }
   3388 
   3389         @Override
   3390         public void setCaptioningEnabled(boolean enabled) {
   3391             if (mCaptioningEnabled != enabled) {
   3392                 mCaptioningEnabled = enabled;
   3393                 int size = mExtraControllerCallbacks.beginBroadcast();
   3394                 for (int i = size - 1; i >= 0; i--) {
   3395                     IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
   3396                     try {
   3397                         cb.onCaptioningEnabledChanged(enabled);
   3398                     } catch (RemoteException e) {
   3399                     }
   3400                 }
   3401                 mExtraControllerCallbacks.finishBroadcast();
   3402             }
   3403         }
   3404 
   3405         @Override
   3406         public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
   3407             if (mRepeatMode != repeatMode) {
   3408                 mRepeatMode = repeatMode;
   3409                 int size = mExtraControllerCallbacks.beginBroadcast();
   3410                 for (int i = size - 1; i >= 0; i--) {
   3411                     IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
   3412                     try {
   3413                         cb.onRepeatModeChanged(repeatMode);
   3414                     } catch (RemoteException e) {
   3415                     }
   3416                 }
   3417                 mExtraControllerCallbacks.finishBroadcast();
   3418             }
   3419         }
   3420 
   3421         @Override
   3422         public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
   3423             if (mShuffleMode != shuffleMode) {
   3424                 mShuffleMode = shuffleMode;
   3425                 int size = mExtraControllerCallbacks.beginBroadcast();
   3426                 for (int i = size - 1; i >= 0; i--) {
   3427                     IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
   3428                     try {
   3429                         cb.onShuffleModeChanged(shuffleMode);
   3430                     } catch (RemoteException e) {
   3431                     }
   3432                 }
   3433                 mExtraControllerCallbacks.finishBroadcast();
   3434             }
   3435         }
   3436 
   3437         @Override
   3438         public void setExtras(Bundle extras) {
   3439             MediaSessionCompatApi21.setExtras(mSessionObj, extras);
   3440         }
   3441 
   3442         @Override
   3443         public Object getMediaSession() {
   3444             return mSessionObj;
   3445         }
   3446 
   3447         @Override
   3448         public Object getRemoteControlClient() {
   3449             return null;
   3450         }
   3451 
   3452         @Override
   3453         public String getCallingPackage() {
   3454             if (android.os.Build.VERSION.SDK_INT < 24) {
   3455                 return null;
   3456             } else {
   3457                 return MediaSessionCompatApi24.getCallingPackage(mSessionObj);
   3458             }
   3459         }
   3460 
   3461         @Override
   3462         public RemoteUserInfo getCurrentControllerInfo() {
   3463             return null;
   3464         }
   3465 
   3466         class ExtraSession extends IMediaSession.Stub {
   3467             @Override
   3468             public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) {
   3469                 // Will not be called.
   3470                 throw new AssertionError();
   3471             }
   3472 
   3473             @Override
   3474             public boolean sendMediaButton(KeyEvent mediaButton) {
   3475                 // Will not be called.
   3476                 throw new AssertionError();
   3477             }
   3478 
   3479             @Override
   3480             public void registerCallbackListener(IMediaControllerCallback cb) {
   3481                 if (!mDestroyed) {
   3482                     mExtraControllerCallbacks.register(cb);
   3483                 }
   3484             }
   3485 
   3486             @Override
   3487             public void unregisterCallbackListener(IMediaControllerCallback cb) {
   3488                 mExtraControllerCallbacks.unregister(cb);
   3489             }
   3490 
   3491             @Override
   3492             public String getPackageName() {
   3493                 // Will not be called.
   3494                 throw new AssertionError();
   3495             }
   3496 
   3497             @Override
   3498             public String getTag() {
   3499                 // Will not be called.
   3500                 throw new AssertionError();
   3501             }
   3502 
   3503             @Override
   3504             public PendingIntent getLaunchPendingIntent() {
   3505                 // Will not be called.
   3506                 throw new AssertionError();
   3507             }
   3508 
   3509             @Override
   3510             @SessionFlags
   3511             public long getFlags() {
   3512                 // Will not be called.
   3513                 throw new AssertionError();
   3514             }
   3515 
   3516             @Override
   3517             public ParcelableVolumeInfo getVolumeAttributes() {
   3518                 // Will not be called.
   3519                 throw new AssertionError();
   3520             }
   3521 
   3522             @Override
   3523             public void adjustVolume(int direction, int flags, String packageName) {
   3524                 // Will not be called.
   3525                 throw new AssertionError();
   3526             }
   3527 
   3528             @Override
   3529             public void setVolumeTo(int value, int flags, String packageName) {
   3530                 // Will not be called.
   3531                 throw new AssertionError();
   3532             }
   3533 
   3534             @Override
   3535             public void prepare() throws RemoteException {
   3536                 // Will not be called.
   3537                 throw new AssertionError();
   3538             }
   3539 
   3540             @Override
   3541             public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException {
   3542                 // Will not be called.
   3543                 throw new AssertionError();
   3544             }
   3545 
   3546             @Override
   3547             public void prepareFromSearch(String query, Bundle extras) throws RemoteException {
   3548                 // Will not be called.
   3549                 throw new AssertionError();
   3550             }
   3551 
   3552             @Override
   3553             public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException {
   3554                 // Will not be called.
   3555                 throw new AssertionError();
   3556             }
   3557 
   3558             @Override
   3559             public void play() throws RemoteException {
   3560                 // Will not be called.
   3561                 throw new AssertionError();
   3562             }
   3563 
   3564             @Override
   3565             public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
   3566                 // Will not be called.
   3567                 throw new AssertionError();
   3568             }
   3569 
   3570             @Override
   3571             public void playFromSearch(String query, Bundle extras) throws RemoteException {
   3572                 // Will not be called.
   3573                 throw new AssertionError();
   3574             }
   3575 
   3576             @Override
   3577             public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
   3578                 // Will not be called.
   3579                 throw new AssertionError();
   3580             }
   3581 
   3582             @Override
   3583             public void skipToQueueItem(long id) {
   3584                 // Will not be called.
   3585                 throw new AssertionError();
   3586             }
   3587 
   3588             @Override
   3589             public void pause() throws RemoteException {
   3590                 // Will not be called.
   3591                 throw new AssertionError();
   3592             }
   3593 
   3594             @Override
   3595             public void stop() throws RemoteException {
   3596                 // Will not be called.
   3597                 throw new AssertionError();
   3598             }
   3599 
   3600             @Override
   3601             public void next() throws RemoteException {
   3602                 // Will not be called.
   3603                 throw new AssertionError();
   3604             }
   3605 
   3606             @Override
   3607             public void previous() throws RemoteException {
   3608                 // Will not be called.
   3609                 throw new AssertionError();
   3610             }
   3611 
   3612             @Override
   3613             public void fastForward() throws RemoteException {
   3614                 // Will not be called.
   3615                 throw new AssertionError();
   3616             }
   3617 
   3618             @Override
   3619             public void rewind() throws RemoteException {
   3620                 // Will not be called.
   3621                 throw new AssertionError();
   3622             }
   3623 
   3624             @Override
   3625             public void seekTo(long pos) throws RemoteException {
   3626                 // Will not be called.
   3627                 throw new AssertionError();
   3628             }
   3629 
   3630             @Override
   3631             public void rate(RatingCompat rating) throws RemoteException {
   3632                 // Will not be called.
   3633                 throw new AssertionError();
   3634             }
   3635 
   3636             @Override
   3637             public void rateWithExtras(RatingCompat rating, Bundle extras) throws RemoteException {
   3638                 // Will not be called.
   3639                 throw new AssertionError();
   3640             }
   3641 
   3642             @Override
   3643             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
   3644                 // Will not be called.
   3645                 throw new AssertionError();
   3646             }
   3647 
   3648             @Override
   3649             public void setRepeatMode(int repeatMode) throws RemoteException {
   3650                 // Will not be called.
   3651                 throw new AssertionError();
   3652             }
   3653 
   3654             @Override
   3655             public void setShuffleModeEnabledRemoved(boolean enabled) throws RemoteException {
   3656                 // Do nothing.
   3657             }
   3658 
   3659             @Override
   3660             public void setShuffleMode(int shuffleMode) throws RemoteException {
   3661                 // Will not be called.
   3662                 throw new AssertionError();
   3663             }
   3664 
   3665             @Override
   3666             public void sendCustomAction(String action, Bundle args) throws RemoteException {
   3667                 // Will not be called.
   3668                 throw new AssertionError();
   3669             }
   3670 
   3671             @Override
   3672             public MediaMetadataCompat getMetadata() {
   3673                 // Will not be called.
   3674                 throw new AssertionError();
   3675             }
   3676 
   3677             @Override
   3678             public PlaybackStateCompat getPlaybackState() {
   3679                 return getStateWithUpdatedPosition(mPlaybackState, mMetadata);
   3680             }
   3681 
   3682             @Override
   3683             public List<QueueItem> getQueue() {
   3684                 // Will not be called.
   3685                 return null;
   3686             }
   3687 
   3688             @Override
   3689             public void addQueueItem(MediaDescriptionCompat descriptionCompat) {
   3690                 // Will not be called.
   3691                 throw new AssertionError();
   3692             }
   3693 
   3694             @Override
   3695             public void addQueueItemAt(MediaDescriptionCompat descriptionCompat, int index) {
   3696                 // Will not be called.
   3697                 throw new AssertionError();
   3698             }
   3699 
   3700             @Override
   3701             public void removeQueueItem(MediaDescriptionCompat description) {
   3702                 // Will not be called.
   3703                 throw new AssertionError();
   3704             }
   3705 
   3706             @Override
   3707             public void removeQueueItemAt(int index) {
   3708                 // Will not be called.
   3709                 throw new AssertionError();
   3710             }
   3711 
   3712             @Override
   3713             public CharSequence getQueueTitle() {
   3714                 // Will not be called.
   3715                 throw new AssertionError();
   3716             }
   3717 
   3718             @Override
   3719             public Bundle getExtras() {
   3720                 // Will not be called.
   3721                 throw new AssertionError();
   3722             }
   3723 
   3724             @Override
   3725             @RatingCompat.Style
   3726             public int getRatingType() {
   3727                 return mRatingType;
   3728             }
   3729 
   3730             @Override
   3731             public boolean isCaptioningEnabled() {
   3732                 return mCaptioningEnabled;
   3733             }
   3734 
   3735             @Override
   3736             @PlaybackStateCompat.RepeatMode
   3737             public int getRepeatMode() {
   3738                 return mRepeatMode;
   3739             }
   3740 
   3741             @Override
   3742             public boolean isShuffleModeEnabledRemoved() {
   3743                 return false;
   3744             }
   3745 
   3746             @Override
   3747             @PlaybackStateCompat.ShuffleMode
   3748             public int getShuffleMode() {
   3749                 return mShuffleMode;
   3750             }
   3751 
   3752             @Override
   3753             public boolean isTransportControlEnabled() {
   3754                 // Will not be called.
   3755                 throw new AssertionError();
   3756             }
   3757         }
   3758     }
   3759 
   3760     @RequiresApi(28)
   3761     static class MediaSessionImplApi28 extends MediaSessionImplApi21 {
   3762         private MediaSession mSession;
   3763 
   3764         MediaSessionImplApi28(Context context, String tag) {
   3765             super(context, tag);
   3766         }
   3767 
   3768         MediaSessionImplApi28(Object mediaSession) {
   3769             super(mediaSession);
   3770             mSession = (MediaSession) mediaSession;
   3771         }
   3772 
   3773         @Override
   3774         public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
   3775             android.media.session.MediaSessionManager.RemoteUserInfo info =
   3776                     mSession.getCurrentControllerInfo();
   3777             return new RemoteUserInfo(info.getPackageName(), info.getPid(), info.getUid());
   3778         }
   3779     }
   3780 }
   3781