Fields Summary |
---|
private static final String | TAG |
private static final boolean | DBG |
private static final int | CONNECT_STATE_DISCONNECTED |
private static final int | CONNECT_STATE_CONNECTING |
private static final int | CONNECT_STATE_CONNECTED |
private static final int | CONNECT_STATE_SUSPENDED |
private final android.content.Context | mContext |
private final android.content.ComponentName | mServiceComponent |
private final ConnectionCallback | mCallback |
private final android.os.Bundle | mRootHints |
private final android.os.Handler | mHandler |
private final android.util.ArrayMap | mSubscriptions |
private int | mState |
private MediaServiceConnection | mServiceConnection |
private android.service.media.IMediaBrowserService | mServiceBinder |
private android.service.media.IMediaBrowserServiceCallbacks | mServiceCallbacks |
private String | mRootId |
private MediaSession.Token | mMediaSessionToken |
private android.os.Bundle | mExtras |
Methods Summary |
---|
public void | connect()Connects to the media browse service.
The connection callback specified in the constructor will be invoked
when the connection completes or fails.
if (mState != CONNECT_STATE_DISCONNECTED) {
throw new IllegalStateException("connect() called while not disconnected (state="
+ getStateLabel(mState) + ")");
}
// TODO: remove this extra check.
if (DBG) {
if (mServiceConnection != null) {
throw new RuntimeException("mServiceConnection should be null. Instead it is "
+ mServiceConnection);
}
}
if (mServiceBinder != null) {
throw new RuntimeException("mServiceBinder should be null. Instead it is "
+ mServiceBinder);
}
if (mServiceCallbacks != null) {
throw new RuntimeException("mServiceCallbacks should be null. Instead it is "
+ mServiceCallbacks);
}
mState = CONNECT_STATE_CONNECTING;
final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
intent.setComponent(mServiceComponent);
final ServiceConnection thisConnection = mServiceConnection = new MediaServiceConnection();
boolean bound = false;
try {
bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} catch (Exception ex) {
Log.e(TAG, "Failed binding to service " + mServiceComponent);
}
if (!bound) {
// Tell them that it didn't work. We are already on the main thread,
// but we don't want to do callbacks inside of connect(). So post it,
// and then check that we are on the same ServiceConnection. We know
// we won't also get an onServiceConnected or onServiceDisconnected,
// so we won't be doing double callbacks.
mHandler.post(new Runnable() {
@Override
public void run() {
// Ensure that nobody else came in or tried to connect again.
if (thisConnection == mServiceConnection) {
forceCloseConnection();
mCallback.onConnectionFailed();
}
}
});
}
if (DBG) {
Log.d(TAG, "connect...");
dump();
}
|
public void | disconnect()Disconnects from the media browse service.
After this, no more callbacks will be received.
// It's ok to call this any state, because allowing this lets apps not have
// to check isConnected() unnecessarily. They won't appreciate the extra
// assertions for this. We do everything we can here to go back to a sane state.
if (mServiceCallbacks != null) {
try {
mServiceBinder.disconnect(mServiceCallbacks);
} catch (RemoteException ex) {
// We are disconnecting anyway. Log, just for posterity but it's not
// a big problem.
Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
}
}
forceCloseConnection();
if (DBG) {
Log.d(TAG, "disconnect...");
dump();
}
|
void | dump()Log internal state.
Log.d(TAG, "MediaBrowser...");
Log.d(TAG, " mServiceComponent=" + mServiceComponent);
Log.d(TAG, " mCallback=" + mCallback);
Log.d(TAG, " mRootHints=" + mRootHints);
Log.d(TAG, " mState=" + getStateLabel(mState));
Log.d(TAG, " mServiceConnection=" + mServiceConnection);
Log.d(TAG, " mServiceBinder=" + mServiceBinder);
Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks);
Log.d(TAG, " mRootId=" + mRootId);
Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken);
|
private void | forceCloseConnection()Null out the variables and unbind from the service. This doesn't include
calling disconnect on the service, because we only try to do that in the
clean shutdown cases.
Everywhere that calls this EXCEPT for disconnect() should follow it with
a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback
for a clean shutdown, but everywhere else is a dirty shutdown and should
notify the app.
if (mServiceConnection != null) {
mContext.unbindService(mServiceConnection);
}
mState = CONNECT_STATE_DISCONNECTED;
mServiceConnection = null;
mServiceBinder = null;
mServiceCallbacks = null;
mRootId = null;
mMediaSessionToken = null;
|
public android.os.Bundle | getExtras()Gets any extras for the media service.
if (!isConnected()) {
throw new IllegalStateException("getExtras() called while not connected (state="
+ getStateLabel(mState) + ")");
}
return mExtras;
|
private android.media.browse.MediaBrowser$ServiceCallbacks | getNewServiceCallbacks()
return new ServiceCallbacks(this);
|
public java.lang.String | getRoot()Gets the root id.
Note that the root id may become invalid or change when when the
browser is disconnected.
if (!isConnected()) {
throw new IllegalStateException("getSessionToken() called while not connected (state="
+ getStateLabel(mState) + ")");
}
return mRootId;
|
public android.content.ComponentName | getServiceComponent()Gets the service component that the media browser is connected to.
if (!isConnected()) {
throw new IllegalStateException("getServiceComponent() called while not connected" +
" (state=" + mState + ")");
}
return mServiceComponent;
|
public MediaSession.Token | getSessionToken()Gets the media session token associated with the media browser.
Note that the session token may become invalid or change when when the
browser is disconnected.
if (!isConnected()) {
throw new IllegalStateException("getSessionToken() called while not connected (state="
+ mState + ")");
}
return mMediaSessionToken;
|
private static java.lang.String | getStateLabel(int state)For debugging.
switch (state) {
case CONNECT_STATE_DISCONNECTED:
return "CONNECT_STATE_DISCONNECTED";
case CONNECT_STATE_CONNECTING:
return "CONNECT_STATE_CONNECTING";
case CONNECT_STATE_CONNECTED:
return "CONNECT_STATE_CONNECTED";
case CONNECT_STATE_SUSPENDED:
return "CONNECT_STATE_SUSPENDED";
default:
return "UNKNOWN/" + state;
}
|
public boolean | isConnected()Returns whether the browser is connected to the service.
return mState == CONNECT_STATE_CONNECTED;
|
private boolean | isCurrent(android.service.media.IMediaBrowserServiceCallbacks callback, java.lang.String funcName)Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
if (mServiceCallbacks != callback) {
if (mState != CONNECT_STATE_DISCONNECTED) {
Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+ mServiceCallbacks + " this=" + this);
}
return false;
}
return true;
|
private final void | onConnectionFailed(android.service.media.IMediaBrowserServiceCallbacks callback)
mHandler.post(new Runnable() {
@Override
public void run() {
Log.e(TAG, "onConnectFailed for " + mServiceComponent);
// Check to make sure there hasn't been a disconnect or a different
// ServiceConnection.
if (!isCurrent(callback, "onConnectFailed")) {
return;
}
// Don't allow them to call us twice.
if (mState != CONNECT_STATE_CONNECTING) {
Log.w(TAG, "onConnect from service while mState="
+ getStateLabel(mState) + "... ignoring");
return;
}
// Clean up
forceCloseConnection();
// Tell the app.
mCallback.onConnectionFailed();
}
});
|
private final void | onLoadChildren(android.service.media.IMediaBrowserServiceCallbacks callback, java.lang.String parentId, android.content.pm.ParceledListSlice list)
mHandler.post(new Runnable() {
@Override
public void run() {
// Check that there hasn't been a disconnect or a different
// ServiceConnection.
if (!isCurrent(callback, "onLoadChildren")) {
return;
}
List<MediaItem> data = list.getList();
if (DBG) {
Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
}
if (data == null) {
data = Collections.emptyList();
}
// Check that the subscription is still subscribed.
final Subscription subscription = mSubscriptions.get(parentId);
if (subscription == null) {
if (DBG) {
Log.d(TAG, "onLoadChildren for id that isn't subscribed id="
+ parentId);
}
return;
}
// Tell the app.
subscription.callback.onChildrenLoaded(parentId, data);
}
});
|
private final void | onServiceConnected(android.service.media.IMediaBrowserServiceCallbacks callback, java.lang.String root, MediaSession.Token session, android.os.Bundle extra)
mHandler.post(new Runnable() {
@Override
public void run() {
// Check to make sure there hasn't been a disconnect or a different
// ServiceConnection.
if (!isCurrent(callback, "onConnect")) {
return;
}
// Don't allow them to call us twice.
if (mState != CONNECT_STATE_CONNECTING) {
Log.w(TAG, "onConnect from service while mState="
+ getStateLabel(mState) + "... ignoring");
return;
}
mRootId = root;
mMediaSessionToken = session;
mExtras = extra;
mState = CONNECT_STATE_CONNECTED;
if (DBG) {
Log.d(TAG, "ServiceCallbacks.onConnect...");
dump();
}
mCallback.onConnected();
// we may receive some subscriptions before we are connected, so re-subscribe
// everything now
for (String id : mSubscriptions.keySet()) {
try {
mServiceBinder.addSubscription(id, mServiceCallbacks);
} catch (RemoteException ex) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reregister. So nothing to do here.
Log.d(TAG, "addSubscription failed with RemoteException parentId=" + id);
}
}
}
});
|
public void | subscribe(java.lang.String parentId, android.media.browse.MediaBrowser$SubscriptionCallback callback)Queries for information about the media items that are contained within
the specified id and subscribes to receive updates when they change.
The list of subscriptions is maintained even when not connected and is
restored after reconnection. It is ok to subscribe while not connected
but the results will not be returned until the connection completes.
If the id is already subscribed with a different callback then the new
callback will replace the previous one.
// Check arguments.
if (parentId == null) {
throw new IllegalArgumentException("parentId is null");
}
if (callback == null) {
throw new IllegalArgumentException("callback is null");
}
// Update or create the subscription.
Subscription sub = mSubscriptions.get(parentId);
boolean newSubscription = sub == null;
if (newSubscription) {
sub = new Subscription(parentId);
mSubscriptions.put(parentId, sub);
}
sub.callback = callback;
// If we are connected, tell the service that we are watching. If we aren't
// connected, the service will be told when we connect.
if (mState == CONNECT_STATE_CONNECTED && newSubscription) {
try {
mServiceBinder.addSubscription(parentId, mServiceCallbacks);
} catch (RemoteException ex) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reregister. So nothing to do here.
Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId);
}
}
|
public void | unsubscribe(java.lang.String parentId)Unsubscribes for changes to the children of the specified media id.
The query callback will no longer be invoked for results associated with
this id once this method returns.
// Check arguments.
if (parentId == null) {
throw new IllegalArgumentException("parentId is null");
}
// Remove from our list.
final Subscription sub = mSubscriptions.remove(parentId);
// Tell the service if necessary.
if (mState == CONNECT_STATE_CONNECTED && sub != null) {
try {
mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
} catch (RemoteException ex) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reregister. So nothing to do here.
Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
}
}
|