MediaController.java revision dba34ba35cd2042d9a8fecfda56e2abe7a680bad
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;
2119c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.AudioManager;
2242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
23c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErikimport android.media.Rating;
24ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErikimport android.media.VolumeProvider;
2501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
2601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
2701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
2801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
3201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
3401fe661ae5da3739215d93922412df4b24c859a2RoboErik
358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
3601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
3701fe661ae5da3739215d93922412df4b24c859a2RoboErik
3801fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
3901fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
4001fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
4101fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
4201fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
45dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown * you have a {@link MediaSession.Token} from the session owner.
4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
4801fe661ae5da3739215d93922412df4b24c859a2RoboErik */
4942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
50bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    private static final String TAG = "MediaController";
5101fe661ae5da3739215d93922412df4b24c859a2RoboErik
528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
53c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
54c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_ROUTE = 4;
5619c9518f6a817d53d5234de0020313cab6950b2fRoboErik    private static final int MSG_UPDATE_VOLUME = 5;
5701fe661ae5da3739215d93922412df4b24c859a2RoboErik
5807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
5901fe661ae5da3739215d93922412df4b24c859a2RoboErik
608ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
618ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
6201fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
6301fe661ae5da3739215d93922412df4b24c859a2RoboErik
6401fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
6573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    private MediaSessionInfo mInfo;
6601fe661ae5da3739215d93922412df4b24c859a2RoboErik
67c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private TransportControls mTransportController;
688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
6942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    private MediaController(ISessionController sessionBinder) {
708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mSessionBinder = sessionBinder;
71c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        mTransportController = new TransportControls();
728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
738ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
7401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
7742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    public static MediaController fromBinder(ISessionController sessionBinder) {
78d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return new MediaController(sessionBinder);
7901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
8001fe661ae5da3739215d93922412df4b24c859a2RoboErik
8101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
82bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * Get a new media controller from a session token which may have
83bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * been obtained from another process.  If successful the controller returned
84bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * will be connected to the session that generated the token.
858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
86bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param token The session token to control.
87bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @return A controller for the session or null if inaccessible.
8801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
89dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown    public static MediaController fromToken(@NonNull MediaSession.Token token) {
908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return fromBinder(token.getBinder());
9101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
9201fe661ae5da3739215d93922412df4b24c859a2RoboErik
9301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
94c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get a {@link TransportControls} instance for this session.
9501fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
96c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return A controls instance
9701fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
98bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @NonNull TransportControls getTransportControls() {
998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return mTransportController;
10001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
10101fe661ae5da3739215d93922412df4b24c859a2RoboErik
10201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
10379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
10479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
10501fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
10679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
10779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
10801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
109bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
11079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
11179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
11279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
11379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
11479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
11501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
11601fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
11779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
11801fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
119c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
12001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
12179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
12201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
12301fe661ae5da3739215d93922412df4b24c859a2RoboErik
12401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
125c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
126c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
127c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
128c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
129bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
130c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
131c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
132c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
133c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
134c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
135c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
136c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
137c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
138c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
139c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
140c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
141c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
142c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
143bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
144c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
145c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
146c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
147c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
148c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
151c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
152c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
153c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
154c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
161c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
171c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
172c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
173c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
174c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
175c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
17673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * Get the flags for this session.
17773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
17873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
17973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @hide
18073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
18173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    public long getFlags() {
18273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
18373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
18473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
18573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
18673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
18773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
18873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
18973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
19073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
191ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Get the current volume info for this session.
192ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
193ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * @return The current volume info or null.
194ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
195bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable VolumeInfo getVolumeInfo() {
196ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
197ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
198ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
199ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
200ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
201ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
202ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            Log.wtf(TAG, "Error calling getVolumeInfo.", e);
203ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
204ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
205ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
206ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
207ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
20819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * Set the volume of the stream or output this session is playing on. The
20919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * command will be ignored if it does not support
21019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
21119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
21219c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
21319c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @see #getVolumeInfo()
21419c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
21519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
21619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
21719c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
21819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
21919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            mSessionBinder.setVolumeTo(value, flags);
22019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
22119c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
22219c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
22319c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
22419c9518f6a817d53d5234de0020313cab6950b2fRoboErik
22519c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
22619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * Adjust the volume of the stream or output this session is playing on.
22719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * Negative values will lower the volume. The command will be ignored if it
22819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * does not support {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
22919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
23019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
23119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
23219c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @see #getVolumeInfo()
23319c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param delta The number of steps to adjust the volume by.
23419c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
23519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
23619c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void adjustVolumeBy(int delta, int flags) {
23719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
23819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            mSessionBinder.adjustVolumeBy(delta, flags);
23919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
24019c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
24119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
24219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
24319c9518f6a817d53d5234de0020313cab6950b2fRoboErik
24419c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
24501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the Session. Updates will be
24601fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
24701fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
248bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
24901fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
250bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback) {
251bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        addCallback(callback, null);
25201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
25301fe661ae5da3739215d93922412df4b24c859a2RoboErik
25401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
25501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the session. Updates will be
2568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
25701fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
258bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
2598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
260bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
26101fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
262bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
263bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
264bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
265bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
26601fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
26701fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
26801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
26901fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
270bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
27101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
27201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
27301fe661ae5da3739215d93922412df4b24c859a2RoboErik
27401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
27501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Stop receiving updates on the specified callback. If an update has
27601fe661ae5da3739215d93922412df4b24c859a2RoboErik     * already been posted you may still receive it after calling this method.
27701fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
278bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
27901fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
280bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void removeCallback(@NonNull Callback callback) {
281bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
282bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
283bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
28401fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
285bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
28601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
28701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
28801fe661ae5da3739215d93922412df4b24c859a2RoboErik
2898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
2908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
2918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
2928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
2938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
2948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
2958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param params Any parameters to include with the command
2968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
2978ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
298bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void sendControlCommand(@NonNull String command, @Nullable Bundle params,
299bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
3018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
3028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
3048ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mSessionBinder.sendCommand(command, params, cb);
3058ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
3068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
3078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
3098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
31007c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
31107c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     * Request that the route picker be shown for this session. This should
31207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     * generally be called in response to a user action.
31342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik     *
31442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik     * @hide
31507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     */
31607c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    public void showRoutePicker() {
31707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        try {
31807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik            mSessionBinder.showRoutePicker();
31907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        } catch (RemoteException e) {
32007c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik            Log.d(TAG, "Dead object in showRoutePicker", e);
32107c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        }
32207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    }
32307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik
324fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    /**
325fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * Get the info for the session this controller is connected to.
326fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
327fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @return The session info for the connected session.
328fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
329fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
330fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    public MediaSessionInfo getSessionInfo() {
33173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        if (mInfo == null) {
33273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
33373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                mInfo = mSessionBinder.getSessionInfo();
33473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
33573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                Log.e(TAG, "Error in getSessionInfo.", e);
33673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
337fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
33873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return mInfo;
339fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
340fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
34101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
34201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
34301fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
34407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
34501fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
34601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
34701fe661ae5da3739215d93922412df4b24c859a2RoboErik
34801fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
3498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
35001fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
35101fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
35201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
3548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
35501fe661ae5da3739215d93922412df4b24c859a2RoboErik
35601fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
35701fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
35801fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
35901fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
36001fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
361d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
36201fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
36301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
36401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik
3668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
367d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
3688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
3708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
3718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
372d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
37401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
375d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
376d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
377d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
378d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
379d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
380d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
381d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
382d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
383d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
38401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
38501fe661ae5da3739215d93922412df4b24c859a2RoboErik
3868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
3878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
3888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
38901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
3928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
3948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
39501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
39701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
39801fe661ae5da3739215d93922412df4b24c859a2RoboErik
399c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
4008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
4018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
402c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
4038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
40401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
40501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
40601fe661ae5da3739215d93922412df4b24c859a2RoboErik
40701fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
4088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
4098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * registered using {@link #addCallback}
41001fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
41101fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
41201fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
4138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
4148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
4158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
41601fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
417bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param event The event from the session.
418bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param extras Optional parameters for the event, may be null.
41901fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
420bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
42101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
42201fe661ae5da3739215d93922412df4b24c859a2RoboErik
42301fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
42401fe661ae5da3739215d93922412df4b24c859a2RoboErik         * Override to handle route changes for this session.
42501fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
42642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik         * @param route The new route
42742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik         * @hide
42801fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
42907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        public void onRouteChanged(RouteInfo route) {
43001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
431c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
432c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
433c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
434c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
435c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
436c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
437bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
438c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
439c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
440c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
441c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
442c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
443bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param metadata The current metadata for the session or null if none.
444c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
445c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
446bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
447c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
44819c9518f6a817d53d5234de0020313cab6950b2fRoboErik
44919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        /**
45019c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * Override to handle changes to the volume info.
45119c9518f6a817d53d5234de0020313cab6950b2fRoboErik         *
45219c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * @param info The current volume info for this session.
45319c9518f6a817d53d5234de0020313cab6950b2fRoboErik         */
45419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(VolumeInfo info) {
45519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
456c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
457c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
458c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
459c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
460c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
461c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
462c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
463c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
464c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
465c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
466c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
467c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
468c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
469c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
470c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
471c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
472c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
473c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
474c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
475c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
476c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
477c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
478c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
479c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
480c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
481c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
482c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
483c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
484c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
485c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
486c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
487c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
488c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
489c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
490c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
491c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
492c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
493c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
494c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
495c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
496c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
497c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
498c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
499c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
500c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
501c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
502c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
503c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
504c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
505c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
506c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
507c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
508c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
509c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
510c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
511c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
512c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
513c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
514c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
515c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
516c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
517c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
518c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
519c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
520c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
521c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
522c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
523c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
524c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
525c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
526c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
527c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
528c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
529c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
530c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
531c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
532c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
533c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
534c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
535c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
536c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
537c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
538c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
539c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
540c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
541c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
542c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
543c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
544c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
545c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
546c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
547c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
548c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
549c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
550c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
551c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
552c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
553c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
554c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
555c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
556c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
557c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
558c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
559c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
560c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
561c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
562c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
563c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
564c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
565c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
566c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
567c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
568c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
569c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
570c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
571c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
572c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
573c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
574c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
575c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
5768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
57701fe661ae5da3739215d93922412df4b24c859a2RoboErik
578ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
579ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Holds information about the way volume is handled for this session.
580ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
581ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    public static final class VolumeInfo {
582ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
583ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mAudioStream;
584ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
585ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
586ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
587ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
588ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
589ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
590ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
591ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public VolumeInfo(int type, int stream, int control, int max, int current) {
592ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
593ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mAudioStream = stream;
594ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
595ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
596ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
597ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
598ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
599ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
600ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume handling, either local or remote. One of:
601ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
60219c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_LOCAL}</li>
60319c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_REMOTE}</li>
604ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
605ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
606ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume handling this session is using.
607ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
608ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeType() {
609ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
610ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
611ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
612ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
613ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the stream this is currently controlling volume on. When the volume
61419c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not
615ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * have meaning and should be ignored.
616ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
617ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The stream this session is playing on.
618ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
619ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getAudioStream() {
620ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mAudioStream;
621ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
622ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
623ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
624ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
625ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
626ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
627ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
628ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
629ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
630ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
631ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
632ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
633ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
634ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
635ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
636ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
637ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
638ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
639ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
640ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
641ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
642ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
643ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
644ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
645ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
646ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
647ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
648ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
649ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
650ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
651ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
652ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
653ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
654ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
655ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
656ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
65707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
65842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
65901fe661ae5da3739215d93922412df4b24c859a2RoboErik
66042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
66142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
66201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
66301fe661ae5da3739215d93922412df4b24c859a2RoboErik
66401fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6658ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
66642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6678ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
668c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
66901fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
67001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
67101fe661ae5da3739215d93922412df4b24c859a2RoboErik
67201fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
67307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        public void onRouteChanged(RouteInfo route) {
67442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
676c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_ROUTE, route, null);
67701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
67801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
67901fe661ae5da3739215d93922412df4b24c859a2RoboErik
68001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
68242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
684c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
68501fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
68601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
68701fe661ae5da3739215d93922412df4b24c859a2RoboErik
68801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
69042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
692c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
69301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
69401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
69501fe661ae5da3739215d93922412df4b24c859a2RoboErik
69619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
69719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
69819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
69919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
70019c9518f6a817d53d5234de0020313cab6950b2fRoboErik                VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType,
70119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
70219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
70319c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
70419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
70519c9518f6a817d53d5234de0020313cab6950b2fRoboErik
70601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
70701fe661ae5da3739215d93922412df4b24c859a2RoboErik
70801fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
70942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
71001fe661ae5da3739215d93922412df4b24c859a2RoboErik
71142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
7128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
7138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
71401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
71501fe661ae5da3739215d93922412df4b24c859a2RoboErik
71601fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
71701fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
71801fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
7198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
72079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
72101fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
7228ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_ROUTE:
72307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik                    mCallback.onRouteChanged((RouteInfo) msg.obj);
724c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
725c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
726c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
727c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
728c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
729c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
730c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
73119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
73219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
73319c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
73401fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
73501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
7368ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
7378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
7388ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
7398ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
74001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
74101fe661ae5da3739215d93922412df4b24c859a2RoboErik
74201fe661ae5da3739215d93922412df4b24c859a2RoboErik}
743