MediaController.java revision 8ae0f34db936a649ddaf9cdd086c224f6514efeb
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 1901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle; 2001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler; 2101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper; 2201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message; 2301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException; 248ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver; 2501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils; 2601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log; 2701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent; 2801fe661ae5da3739215d93922412df4b24c859a2RoboErik 298ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference; 3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList; 3101fe661ae5da3739215d93922412df4b24c859a2RoboErik 3201fe661ae5da3739215d93922412df4b24c859a2RoboErik/** 3301fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and 3401fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to 3501fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes. 3601fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p> 3701fe661ae5da3739215d93922412df4b24c859a2RoboErik * A MediaController can be created through {@link MediaSessionManager} if you 3801fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if 3901fe661ae5da3739215d93922412df4b24c859a2RoboErik * you have a {@link MediaSessionToken} from the session owner. 4001fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p> 4101fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe. 4201fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 4301fe661ae5da3739215d93922412df4b24c859a2RoboErikpublic final class MediaController { 4401fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final String TAG = "MediaController"; 4501fe661ae5da3739215d93922412df4b24c859a2RoboErik 468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private static final int MSG_EVENT = 1; 4701fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_PLAYBACK_STATE = 2; 4801fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_METADATA = 3; 498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private static final int MSG_ROUTE = 4; 5001fe661ae5da3739215d93922412df4b24c859a2RoboErik 5101fe661ae5da3739215d93922412df4b24c859a2RoboErik private final IMediaController mSessionBinder; 5201fe661ae5da3739215d93922412df4b24c859a2RoboErik 538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private final CallbackStub mCbStub = new CallbackStub(this); 548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>(); 5501fe661ae5da3739215d93922412df4b24c859a2RoboErik private final Object mLock = new Object(); 5601fe661ae5da3739215d93922412df4b24c859a2RoboErik 5701fe661ae5da3739215d93922412df4b24c859a2RoboErik private boolean mCbRegistered = false; 5801fe661ae5da3739215d93922412df4b24c859a2RoboErik 598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private TransportController mTransportController; 608ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik 618ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private MediaController(IMediaController sessionBinder) { 628ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mSessionBinder = sessionBinder; 638ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik 6501fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @hide 6701fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public static MediaController fromBinder(IMediaController sessionBinder) { 698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MediaController controller = new MediaController(sessionBinder); 708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik try { 718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik controller.mSessionBinder.registerCallbackListener(controller.mCbStub); 728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (controller.mSessionBinder.isTransportControlEnabled()) { 738ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik controller.mTransportController = new TransportController(sessionBinder); 748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } catch (RemoteException e) { 768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik Log.wtf(TAG, "MediaController created with expired token", e); 778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik controller = null; 788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return controller; 8001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 8101fe661ae5da3739215d93922412df4b24c859a2RoboErik 8201fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Get a new MediaController for a MediaSessionToken. If successful the 848ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * controller returned will be connected to the session that generated the 858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * token. 868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * 878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @param token The session token to use 888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @return A controller for the session or null 8901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public static MediaController fromToken(MediaSessionToken token) { 918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return fromBinder(token.getBinder()); 9201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 9301fe661ae5da3739215d93922412df4b24c859a2RoboErik 9401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 958ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Get a TransportController if the session supports it. If it is not 968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * supported null will be returned. 9701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @return A TransportController or null 9901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 1008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public TransportController getTransportController() { 1018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return mTransportController; 10201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 10301fe661ae5da3739215d93922412df4b24c859a2RoboErik 10401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 10501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Send the specified media button to the session. Only media keys can be 10601fe661ae5da3739215d93922412df4b24c859a2RoboErik * sent using this method. 10701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 10801fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param keycode The media button keycode, such as 1099524ed59a44b6850f3f708e50ef0708d2a462316RoboErik * {@link KeyEvent#KEYCODE_MEDIA_PLAY}. 11001fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 11101fe661ae5da3739215d93922412df4b24c859a2RoboErik public void sendMediaButton(int keycode) { 11201fe661ae5da3739215d93922412df4b24c859a2RoboErik if (!KeyEvent.isMediaKey(keycode)) { 11301fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("May only send media buttons through " 11401fe661ae5da3739215d93922412df4b24c859a2RoboErik + "sendMediaButton"); 11501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 11601fe661ae5da3739215d93922412df4b24c859a2RoboErik // TODO do something better than key down/up events 11701fe661ae5da3739215d93922412df4b24c859a2RoboErik KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keycode); 11801fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 11901fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.sendMediaButton(event); 12001fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 12101fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in sendMediaButton", e); 12201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 12301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 12401fe661ae5da3739215d93922412df4b24c859a2RoboErik 12501fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 12601fe661ae5da3739215d93922412df4b24c859a2RoboErik * Adds a callback to receive updates from the Session. Updates will be 12701fe661ae5da3739215d93922412df4b24c859a2RoboErik * posted on the caller's thread. 12801fe661ae5da3739215d93922412df4b24c859a2RoboErik * 12901fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb The callback object, must not be null 13001fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 13101fe661ae5da3739215d93922412df4b24c859a2RoboErik public void addCallback(Callback cb) { 13201fe661ae5da3739215d93922412df4b24c859a2RoboErik addCallback(cb, null); 13301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 13401fe661ae5da3739215d93922412df4b24c859a2RoboErik 13501fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 13601fe661ae5da3739215d93922412df4b24c859a2RoboErik * Adds a callback to receive updates from the session. Updates will be 1378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * posted on the specified handler's thread. 13801fe661ae5da3739215d93922412df4b24c859a2RoboErik * 13901fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb Cannot be null. 1408ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @param handler The handler to post updates on. If null the callers thread 14101fe661ae5da3739215d93922412df4b24c859a2RoboErik * will be used 14201fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 14301fe661ae5da3739215d93922412df4b24c859a2RoboErik public void addCallback(Callback cb, Handler handler) { 14401fe661ae5da3739215d93922412df4b24c859a2RoboErik if (handler == null) { 14501fe661ae5da3739215d93922412df4b24c859a2RoboErik handler = new Handler(); 14601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 14701fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 14801fe661ae5da3739215d93922412df4b24c859a2RoboErik addCallbackLocked(cb, handler); 14901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 15001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 15101fe661ae5da3739215d93922412df4b24c859a2RoboErik 15201fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 15301fe661ae5da3739215d93922412df4b24c859a2RoboErik * Stop receiving updates on the specified callback. If an update has 15401fe661ae5da3739215d93922412df4b24c859a2RoboErik * already been posted you may still receive it after calling this method. 15501fe661ae5da3739215d93922412df4b24c859a2RoboErik * 15601fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb The callback to remove 15701fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 15801fe661ae5da3739215d93922412df4b24c859a2RoboErik public void removeCallback(Callback cb) { 15901fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 16001fe661ae5da3739215d93922412df4b24c859a2RoboErik removeCallbackLocked(cb); 16101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 16201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 16301fe661ae5da3739215d93922412df4b24c859a2RoboErik 1648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik /** 1658ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Sends a generic command to the session. It is up to the session creator 1668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * to decide what commands and parameters they will support. As such, 1678ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * commands should only be sent to sessions that the controller owns. 1688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * 1698ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @param command The command to send 1708ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @param params Any parameters to include with the command 1718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * @param cb The callback to receive the result on 1728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik */ 1738ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void sendCommand(String command, Bundle params, ResultReceiver cb) { 1748ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (TextUtils.isEmpty(command)) { 1758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik throw new IllegalArgumentException("command cannot be null or empty"); 1768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 1778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik try { 1788ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mSessionBinder.sendCommand(command, params, cb); 1798ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } catch (RemoteException e) { 1808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik Log.d(TAG, "Dead object in sendCommand.", e); 1818ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 1828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 1838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik 18401fe661ae5da3739215d93922412df4b24c859a2RoboErik /* 18501fe661ae5da3739215d93922412df4b24c859a2RoboErik * @hide 18601fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 18701fe661ae5da3739215d93922412df4b24c859a2RoboErik IMediaController getSessionBinder() { 18801fe661ae5da3739215d93922412df4b24c859a2RoboErik return mSessionBinder; 18901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19001fe661ae5da3739215d93922412df4b24c859a2RoboErik 19101fe661ae5da3739215d93922412df4b24c859a2RoboErik private void addCallbackLocked(Callback cb, Handler handler) { 19201fe661ae5da3739215d93922412df4b24c859a2RoboErik if (cb == null) { 19301fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Callback cannot be null"); 19401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19501fe661ae5da3739215d93922412df4b24c859a2RoboErik if (handler == null) { 19601fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Handler cannot be null"); 19701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 1988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (getHandlerForCallbackLocked(cb) != null) { 19901fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.w(TAG, "Callback is already added, ignoring"); 20001fe661ae5da3739215d93922412df4b24c859a2RoboErik return; 20101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2028ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MessageHandler holder = new MessageHandler(handler.getLooper(), cb); 2038ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallbacks.add(holder); 20401fe661ae5da3739215d93922412df4b24c859a2RoboErik 20501fe661ae5da3739215d93922412df4b24c859a2RoboErik if (!mCbRegistered) { 20601fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 20701fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.registerCallbackListener(mCbStub); 20801fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbRegistered = true; 20901fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 21001fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in registerCallback", e); 21101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21401fe661ae5da3739215d93922412df4b24c859a2RoboErik 2158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private boolean removeCallbackLocked(Callback cb) { 21601fe661ae5da3739215d93922412df4b24c859a2RoboErik if (cb == null) { 21701fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Callback cannot be null"); 21801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik for (int i = mCallbacks.size() - 1; i >= 0; i--) { 2208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MessageHandler handler = mCallbacks.get(i); 2218ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (cb == handler.mCallback) { 2228ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallbacks.remove(i); 2238ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return true; 22401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2268ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return false; 22701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22801fe661ae5da3739215d93922412df4b24c859a2RoboErik 2298ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private MessageHandler getHandlerForCallbackLocked(Callback cb) { 2308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (cb == null) { 2318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik throw new IllegalArgumentException("Callback cannot be null"); 23201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik for (int i = mCallbacks.size() - 1; i >= 0; i--) { 2348ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MessageHandler handler = mCallbacks.get(i); 2358ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (cb == handler.mCallback) { 2368ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return handler; 2378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 23801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2398ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik return null; 24001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 24101fe661ae5da3739215d93922412df4b24c859a2RoboErik 2428ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private void postEvent(String event, Bundle extras) { 2438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik synchronized (mLock) { 2448ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik for (int i = mCallbacks.size() - 1; i >= 0; i--) { 2458ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallbacks.get(i).post(MSG_EVENT, event, extras); 2468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 24701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 24801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 24901fe661ae5da3739215d93922412df4b24c859a2RoboErik 2508ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private void postRouteChanged(Bundle routeDescriptor) { 2518ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik synchronized (mLock) { 2528ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik for (int i = mCallbacks.size() - 1; i >= 0; i--) { 2538ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallbacks.get(i).post(MSG_ROUTE, null, routeDescriptor); 2548ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 25501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 25601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 25701fe661ae5da3739215d93922412df4b24c859a2RoboErik 25801fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 2598ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Callback for receiving updates on from the session. A Callback can be 2608ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * registered using {@link #addCallback} 26101fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 26201fe661ae5da3739215d93922412df4b24c859a2RoboErik public static abstract class Callback { 26301fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 2648ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Override to handle custom events sent by the session owner without a 2658ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * specified interface. Controllers should only handle these for 2668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * sessions they own. 26701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 26801fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param event 26901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 27001fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onEvent(String event, Bundle extras) { 27101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 27201fe661ae5da3739215d93922412df4b24c859a2RoboErik 27301fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 27401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Override to handle route changes for this session. 27501fe661ae5da3739215d93922412df4b24c859a2RoboErik * 27601fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param route 27701fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 27801fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onRouteChanged(Bundle route) { 27901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 2808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 28101fe661ae5da3739215d93922412df4b24c859a2RoboErik 2828ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private final static class CallbackStub extends IMediaControllerCallback.Stub { 2838ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private final WeakReference<MediaController> mController; 28401fe661ae5da3739215d93922412df4b24c859a2RoboErik 2858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public CallbackStub(MediaController controller) { 2868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mController = new WeakReference<MediaController>(controller); 28701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 28801fe661ae5da3739215d93922412df4b24c859a2RoboErik 28901fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 2908ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void onEvent(String event, Bundle extras) { 2918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MediaController controller = mController.get(); 2928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (controller != null) { 2938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik controller.postEvent(event, extras); 29401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 29501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 29601fe661ae5da3739215d93922412df4b24c859a2RoboErik 29701fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 2988ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void onRouteChanged(Bundle mediaRouteDescriptor) { 2998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MediaController controller = mController.get(); 3008ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (controller != null) { 3018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik controller.postRouteChanged(mediaRouteDescriptor); 30201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 30301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 30401fe661ae5da3739215d93922412df4b24c859a2RoboErik 30501fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 3068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void onPlaybackStateChanged(PlaybackState state) { 3078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MediaController controller = mController.get(); 3088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (controller != null) { 3098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik TransportController tc = controller.getTransportController(); 3108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (tc != null) { 3118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik tc.postPlaybackStateChanged(state); 3128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 31301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 31401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 31501fe661ae5da3739215d93922412df4b24c859a2RoboErik 31601fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 3178ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void onMetadataChanged(MediaMetadata metadata) { 3188ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik MediaController controller = mController.get(); 3198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (controller != null) { 3208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik TransportController tc = controller.getTransportController(); 3218ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik if (tc != null) { 3228ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik tc.postMetadataChanged(metadata); 3238ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 32401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32601fe661ae5da3739215d93922412df4b24c859a2RoboErik 32701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32801fe661ae5da3739215d93922412df4b24c859a2RoboErik 32901fe661ae5da3739215d93922412df4b24c859a2RoboErik private final static class MessageHandler extends Handler { 3308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik private final MediaController.Callback mCallback; 33101fe661ae5da3739215d93922412df4b24c859a2RoboErik 33201fe661ae5da3739215d93922412df4b24c859a2RoboErik public MessageHandler(Looper looper, MediaController.Callback cb) { 3338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik super(looper, null, true); 3348ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallback = cb; 33501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 33601fe661ae5da3739215d93922412df4b24c859a2RoboErik 33701fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 33801fe661ae5da3739215d93922412df4b24c859a2RoboErik public void handleMessage(Message msg) { 33901fe661ae5da3739215d93922412df4b24c859a2RoboErik switch (msg.what) { 3408ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik case MSG_EVENT: 3418ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallback.onEvent((String) msg.obj, msg.getData()); 34201fe661ae5da3739215d93922412df4b24c859a2RoboErik break; 3438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik case MSG_ROUTE: 3448ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik mCallback.onRouteChanged(msg.getData()); 34501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 34601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 3478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik 3488ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik public void post(int what, Object obj, Bundle data) { 3498ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik obtainMessage(what, obj).sendToTarget(); 3508ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik } 35101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 35201fe661ae5da3739215d93922412df4b24c859a2RoboErik 35301fe661ae5da3739215d93922412df4b24c859a2RoboErik} 354