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    /**
5078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
50814f717a506a0d22facbec07386b06634e0c6a8eeRoboErik     * 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        /**
606f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific {@link Uri}.
607f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
6083625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik         * @param mediaId 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         */
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        /**
647a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * Play an item with a specific id in the play queue. If you specify an
648a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik         * id that is not in the play queue, the behavior is undefined.
649f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
6503625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        public void skipToQueueItem(long id) {
651f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
6523625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                mSessionBinder.skipToQueueItem(id);
653f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
654a66c40bf6e0fb79ead6d8a9fc29c5671fa7b1206RoboErik                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
655f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
656f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
657f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
658f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
659c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
660c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
661c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
662c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
663c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
664c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
665c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
666c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
667c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
668c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
669c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
670c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
671c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
672c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
673c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
674c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
675c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
676c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
677c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
678c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
679c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
680c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
681c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
682c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
683c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
685c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
686c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
687c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
688c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
689c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
690c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
691c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
693c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
694c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
695c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
696c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
697c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
698c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
699c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
700c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
701c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
702c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
703c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
704c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
705c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
706c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
707c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
708c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
709c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
710c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
711c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
712c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
713c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
714c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
715c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
716c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
717c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
718c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
719c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
720c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
721c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
722c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
723c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
731c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
732c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
733c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
734c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
735c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
736c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
737c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
738c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
739c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
740c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
741c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
742c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
743c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
744c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
745c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
746c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
747c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
748c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
749c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
750c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
751c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
752c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
753c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
754c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
755f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
756f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
757f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
758f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
759f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
760f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
761f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
762f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
763f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
764f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
765f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
766f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
767f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
768f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
769f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
770f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
771f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
772f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
773f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
774f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
775f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
776f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
777f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
778f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
779f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
780f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
781f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
782f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
783f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
784f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
785f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
786f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
787f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
788f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
789f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
7908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
79101fe661ae5da3739215d93922412df4b24c859a2RoboErik
792ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
793d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * Holds information about the current playback and how audio is handled for
794d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik     * this session.
795ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
796d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik    public static final class PlaybackInfo {
797d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
798d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses remote playback.
799d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
800d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_REMOTE = 2;
801d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        /**
802d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * The session uses local playback.
803d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         */
804d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public static final int PLAYBACK_TYPE_LOCAL = 1;
805d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik
806ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
807ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
808ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
809ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
8109db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
811ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
812ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
813ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
814ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
815d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
816ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
8179db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
818ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
819ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
820ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
821ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
822ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
823ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
824d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * Get the type of playback which affects volume handling. One of:
825ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
826d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
827d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
828ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
829ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
830d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * @return The type of playback this session is using.
831ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
832d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik        public int getPlaybackType() {
833ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
834ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
835ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
836ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
8379db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
8389db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
839d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
8409db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
841ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
8429db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
843ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
8449db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
8459db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
846ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
847ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
848ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
849ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
850ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
851ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
852ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
853ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
854ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
855ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
856ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
857ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
858ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
859ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
860ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
861ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
862ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
863ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
864ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
865ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
866ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
867ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
868ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
869ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
870ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
871ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
872ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
873ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
874ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
875ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
876ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
877ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
878ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
879ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
880ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
881ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
88207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
88342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
88401fe661ae5da3739215d93922412df4b24c859a2RoboErik
88542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
88642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
88701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
88801fe661ae5da3739215d93922412df4b24c859a2RoboErik
88901fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
89024762bffc3358762666079cd802040a316b3260dRoboErik        public void onSessionDestroyed() {
89124762bffc3358762666079cd802040a316b3260dRoboErik            MediaController controller = mController.get();
89224762bffc3358762666079cd802040a316b3260dRoboErik            if (controller != null) {
89324762bffc3358762666079cd802040a316b3260dRoboErik                controller.postMessage(MSG_DESTROYED, null, null);
89424762bffc3358762666079cd802040a316b3260dRoboErik            }
89524762bffc3358762666079cd802040a316b3260dRoboErik        }
89624762bffc3358762666079cd802040a316b3260dRoboErik
89724762bffc3358762666079cd802040a316b3260dRoboErik        @Override
8988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
89942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
901c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
90201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
90301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
90401fe661ae5da3739215d93922412df4b24c859a2RoboErik
90501fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
90742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
909c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
91001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
91101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
91201fe661ae5da3739215d93922412df4b24c859a2RoboErik
91301fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
9148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
91542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
9168ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
917c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
91801fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
91901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
92001fe661ae5da3739215d93922412df4b24c859a2RoboErik
92119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
922f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
92303fce072cac092923e10a6b5f09fcde333375f9eRoboErik            List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
92403fce072cac092923e10a6b5f09fcde333375f9eRoboErik                    .getList();
925f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
926f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
927f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
928f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
929f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
930f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
931f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
932f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
933f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
934f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
935f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
936f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
937f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
938f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
939f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
940f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
941f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
942f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
943f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
944f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
945f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
946f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
947f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
94819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
94919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
95019c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
951d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
95219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
95319c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
95419c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
95519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
95619c9518f6a817d53d5234de0020313cab6950b2fRoboErik
95701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
95801fe661ae5da3739215d93922412df4b24c859a2RoboErik
95901fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
96042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
9613e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik        private boolean mRegistered = false;
96201fe661ae5da3739215d93922412df4b24c859a2RoboErik
96342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
9648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
9658ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
96601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
96701fe661ae5da3739215d93922412df4b24c859a2RoboErik
96801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
96901fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
9703e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            if (!mRegistered) {
9713e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik                return;
9723e0cfcda31301e53c92cb2f91ed8be1cd6efd8bcRoboErik            }
97301fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
9748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
97579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
97601fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
977c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
978c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
979c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
980c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
981c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
982c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
983f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
9843625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                    mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
985f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
986f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
987f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
988f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
989f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
990f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
991f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
99219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
993d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik                    mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
99419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
99524762bffc3358762666079cd802040a316b3260dRoboErik                case MSG_DESTROYED:
99624762bffc3358762666079cd802040a316b3260dRoboErik                    mCallback.onSessionDestroyed();
99724762bffc3358762666079cd802040a316b3260dRoboErik                    break;
99801fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
99901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
10008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
10018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
10028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
10038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
100401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
100501fe661ae5da3739215d93922412df4b24c859a2RoboErik
100601fe661ae5da3739215d93922412df4b24c859a2RoboErik}
1007