diff options
| -rw-r--r-- | media/java/android/service/media/MediaBrowserService.java | 91 |
1 files changed, 55 insertions, 36 deletions
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index ba7ab9aa97ed..fa9afa872091 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -103,11 +103,9 @@ public abstract class MediaBrowserService extends Service { RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED }) private @interface ResultFlags { } - private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); - private ConnectionRecord mCurConnection; private final Handler mHandler = new Handler(); - private ServiceBinder mBinder; - MediaSession.Token mSession; + + private final ServiceState mServiceState = new ServiceState(); /** * All the info about a connection. @@ -139,7 +137,7 @@ public abstract class MediaBrowserService extends Service { service.mHandler.post(new Runnable() { @Override public void run() { - service.mConnections.remove(callbacks.asBinder()); + service.mServiceState.mConnections.remove(callbacks.asBinder()); } }); } @@ -238,17 +236,16 @@ public abstract class MediaBrowserService extends Service { @Override public void run() { final IBinder b = callbacks.asBinder(); - // Clear out the old subscriptions. We are getting new ones. - service.mConnections.remove(b); + service.mServiceState.mConnections.remove(b); // Temporarily sets a placeholder ConnectionRecord to make // getCurrentBrowserInfo() work in onGetRoot(). - service.mCurConnection = + service.mServiceState.mCurConnection = new ConnectionRecord( service, pkg, pid, uid, rootHints, callbacks, null); BrowserRoot root = service.onGetRoot(pkg, uid, rootHints); - service.mCurConnection = null; + service.mServiceState.mCurConnection = null; // If they didn't return something, don't allow this client. if (root == null) { @@ -265,16 +262,18 @@ public abstract class MediaBrowserService extends Service { ConnectionRecord connection = new ConnectionRecord( service, pkg, pid, uid, rootHints, callbacks, root); - service.mConnections.put(b, connection); + service.mServiceState.mConnections.put(b, connection); b.linkToDeath(connection, 0); - if (service.mSession != null) { - callbacks.onConnect(connection.root.getRootId(), - service.mSession, connection.root.getExtras()); + if (service.mServiceState.mSession != null) { + callbacks.onConnect( + connection.root.getRootId(), + service.mServiceState.mSession, + connection.root.getExtras()); } } catch (RemoteException ex) { Log.w(TAG, "Calling onConnect() failed. Dropping client. " + "pkg=" + pkg); - service.mConnections.remove(b); + service.mServiceState.mConnections.remove(b); } } } @@ -294,7 +293,7 @@ public abstract class MediaBrowserService extends Service { final IBinder b = callbacks.asBinder(); // Clear out the old subscriptions. We are getting new ones. - final ConnectionRecord old = service.mConnections.remove(b); + final ConnectionRecord old = service.mServiceState.mConnections.remove(b); if (old != null) { // TODO old.callbacks.asBinder().unlinkToDeath(old, 0); @@ -322,7 +321,7 @@ public abstract class MediaBrowserService extends Service { final IBinder b = callbacks.asBinder(); // Get the record for the connection - final ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "addSubscription for callback that isn't registered id=" + id); @@ -353,7 +352,7 @@ public abstract class MediaBrowserService extends Service { public void run() { final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id); @@ -379,7 +378,7 @@ public abstract class MediaBrowserService extends Service { @Override public void run() { final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = service.mConnections.get(b); + ConnectionRecord connection = service.mServiceState.mConnections.get(b); if (connection == null) { Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); return; @@ -393,13 +392,13 @@ public abstract class MediaBrowserService extends Service { @Override public void onCreate() { super.onCreate(); - mBinder = new ServiceBinder(this); + mServiceState.mBinder = new ServiceBinder(this); } @Override public IBinder onBind(Intent intent) { if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mBinder; + return mServiceState.mBinder; } return null; } @@ -524,14 +523,14 @@ public abstract class MediaBrowserService extends Service { if (token == null) { throw new IllegalArgumentException("Session token may not be null."); } - if (mSession != null) { + if (mServiceState.mSession != null) { throw new IllegalStateException("The session token has already been set."); } - mSession = token; + mServiceState.mSession = token; mHandler.post(new Runnable() { @Override public void run() { - Iterator<ConnectionRecord> iter = mConnections.values().iterator(); + Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator(); while (iter.hasNext()) { ConnectionRecord connection = iter.next(); try { @@ -551,7 +550,7 @@ public abstract class MediaBrowserService extends Service { * or if it has been destroyed. */ public @Nullable MediaSession.Token getSessionToken() { - return mSession; + return mServiceState.mSession; } /** @@ -567,11 +566,12 @@ public abstract class MediaBrowserService extends Service { * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED */ public final Bundle getBrowserRootHints() { - if (mCurConnection == null) { + ConnectionRecord curConnection = mServiceState.mCurConnection; + if (curConnection == null) { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); + return curConnection.rootHints == null ? null : new Bundle(curConnection.rootHints); } /** @@ -582,11 +582,12 @@ public abstract class MediaBrowserService extends Service { * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) */ public final RemoteUserInfo getCurrentBrowserInfo() { - if (mCurConnection == null) { + ConnectionRecord curConnection = mServiceState.mCurConnection; + if (curConnection == null) { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); + return new RemoteUserInfo(curConnection.pkg, curConnection.pid, curConnection.uid); } /** @@ -626,8 +627,8 @@ public abstract class MediaBrowserService extends Service { mHandler.post(new Runnable() { @Override public void run() { - for (IBinder binder : mConnections.keySet()) { - ConnectionRecord connection = mConnections.get(binder); + for (IBinder binder : mServiceState.mConnections.keySet()) { + ConnectionRecord connection = mServiceState.mConnections.get(binder); List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId); if (callbackList != null) { @@ -717,7 +718,7 @@ public abstract class MediaBrowserService extends Service { new Result<List<MediaBrowser.MediaItem>>(parentId) { @Override void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { + if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) { if (DBG) { Log.d(TAG, "Not sending onLoadChildren result for connection that has" + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); @@ -747,13 +748,13 @@ public abstract class MediaBrowserService extends Service { } }; - mCurConnection = connection; + mServiceState.mCurConnection = connection; if (options == null) { onLoadChildren(parentId, result); } else { onLoadChildren(parentId, result, options); } - mCurConnection = null; + mServiceState.mCurConnection = null; if (!result.isDone()) { throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" @@ -767,7 +768,7 @@ public abstract class MediaBrowserService extends Service { new Result<MediaBrowser.MediaItem>(itemId) { @Override void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { + if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) { if (DBG) { Log.d(TAG, "Not sending onLoadItem result for connection that has" + " been disconnected. pkg=" + connection.pkg + " id=" + itemId); @@ -784,9 +785,9 @@ public abstract class MediaBrowserService extends Service { } }; - mCurConnection = connection; + mServiceState.mCurConnection = connection; onLoadItem(itemId, result); - mCurConnection = null; + mServiceState.mCurConnection = null; if (!result.isDone()) { throw new IllegalStateException("onLoadItem must call detach() or sendResult()" @@ -881,4 +882,22 @@ public abstract class MediaBrowserService extends Service { return mExtras; } } + + /** + * Holds all state associated with {@link #mSession}. + * + * <p>This class decouples the state associated with the session from the lifecycle of the + * service. This allows us to put the service in a valid state once the session is released + * (which is an irrecoverable invalid state). More details about this in b/185136506. + */ + private static class ServiceState { + + // Fields accessed from any caller thread. + @Nullable private MediaSession.Token mSession; + @Nullable private ServiceBinder mBinder; + + // Fields accessed from mHandler only. + @NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); + @Nullable private ConnectionRecord mCurConnection; + } } |