MediaController.java revision ef3c9e9b057a5aac2d0d012e8e6385660478e203
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
1942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
20c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErikimport android.media.Rating;
21ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErikimport android.media.VolumeProvider;
2201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
2301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
2401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
2501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
2601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
278ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
2801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErik
328ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
3401fe661ae5da3739215d93922412df4b24c859a2RoboErik
3501fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
3601fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
3701fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
3801fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
3901fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
4101fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
4242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * you have a {@link MediaSessionToken} from the session owner.
4301fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
4501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
4642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
4707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private static final String TAG = "SessionController";
4801fe661ae5da3739215d93922412df4b24c859a2RoboErik
498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
50c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
51c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_ROUTE = 4;
5301fe661ae5da3739215d93922412df4b24c859a2RoboErik
5407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
5501fe661ae5da3739215d93922412df4b24c859a2RoboErik
568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
578ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
5801fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
5901fe661ae5da3739215d93922412df4b24c859a2RoboErik
6001fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
6173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    private MediaSessionInfo mInfo;
6201fe661ae5da3739215d93922412df4b24c859a2RoboErik
63c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private TransportControls mTransportController;
648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
6542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    private MediaController(ISessionController sessionBinder) {
668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mSessionBinder = sessionBinder;
67c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        mTransportController = new TransportControls();
688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
7001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
7201fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
7342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    public static MediaController fromBinder(ISessionController sessionBinder) {
74d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return new MediaController(sessionBinder);
7501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik
7701fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Get a new MediaController for a MediaSessionToken. If successful the
798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * controller returned will be connected to the session that generated the
808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * token.
818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param token The session token to use
838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @return A controller for the session or null
8401fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
8542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    public static MediaController fromToken(MediaSessionToken token) {
868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return fromBinder(token.getBinder());
8701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
8801fe661ae5da3739215d93922412df4b24c859a2RoboErik
8901fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
90c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get a {@link TransportControls} instance for this session.
9101fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
92c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return A controls instance
9301fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
94c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public TransportControls getTransportControls() {
958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return mTransportController;
9601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
9701fe661ae5da3739215d93922412df4b24c859a2RoboErik
9801fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
9979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
10079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
10101fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
10279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
10379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
10401fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
10579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik    public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) {
10679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
10779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
10879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
10979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
11079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
11101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
11201fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
11379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
11401fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
115c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
11601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
11779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
11801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
11901fe661ae5da3739215d93922412df4b24c859a2RoboErik
12001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
121c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
122c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
123c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
124c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
125c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public PlaybackState getPlaybackState() {
126c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
127c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
128c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
129c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
130c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
131c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
132c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
133c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
134c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
135c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
136c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
137c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
138c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
139c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public MediaMetadata getMetadata() {
140c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
141c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
142c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
143c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
144c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
145c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
146c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
147c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
148c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
151c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
152c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
153c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
154c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
161c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
171c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
17273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * Get the flags for this session.
17373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
17473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
17573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @hide
17673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
17773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    public long getFlags() {
17873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
17973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
18073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
18173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
18273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
18373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
18473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
18573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
18673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
187ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Get the current volume info for this session.
188ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
189ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * @return The current volume info or null.
190ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
191ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    public VolumeInfo getVolumeInfo() {
192ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
193ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
194ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
195ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
196ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
197ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
198ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            Log.wtf(TAG, "Error calling getVolumeInfo.", e);
199ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
200ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
201ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
202ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
203ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
20401fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the Session. Updates will be
20501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
20601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
20701fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @param cb The callback object, must not be null
20801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
20901fe661ae5da3739215d93922412df4b24c859a2RoboErik    public void addCallback(Callback cb) {
21001fe661ae5da3739215d93922412df4b24c859a2RoboErik        addCallback(cb, null);
21101fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
21201fe661ae5da3739215d93922412df4b24c859a2RoboErik
21301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
21401fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the session. Updates will be
2158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
21601fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
21701fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @param cb Cannot be null.
2188ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
21901fe661ae5da3739215d93922412df4b24c859a2RoboErik     *            will be used
22001fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
22101fe661ae5da3739215d93922412df4b24c859a2RoboErik    public void addCallback(Callback cb, Handler handler) {
22201fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
22301fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
22401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
22501fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
22601fe661ae5da3739215d93922412df4b24c859a2RoboErik            addCallbackLocked(cb, handler);
22701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
22801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
22901fe661ae5da3739215d93922412df4b24c859a2RoboErik
23001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
23101fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Stop receiving updates on the specified callback. If an update has
23201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * already been posted you may still receive it after calling this method.
23301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
23401fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @param cb The callback to remove
23501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
23601fe661ae5da3739215d93922412df4b24c859a2RoboErik    public void removeCallback(Callback cb) {
23701fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
23801fe661ae5da3739215d93922412df4b24c859a2RoboErik            removeCallbackLocked(cb);
23901fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
24001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
24101fe661ae5da3739215d93922412df4b24c859a2RoboErik
2428ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
2438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
2448ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
2458ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
2468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
2478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
2488ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param params Any parameters to include with the command
2498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
2508ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
25179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik    public void sendControlCommand(String command, Bundle params, ResultReceiver cb) {
2528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
2538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
2548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
2558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
2568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mSessionBinder.sendCommand(command, params, cb);
2578ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
2588ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
2598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
2608ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
2618ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
26207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
26307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     * Request that the route picker be shown for this session. This should
26407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     * generally be called in response to a user action.
26542ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik     *
26642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik     * @hide
26707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik     */
26807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    public void showRoutePicker() {
26907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        try {
27007c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik            mSessionBinder.showRoutePicker();
27107c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        } catch (RemoteException e) {
27207c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik            Log.d(TAG, "Dead object in showRoutePicker", e);
27307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        }
27407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    }
27507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik
276fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    /**
277fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * Get the info for the session this controller is connected to.
278fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
279fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @return The session info for the connected session.
280fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
281fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
282fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    public MediaSessionInfo getSessionInfo() {
28373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        if (mInfo == null) {
28473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
28573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                mInfo = mSessionBinder.getSessionInfo();
28673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
28773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                Log.e(TAG, "Error in getSessionInfo.", e);
28873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
289fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
29073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return mInfo;
291fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
292fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
29301fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
29401fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
29501fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
29607c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
29701fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
29801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
29901fe661ae5da3739215d93922412df4b24c859a2RoboErik
30001fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
30101fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (cb == null) {
30201fe661ae5da3739215d93922412df4b24c859a2RoboErik            throw new IllegalArgumentException("Callback cannot be null");
30301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
30401fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
30501fe661ae5da3739215d93922412df4b24c859a2RoboErik            throw new IllegalArgumentException("Handler cannot be null");
30601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
30801fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
30901fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
31001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
3128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
31301fe661ae5da3739215d93922412df4b24c859a2RoboErik
31401fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
31501fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
31601fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
31701fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
31801fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
319d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
32001fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
32101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
32201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
32301fe661ae5da3739215d93922412df4b24c859a2RoboErik
3248ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
32501fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (cb == null) {
32601fe661ae5da3739215d93922412df4b24c859a2RoboErik            throw new IllegalArgumentException("Callback cannot be null");
32701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
328d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
3298ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
3318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
3328ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
333d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
33401fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
33501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
336d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
337d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
338d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
339d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
340d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
341d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
342d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
343d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
344d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
34501fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
34601fe661ae5da3739215d93922412df4b24c859a2RoboErik
3478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
3488ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
3498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
35001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3518ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
3538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
3548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
3558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
35601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3578ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
35801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
35901fe661ae5da3739215d93922412df4b24c859a2RoboErik
360c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
3618ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
3628ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
363c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
3648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
36601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
36701fe661ae5da3739215d93922412df4b24c859a2RoboErik
36801fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
3698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
3708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * registered using {@link #addCallback}
37101fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
37201fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
3748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
3758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
3768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
37701fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
37801fe661ae5da3739215d93922412df4b24c859a2RoboErik         * @param event
37901fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
38079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        public void onSessionEvent(String event, Bundle extras) {
38101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
38201fe661ae5da3739215d93922412df4b24c859a2RoboErik
38301fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
38401fe661ae5da3739215d93922412df4b24c859a2RoboErik         * Override to handle route changes for this session.
38501fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
38642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik         * @param route The new route
38742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik         * @hide
38801fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
38907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        public void onRouteChanged(RouteInfo route) {
39001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
391c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
392c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
393c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
394c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
395c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
396c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
397c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void onPlaybackStateChanged(PlaybackState state) {
398c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
399c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
400c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
401c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
402c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
403c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
404c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param metadata The current metadata for the session or null
405c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
406c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void onMetadataChanged(MediaMetadata metadata) {
407c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
408c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
409c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
410c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
411c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
412c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
413c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
414c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
415c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
416c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
417c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
418c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
419c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
420c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
421c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
422c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
423c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
424c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
425c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
426c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
427c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
428c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
429c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
430c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
431c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
432c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
433c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
434c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
435c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
436c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
437c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
438c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
439c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
440c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
441c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
442c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
443c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
444c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
445c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
446c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
447c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
448c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
449c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
450c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
451c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
452c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
453c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
454c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
455c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
456c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
457c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
458c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
459c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
460c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
461c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
462c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
463c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
464c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
465c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
466c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
467c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
468c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
469c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
470c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
471c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
472c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
473c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
474c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
475c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
476c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
477c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
478c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
479c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
480c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
481c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
482c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
483c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
484c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
485c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
486c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
487c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
488c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
489c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
490c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
491c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
492c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
493c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
494c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
495c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
496c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
497c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
498c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
499c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
500c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
501c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
502c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
503c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
504c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
505c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
506c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
507c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
508c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
509c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
510c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
511c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
512c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
513c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
514c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
515c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
516c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
517c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
518c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
519c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
520c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
521c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
522c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
523c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
524c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
525c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
526c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
527c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
5288ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
52901fe661ae5da3739215d93922412df4b24c859a2RoboErik
530ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
531ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Holds information about the way volume is handled for this session.
532ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
533ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    public static final class VolumeInfo {
534ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
535ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mAudioStream;
536ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
537ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
538ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
539ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
540ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
541ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
542ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
543ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public VolumeInfo(int type, int stream, int control, int max, int current) {
544ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
545ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mAudioStream = stream;
546ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
547ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
548ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
549ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
550ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
551ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
552ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume handling, either local or remote. One of:
553ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
554ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
555ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
556ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
557ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
558ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume handling this session is using.
559ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
560ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeType() {
561ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
562ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
563ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
564ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
565ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the stream this is currently controlling volume on. When the volume
566ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
567ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * have meaning and should be ignored.
568ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
569ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The stream this session is playing on.
570ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
571ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getAudioStream() {
572ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mAudioStream;
573ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
574ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
575ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
576ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
577ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
578ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
579ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
580ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
581ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
582ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
583ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
584ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
585ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
586ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
587ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
588ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
589ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
590ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
591ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
592ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
593ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
594ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
595ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
596ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
597ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
598ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
599ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
600ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
601ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
602ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
603ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
604ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
605ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
606ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
607ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
608ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
60907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
61042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
61101fe661ae5da3739215d93922412df4b24c859a2RoboErik
61242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
61342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
61401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
61501fe661ae5da3739215d93922412df4b24c859a2RoboErik
61601fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6178ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
61842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
620c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
62101fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
62201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
62301fe661ae5da3739215d93922412df4b24c859a2RoboErik
62401fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
62507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik        public void onRouteChanged(RouteInfo route) {
62642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6278ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
628c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_ROUTE, route, null);
62901fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
63001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
63101fe661ae5da3739215d93922412df4b24c859a2RoboErik
63201fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
63442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
636c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
63701fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
63801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
63901fe661ae5da3739215d93922412df4b24c859a2RoboErik
64001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
6418ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
64242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
6438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
644c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
64501fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
64601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
64701fe661ae5da3739215d93922412df4b24c859a2RoboErik
64801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
64901fe661ae5da3739215d93922412df4b24c859a2RoboErik
65001fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
65142ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
65201fe661ae5da3739215d93922412df4b24c859a2RoboErik
65342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
6548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
6558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
65601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
65701fe661ae5da3739215d93922412df4b24c859a2RoboErik
65801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
65901fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
66001fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
6618ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
66279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
66301fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
6648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_ROUTE:
66507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik                    mCallback.onRouteChanged((RouteInfo) msg.obj);
666c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
667c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
668c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
669c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
670c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
671c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
672c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
67301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
67401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
6758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
6768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
6778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
6788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
67901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
68001fe661ae5da3739215d93922412df4b24c859a2RoboErik
68101fe661ae5da3739215d93922412df4b24c859a2RoboErik}
682