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