MediaController.java revision d2b8c947ddfc6349a3ae6c3968b422b9cf50d7ed
101fe661ae5da3739215d93922412df4b24c859a2RoboErik/*
201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project
301fe661ae5da3739215d93922412df4b24c859a2RoboErik *
401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License");
501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License.
601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at
701fe661ae5da3739215d93922412df4b24c859a2RoboErik *
801fe661ae5da3739215d93922412df4b24c859a2RoboErik *      http://www.apache.org/licenses/LICENSE-2.0
901fe661ae5da3739215d93922412df4b24c859a2RoboErik *
1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software
1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS,
1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and
1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License.
1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
1601fe661ae5da3739215d93922412df4b24c859a2RoboErik
172f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikpackage android.media.session;
1801fe661ae5da3739215d93922412df4b24c859a2RoboErik
19bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.NonNull;
20bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.Nullable;
21e34c09daf89fb888fe2638e71758573462d85173RoboErikimport android.app.PendingIntent;
22031149cd5f22bd858142633c7a763450f42793f7RoboErikimport android.content.Context;
23f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.content.pm.ParceledListSlice;
249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErikimport android.media.AudioAttributes;
2519c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.AudioManager;
2642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
27c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErikimport android.media.Rating;
28ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErikimport android.media.VolumeProvider;
29f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.net.Uri;
3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
3201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
3401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
3601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
3701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
3901fe661ae5da3739215d93922412df4b24c859a2RoboErik
408ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
4101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
42f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport java.util.List;
4301fe661ae5da3739215d93922412df4b24c859a2RoboErik
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
4501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
4801fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
5001fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
51dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown * you have a {@link MediaSession.Token} from the session owner.
5201fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
5301fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
5401fe661ae5da3739215d93922412df4b24c859a2RoboErik */
5542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
56bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    private static final String TAG = "MediaController";
5701fe661ae5da3739215d93922412df4b24c859a2RoboErik
588ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
59c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
60c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
6101a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown    private static final int MSG_UPDATE_VOLUME = 4;
62f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE = 5;
63f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE_TITLE = 6;
64f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_EXTRAS = 7;
6524762bffc3358762666079cd802040a316b3260dRoboErik    private static final int MSG_DESTROYED = 8;
6601fe661ae5da3739215d93922412df4b24c859a2RoboErik
6707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
6801fe661ae5da3739215d93922412df4b24c859a2RoboErik
6976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    private final MediaSession.Token mToken;
70031149cd5f22bd858142633c7a763450f42793f7RoboErik    private final Context mContext;
718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
7301fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
7401fe661ae5da3739215d93922412df4b24c859a2RoboErik
7501fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
76aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mPackageName;
77aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mTag;
7801fe661ae5da3739215d93922412df4b24c859a2RoboErik
791a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    private final TransportControls mTransportControls;
808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
8101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
828b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Call for creating a MediaController directly from a binder. Should only
838b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * be used by framework code.
848b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     *
858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
8601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
87031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(Context context, ISessionController sessionBinder) {
888b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        if (sessionBinder == null) {
898b4bffcac996b4083e720310a09d315ca1c4a000RoboErik            throw new IllegalArgumentException("Session token cannot be null");
908b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        }
91031149cd5f22bd858142633c7a763450f42793f7RoboErik        if (context == null) {
92031149cd5f22bd858142633c7a763450f42793f7RoboErik            throw new IllegalArgumentException("Context cannot be null");
93031149cd5f22bd858142633c7a763450f42793f7RoboErik        }
948b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mSessionBinder = sessionBinder;
958b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mTransportControls = new TransportControls();
9676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        mToken = new MediaSession.Token(sessionBinder);
97031149cd5f22bd858142633c7a763450f42793f7RoboErik        mContext = context;
9801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
9901fe661ae5da3739215d93922412df4b24c859a2RoboErik
10001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1018b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Create a new MediaController from a session's token.
1028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
103031149cd5f22bd858142633c7a763450f42793f7RoboErik     * @param context The caller's context.
1048b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * @param token The token for the session.
10501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
106031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
107031149cd5f22bd858142633c7a763450f42793f7RoboErik        this(context, token.getBinder());
10801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
10901fe661ae5da3739215d93922412df4b24c859a2RoboErik
11001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1111a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Get a {@link TransportControls} instance to send transport actions to
1121a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * the associated session.
11301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
1141a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return A transport controls instance.
11501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
116bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @NonNull TransportControls getTransportControls() {
1171a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return mTransportControls;
1181a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    }
1191a937b04e63539cb1fab1bde601031d415c7156fJeff Brown
1201a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    /**
12179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
12279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
12301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
12479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
12579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
12601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
127bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
12879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
12979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
13079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
13179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
13279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
13301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
13401fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
13579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
13601fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
137c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
13801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
13979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
14001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
14101fe661ae5da3739215d93922412df4b24c859a2RoboErik
14201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
143c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
144c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
145c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
146c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
147bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
148c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
151c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
152c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
153c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
154c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
161bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
171477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * Get the current play queue for this session if one is set. If you only
172477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * care about the current item {@link #getMetadata()} should be used.
173f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     *
174f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * @return The current play queue or null.
175f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     */
176a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik    public @Nullable List<MediaSession.Item> getQueue() {
177f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        try {
178f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            ParceledListSlice queue = mSessionBinder.getQueue();
179f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (queue != null) {
180f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                return queue.getList();
181f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
182f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        } catch (RemoteException e) {
183f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            Log.wtf(TAG, "Error calling getQueue.", e);
184f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
185f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        return null;
186f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    }
187f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
188f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    /**
18951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the queue title for this session.
19051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
19151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable CharSequence getQueueTitle() {
19251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
19351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getQueueTitle();
19451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
19551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getQueueTitle", e);
19651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
19751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
19851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
19951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
20051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
20151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the extras for this session.
20251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
20351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable Bundle getExtras() {
20451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
20551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getExtras();
20651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
20751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getExtras", e);
20851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
20951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
21051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
21151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
21251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
213c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
214c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
215c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
216c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
217c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
218c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
219c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
220c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
221c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
222c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
223c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
224c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
225c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
226c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
227c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
228c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
229c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
230c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
231c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
232c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
233c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
234c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
235c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
23676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the flags for this session. Flags are defined in {@link MediaSession}.
23773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
23873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
23973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
24076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @MediaSession.SessionFlags long getFlags() {
24173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
24273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
24373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
24473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
24573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
24673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
24773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
24873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
24973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
250d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Get the current playback info for this session.
251ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
252d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @return The current playback info or null.
253ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
254d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PlaybackInfo getPlaybackInfo() {
255ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
256ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
257d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik            return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
258ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
259ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
260ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
261a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            Log.wtf(TAG, "Error calling getAudioInfo.", e);
262ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
263ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
264ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
265ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
266ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
267e34c09daf89fb888fe2638e71758573462d85173RoboErik     * Get an intent for launching UI associated with this session if one
268e34c09daf89fb888fe2638e71758573462d85173RoboErik     * exists.
269e34c09daf89fb888fe2638e71758573462d85173RoboErik     *
270e34c09daf89fb888fe2638e71758573462d85173RoboErik     * @return A {@link PendingIntent} to launch UI or null.
271e34c09daf89fb888fe2638e71758573462d85173RoboErik     */
272d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PendingIntent getSessionActivity() {
273e34c09daf89fb888fe2638e71758573462d85173RoboErik        try {
274e34c09daf89fb888fe2638e71758573462d85173RoboErik            return mSessionBinder.getLaunchPendingIntent();
275e34c09daf89fb888fe2638e71758573462d85173RoboErik        } catch (RemoteException e) {
276e34c09daf89fb888fe2638e71758573462d85173RoboErik            Log.wtf(TAG, "Error calling getPendingIntent.", e);
277e34c09daf89fb888fe2638e71758573462d85173RoboErik        }
278e34c09daf89fb888fe2638e71758573462d85173RoboErik        return null;
279e34c09daf89fb888fe2638e71758573462d85173RoboErik    }
280e34c09daf89fb888fe2638e71758573462d85173RoboErik
281e34c09daf89fb888fe2638e71758573462d85173RoboErik    /**
28276fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the token for the session this is connected to.
28376fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     *
28476fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * @return The token for the connected session.
28576fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     */
28676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @NonNull MediaSession.Token getSessionToken() {
28776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        return mToken;
28876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    }
28976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik
29076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    /**
2919db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Set the volume of the output this session is playing on. The command will
2929db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * be ignored if it does not support
29319c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
29419c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
29519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
296d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
29719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
298d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @param flags Flags from {@link AudioManager} to include with the volume
299d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     *            request.
30019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
30119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
30219c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3030dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
30419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
30519c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
30619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
30719c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
30819c9518f6a817d53d5234de0020313cab6950b2fRoboErik
30919c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
3109db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Adjust the volume of the output this session is playing on. The direction
3119db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
3121ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
3131ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * The command will be ignored if the session does not support
3141ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
31519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
31619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
31719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
318d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
3191ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * @param direction The direction to adjust the volume in.
32019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
32119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
3221ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik    public void adjustVolume(int direction, int flags) {
32319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3240dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
32519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
32619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
32719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
32819c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
32919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
33019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
33101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the Session. Updates will be
33201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
33301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
334bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
33501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
336bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback) {
337bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        addCallback(callback, null);
33801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
33901fe661ae5da3739215d93922412df4b24c859a2RoboErik
34001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
34101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the session. Updates will be
3428ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
34301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
344bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
3458ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
346bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
34701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
348bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
349bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
350bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
351bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
35201fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
35301fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
35401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
35501fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
356bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
35701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
35801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
35901fe661ae5da3739215d93922412df4b24c859a2RoboErik
36001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
36101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Stop receiving updates on the specified callback. If an update has
36201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * already been posted you may still receive it after calling this method.
36301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
364bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
366bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void removeCallback(@NonNull Callback callback) {
367bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
368bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
369bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
37001fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
371bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
37201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
37401fe661ae5da3739215d93922412df4b24c859a2RoboErik
3758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
3768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
3778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
3788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
3798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
3808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
381f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal     * @param args Any parameters to include with the command
3828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
3838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
384f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal    public void sendCommand(@NonNull String command, @Nullable Bundle args,
385bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
3878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
3888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
390f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            mSessionBinder.sendCommand(command, args, cb);
3918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
3928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
3958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
39607c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
397aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session owner's package name.
398fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
399aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The package name of of the session owner.
400aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     */
401aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getPackageName() {
402aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mPackageName == null) {
403aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            try {
404aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mPackageName = mSessionBinder.getPackageName();
405aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            } catch (RemoteException e) {
406aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getPackageName.", e);
407aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            }
408aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        }
409aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mPackageName;
410aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    }
411aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik
412aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    /**
413aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session's tag for debugging purposes.
414aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     *
415aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The session's tag.
416fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
417fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
418aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getTag() {
419aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mTag == null) {
42073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
421aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mTag = mSessionBinder.getTag();
42273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
423aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getTag.", e);
42473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
425fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
426aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mTag;
427fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
428fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
42901fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
43001fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
43101fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
43207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
43301fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
43401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
43501fe661ae5da3739215d93922412df4b24c859a2RoboErik
43601fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
4378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
43801fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
43901fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
44001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4418ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
4428ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
44301fe661ae5da3739215d93922412df4b24c859a2RoboErik
44401fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
44501fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
44601fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
44701fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
44801fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
449d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
45001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
45101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
45201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
45301fe661ae5da3739215d93922412df4b24c859a2RoboErik
4548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
455d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
4568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4578ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4588ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
460d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
46101fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
46201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
463d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
464d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
465d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
466d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
467d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
468d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
469d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
470d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
471d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
47201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
47301fe661ae5da3739215d93922412df4b24c859a2RoboErik
4748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
4758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
4768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
47701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
4828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
48301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4848ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
48501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
48601fe661ae5da3739215d93922412df4b24c859a2RoboErik
487c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
4888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
4898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
490c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
4918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
49201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
49301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
49401fe661ae5da3739215d93922412df4b24c859a2RoboErik
49501fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
4968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
4978ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * registered using {@link #addCallback}
49801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
49901fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
50001fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
50124762bffc3358762666079cd802040a316b3260dRoboErik         * Override to handle the session being destroyed. The session is no
50224762bffc3358762666079cd802040a316b3260dRoboErik         * longer valid after this call and calls to it will be ignored.
50324762bffc3358762666079cd802040a316b3260dRoboErik         */
50424762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
50524762bffc3358762666079cd802040a316b3260dRoboErik        }
50624762bffc3358762666079cd802040a316b3260dRoboErik
50724762bffc3358762666079cd802040a316b3260dRoboErik        /**
5088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
5098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
5108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
51101fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
512bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param event The event from the session.
513bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param extras Optional parameters for the event, may be null.
51401fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
515bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
51601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
51701fe661ae5da3739215d93922412df4b24c859a2RoboErik
51801fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
519c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
520c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
521c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
522c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
523bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
524c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
525c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
526c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
527c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
528c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
529bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param metadata The current metadata for the session or null if none.
530c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
531c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
532bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
533c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
53419c9518f6a817d53d5234de0020313cab6950b2fRoboErik
53519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        /**
536a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to items in the queue.
537f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
538a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param queue A list of items in the current play queue. It should
539a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            include the currently playing item as well as previous and
540a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            upcoming items if applicable.
541a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @see MediaSession.Item
542f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
543a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public void onQueueChanged(@Nullable List<MediaSession.Item> queue) {
544f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
545f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
546f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
547f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the queue title.
548f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
549f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param title The title that should be displayed along with the play queue such as
550f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *              "Now Playing". May be null if there is no such title.
551f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
552f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(@Nullable CharSequence title) {
553f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
554f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
555f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
556f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the {@link MediaSession} extras.
557f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
558f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras The extras that can include other information associated with the
559f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               {@link MediaSession}.
560f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
561f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(@Nullable Bundle extras) {
562f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
563f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
564f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
565a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to the audio info.
56619c9518f6a817d53d5234de0020313cab6950b2fRoboErik         *
567a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param info The current audio info for this session.
56819c9518f6a817d53d5234de0020313cab6950b2fRoboErik         */
569d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public void onAudioInfoChanged(PlaybackInfo info) {
57019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
571c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
572c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
573c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
574c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
575c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
576c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
577c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
578c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
579c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
580c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
581c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
582c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
583c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
584c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
585c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
586c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
587c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
588c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
589c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
590c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
591c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
592c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
593c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
594c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
595f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific {@link Uri}.
596f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
597f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param uri The uri of the requested media.
598f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the media item
599f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               to be played.
600f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
601f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playUri(Uri uri, Bundle extras) {
602f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (uri == null) {
603f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
604f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
605f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
606f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playUri(uri, extras);
607f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
608f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
609f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
610f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
611f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
612f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
613f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific search query.
614f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
615f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param query The search query.
616f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the query.
617f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
618f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playFromSearch(String query, Bundle extras) {
619f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (TextUtils.isEmpty(query)) {
620f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException(
621f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                        "You must specify a non-empty search query for playFromSearch.");
622f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
623f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
624f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playFromSearch(query, extras);
625f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
626f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + query + ").", e);
627f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
628f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
629f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
630f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
631a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Play an item with a specific id in the play queue. If you specify an
632a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * id that is not in the play queue, the behavior is undefined.
633f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
634a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public void skipToItem(long id) {
635f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
636f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.skipToTrack(id);
637f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
638a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
639f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
640f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
641f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
642f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
643c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
644c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
645c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
646c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
647c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
648c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
649c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
650c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
651c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
652c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
653c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
654c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
655c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
656c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
657c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
658c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
659c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
660c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
661c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
662c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
663c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
664c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
665c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
666c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
667c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
668c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
669c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
670c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
671c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
672c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
673c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
674c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
675c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
676c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
677c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
678c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
679c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
680c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
681c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
682c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
683c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
685c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
686c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
687c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
688c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
689c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
690c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
691c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
693c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
694c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
695c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
696c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
697c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
698c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
699c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
700c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
701c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
702c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
703c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
704c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
705c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
706c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
707c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
708c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
709c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
710c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
711c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
712c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
713c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
714c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
715c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
716c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
717c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
718c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
719c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
720c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
721c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
722c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
723c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
731c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
732c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
733c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
734c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
735c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
736c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
737c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
738c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
739f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
740f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
741f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
742f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
743f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
744f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
745f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
746f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
747f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
748f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
749f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
750f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
751f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
752f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
753f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
754f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
755f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
756f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
757f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
758f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
759f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
760f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
761f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
762f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
763f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
764f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
765f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
766f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
767f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
768f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
769f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
770f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
771f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
772f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
773f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
7748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
77501fe661ae5da3739215d93922412df4b24c859a2RoboErik
776ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
777d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Holds information about the current playback and how audio is handled for
778d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * this session.
779ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
780d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public static final class PlaybackInfo {
781d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
782d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses remote playback.
783d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
784d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_REMOTE = 2;
785d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
786d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses local playback.
787d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
788d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_LOCAL = 1;
789d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik
790ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
791ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
792ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
793ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
7949db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
795ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
796ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
797ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
798ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
799d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
800ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
8019db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
802ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
803ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
804ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
805ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
806ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
807ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
808d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * Get the type of playback which affects volume handling. One of:
809ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
810d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
811d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
812ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
813ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
814d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * @return The type of playback this session is using.
815ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
816d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public int getPlaybackType() {
817ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
818ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
819ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
820ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
8219db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
8229db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
823d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
8249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
825ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
8269db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
827ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
8289db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
8299db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
830ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
831ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
832ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
833ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
834ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
835ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
836ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
837ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
838ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
839ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
840ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
841ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
842ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
843ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
844ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
845ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
846ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
847ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
848ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
849ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
850ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
851ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
852ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
853ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
854ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
855ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
856ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
857ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
858ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
859ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
860ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
861ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
862ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
863ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
864ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
865ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
86607c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
86742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
86801fe661ae5da3739215d93922412df4b24c859a2RoboErik
86942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
87042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
87101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
87201fe661ae5da3739215d93922412df4b24c859a2RoboErik
87301fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
87424762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
87524762bffc3358762666079cd802040a316b3260dRoboErik            MediaController controller = mController.get();
87624762bffc3358762666079cd802040a316b3260dRoboErik            if (controller != null) {
87724762bffc3358762666079cd802040a316b3260dRoboErik                controller.postMessage(MSG_DESTROYED, null, null);
87824762bffc3358762666079cd802040a316b3260dRoboErik            }
87924762bffc3358762666079cd802040a316b3260dRoboErik        }
88024762bffc3358762666079cd802040a316b3260dRoboErik
88124762bffc3358762666079cd802040a316b3260dRoboErik        @Override
8828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
88342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8848ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
885c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
88601fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
88701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
88801fe661ae5da3739215d93922412df4b24c859a2RoboErik
88901fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
8908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
89142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
893c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
89401fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
89501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
89601fe661ae5da3739215d93922412df4b24c859a2RoboErik
89701fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
8988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
89942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
901c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
90201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
90301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
90401fe661ae5da3739215d93922412df4b24c859a2RoboErik
90519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
906f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
907a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            List<MediaSession.Item> queue = parceledQueue.getList();
908f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
909f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
910f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
911f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
912f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
913f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
914f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
915f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
916f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
917f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
918f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
919f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
920f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
921f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
922f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
923f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
924f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
925f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
926f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
927f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
928f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
929f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
930f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
93119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
93219c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
93319c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
934d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
93519c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
93619c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
93719c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
93819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
93919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
94001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
94101fe661ae5da3739215d93922412df4b24c859a2RoboErik
94201fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
94342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
94401fe661ae5da3739215d93922412df4b24c859a2RoboErik
94542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
9468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
9478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
94801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
94901fe661ae5da3739215d93922412df4b24c859a2RoboErik
95001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
95101fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
95201fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
9538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
95479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
95501fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
956c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
957c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
958c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
959c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
960c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
961c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
962f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
963a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                    mCallback.onQueueChanged((List<MediaSession.Item>) msg.obj);
964f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
965f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
966f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
967f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
968f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
969f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
970f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
97119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
972d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                    mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
97319c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
97424762bffc3358762666079cd802040a316b3260dRoboErik                case MSG_DESTROYED:
97524762bffc3358762666079cd802040a316b3260dRoboErik                    mCallback.onSessionDestroyed();
97624762bffc3358762666079cd802040a316b3260dRoboErik                    break;
97701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
97801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
9798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
9808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
9818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
9828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
98301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
98401fe661ae5da3739215d93922412df4b24c859a2RoboErik
98501fe661ae5da3739215d93922412df4b24c859a2RoboErik}
986