MediaController.java revision a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206
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
5101fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
52dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown * you have a {@link MediaSession.Token} from the session owner.
5301fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
5401fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
5501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
5642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
57bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    private static final String TAG = "MediaController";
5801fe661ae5da3739215d93922412df4b24c859a2RoboErik
598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
60c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
61c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
6201a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown    private static final int MSG_UPDATE_VOLUME = 4;
63f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE = 5;
64f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE_TITLE = 6;
65f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_EXTRAS = 7;
6624762bffc3358762666079cd802040a316b3260dRoboErik    private static final int MSG_DESTROYED = 8;
6701fe661ae5da3739215d93922412df4b24c859a2RoboErik
6807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
6901fe661ae5da3739215d93922412df4b24c859a2RoboErik
7076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    private final MediaSession.Token mToken;
71031149cd5f22bd858142633c7a763450f42793f7RoboErik    private final Context mContext;
728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
738ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
7401fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
7501fe661ae5da3739215d93922412df4b24c859a2RoboErik
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
77aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mPackageName;
78aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    private String mTag;
7901fe661ae5da3739215d93922412df4b24c859a2RoboErik
801a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    private final TransportControls mTransportControls;
818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
8201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
838b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Call for creating a MediaController directly from a binder. Should only
848b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * be used by framework code.
858b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     *
868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
8701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
88031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(Context context, ISessionController sessionBinder) {
898b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        if (sessionBinder == null) {
908b4bffcac996b4083e720310a09d315ca1c4a000RoboErik            throw new IllegalArgumentException("Session token cannot be null");
918b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        }
92031149cd5f22bd858142633c7a763450f42793f7RoboErik        if (context == null) {
93031149cd5f22bd858142633c7a763450f42793f7RoboErik            throw new IllegalArgumentException("Context cannot be null");
94031149cd5f22bd858142633c7a763450f42793f7RoboErik        }
958b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mSessionBinder = sessionBinder;
968b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mTransportControls = new TransportControls();
9776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        mToken = new MediaSession.Token(sessionBinder);
98031149cd5f22bd858142633c7a763450f42793f7RoboErik        mContext = context;
9901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
10001fe661ae5da3739215d93922412df4b24c859a2RoboErik
10101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1028b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Create a new MediaController from a session's token.
1038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
104031149cd5f22bd858142633c7a763450f42793f7RoboErik     * @param context The caller's context.
1058b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * @param token The token for the session.
10601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
107031149cd5f22bd858142633c7a763450f42793f7RoboErik    public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
108031149cd5f22bd858142633c7a763450f42793f7RoboErik        this(context, token.getBinder());
10901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
11001fe661ae5da3739215d93922412df4b24c859a2RoboErik
11101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1121a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Get a {@link TransportControls} instance to send transport actions to
1131a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * the associated session.
11401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
1151a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return A transport controls instance.
11601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
117bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @NonNull TransportControls getTransportControls() {
1181a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return mTransportControls;
1191a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    }
1201a937b04e63539cb1fab1bde601031d415c7156fJeff Brown
1211a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    /**
1221a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Creates a media router delegate through which the destination of the media
1231a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * router may be observed and controlled.
1241a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     *
1251a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return The media router delegate, or null if the media session does
1261a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * not support media routing.
1271a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     */
1281a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
1291a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return new MediaRouter.Delegate();
13001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
13101fe661ae5da3739215d93922412df4b24c859a2RoboErik
13201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
13379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
13479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
13501fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
13679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
13779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
13801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
139bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
14079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
14179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
14279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
14379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
14479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
14501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
14601fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
14779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
14801fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
15001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
15179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
15201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
15301fe661ae5da3739215d93922412df4b24c859a2RoboErik
15401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
159bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
161c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
171c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
172c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
173bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
174c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
175c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
176c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
177c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
178c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
179c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
180c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
181c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
182c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
183477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * Get the current play queue for this session if one is set. If you only
184477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * care about the current item {@link #getMetadata()} should be used.
185f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     *
186f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * @return The current play queue or null.
187f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     */
188a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik    public @Nullable List<MediaSession.Item> getQueue() {
189f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        try {
190f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            ParceledListSlice queue = mSessionBinder.getQueue();
191f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (queue != null) {
192f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                return queue.getList();
193f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
194f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        } catch (RemoteException e) {
195f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            Log.wtf(TAG, "Error calling getQueue.", e);
196f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
197f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        return null;
198f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    }
199f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
200f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    /**
20151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the queue title for this session.
20251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
20351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable CharSequence getQueueTitle() {
20451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
20551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getQueueTitle();
20651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
20751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getQueueTitle", e);
20851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
20951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
21051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
21151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
21251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
21351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the extras for this session.
21451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
21551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable Bundle getExtras() {
21651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
21751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getExtras();
21851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
21951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getExtras", e);
22051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
22151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
22251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
22351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
22451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
225c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
226c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
227c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
228c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
229c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
230c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
231c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
232c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
233c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
234c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
235c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
236c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
237c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
238c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
239c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
240c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
241c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
242c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
243c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
244c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
245c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
246c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
247c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
24876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the flags for this session. Flags are defined in {@link MediaSession}.
24973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
25073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
25173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
25276fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @MediaSession.SessionFlags long getFlags() {
25373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
25473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
25573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
25673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
25773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
25873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
25973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
26073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
26173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
262a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik     * Get the current audio info for this session.
263ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
264a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik     * @return The current audio info or null.
265ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
266a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik    public @Nullable AudioInfo getAudioInfo() {
267ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
268ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
269a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            return new AudioInfo(result.volumeType, result.audioAttrs, result.controlType,
270ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
271ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
272ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
273a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            Log.wtf(TAG, "Error calling getAudioInfo.", e);
274ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
275ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
276ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
277ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
278ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
279e34c09daf89fb888fe2638e71758573462d85173RoboErik     * Get an intent for launching UI associated with this session if one
280e34c09daf89fb888fe2638e71758573462d85173RoboErik     * exists.
281e34c09daf89fb888fe2638e71758573462d85173RoboErik     *
282e34c09daf89fb888fe2638e71758573462d85173RoboErik     * @return A {@link PendingIntent} to launch UI or null.
283e34c09daf89fb888fe2638e71758573462d85173RoboErik     */
284e34c09daf89fb888fe2638e71758573462d85173RoboErik    public @Nullable PendingIntent getLaunchActivity() {
285e34c09daf89fb888fe2638e71758573462d85173RoboErik        try {
286e34c09daf89fb888fe2638e71758573462d85173RoboErik            return mSessionBinder.getLaunchPendingIntent();
287e34c09daf89fb888fe2638e71758573462d85173RoboErik        } catch (RemoteException e) {
288e34c09daf89fb888fe2638e71758573462d85173RoboErik            Log.wtf(TAG, "Error calling getPendingIntent.", e);
289e34c09daf89fb888fe2638e71758573462d85173RoboErik        }
290e34c09daf89fb888fe2638e71758573462d85173RoboErik        return null;
291e34c09daf89fb888fe2638e71758573462d85173RoboErik    }
292e34c09daf89fb888fe2638e71758573462d85173RoboErik
293e34c09daf89fb888fe2638e71758573462d85173RoboErik    /**
29476fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the token for the session this is connected to.
29576fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     *
29676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * @return The token for the connected session.
29776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     */
29876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @NonNull MediaSession.Token getSessionToken() {
29976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        return mToken;
30076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    }
30176fca4e177e18b591439fdff64b8f5242a5122d0RoboErik
30276fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    /**
3039db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Set the volume of the output this session is playing on. The command will
3049db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * be ignored if it does not support
30519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
30619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
30719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
308a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik     * @see #getAudioInfo()
30919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
31019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
31119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
31219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
31319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3140dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
31519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
31619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
31719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
31819c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
31919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
32019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
3219db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Adjust the volume of the output this session is playing on. The direction
3229db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
3231ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
3241ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * The command will be ignored if the session does not support
3251ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
32619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
32719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
32819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
329a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik     * @see #getAudioInfo()
3301ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * @param direction The direction to adjust the volume in.
33119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
33219c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
3331ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik    public void adjustVolume(int direction, int flags) {
33419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3350dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
33619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
33719c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
33819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
33919c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
34019c9518f6a817d53d5234de0020313cab6950b2fRoboErik
34119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
34201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the Session. Updates will be
34301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
34401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
345bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
34601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
347bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback) {
348bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        addCallback(callback, null);
34901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
35001fe661ae5da3739215d93922412df4b24c859a2RoboErik
35101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
35201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the session. Updates will be
3538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
35401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
355bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
3568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
357bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
35801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
359bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
360bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
361bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
362bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
36301fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
36401fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
36601fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
367bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
36801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
36901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
37001fe661ae5da3739215d93922412df4b24c859a2RoboErik
37101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
37201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Stop receiving updates on the specified callback. If an update has
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * already been posted you may still receive it after calling this method.
37401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
375bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
37601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
377bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void removeCallback(@NonNull Callback callback) {
378bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
379bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
380bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
38101fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
382bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
38301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
38401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
38501fe661ae5da3739215d93922412df4b24c859a2RoboErik
3868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
3878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
3888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
3898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
3908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
3918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
392f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal     * @param args Any parameters to include with the command
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
3948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
395f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal    public void sendCommand(@NonNull String command, @Nullable Bundle args,
396bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3978ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
3988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
3998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
4008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
401f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            mSessionBinder.sendCommand(command, args, cb);
4028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
4038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
4048ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
4058ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
4068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
40707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
408aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session owner's package name.
409fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
410aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The package name of of the session owner.
411aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     */
412aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getPackageName() {
413aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mPackageName == null) {
414aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            try {
415aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mPackageName = mSessionBinder.getPackageName();
416aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            } catch (RemoteException e) {
417aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getPackageName.", e);
418aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            }
419aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        }
420aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mPackageName;
421aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    }
422aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik
423aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    /**
424aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session's tag for debugging purposes.
425aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     *
426aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The session's tag.
427fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
428fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
429aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getTag() {
430aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mTag == null) {
43173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
432aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mTag = mSessionBinder.getTag();
43373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
434aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getTag.", e);
43573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
436fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
437aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mTag;
438fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
439fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
44001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
44101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
44201fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
44307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
44401fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
44501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
44601fe661ae5da3739215d93922412df4b24c859a2RoboErik
44701fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
4488ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
44901fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
45001fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
45101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
4538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
45401fe661ae5da3739215d93922412df4b24c859a2RoboErik
45501fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
45601fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
45701fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
45801fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
45901fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
460d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
46101fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
46201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
46301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
46401fe661ae5da3739215d93922412df4b24c859a2RoboErik
4658ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
466d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
4678ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
471d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
47201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
47301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
474d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
475d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
476d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
477d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
478d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
479d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
480d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
481d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
482d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
48301fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
48401fe661ae5da3739215d93922412df4b24c859a2RoboErik
4858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
4868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
4878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
48801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
4938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
49401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
49601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
49701fe661ae5da3739215d93922412df4b24c859a2RoboErik
498c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
4998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
5008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
501c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
5028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
50301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
50401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
50501fe661ae5da3739215d93922412df4b24c859a2RoboErik
50601fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
5078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
5088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * registered using {@link #addCallback}
50901fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
51001fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
51101fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
51224762bffc3358762666079cd802040a316b3260dRoboErik         * Override to handle the session being destroyed. The session is no
51324762bffc3358762666079cd802040a316b3260dRoboErik         * longer valid after this call and calls to it will be ignored.
51424762bffc3358762666079cd802040a316b3260dRoboErik         */
51524762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
51624762bffc3358762666079cd802040a316b3260dRoboErik        }
51724762bffc3358762666079cd802040a316b3260dRoboErik
51824762bffc3358762666079cd802040a316b3260dRoboErik        /**
5198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
5208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
5218ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
52201fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
523bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param event The event from the session.
524bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param extras Optional parameters for the event, may be null.
52501fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
526bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
52701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
52801fe661ae5da3739215d93922412df4b24c859a2RoboErik
52901fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
530c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
531c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
532c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
533c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
534bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
535c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
536c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
537c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
538c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
539c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
540bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param metadata The current metadata for the session or null if none.
541c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
542c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
543bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
544c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
54519c9518f6a817d53d5234de0020313cab6950b2fRoboErik
54619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        /**
547a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to items in the queue.
548f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
549a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param queue A list of items in the current play queue. It should
550a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            include the currently playing item as well as previous and
551a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         *            upcoming items if applicable.
552a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @see MediaSession.Item
553f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
554a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public void onQueueChanged(@Nullable List<MediaSession.Item> queue) {
555f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
556f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
557f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
558f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the queue title.
559f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
560f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param title The title that should be displayed along with the play queue such as
561f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *              "Now Playing". May be null if there is no such title.
562f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
563f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(@Nullable CharSequence title) {
564f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
565f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
566f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
567f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the {@link MediaSession} extras.
568f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
569f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras The extras that can include other information associated with the
570f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               {@link MediaSession}.
571f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
572f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(@Nullable Bundle extras) {
573f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
574f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
575f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
576a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Override to handle changes to the audio info.
57719c9518f6a817d53d5234de0020313cab6950b2fRoboErik         *
578a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * @param info The current audio info for this session.
57919c9518f6a817d53d5234de0020313cab6950b2fRoboErik         */
580a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public void onAudioInfoChanged(AudioInfo info) {
58119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
582c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
583c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
584c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
585c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
586c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
587c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
588c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
589c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
590c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
591c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
592c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
593c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
594c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
595c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
596c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
597c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
598c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
599c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
600c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
601c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
602c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
603c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
604c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
605c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
606f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific {@link Uri}.
607f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
608f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param uri The uri of the requested media.
609f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the media item
610f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               to be played.
611f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
612f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playUri(Uri uri, Bundle extras) {
613f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (uri == null) {
614f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
615f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
616f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
617f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playUri(uri, extras);
618f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
619f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
620f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
621f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
622f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
623f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
624f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific search query.
625f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
626f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param query The search query.
627f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the query.
628f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
629f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playFromSearch(String query, Bundle extras) {
630f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (TextUtils.isEmpty(query)) {
631f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException(
632f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                        "You must specify a non-empty search query for playFromSearch.");
633f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
634f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
635f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playFromSearch(query, extras);
636f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
637f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + query + ").", e);
638f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
639f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
640f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
641f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
642a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Play an item with a specific id in the play queue. If you specify an
643a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * id that is not in the play queue, the behavior is undefined.
644f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
645a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public void skipToItem(long id) {
646f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
647f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.skipToTrack(id);
648f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
649a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
650f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
651f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
652f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
653f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
654c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
655c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
656c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
657c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
658c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
659c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
660c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
661c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
662c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
663c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
664c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
665c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
666c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
667c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
668c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
669c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
670c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
671c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
672c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
673c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
674c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
675c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
676c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
677c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
678c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
679c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
680c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
681c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
682c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
683c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
685c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
686c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
687c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
688c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
689c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
690c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
691c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
693c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
694c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
695c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
696c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
697c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
698c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
699c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
700c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
701c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
702c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
703c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
704c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
705c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
706c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
707c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
708c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
709c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
710c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
711c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
712c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
713c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
714c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
715c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
716c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
717c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
718c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
719c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
720c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
721c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
722c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
723c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
731c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
732c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
733c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
734c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
735c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
736c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
737c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
738c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
739c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
740c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
741c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
742c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
743c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
744c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
745c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
746c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
747c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
748c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
749c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
750f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
751f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
752f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
753f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
754f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
755f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
756f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
757f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
758f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
759f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
760f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
761f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
762f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
763f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
764f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
765f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
766f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
767f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
768f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
769f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
770f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
771f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
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 String action, @Nullable Bundle args) {
776f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
777f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
778f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
779f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
780f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
781f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
782f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
783f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
784f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
7858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
78601fe661ae5da3739215d93922412df4b24c859a2RoboErik
787ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
788a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik     * Holds information about the way audio is handled for this session.
789ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
790a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik    public static final class AudioInfo {
791ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
792ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
793ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
794ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
7959db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
796ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
797ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
798ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
799ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
800a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik        public AudioInfo(int type, AudioAttributes attrs, int control, int max, int current) {
801ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
8029db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
803ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
804ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
805ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
806ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
807ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
808ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
809ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume handling, either local or remote. One of:
810ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
81119c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_LOCAL}</li>
81219c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_REMOTE}</li>
813ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
814ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
815ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume handling this session is using.
816ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
817ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeType() {
818ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
819ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
820ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
821ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
8229db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
8239db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
8249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * {@link MediaSession#PLAYBACK_TYPE_REMOTE} these may be ignored by the
8259db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
826ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
8279db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
828ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
8299db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
8309db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
831ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
832ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
833ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
834ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
835ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
836ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
837ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
838ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
839ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
840ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
841ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
842ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
843ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
844ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
845ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
846ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
847ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
848ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
849ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
850ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
851ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
852ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
853ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
854ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
855ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
856ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
857ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
858ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
859ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
860ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
861ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
862ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
863ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
864ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
865ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
866ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
86707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
86842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
86901fe661ae5da3739215d93922412df4b24c859a2RoboErik
87042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
87142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
87201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
87301fe661ae5da3739215d93922412df4b24c859a2RoboErik
87401fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
87524762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
87624762bffc3358762666079cd802040a316b3260dRoboErik            MediaController controller = mController.get();
87724762bffc3358762666079cd802040a316b3260dRoboErik            if (controller != null) {
87824762bffc3358762666079cd802040a316b3260dRoboErik                controller.postMessage(MSG_DESTROYED, null, null);
87924762bffc3358762666079cd802040a316b3260dRoboErik            }
88024762bffc3358762666079cd802040a316b3260dRoboErik        }
88124762bffc3358762666079cd802040a316b3260dRoboErik
88224762bffc3358762666079cd802040a316b3260dRoboErik        @Override
8838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
88442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
886c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
88701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
88801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
88901fe661ae5da3739215d93922412df4b24c859a2RoboErik
89001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
8918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
89242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
894c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
89501fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
89601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
89701fe661ae5da3739215d93922412df4b24c859a2RoboErik
89801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
8998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
90042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
902c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
90301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
90401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
90501fe661ae5da3739215d93922412df4b24c859a2RoboErik
90619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
907f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
908a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            List<MediaSession.Item> queue = parceledQueue.getList();
909f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
910f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
911f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
912f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
913f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
914f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
915f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
916f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
917f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
918f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
919f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
920f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
921f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
922f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
923f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
924f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
925f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
926f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
927f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
928f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
929f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
930f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
931f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
93219c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
93319c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
93419c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
935a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                AudioInfo info = new AudioInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
93619c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
93719c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
93819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
93919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
94019c9518f6a817d53d5234de0020313cab6950b2fRoboErik
94101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
94201fe661ae5da3739215d93922412df4b24c859a2RoboErik
94301fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
94442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
94501fe661ae5da3739215d93922412df4b24c859a2RoboErik
94642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
9478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
9488ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
94901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
95001fe661ae5da3739215d93922412df4b24c859a2RoboErik
95101fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
95201fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
95301fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
9548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
95579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
95601fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
957c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
958c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
959c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
960c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
961c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
962c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
963f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
964a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                    mCallback.onQueueChanged((List<MediaSession.Item>) msg.obj);
965f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
966f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
967f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
968f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
969f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
970f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
971f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
97219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
973a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                    mCallback.onAudioInfoChanged((AudioInfo) msg.obj);
97419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
97524762bffc3358762666079cd802040a316b3260dRoboErik                case MSG_DESTROYED:
97624762bffc3358762666079cd802040a316b3260dRoboErik                    mCallback.onSessionDestroyed();
97724762bffc3358762666079cd802040a316b3260dRoboErik                    break;
97801fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
97901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
9808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
9818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
9828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
9838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
98401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
98501fe661ae5da3739215d93922412df4b24c859a2RoboErik
98601fe661ae5da3739215d93922412df4b24c859a2RoboErik}
987