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