101fe661ae5da3739215d93922412df4b24c859a2RoboErik/*
201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project
301fe661ae5da3739215d93922412df4b24c859a2RoboErik *
401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License");
501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License.
601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at
701fe661ae5da3739215d93922412df4b24c859a2RoboErik *
801fe661ae5da3739215d93922412df4b24c859a2RoboErik *      http://www.apache.org/licenses/LICENSE-2.0
901fe661ae5da3739215d93922412df4b24c859a2RoboErik *
1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software
1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS,
1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and
1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License.
1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
1601fe661ae5da3739215d93922412df4b24c859a2RoboErik
172f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikpackage android.media.session;
1801fe661ae5da3739215d93922412df4b24c859a2RoboErik
19bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.NonNull;
20bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.Nullable;
21e34c09daf89fb888fe2638e71758573462d85173RoboErikimport android.app.PendingIntent;
22031149cd5f22bd858142633c7a763450f42793f7RoboErikimport android.content.Context;
23f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.content.pm.ParceledListSlice;
249db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErikimport android.media.AudioAttributes;
2519c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.AudioManager;
2642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
27c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErikimport android.media.Rating;
28ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErikimport android.media.VolumeProvider;
29f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.net.Uri;
3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
3201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
3401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
3601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
3701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
3901fe661ae5da3739215d93922412df4b24c859a2RoboErik
408ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
4101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
42f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport java.util.List;
4301fe661ae5da3739215d93922412df4b24c859a2RoboErik
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
4501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
4801fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
50f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an
51f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * enabled notification listener or by getting a {@link MediaSession.Token}
52f2133474afce7808541c92a1e5c78eb8b0950329RoboErik * directly 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    /**
12279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
12379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
12401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
12579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
12679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
12701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
128bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
12979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
13079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
13179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
13279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
13379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
13401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
13501fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
13679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
13701fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
138c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
13901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
14079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
14101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
14201fe661ae5da3739215d93922412df4b24c859a2RoboErik
14301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
144c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
145c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
146c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
147c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
148bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
151c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
152c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
153c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
154c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
161c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
162bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
171c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
172477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * Get the current play queue for this session if one is set. If you only
173477d1197c3c25c01ace7ea4494437c23720a2eb3RoboErik     * care about the current item {@link #getMetadata()} should be used.
174f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     *
175f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * @return The current play queue or null.
176f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     */
1773625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik    public @Nullable List<MediaSession.QueueItem> getQueue() {
178f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        try {
179f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            ParceledListSlice queue = mSessionBinder.getQueue();
180f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (queue != null) {
181f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                return queue.getList();
182f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
183f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        } catch (RemoteException e) {
184f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            Log.wtf(TAG, "Error calling getQueue.", e);
185f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
186f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        return null;
187f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    }
188f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
189f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    /**
19051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the queue title for this session.
19151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
19251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable CharSequence getQueueTitle() {
19351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
19451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getQueueTitle();
19551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
19651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getQueueTitle", e);
19751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
19851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
19951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
20051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
20151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
20251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     * Get the extras for this session.
20351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen     */
20451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    public @Nullable Bundle getExtras() {
20551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        try {
20651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            return mSessionBinder.getExtras();
20751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        } catch (RemoteException e) {
20851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen            Log.wtf(TAG, "Error calling getExtras", e);
20951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        }
21051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen        return null;
21151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    }
21251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen
21351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen    /**
214c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
215c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
216c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
217c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
218c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
219c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
220c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
221c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
222c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
223c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
224c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
225c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
226c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
227c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
228c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
229c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
230c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
231c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
232c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
233c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
234c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
235c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
236c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
23776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the flags for this session. Flags are defined in {@link MediaSession}.
23873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
23973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
24073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
24176fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @MediaSession.SessionFlags long getFlags() {
24273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
24373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
24473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
24573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
24673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
24773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
24873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
24973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
25073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
251d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Get the current playback info for this session.
252ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
253d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @return The current playback info or null.
254ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
255d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PlaybackInfo getPlaybackInfo() {
256ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
257ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
258d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik            return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
259ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
260ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
261ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
262a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik            Log.wtf(TAG, "Error calling getAudioInfo.", e);
263ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
264ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
265ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
266ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
267ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
268e34c09daf89fb888fe2638e71758573462d85173RoboErik     * Get an intent for launching UI associated with this session if one
269e34c09daf89fb888fe2638e71758573462d85173RoboErik     * exists.
270e34c09daf89fb888fe2638e71758573462d85173RoboErik     *
271e34c09daf89fb888fe2638e71758573462d85173RoboErik     * @return A {@link PendingIntent} to launch UI or null.
272e34c09daf89fb888fe2638e71758573462d85173RoboErik     */
273d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public @Nullable PendingIntent getSessionActivity() {
274e34c09daf89fb888fe2638e71758573462d85173RoboErik        try {
275e34c09daf89fb888fe2638e71758573462d85173RoboErik            return mSessionBinder.getLaunchPendingIntent();
276e34c09daf89fb888fe2638e71758573462d85173RoboErik        } catch (RemoteException e) {
277e34c09daf89fb888fe2638e71758573462d85173RoboErik            Log.wtf(TAG, "Error calling getPendingIntent.", e);
278e34c09daf89fb888fe2638e71758573462d85173RoboErik        }
279e34c09daf89fb888fe2638e71758573462d85173RoboErik        return null;
280e34c09daf89fb888fe2638e71758573462d85173RoboErik    }
281e34c09daf89fb888fe2638e71758573462d85173RoboErik
282e34c09daf89fb888fe2638e71758573462d85173RoboErik    /**
28376fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * Get the token for the session this is connected to.
28476fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     *
28576fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     * @return The token for the connected session.
28676fca4e177e18b591439fdff64b8f5242a5122d0RoboErik     */
28776fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    public @NonNull MediaSession.Token getSessionToken() {
28876fca4e177e18b591439fdff64b8f5242a5122d0RoboErik        return mToken;
28976fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    }
29076fca4e177e18b591439fdff64b8f5242a5122d0RoboErik
29176fca4e177e18b591439fdff64b8f5242a5122d0RoboErik    /**
2929db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Set the volume of the output this session is playing on. The command will
2939db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * be ignored if it does not support
29419c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
29519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
29619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
297d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
29819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
299d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @param flags Flags from {@link AudioManager} to include with the volume
300d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     *            request.
30119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
30219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
30319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3040dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
30519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
30619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
30719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
30819c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
30919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
31019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
3119db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Adjust the volume of the output this session is playing on. The direction
3129db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
3131ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
3141ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * The command will be ignored if the session does not support
3151ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
31619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
31719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
31819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
319d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * @see #getPlaybackInfo()
3201ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * @param direction The direction to adjust the volume in.
32119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
32219c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
3231ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik    public void adjustVolume(int direction, int flags) {
32419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
3250dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
32619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
32719c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
32819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
32919c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
33019c9518f6a817d53d5234de0020313cab6950b2fRoboErik
33119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
33214f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Registers a callback to receive updates from the Session. Updates will be
33301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
33401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
335bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
33601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
33714f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void registerCallback(@NonNull Callback callback) {
33814f717a506a0d22facbec07386b06634e0c6a8eeRoboErik        registerCallback(callback, null);
33901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
34001fe661ae5da3739215d93922412df4b24c859a2RoboErik
34101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
34214f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Registers a callback to receive updates from the session. Updates will be
3438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
34401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
345bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
3468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
347bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
34801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
34914f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
350bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
351bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
352bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
35301fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
35401fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
35501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
35601fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
357bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
35801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
35901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
36001fe661ae5da3739215d93922412df4b24c859a2RoboErik
36101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
36214f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * Unregisters the specified callback. If an update has already been posted
36314f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * you may still receive it after calling this method.
36401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
365bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
36601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
36714f717a506a0d22facbec07386b06634e0c6a8eeRoboErik    public void unregisterCallback(@NonNull Callback callback) {
368bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
369bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
370bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
37101fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
372bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
37401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
37501fe661ae5da3739215d93922412df4b24c859a2RoboErik
3768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
3778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
3788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
3798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
3808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
3818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
382f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal     * @param args Any parameters to include with the command
3838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
3848ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
385f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal    public void sendCommand(@NonNull String command, @Nullable Bundle args,
386bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
3888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
3898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
391f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            mSessionBinder.sendCommand(command, args, cb);
3928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
3948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
3968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
39707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
398aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session owner's package name.
399fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
400aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The package name of of the session owner.
401aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     */
402aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getPackageName() {
403aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mPackageName == null) {
404aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            try {
405aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mPackageName = mSessionBinder.getPackageName();
406aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            } catch (RemoteException e) {
407aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getPackageName.", e);
408aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik            }
409aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        }
410aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mPackageName;
411aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    }
412aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik
413aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    /**
414aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * Get the session's tag for debugging purposes.
415aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     *
416aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik     * @return The session's tag.
417fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
418fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
419aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik    public String getTag() {
420aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        if (mTag == null) {
42173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
422aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                mTag = mSessionBinder.getTag();
42373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
424aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Dead object in getTag.", e);
42573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
426fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
427aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik        return mTag;
428fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
429fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
43001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
43101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
43201fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
43307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
43401fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
43501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
43601fe661ae5da3739215d93922412df4b24c859a2RoboErik
437b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    /**
438b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer     * @hide
439b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer     */
440b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    public boolean controlsSameSession(MediaController other) {
441b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer        if (other == null) return false;
442b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer        return mSessionBinder.asBinder() == other.getSessionBinder().asBinder();
443b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer    }
444b5245d8904160917942fe91fc8db7f1dfa098fc0Christoph Studer
44501fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
4468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
44701fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
44801fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
44901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4508ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
4518ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
4523e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik        holder.mRegistered = true;
45301fe661ae5da3739215d93922412df4b24c859a2RoboErik
45401fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
45501fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
45601fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
45701fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
45801fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
459d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
46001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
46101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
46201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
46301fe661ae5da3739215d93922412df4b24c859a2RoboErik
4648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
465d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
4668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4678ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
470d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
4713e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik                handler.mRegistered = false;
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    /**
507bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock     * Callback for receiving updates from the session. A Callback can be
508bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock     * registered using {@link #registerCallback}.
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.
5523625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik         * @see MediaSession.QueueItem
553f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
5543625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void onQueueChanged(@Nullable List<MediaSession.QueueItem> 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         */
580d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public void onAudioInfoChanged(PlaybackInfo 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        /**
606c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         * Request that the player start playback for a specific media id.
607f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
608c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         * @param mediaId The id 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         */
6123625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void playFromMediaId(String mediaId, Bundle extras) {
6133625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik            if (TextUtils.isEmpty(mediaId)) {
6143625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                throw new IllegalArgumentException(
6153625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                        "You must specify a non-empty String for playFromMediaId.");
616f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
617f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
6183625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                mSessionBinder.playFromMediaId(mediaId, extras);
619f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
6203625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
621f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
622f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
623f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
624f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
625f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific search query.
6264b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * An empty or null query should be treated as a request to play any
6274b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * music.
628f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
629f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param query The search query.
6304b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         * @param extras Optional extras that can include extra information
6314b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik         *            about the query.
632f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
633f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playFromSearch(String query, Bundle extras) {
6344b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik            if (query == null) {
6354b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                // This is to remain compatible with
6364b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
6374b253d2bcd62ea2a9afb067c1f1363fa7b752185RoboErik                query = "";
638f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
639f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
640f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playFromSearch(query, extras);
641f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
642f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + query + ").", e);
643f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
644f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
645f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
646f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
647c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         * Request that the player start playback for a specific {@link Uri}.
648c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         *
649c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         * @param uri  The URI of the requested media.
650c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         * @param extras Optional extras that can include extra information about the media item
651c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         *               to be played.
652c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand         */
653c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand        public void playFromUri(Uri uri, Bundle extras) {
654c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand            if (uri == null || Uri.EMPTY.equals(uri)) {
655c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand                throw new IllegalArgumentException(
656c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand                        "You must specify a non-empty Uri for playFromUri.");
657c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand            }
658c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand            try {
659c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand                mSessionBinder.playFromUri(uri, extras);
660c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand            } catch (RemoteException e) {
661c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
662c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand            }
663c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand        }
664c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand
665c2045470a343923b0d74ddbd6a6b80fcf6652d66P.Y. Laligand        /**
666a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Play an item with a specific id in the play queue. If you specify an
667a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * id that is not in the play queue, the behavior is undefined.
668f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
6693625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void skipToQueueItem(long id) {
670f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
6713625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                mSessionBinder.skipToQueueItem(id);
672f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
673a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
674f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
675f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
676f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
677f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
678c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
679c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
680c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
681c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
682c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
683c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
685c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
686c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
687c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
688c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
689c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
690c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
691c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
693c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
694c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
695c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
696c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
697c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
698c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
699c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
700c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
701c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
702c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
703c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
704c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
705c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
706c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
707c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
708c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
709c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
710c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
711c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
712c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
713c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
714c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
715c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
716c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
717c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
718c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
719c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
720c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
721c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
722c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
723c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
731c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
732c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
733c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
734c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
735c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
736c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
737c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
738c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
739c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
740c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
741c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
742c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
743c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
744c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
745c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
746c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
747c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
748c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
749c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
750c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
751c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
752c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
753c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
754c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
755c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
756c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
757c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
758c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
759c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
760c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
761c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
762c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
763c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
764c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
765c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
766c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
767c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
768c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
769c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
770c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
771c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
772c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
773c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
774f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
775f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
776f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
777f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
778f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
779f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
780f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
781f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
782f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
783f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
784f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
785f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
786f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
787f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
788f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
789f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
790f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
791f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
792f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
793f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
794f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
795f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
796f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
797f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
798f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
799f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
800f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
801f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
802f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
803f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
804f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
805f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
806f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
807f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
808f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
8098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
81001fe661ae5da3739215d93922412df4b24c859a2RoboErik
811ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
812d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Holds information about the current playback and how audio is handled for
813d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * this session.
814ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
815d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public static final class PlaybackInfo {
816d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
817d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses remote playback.
818d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
819d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_REMOTE = 2;
820d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
821d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses local playback.
822d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
823d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_LOCAL = 1;
824d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik
825ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
826ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
827ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
828ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
8299db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
830ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
831ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
832ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
833ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
834d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
835ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
8369db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
837ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
838ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
839ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
840ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
841ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
842ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
843d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * Get the type of playback which affects volume handling. One of:
844ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
845d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
846d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
847ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
848ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
849d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * @return The type of playback this session is using.
850ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
851d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public int getPlaybackType() {
852ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
853ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
854ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
855ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
8569db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
8579db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
858d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
8599db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
860ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
8619db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
862ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
8639db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
8649db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
865ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
866ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
867ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
868ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
869ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
870ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
871ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
872ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
873ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
874ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
875ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
876ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
877ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
878ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
879ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
880ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
881ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
882ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
883ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
884ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
885ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
886ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
887ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
888ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
889ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
890ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
891ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
892ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
893ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
894ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
895ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
896ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
897ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
898ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
899ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
900ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
90107c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
90242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
90301fe661ae5da3739215d93922412df4b24c859a2RoboErik
90442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
90542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
90601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
90701fe661ae5da3739215d93922412df4b24c859a2RoboErik
90801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
90924762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
91024762bffc3358762666079cd802040a316b3260dRoboErik            MediaController controller = mController.get();
91124762bffc3358762666079cd802040a316b3260dRoboErik            if (controller != null) {
91224762bffc3358762666079cd802040a316b3260dRoboErik                controller.postMessage(MSG_DESTROYED, null, null);
91324762bffc3358762666079cd802040a316b3260dRoboErik            }
91424762bffc3358762666079cd802040a316b3260dRoboErik        }
91524762bffc3358762666079cd802040a316b3260dRoboErik
91624762bffc3358762666079cd802040a316b3260dRoboErik        @Override
9178ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
91842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
920c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
92101fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
92201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
92301fe661ae5da3739215d93922412df4b24c859a2RoboErik
92401fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9258ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
92642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9278ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
928c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
92901fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
93001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
93101fe661ae5da3739215d93922412df4b24c859a2RoboErik
93201fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
93442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
936c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
93701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
93801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
93901fe661ae5da3739215d93922412df4b24c859a2RoboErik
94019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
941f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
94203fce072cac092923e10a6b5f09fcde333375f9eRoboErik            List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
94303fce072cac092923e10a6b5f09fcde333375f9eRoboErik                    .getList();
944f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
945f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
946f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
947f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
948f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
949f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
950f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
951f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
952f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
953f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
954f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
955f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
956f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
957f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
958f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
959f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
960f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
961f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
962f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
963f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
964f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
965f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
966f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
96719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
96819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
96919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
970d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
97119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
97219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
97319c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
97419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
97519c9518f6a817d53d5234de0020313cab6950b2fRoboErik
97601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
97701fe661ae5da3739215d93922412df4b24c859a2RoboErik
97801fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
97942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
9803e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik        private boolean mRegistered = false;
98101fe661ae5da3739215d93922412df4b24c859a2RoboErik
98242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
9838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
9848ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
98501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
98601fe661ae5da3739215d93922412df4b24c859a2RoboErik
98701fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
98801fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
9893e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            if (!mRegistered) {
9903e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik                return;
9913e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            }
99201fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
9938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
99479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
99501fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
996c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
997c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
998c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
999c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
1000c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
1001c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
1002f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
10033625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                    mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
1004f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
1005f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
1006f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
1007f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
1008f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
1009f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
1010f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
101119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
1012d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                    mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
101319c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
101424762bffc3358762666079cd802040a316b3260dRoboErik                case MSG_DESTROYED:
101524762bffc3358762666079cd802040a316b3260dRoboErik                    mCallback.onSessionDestroyed();
101624762bffc3358762666079cd802040a316b3260dRoboErik                    break;
101701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
101801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
10198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
10208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
1021060b4e1f17db00c8cfb25f4d81726c37d580f723Sungsoo Lim            Message msg = obtainMessage(what, obj);
1022060b4e1f17db00c8cfb25f4d81726c37d580f723Sungsoo Lim            msg.setData(data);
1023060b4e1f17db00c8cfb25f4d81726c37d580f723Sungsoo Lim            msg.sendToTarget();
10248ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
102501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
102601fe661ae5da3739215d93922412df4b24c859a2RoboErik
102701fe661ae5da3739215d93922412df4b24c859a2RoboErik}
1028