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