117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/* 217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Copyright (C) 2014 The Android Open Source Project 317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Licensed under the Apache License, Version 2.0 (the "License"); 517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * you may not use this file except in compliance with the License. 617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * You may obtain a copy of the License at 717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * http://www.apache.org/licenses/LICENSE-2.0 917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 1017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Unless required by applicable law or agreed to in writing, software 1117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * distributed under the License is distributed on an "AS IS" BASIS, 1217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * See the License for the specific language governing permissions and 1417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * limitations under the License. 1517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 1617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 1717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenpackage android.media.browse; 1817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 193625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.annotation.IntDef; 2017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.annotation.NonNull; 21319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chenimport android.annotation.Nullable; 2217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.ComponentName; 2317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.Context; 2417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.Intent; 2517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.ServiceConnection; 2617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.pm.ParceledListSlice; 273625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.media.MediaDescription; 28c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErikimport android.media.session.MediaController; 2917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.media.session.MediaSession; 3017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Bundle; 3117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Handler; 3217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.IBinder; 333625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.os.Parcel; 343625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.os.Parcelable; 3517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.RemoteException; 363625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.MediaBrowserService; 373625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserService; 383625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserServiceCallbacks; 393625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.text.TextUtils; 4017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.ArrayMap; 4117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.Log; 4217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 433625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport java.lang.annotation.Retention; 443625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport java.lang.annotation.RetentionPolicy; 4517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.lang.ref.WeakReference; 4617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.Collections; 4717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.List; 4817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 4917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/** 5017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Browses media content offered by a link MediaBrowserService. 5117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 5217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * This object is not thread-safe. All calls should happen on the thread on which the browser 5317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * was constructed. 5417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 5517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 5617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenpublic final class MediaBrowser { 5717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final String TAG = "MediaBrowser"; 5817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final boolean DBG = false; 5917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 6017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final int CONNECT_STATE_DISCONNECTED = 0; 6117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final int CONNECT_STATE_CONNECTING = 1; 6217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final int CONNECT_STATE_CONNECTED = 2; 6317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final int CONNECT_STATE_SUSPENDED = 3; 6417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 6517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final Context mContext; 6617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final ComponentName mServiceComponent; 6717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final ConnectionCallback mCallback; 6817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final Bundle mRootHints; 6917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final Handler mHandler = new Handler(); 70c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik private final ArrayMap<String,Subscription> mSubscriptions = 71c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik new ArrayMap<String, MediaBrowser.Subscription>(); 7217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 7317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private int mState = CONNECT_STATE_DISCONNECTED; 7417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private MediaServiceConnection mServiceConnection; 7517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private IMediaBrowserService mServiceBinder; 7617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private IMediaBrowserServiceCallbacks mServiceCallbacks; 77c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik private String mRootId; 7817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private MediaSession.Token mMediaSessionToken; 79319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen private Bundle mExtras; 8017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 8117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 8217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Creates a media browser for the specified media browse service. 8317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 8417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param context The context. 8517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param serviceComponent The component name of the media browse service. 8617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param callback The connection callback. 8717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param rootHints An optional bundle of service-specific arguments to send 88c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * to the media browse service when connecting and retrieving the root id 8917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * for browsing, or null if none. The contents of this bundle may affect 9017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * the information returned when browsing. 9117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 9217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public MediaBrowser(Context context, ComponentName serviceComponent, 9317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen ConnectionCallback callback, Bundle rootHints) { 9417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (context == null) { 9517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalArgumentException("context must not be null"); 9617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 9717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (serviceComponent == null) { 9817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalArgumentException("service component must not be null"); 9917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 10017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (callback == null) { 10117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalArgumentException("connection callback must not be null"); 10217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 10317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mContext = context; 10417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceComponent = serviceComponent; 10517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mCallback = callback; 10617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mRootHints = rootHints; 10717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 10817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 10917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 11017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Connects to the media browse service. 11117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 11217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * The connection callback specified in the constructor will be invoked 11317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * when the connection completes or fails. 11417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 11517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 11617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void connect() { 11717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState != CONNECT_STATE_DISCONNECTED) { 11817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalStateException("connect() called while not disconnected (state=" 11917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + getStateLabel(mState) + ")"); 12017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 12117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // TODO: remove this extra check. 12217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 12317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceConnection != null) { 12417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new RuntimeException("mServiceConnection should be null. Instead it is " 12517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mServiceConnection); 12617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 12717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 12817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceBinder != null) { 12917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new RuntimeException("mServiceBinder should be null. Instead it is " 13017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mServiceBinder); 13117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 13217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceCallbacks != null) { 13317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new RuntimeException("mServiceCallbacks should be null. Instead it is " 13417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mServiceCallbacks); 13517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 13617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 13717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mState = CONNECT_STATE_CONNECTING; 13817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 13992e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE); 14017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen intent.setComponent(mServiceComponent); 14117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 14217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final ServiceConnection thisConnection = mServiceConnection = new MediaServiceConnection(); 14317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 1442354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen boolean bound = false; 14517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 146c8a3e0dc9d4c1e59ad250f45c2ee416beee8dd89Yao Chen bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); 14717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (Exception ex) { 14817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.e(TAG, "Failed binding to service " + mServiceComponent); 1492354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen } 15017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 1512354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen if (!bound) { 15217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Tell them that it didn't work. We are already on the main thread, 15317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // but we don't want to do callbacks inside of connect(). So post it, 15417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // and then check that we are on the same ServiceConnection. We know 15517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // we won't also get an onServiceConnected or onServiceDisconnected, 15617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // so we won't be doing double callbacks. 15717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 1582354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen @Override 1592354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen public void run() { 1602354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen // Ensure that nobody else came in or tried to connect again. 1612354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen if (thisConnection == mServiceConnection) { 1622354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen forceCloseConnection(); 1632354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen mCallback.onConnectionFailed(); 16417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 1652354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen } 1662354c5eb92016a72688b80c80fb6b94ce8ac4047Yao Chen }); 16717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 16817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 16917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 17017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "connect..."); 17117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 17217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 17317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 17417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 17517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 17617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Disconnects from the media browse service. 17717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * After this, no more callbacks will be received. 17817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 17917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void disconnect() { 18017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // It's ok to call this any state, because allowing this lets apps not have 18117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // to check isConnected() unnecessarily. They won't appreciate the extra 18217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // assertions for this. We do everything we can here to go back to a sane state. 18317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceCallbacks != null) { 18417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 18517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceBinder.disconnect(mServiceCallbacks); 18617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 18717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // We are disconnecting anyway. Log, just for posterity but it's not 18817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // a big problem. 18917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "RemoteException during connect for " + mServiceComponent); 19017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 19117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 19217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen forceCloseConnection(); 19317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 19417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 19517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "disconnect..."); 19617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 19717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 19817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 19917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 20017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 20117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Null out the variables and unbind from the service. This doesn't include 20217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * calling disconnect on the service, because we only try to do that in the 20317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * clean shutdown cases. 20417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 20517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Everywhere that calls this EXCEPT for disconnect() should follow it with 20617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback 20717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * for a clean shutdown, but everywhere else is a dirty shutdown and should 20817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * notify the app. 20917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 21017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private void forceCloseConnection() { 21117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceConnection != null) { 21217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mContext.unbindService(mServiceConnection); 21317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 21417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mState = CONNECT_STATE_DISCONNECTED; 21517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceConnection = null; 21617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceBinder = null; 21717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceCallbacks = null; 218c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mRootId = null; 21917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mMediaSessionToken = null; 22017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 22117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 22217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 22317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Returns whether the browser is connected to the service. 22417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 22517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public boolean isConnected() { 22617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return mState == CONNECT_STATE_CONNECTED; 22717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 22817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 22917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 23051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen * Gets the service component that the media browser is connected to. 23151fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen */ 23251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen public @NonNull ComponentName getServiceComponent() { 23351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen if (!isConnected()) { 23451fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen throw new IllegalStateException("getServiceComponent() called while not connected" + 23551fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen " (state=" + mState + ")"); 23651fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen } 23751fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen return mServiceComponent; 23851fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen } 23951fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen 24051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen /** 241c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * Gets the root id. 24217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 243c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * Note that the root id may become invalid or change when when the 24417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * browser is disconnected. 24517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 24617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 24717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @throws IllegalStateException if not connected. 248319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen */ 249c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public @NonNull String getRoot() { 25051fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen if (!isConnected()) { 25117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalStateException("getSessionToken() called while not connected (state=" 25217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + getStateLabel(mState) + ")"); 25317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 254c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik return mRootId; 25517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 25617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 25717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 258319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen * Gets any extras for the media service. 259319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen * 260319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen * @throws IllegalStateException if not connected. 261319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen */ 262319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen public @Nullable Bundle getExtras() { 26351fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen if (!isConnected()) { 264319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen throw new IllegalStateException("getExtras() called while not connected (state=" 265319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen + getStateLabel(mState) + ")"); 266319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 267319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen return mExtras; 268319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 269319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 270319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen /** 27117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Gets the media session token associated with the media browser. 27217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 27317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Note that the session token may become invalid or change when when the 27417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * browser is disconnected. 27517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 27617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 27717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @return The session token for the browser, never null. 27817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 27917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @throws IllegalStateException if not connected. 28017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 28117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public @NonNull MediaSession.Token getSessionToken() { 28251fdfa273e036d0e4f5e5c624988b33873fa3ec7Yao Chen if (!isConnected()) { 28317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalStateException("getSessionToken() called while not connected (state=" 28417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mState + ")"); 28517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 28617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return mMediaSessionToken; 28717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 28817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 28917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 29017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Queries for information about the media items that are contained within 291c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * the specified id and subscribes to receive updates when they change. 29217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 29317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * The list of subscriptions is maintained even when not connected and is 29417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * restored after reconnection. It is ok to subscribe while not connected 29517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * but the results will not be returned until the connection completes. 29617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p><p> 297c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * If the id is already subscribed with a different callback then the new 29817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * callback will replace the previous one. 29917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 30017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 301c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * @param parentId The id of the parent media item whose list of children 30217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * will be subscribed. 30317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param callback The callback to receive the list of children. 30417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 305c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { 30617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check arguments. 307c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik if (parentId == null) { 308c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik throw new IllegalArgumentException("parentId is null"); 30917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 31017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (callback == null) { 31117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalArgumentException("callback is null"); 31217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 31317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 31417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Update or create the subscription. 315c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Subscription sub = mSubscriptions.get(parentId); 31617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen boolean newSubscription = sub == null; 31717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (newSubscription) { 318c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik sub = new Subscription(parentId); 319c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mSubscriptions.put(parentId, sub); 32017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 32117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen sub.callback = callback; 32217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 32317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // If we are connected, tell the service that we are watching. If we aren't 32417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // connected, the service will be told when we connect. 32517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState == CONNECT_STATE_CONNECTED && newSubscription) { 32617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 327c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mServiceBinder.addSubscription(parentId, mServiceCallbacks); 32817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 32917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Process is crashing. We will disconnect, and upon reconnect we will 33017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // automatically reregister. So nothing to do here. 331c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId); 33217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 33617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 337c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * Unsubscribes for changes to the children of the specified media id. 33817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 33917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * The query callback will no longer be invoked for results associated with 340c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * this id once this method returns. 34117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 34217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 343c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * @param parentId The id of the parent media item whose list of children 34417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * will be unsubscribed. 34517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 346c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void unsubscribe(@NonNull String parentId) { 34717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check arguments. 348c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik if (parentId == null) { 349c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik throw new IllegalArgumentException("parentId is null"); 35017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 35117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 35217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Remove from our list. 353c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final Subscription sub = mSubscriptions.remove(parentId); 35417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 35517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Tell the service if necessary. 35617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState == CONNECT_STATE_CONNECTED && sub != null) { 35717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 358c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mServiceBinder.removeSubscription(parentId, mServiceCallbacks); 35917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 36017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Process is crashing. We will disconnect, and upon reconnect we will 36117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // automatically reregister. So nothing to do here. 362c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId); 36317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 36417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 36517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 36617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 36717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 36817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * For debugging. 36917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 37017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static String getStateLabel(int state) { 37117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen switch (state) { 37217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen case CONNECT_STATE_DISCONNECTED: 37317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return "CONNECT_STATE_DISCONNECTED"; 37417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen case CONNECT_STATE_CONNECTING: 37517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return "CONNECT_STATE_CONNECTING"; 37617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen case CONNECT_STATE_CONNECTED: 37717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return "CONNECT_STATE_CONNECTED"; 37817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen case CONNECT_STATE_SUSPENDED: 37917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return "CONNECT_STATE_SUSPENDED"; 38017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen default: 38117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return "UNKNOWN/" + state; 38217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 38317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 38417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 38517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback, 386c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final String root, final MediaSession.Token session, final Bundle extra) { 38717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 38817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 38917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 39017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check to make sure there hasn't been a disconnect or a different 39117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // ServiceConnection. 39217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isCurrent(callback, "onConnect")) { 39317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 39417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 39517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Don't allow them to call us twice. 39617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState != CONNECT_STATE_CONNECTING) { 39717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "onConnect from service while mState=" 39817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + getStateLabel(mState) + "... ignoring"); 39917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 40017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 401c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mRootId = root; 40217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mMediaSessionToken = session; 403319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen mExtras = extra; 40417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mState = CONNECT_STATE_CONNECTED; 40517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 40617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 40717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "ServiceCallbacks.onConnect..."); 40817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 40917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 41017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mCallback.onConnected(); 41117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 41217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // we may receive some subscriptions before we are connected, so re-subscribe 41317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // everything now 414c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik for (String id : mSubscriptions.keySet()) { 41517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 416c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mServiceBinder.addSubscription(id, mServiceCallbacks); 41717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 41817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Process is crashing. We will disconnect, and upon reconnect we will 41917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // automatically reregister. So nothing to do here. 420c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, "addSubscription failed with RemoteException parentId=" + id); 42117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 42217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 42317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 42417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 42517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 42617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 42717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) { 42817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 42917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 43017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 43117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.e(TAG, "onConnectFailed for " + mServiceComponent); 43217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 43317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check to make sure there hasn't been a disconnect or a different 43417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // ServiceConnection. 43517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isCurrent(callback, "onConnectFailed")) { 43617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 43717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 43817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Don't allow them to call us twice. 43917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState != CONNECT_STATE_CONNECTING) { 44017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "onConnect from service while mState=" 44117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + getStateLabel(mState) + "... ignoring"); 44217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 44317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 44417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 44517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Clean up 44617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen forceCloseConnection(); 44717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 44817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Tell the app. 44917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mCallback.onConnectionFailed(); 45017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 45117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 45217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 45317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 454c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, 455c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final String parentId, final ParceledListSlice list) { 45617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 45717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 45817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 45917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check that there hasn't been a disconnect or a different 46017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // ServiceConnection. 46117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isCurrent(callback, "onLoadChildren")) { 46217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 46317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 46417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 4653625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik List<MediaItem> data = list.getList(); 46617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 467c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId); 46817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 46917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (data == null) { 47017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen data = Collections.emptyList(); 47117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 47217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 47317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check that the subscription is still subscribed. 474c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final Subscription subscription = mSubscriptions.get(parentId); 47517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (subscription == null) { 47617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 477c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" 478c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik + parentId); 47917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 48117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 48317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Tell the app. 484c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik subscription.callback.onChildrenLoaded(parentId, data); 48517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 48717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 48917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 49017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. 49117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 49217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) { 49317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceCallbacks != callback) { 49417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState != CONNECT_STATE_DISCONNECTED) { 49517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" 49617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mServiceCallbacks + " this=" + this); 49717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 49817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return false; 49917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 50017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return true; 50117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 50217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 50317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private ServiceCallbacks getNewServiceCallbacks() { 50417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return new ServiceCallbacks(this); 50517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 50617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 50717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 50817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Log internal state. 50917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @hide 51017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 51117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen void dump() { 51217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "MediaBrowser..."); 51317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mServiceComponent=" + mServiceComponent); 51417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mCallback=" + mCallback); 51517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mRootHints=" + mRootHints); 51617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mState=" + getStateLabel(mState)); 51717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mServiceConnection=" + mServiceConnection); 51817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mServiceBinder=" + mServiceBinder); 51917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks); 520c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.d(TAG, " mRootId=" + mRootId); 52117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken); 52217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 52317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 5243625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static class MediaItem implements Parcelable { 5253625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik private final int mFlags; 5263625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik private final MediaDescription mDescription; 5273625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5283625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** @hide */ 5293625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Retention(RetentionPolicy.SOURCE) 5303625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE }) 5313625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public @interface Flags { } 53217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 53317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 5343625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Flag: Indicates that the item has children of its own. 53517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 5363625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static final int FLAG_BROWSABLE = 1 << 0; 5373625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5383625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** 5393625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Flag: Indicates that the item is playable. 5403625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * <p> 541c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * The id of this item may be passed to 542c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)} 5433625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * to start playing it. 5443625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * </p> 5453625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik */ 5463625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static final int FLAG_PLAYABLE = 1 << 1; 5473625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5483625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** 5493625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Create a new MediaItem for use in browsing media. 5503625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * @param description The description of the media, which must include a 5513625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * media id. 5525dfa0a5f7a6365ddf3939f29227366172979e53eRoboErik * @param flags The flags for this item. 5533625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik */ 5545dfa0a5f7a6365ddf3939f29227366172979e53eRoboErik public MediaItem(@NonNull MediaDescription description, @Flags int flags) { 5553625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik if (description == null) { 5563625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik throw new IllegalArgumentException("description cannot be null"); 5573625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5583625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik if (TextUtils.isEmpty(description.getMediaId())) { 5593625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik throw new IllegalArgumentException("description must have a non-empty media id"); 5603625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5613625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik mFlags = flags; 5623625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik mDescription = description; 56317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 56417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 56517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 5663625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Private constructor. 56717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 5683625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik private MediaItem(Parcel in) { 5693625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik mFlags = in.readInt(); 5703625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik mDescription = MediaDescription.CREATOR.createFromParcel(in); 57117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 57217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 5733625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Override 5743625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public int describeContents() { 5753625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return 0; 5763625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5773625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5783625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Override 5793625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public void writeToParcel(Parcel out, int flags) { 5803625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik out.writeInt(mFlags); 5813625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik mDescription.writeToParcel(out, flags); 5823625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5833625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5843625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Override 5853625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public String toString() { 5863625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik final StringBuilder sb = new StringBuilder("MediaItem{"); 5873625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik sb.append("mFlags=").append(mFlags); 5883625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik sb.append(", mDescription=").append(mDescription); 5893625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik sb.append('}'); 5903625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return sb.toString(); 5913625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5923625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 5933625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static final Parcelable.Creator<MediaItem> CREATOR = 5943625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik new Parcelable.Creator<MediaItem>() { 5953625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Override 5963625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public MediaItem createFromParcel(Parcel in) { 5973625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return new MediaItem(in); 5983625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 5993625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 6003625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @Override 6013625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public MediaItem[] newArray(int size) { 6023625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return new MediaItem[size]; 6033625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 6043625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik }; 6053625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik 60617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 6073625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Gets the flags of the item. 60817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 6093625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public @Flags int getFlags() { 6103625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return mFlags; 61117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 61217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 61317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 6143625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Returns whether this item is browsable. 6153625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * @see #FLAG_BROWSABLE 61617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 6173625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public boolean isBrowsable() { 6183625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return (mFlags & FLAG_BROWSABLE) != 0; 61917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 62017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 62117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 6223625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Returns whether this item is playable. 6233625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * @see #FLAG_PLAYABLE 62417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 6253625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public boolean isPlayable() { 6263625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return (mFlags & FLAG_PLAYABLE) != 0; 62717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 62817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 62917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 6303625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Returns the description of the media. 63117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 6323625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public @NonNull MediaDescription getDescription() { 6333625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return mDescription; 63417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 63517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 63617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 6373625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Returns the media id for this item. 63817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 6393625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public @NonNull String getMediaId() { 6403625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik return mDescription.getMediaId(); 64117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 64217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 64317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 64480bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen 6453625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** 6463625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Callbacks for connection related events. 6473625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik */ 6483625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static class ConnectionCallback { 64980bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen /** 6503625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed. 65180bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen */ 6523625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public void onConnected() { 653319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 654319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 65580bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen /** 6563625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Invoked when the client is disconnected from the media browser. 65780bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen */ 6583625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public void onConnectionSuspended() { 659319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 660319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 66180bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen /** 6623625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Invoked when the connection to the media browser failed. 66380bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen */ 6643625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public void onConnectionFailed() { 665319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 6663625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 667319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 6683625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** 6693625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Callbacks for subscription related events. 6703625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik */ 6713625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik public static abstract class SubscriptionCallback { 6723625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik /** 6733625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * Called when the list of children is loaded or updated. 6743625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik */ 675c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void onChildrenLoaded(@NonNull String parentId, 6763625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @NonNull List<MediaItem> children) { 67780bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen } 67880bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen 67980bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen /** 680c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * Called when the id doesn't exist or other errors in subscribing. 6813625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * <p> 6823625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} 6833625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * called, because some errors may heal themselves. 6843625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik * </p> 68580bb169d0ff778ca7073486f4e2900f8479c2daaYao Chen */ 686c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void onError(@NonNull String id) { 687319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 688319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 689319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 69017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 69117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * ServiceConnection to the other app. 69217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 69317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private class MediaServiceConnection implements ServiceConnection { 69417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 69517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void onServiceConnected(ComponentName name, IBinder binder) { 69617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 69717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name 69817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + " binder=" + binder); 69917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 70017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 70117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 70217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Make sure we are still the current connection, and that they haven't called 70317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // disconnect(). 70417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isCurrent("onServiceConnected")) { 70517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 70617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 70717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 70817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Save their binder 70917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceBinder = IMediaBrowserService.Stub.asInterface(binder); 71017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 71117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // We make a new mServiceCallbacks each time we connect so that we can drop 71217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // responses from previous connections. 71317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceCallbacks = getNewServiceCallbacks(); 71488b84178d6a8551ea1b534d8750def8dceebdc2aRoboErik mState = CONNECT_STATE_CONNECTING; 71517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 71617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Call connect, which is async. When we get a response from that we will 71717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // say that we're connected. 71817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 71917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 72017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "ServiceCallbacks.onConnect..."); 72117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 72217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 72317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceBinder.connect(mContext.getPackageName(), mRootHints, mServiceCallbacks); 72417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 72517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Connect failed, which isn't good. But the auto-reconnect on the service 72617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // will take over and we will come back. We will also get the 72717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // onServiceDisconnected, which has all the cleanup code. So let that do it. 72817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "RemoteException during connect for " + mServiceComponent); 72917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 73017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "ServiceCallbacks.onConnect..."); 73117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 73217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 73317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 73417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 73517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 73617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 73717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void onServiceDisconnected(ComponentName name) { 73817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (DBG) { 73917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name 74017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + " this=" + this + " mServiceConnection=" + mServiceConnection); 74117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen dump(); 74217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 74317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 74417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Make sure we are still the current connection, and that they haven't called 74517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // disconnect(). 74617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isCurrent("onServiceDisconnected")) { 74717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 74817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 74917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 75017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Clear out what we set in onServiceConnected 75117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceBinder = null; 75217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mServiceCallbacks = null; 75317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 75417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // And tell the app that it's suspended. 75517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mState = CONNECT_STATE_SUSPENDED; 75617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mCallback.onConnectionSuspended(); 75717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 75817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 75917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 76017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Return true if this is the current ServiceConnection. Also logs if it's not. 76117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 76217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private boolean isCurrent(String funcName) { 76317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mServiceConnection != this) { 76417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mState != CONNECT_STATE_DISCONNECTED) { 76517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Check mState, because otherwise this log is noisy. 76617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" 76717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + mServiceConnection + " this=" + this); 76817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 76917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return false; 77017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 77117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return true; 77217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 7733625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik } 77417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 77517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 77617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Callbacks from the service. 77717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 77817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub { 77917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private WeakReference<MediaBrowser> mMediaBrowser; 78017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 78117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public ServiceCallbacks(MediaBrowser mediaBrowser) { 78217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser); 78317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 78417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 78517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 78617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * The other side has acknowledged our connection. The parameters to this function 78717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * are the initial data as requested. 78817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 78917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 790c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void onConnect(final String root, final MediaSession.Token session, 791319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen final Bundle extras) { 79217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen MediaBrowser mediaBrowser = mMediaBrowser.get(); 79317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mediaBrowser != null) { 794319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen mediaBrowser.onServiceConnected(this, root, session, extras); 79517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 79617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 79717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 79817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 79917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * The other side does not like us. Tell the app via onConnectionFailed. 80017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 80117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 80217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void onConnectFailed() { 80317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen MediaBrowser mediaBrowser = mMediaBrowser.get(); 80417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mediaBrowser != null) { 80517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mediaBrowser.onConnectionFailed(this); 80617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 80717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 80817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 80917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 810c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public void onLoadChildren(final String parentId, final ParceledListSlice list) { 81117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen MediaBrowser mediaBrowser = mMediaBrowser.get(); 81217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (mediaBrowser != null) { 813c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mediaBrowser.onLoadChildren(this, parentId, list); 81417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 81517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 81617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 81717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 81817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static class Subscription { 819c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final String id; 82017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen SubscriptionCallback callback; 82117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 822c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Subscription(String id) { 823c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik this.id = id; 82417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 82517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 82617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen} 827