MediaController.java revision bc4cf00dc5bf6e0e3c01206d5c46e64306df260a
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;
291a937b04e63539cb1fab1bde601031d415c7156fJeff Brownimport android.media.routing.MediaRouter;
30f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.net.Uri;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
3201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
3401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
3501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
368ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
3701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
3801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
4001fe661ae5da3739215d93922412df4b24c859a2RoboErik
418ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
4201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
43f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport java.util.List;
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik
4501fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
4801fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
4901fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
5042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
51f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an
52f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * enabled notification listener or by getting a {@link MediaSession.Token}
53f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * directly from the session owner.
5401fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
5501fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
5601fe661ae5da3739215d93922412df4b24c859a2RoboErik */
5742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
58bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    private static final String TAG = "MediaController";
5901fe661ae5da3739215d93922412df4b24c859a2RoboErik
608ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
61c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
62c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
6301a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown    private static final int MSG_UPDATE_VOLUME = 4;
64f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE = 5;
65f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE_TITLE = 6;
66f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_EXTRAS = 7;
6724762bffc3358762666079cd802040a316b3260dRoboErik    private static final int MSG_DESTROYED = 8;
6801fe661ae5da3739215d93922412df4b24c859a2RoboErik
6907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
7001fe661ae5da3739215d93922412df4b24c859a2RoboErik
7176fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    private final MediaSession.Token mToken;
72031149cd5f22bd858142633c7a763450f42793f7RoboErik    private final Context mContext;
738ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
7501fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik
7701fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
78aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mPackageName;
79aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mTag;
8001fe661ae5da3739215d93922412df4b24c859a2RoboErik
811a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    private final TransportControls mTransportControls;
828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
8301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
848b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Call for creating a MediaController directly from a binder. Should only
858b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * be used by framework code.
868b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     *
878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
8801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
89031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(Context context, ISessionController sessionBinder) {
908b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        if (sessionBinder == null) {
918b4bffcac996b4083e720310a09d315ca1c4a000RoboErik            throw new IllegalArgumentException("Session token cannot be null");
928b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        }
93031149cd5f22bd858142633c7a763450f42793f7RoboErik        if (context == null) {
94031149cd5f22bd858142633c7a763450f42793f7RoboErik            throw new IllegalArgumentException("Context cannot be null");
95031149cd5f22bd858142633c7a763450f42793f7RoboErik        }
968b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mSessionBinder = sessionBinder;
978b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mTransportControls = new TransportControls();
9876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        mToken = new MediaSession.Token(sessionBinder);
99031149cd5f22bd858142633c7a763450f42793f7RoboErik        mContext = context;
10001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
10101fe661ae5da3739215d93922412df4b24c859a2RoboErik
10201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1038b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Create a new MediaController from a session's token.
1048ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
105031149cd5f22bd858142633c7a763450f42793f7RoboErik     * @param context The caller's context.
1068b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * @param token The token for the session.
10701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
108031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
109031149cd5f22bd858142633c7a763450f42793f7RoboErik        this(context, token.getBinder());
11001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
11101fe661ae5da3739215d93922412df4b24c859a2RoboErik
11201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1131a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Get a {@link TransportControls} instance to send transport actions to
1141a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * the associated session.
11501fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
1161a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return A transport controls instance.
11701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
118bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @NonNull TransportControls getTransportControls() {
1191a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return mTransportControls;
1201a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    }
1211a937b04e63539cb1fab1bde601031d415c7156fJeff Brown
1221a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    /**
1231a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Creates a media router delegate through which the destination of the media
1241a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * router may be observed and controlled.
1251a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     *
1261a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return The media router delegate, or null if the media session does
1271a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * not support media routing.
1281a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     */
1291a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
1301a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return new MediaRouter.Delegate();
13101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
13201fe661ae5da3739215d93922412df4b24c859a2RoboErik
13301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
13479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
13579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
13601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
13779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
13879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
13901fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
140bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
14179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
14279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
14379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
14479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
14579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
14601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
14701fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
14879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
14901fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
15101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
15279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
15301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
15401fe661ae5da3739215d93922412df4b24c859a2RoboErik
15501fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
160bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
161c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
171c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
172c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
173c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
174bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
175c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
176c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
177c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
178c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
179c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
180c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
181c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
182c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
183c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
184477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * Get the current play queue for this session if one is set. If you only
185477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * care about the current item {@link #getMetadata()} should be used.
186f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     *
187f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * @return The current play queue or null.
188f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     */
1893625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik    public @Nullable List<MediaSession.QueueItem> getQueue() {
190f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        try {
191f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            ParceledListSlice queue = mSessionBinder.getQueue();
192f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (queue != null) {
193f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                return queue.getList();
194f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
195f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        } catch (RemoteException e) {
196f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            Log.wtf(TAG, "Error calling getQueue.", e);
197f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
198f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        return null;
199f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    }
200f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
201f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    /**
20251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the queue title for this session.
20351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
20451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable CharSequence getQueueTitle() {
20551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
20651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getQueueTitle();
20751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
20851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getQueueTitle", e);
20951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
21051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
21151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
21251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
21351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
21451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the extras for this session.
21551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
21651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable Bundle getExtras() {
21751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
21851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getExtras();
21951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
22051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getExtras", e);
22151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
22251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
22351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
22451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
22551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
226c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
227c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
228c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
229c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
230c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
231c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
232c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
233c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
234c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
235c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
236c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
237c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
238c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
239c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
240c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
241c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
242c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
243c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
244c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
245c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
246c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
247c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
248c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
24976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the flags for this session. Flags are defined in {@link MediaSession}.
25073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
25173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
25273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
25376fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @MediaSession.SessionFlags long getFlags() {
25473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
25573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
25673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
25773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
25873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
25973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
26073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
26173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
26273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
263d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Get the current playback info for this session.
264ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
265d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @return The current playback info or null.
266ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
267d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PlaybackInfo getPlaybackInfo() {
268ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
269ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
270d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik            return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
271ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
272ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
273ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
274a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            Log.wtf(TAG, "Error calling getAudioInfo.", e);
275ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
276ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
277ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
278ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
279ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
280e34c09daf89fb888fe2638e71758573462d85173RoboErik     * Get an intent for launching UI associated with this session if one
281e34c09daf89fb888fe2638e71758573462d85173RoboErik     * exists.
282e34c09daf89fb888fe2638e71758573462d85173RoboErik     *
283e34c09daf89fb888fe2638e71758573462d85173RoboErik     * @return A {@link PendingIntent} to launch UI or null.
284e34c09daf89fb888fe2638e71758573462d85173RoboErik     */
285d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PendingIntent getSessionActivity() {
286e34c09daf89fb888fe2638e71758573462d85173RoboErik        try {
287e34c09daf89fb888fe2638e71758573462d85173RoboErik            return mSessionBinder.getLaunchPendingIntent();
288e34c09daf89fb888fe2638e71758573462d85173RoboErik        } catch (RemoteException e) {
289e34c09daf89fb888fe2638e71758573462d85173RoboErik            Log.wtf(TAG, "Error calling getPendingIntent.", e);
290e34c09daf89fb888fe2638e71758573462d85173RoboErik        }
291e34c09daf89fb888fe2638e71758573462d85173RoboErik        return null;
292e34c09daf89fb888fe2638e71758573462d85173RoboErik    }
293e34c09daf89fb888fe2638e71758573462d85173RoboErik
294e34c09daf89fb888fe2638e71758573462d85173RoboErik    /**
29576fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the token for the session this is connected to.
29676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     *
29776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * @return The token for the connected session.
29876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     */
29976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @NonNull MediaSession.Token getSessionToken() {
30076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        return mToken;
30176fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    }
30276fca4e177e18b591439fdff64b8f5242a5122d0RoboErik
30376fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    /**
3049db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Set the volume of the output this session is playing on. The command will
3059db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * be ignored if it does not support
30619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
30719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
30819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
309d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
31019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
311d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @param flags Flags from {@link AudioManager} to include with the volume
312d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     *            request.
31319c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
31419c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
31519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3160dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
31719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
31819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
31919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
32019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
32119c9518f6a817d53d5234de0020313cab6950b2fRoboErik
32219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
3239db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Adjust the volume of the output this session is playing on. The direction
3249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
3251ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
3261ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * The command will be ignored if the session does not support
3271ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
32819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
32919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
33019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
331d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
3321ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * @param direction The direction to adjust the volume in.
33319c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
33419c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
3351ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik    public void adjustVolume(int direction, int flags) {
33619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3370dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
33819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
33919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
34019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
34119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
34219c9518f6a817d53d5234de0020313cab6950b2fRoboErik
34319c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
34414f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Registers a callback to receive updates from the Session. Updates will be
34501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
34601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
347bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
34801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
34914f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void registerCallback(@NonNull Callback callback) {
35014f717a506a0d22facbec07386b06634e0c6a8eeRoboErik        registerCallback(callback, null);
35101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
35201fe661ae5da3739215d93922412df4b24c859a2RoboErik
35301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
35414f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Registers a callback to receive updates from the session. Updates will be
3558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
35601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
357bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
3588ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
359bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
36001fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
36114f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
362bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
363bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
364bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
36601fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
36701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
36801fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
369bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
37001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
37101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
37201fe661ae5da3739215d93922412df4b24c859a2RoboErik
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
37414f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Unregisters the specified callback. If an update has already been posted
37514f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * you may still receive it after calling this method.
37601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
377bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
37801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
37914f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void unregisterCallback(@NonNull Callback callback) {
380bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
381bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
382bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
38301fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
384bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
38501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
38601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
38701fe661ae5da3739215d93922412df4b24c859a2RoboErik
3888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
3898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
3908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
3918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
3928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
394f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal     * @param args Any parameters to include with the command
3958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
3968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
397f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal    public void sendCommand(@NonNull String command, @Nullable Bundle args,
398bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
4008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
4018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
4028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
403f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            mSessionBinder.sendCommand(command, args, cb);
4048ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
4058ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
4068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
4078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
4088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
40907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
410aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session owner's package name.
411fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
412aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The package name of of the session owner.
413aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     */
414aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getPackageName() {
415aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mPackageName == null) {
416aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            try {
417aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mPackageName = mSessionBinder.getPackageName();
418aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            } catch (RemoteException e) {
419aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getPackageName.", e);
420aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            }
421aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        }
422aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mPackageName;
423aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    }
424aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik
425aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    /**
426aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session's tag for debugging purposes.
427aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     *
428aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The session's tag.
429fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
430fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
431aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getTag() {
432aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mTag == null) {
43373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
434aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mTag = mSessionBinder.getTag();
43573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
436aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getTag.", e);
43773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
438fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
439aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mTag;
440fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
441fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
44201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
44301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
44401fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
44507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
44601fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
44701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
44801fe661ae5da3739215d93922412df4b24c859a2RoboErik
449b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    /**
450b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer     * @hide
451b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer     */
452b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    public boolean controlsSameSession(MediaController other) {
453b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer        if (other == null) return false;
454b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer        return mSessionBinder.asBinder() == other.getSessionBinder().asBinder();
455b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    }
456b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer
45701fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
4588ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
45901fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
46001fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
46101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4628ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
4638ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
4643e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik        holder.mRegistered = true;
46501fe661ae5da3739215d93922412df4b24c859a2RoboErik
46601fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
46701fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
46801fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
46901fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
47001fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
471d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
47201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
47301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
47401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
47501fe661ae5da3739215d93922412df4b24c859a2RoboErik
4768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
477d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
4788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
482d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
4833e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik                handler.mRegistered = false;
48401fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
48501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
486d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
487d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
488d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
489d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
490d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
491d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
492d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
493d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
494d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
49501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
49601fe661ae5da3739215d93922412df4b24c859a2RoboErik
4978ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
4988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
4998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
50001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
5018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
5028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
5038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
5048ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
5058ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
50601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
5078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
50801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
50901fe661ae5da3739215d93922412df4b24c859a2RoboErik
510c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
5118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
5128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
513c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
5148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
51501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
51601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
51701fe661ae5da3739215d93922412df4b24c859a2RoboErik
51801fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
519bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock     * Callback for receiving updates from the session. A Callback can be
520bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock     * registered using {@link #registerCallback}.
52101fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
52201fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
52301fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
52424762bffc3358762666079cd802040a316b3260dRoboErik         * Override to handle the session being destroyed. The session is no
52524762bffc3358762666079cd802040a316b3260dRoboErik         * longer valid after this call and calls to it will be ignored.
52624762bffc3358762666079cd802040a316b3260dRoboErik         */
52724762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
52824762bffc3358762666079cd802040a316b3260dRoboErik        }
52924762bffc3358762666079cd802040a316b3260dRoboErik
53024762bffc3358762666079cd802040a316b3260dRoboErik        /**
5318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
5328ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
5338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
53401fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
535bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param event The event from the session.
536bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param extras Optional parameters for the event, may be null.
53701fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
538bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
53901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
54001fe661ae5da3739215d93922412df4b24c859a2RoboErik
54101fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
542c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
543c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
544c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
545c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
546bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
547c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
548c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
549c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
550c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
551c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
552bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param metadata The current metadata for the session or null if none.
553c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
554c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
555bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
556c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
55719c9518f6a817d53d5234de0020313cab6950b2fRoboErik
55819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        /**
559a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to items in the queue.
560f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
561a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param queue A list of items in the current play queue. It should
562a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            include the currently playing item as well as previous and
563a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            upcoming items if applicable.
5643625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik         * @see MediaSession.QueueItem
565f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
5663625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
567f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
568f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
569f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
570f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the queue title.
571f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
572f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param title The title that should be displayed along with the play queue such as
573f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *              "Now Playing". May be null if there is no such title.
574f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
575f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(@Nullable CharSequence title) {
576f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
577f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
578f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
579f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the {@link MediaSession} extras.
580f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
581f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras The extras that can include other information associated with the
582f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               {@link MediaSession}.
583f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
584f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(@Nullable Bundle extras) {
585f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
586f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
587f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
588a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to the audio info.
58919c9518f6a817d53d5234de0020313cab6950b2fRoboErik         *
590a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param info The current audio info for this session.
59119c9518f6a817d53d5234de0020313cab6950b2fRoboErik         */
592d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public void onAudioInfoChanged(PlaybackInfo info) {
59319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
594c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
595c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
596c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
597c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
598c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
599c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
600c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
601c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
602c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
603c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
604c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
605c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
606c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
607c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
608c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
609c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
610c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
611c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
612c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
613c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
614c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
615c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
616c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
617c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
618f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific {@link Uri}.
619f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
6203625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik         * @param mediaId The uri of the requested media.
621f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the media item
622f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               to be played.
623f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
6243625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void playFromMediaId(String mediaId, Bundle extras) {
6253625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik            if (TextUtils.isEmpty(mediaId)) {
6263625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                throw new IllegalArgumentException(
6273625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                        "You must specify a non-empty String for playFromMediaId.");
628f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
629f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
6303625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                mSessionBinder.playFromMediaId(mediaId, extras);
631f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
6323625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
633f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
634f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
635f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
636f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
637f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific search query.
6384b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * An empty or null query should be treated as a request to play any
6394b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * music.
640f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
641f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param query The search query.
6424b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * @param extras Optional extras that can include extra information
6434b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         *            about the query.
644f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
645f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playFromSearch(String query, Bundle extras) {
6464b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik            if (query == null) {
6474b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                // This is to remain compatible with
6484b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
6494b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                query = "";
650f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
651f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
652f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playFromSearch(query, extras);
653f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
654f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + query + ").", e);
655f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
656f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
657f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
658f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
659a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Play an item with a specific id in the play queue. If you specify an
660a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * id that is not in the play queue, the behavior is undefined.
661f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
6623625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void skipToQueueItem(long id) {
663f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
6643625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                mSessionBinder.skipToQueueItem(id);
665f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
666a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
667f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
668f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
669f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
670f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
671c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
672c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
673c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
674c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
675c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
676c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
677c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
678c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
679c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
680c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
681c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
682c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
683c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
685c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
686c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
687c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
688c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
689c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
690c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
691c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
693c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
694c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
695c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
696c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
697c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
698c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
699c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
700c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
701c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
702c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
703c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
704c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
705c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
706c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
707c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
708c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
709c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
710c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
711c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
712c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
713c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
714c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
715c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
716c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
717c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
718c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
719c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
720c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
721c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
722c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
723c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
731c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
732c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
733c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
734c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
735c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
736c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
737c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
738c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
739c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
740c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
741c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
742c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
743c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
744c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
745c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
746c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
747c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
748c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
749c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
750c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
751c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
752c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
753c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
754c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
755c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
756c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
757c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
758c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
759c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
760c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
761c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
762c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
763c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
764c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
765c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
766c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
767f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
768f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
769f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
770f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
771f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
772f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
773f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
774f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
775f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
776f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
777f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
778f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
779f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
780f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
781f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
782f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
783f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
784f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
785f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
786f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
787f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
788f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
789f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
790f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
791f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
792f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
793f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
794f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
795f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
796f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
797f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
798f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
799f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
800f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
801f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
8028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
80301fe661ae5da3739215d93922412df4b24c859a2RoboErik
804ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
805d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Holds information about the current playback and how audio is handled for
806d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * this session.
807ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
808d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public static final class PlaybackInfo {
809d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
810d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses remote playback.
811d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
812d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_REMOTE = 2;
813d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
814d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses local playback.
815d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
816d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_LOCAL = 1;
817d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik
818ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
819ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
820ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
821ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
8229db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
823ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
824ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
825ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
826ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
827d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
828ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
8299db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
830ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
831ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
832ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
833ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
834ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
835ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
836d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * Get the type of playback which affects volume handling. One of:
837ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
838d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
839d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
840ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
841ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
842d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * @return The type of playback this session is using.
843ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
844d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public int getPlaybackType() {
845ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
846ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
847ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
848ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
8499db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
8509db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
851d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
8529db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
853ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
8549db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
855ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
8569db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
8579db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
858ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
859ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
860ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
861ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
862ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
863ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
864ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
865ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
866ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
867ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
868ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
869ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
870ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
871ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
872ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
873ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
874ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
875ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
876ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
877ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
878ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
879ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
880ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
881ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
882ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
883ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
884ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
885ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
886ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
887ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
888ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
889ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
890ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
891ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
892ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
893ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
89407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
89542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
89601fe661ae5da3739215d93922412df4b24c859a2RoboErik
89742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
89842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
89901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
90001fe661ae5da3739215d93922412df4b24c859a2RoboErik
90101fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
90224762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
90324762bffc3358762666079cd802040a316b3260dRoboErik            MediaController controller = mController.get();
90424762bffc3358762666079cd802040a316b3260dRoboErik            if (controller != null) {
90524762bffc3358762666079cd802040a316b3260dRoboErik                controller.postMessage(MSG_DESTROYED, null, null);
90624762bffc3358762666079cd802040a316b3260dRoboErik            }
90724762bffc3358762666079cd802040a316b3260dRoboErik        }
90824762bffc3358762666079cd802040a316b3260dRoboErik
90924762bffc3358762666079cd802040a316b3260dRoboErik        @Override
9108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
91142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
913c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
91401fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
91501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
91601fe661ae5da3739215d93922412df4b24c859a2RoboErik
91701fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9188ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
91942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
921c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
92201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
92301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
92401fe661ae5da3739215d93922412df4b24c859a2RoboErik
92501fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9268ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
92742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9288ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
929c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
93001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
93101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
93201fe661ae5da3739215d93922412df4b24c859a2RoboErik
93319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
934f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
93503fce072cac092923e10a6b5f09fcde333375f9eRoboErik            List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
93603fce072cac092923e10a6b5f09fcde333375f9eRoboErik                    .getList();
937f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
938f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
939f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
940f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
941f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
942f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
943f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
944f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
945f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
946f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
947f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
948f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
949f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
950f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
951f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
952f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
953f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
954f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
955f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
956f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
957f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
958f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
959f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
96019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
96119c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
96219c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
963d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
96419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
96519c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
96619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
96719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
96819c9518f6a817d53d5234de0020313cab6950b2fRoboErik
96901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
97001fe661ae5da3739215d93922412df4b24c859a2RoboErik
97101fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
97242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
9733e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik        private boolean mRegistered = false;
97401fe661ae5da3739215d93922412df4b24c859a2RoboErik
97542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
9768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
9778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
97801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
97901fe661ae5da3739215d93922412df4b24c859a2RoboErik
98001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
98101fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
9823e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            if (!mRegistered) {
9833e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik                return;
9843e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            }
98501fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
9868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
98779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
98801fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
989c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
990c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
991c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
992c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
993c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
994c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
995f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
9963625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                    mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
997f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
998f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
999f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
1000f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
1001f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
1002f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
1003f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
100419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
1005d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                    mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
100619c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
100724762bffc3358762666079cd802040a316b3260dRoboErik                case MSG_DESTROYED:
100824762bffc3358762666079cd802040a316b3260dRoboErik                    mCallback.onSessionDestroyed();
100924762bffc3358762666079cd802040a316b3260dRoboErik                    break;
101001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
101101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
10128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
10138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
10148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
10158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
101601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
101701fe661ae5da3739215d93922412df4b24c859a2RoboErik
101801fe661ae5da3739215d93922412df4b24c859a2RoboErik}
1019