diff options
author | RoboErik <epastern@google.com> | 2014-05-15 23:08:25 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-05-15 23:08:26 +0000 |
commit | 7e89723bb4204537ede2058b61b94c4e99444c03 (patch) | |
tree | 38796b8cee55e82974fba646971c739e1b7394e9 /services | |
parent | bc3376f1c28ebd03765127cb8351008ddb62fc19 (diff) | |
parent | 4646d288821d62fdfe481be67d8b7fed7d7eabd8 (diff) | |
download | frameworks_base-7e89723bb4204537ede2058b61b94c4e99444c03.zip frameworks_base-7e89723bb4204537ede2058b61b94c4e99444c03.tar.gz frameworks_base-7e89723bb4204537ede2058b61b94c4e99444c03.tar.bz2 |
Merge "Add UserRecords to separate user interactions"
Diffstat (limited to 'services')
5 files changed, 433 insertions, 91 deletions
diff --git a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java index c4e2058..1c5cacd 100644 --- a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java @@ -58,12 +58,16 @@ public class MediaRouteProviderProxy { private final int mUserId; // Interfaces declared in the manifest private final ArrayList<String> mInterfaces = new ArrayList<String>(); - private final ArrayList<RouteConnectionRecord> mConnections = new ArrayList<RouteConnectionRecord>(); + private final ArrayList<RouteConnectionRecord> mConnections + = new ArrayList<RouteConnectionRecord>(); + // The sessions that have a route from this provider selected + private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); private final Handler mHandler = new Handler(); private Intent mBindIntent; private IRouteProvider mBinder; private boolean mRunning; + private boolean mPaused; private boolean mInterested; private boolean mBound; private int mRetryCount; @@ -83,6 +87,12 @@ public class MediaRouteProviderProxy { mBindIntent.setComponent(mComponentName); } + public void destroy() { + stop(); + mSessions.clear(); + updateBinding(); + } + /** * Send any cleanup messages and unbind from the media route provider */ @@ -236,6 +246,19 @@ public class MediaRouteProviderProxy { return mId; } + public void addSession(MediaSessionRecord session) { + mSessions.add(session); + } + + public void removeSession(MediaSessionRecord session) { + mSessions.remove(session); + updateBinding(); + } + + public int getSessionCount() { + return mSessions.size(); + } + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mId + " " + this); String indent = prefix + " "; @@ -257,8 +280,10 @@ public class MediaRouteProviderProxy { } } + // We want to bind as long as we're interested in this provider or there are + // sessions connected to it. private boolean shouldBind() { - return mRunning && mInterested; + return (mRunning && mInterested) || (!mSessions.isEmpty()); } private void bind() { diff --git a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java index cf1d95a..734eab9 100644 --- a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java +++ b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java @@ -90,6 +90,8 @@ public class MediaRouteProviderWatcher { } } + // Stop discovering providers and routes. Providers that still have an + // active session connected to them will not unbind. public void stop() { if (mRunning) { mRunning = false; @@ -97,13 +99,21 @@ public class MediaRouteProviderWatcher { mContext.unregisterReceiver(mScanPackagesReceiver); mHandler.removeCallbacks(mScanPackagesRunnable); - // Stop all providers. + // Stop all inactive providers. for (int i = mProviders.size() - 1; i >= 0; i--) { mProviders.get(i).stop(); } } } + // Clean up the providers forcibly unbinding if necessary + public void destroy() { + for (int i = mProviders.size() - 1; i >= 0; i--) { + mProviders.get(i).destroy(); + mProviders.remove(i); + } + } + public ArrayList<MediaRouteProviderProxy> getProviders() { return mProviders; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 41ab626..9677577 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -113,6 +113,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { // End TransportPerformer fields private boolean mIsActive = false; + private boolean mDestroyed = false; public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { @@ -220,10 +221,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public void selectRoute(RouteInfo route) { synchronized (mLock) { if (route != mRoute) { - if (mConnection != null) { - mConnection.disconnect(); - mConnection = null; - } + disconnect(Session.DISCONNECT_REASON_ROUTE_CHANGED); } mRoute = route; } @@ -261,14 +259,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public boolean setRouteConnected(RouteInfo route, RouteOptions request, RouteConnectionRecord connection) { synchronized (mLock) { + if (mDestroyed) { + Log.i(TAG, "setRouteConnected: session has been destroyed"); + connection.disconnect(); + return false; + } if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) { Log.w(TAG, "setRouteConnected: connected route is stale"); - // TODO figure out disconnection path + connection.disconnect(); return false; } if (request != mRequest) { Log.w(TAG, "setRouteConnected: connection request is stale"); - // TODO figure out disconnection path + connection.disconnect(); return false; } mConnection = connection; @@ -284,7 +287,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @return True if the session is active, false otherwise. */ public boolean isActive() { - return mIsActive; + return mIsActive && !mDestroyed; } /** @@ -307,6 +310,30 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return false; } + /** + * @return True if this session is currently connected to a route. + */ + public boolean isConnected() { + return mConnection != null; + } + + public void disconnect(int reason) { + synchronized (mLock) { + if (!mDestroyed) { + disconnectLocked(reason); + } + } + } + + private void disconnectLocked(int reason) { + if (mConnection != null) { + mConnection.setListener(null); + mConnection.disconnect(); + mConnection = null; + pushDisconnected(reason); + } + } + public boolean isTransportControlEnabled() { return hasFlag(Session.FLAG_HANDLES_TRANSPORT_CONTROLS); } @@ -316,6 +343,28 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mService.sessionDied(this); } + /** + * Finish cleaning up this session, including disconnecting if connected and + * removing the death observer from the callback binder. + */ + public void onDestroy() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + if (isConnected()) { + disconnectLocked(Session.DISCONNECT_REASON_SESSION_DESTROYED); + } + mRoute = null; + mRequest = null; + mDestroyed = true; + } + } + + public ISessionCallback getCallback() { + return mSessionCb.mCb; + } + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mTag + " " + this); @@ -323,7 +372,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid + ", userId=" + mUserId); pw.println(indent + "info=" + mSessionInfo.toString()); - pw.println(indent + "published=" + mIsActive); + pw.println(indent + "active=" + mIsActive); pw.println(indent + "flags=" + mFlags); pw.println(indent + "rating type=" + mRatingType); pw.println(indent + "controllers: " + mControllerCallbacks.size()); @@ -356,12 +405,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return "size=" + fields + ", title=" + title; } - private void onDestroy() { - mService.destroySession(this); + private void pushDisconnected(int reason) { + synchronized (mLock) { + mSessionCb.sendRouteDisconnected(reason); + } } private void pushPlaybackStateUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -376,6 +430,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushMetadataUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -390,6 +447,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushRouteUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -404,6 +464,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushEvent(String event, Bundle data) { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -417,6 +480,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushRouteCommand(RouteCommand command, ResultReceiver cb) { synchronized (mLock) { + if (mDestroyed) { + return; + } if (mRoute == null || !TextUtils.equals(command.getRouteInfo(), mRoute.getId())) { if (cb != null) { cb.send(RouteInterface.RESULT_ROUTE_IS_STALE, null); @@ -470,14 +536,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void disconnect() { - // TODO + MediaSessionRecord.this.disconnect(Session.DISCONNECT_REASON_PROVIDER_DISCONNECTED); } }; private final class SessionStub extends ISession.Stub { @Override public void destroy() { - onDestroy(); + mService.destroySession(MediaSessionRecord.this); } @Override @@ -554,6 +620,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public void disconnectFromRoute(RouteInfo route) { + if (route != null && mRoute != null + && TextUtils.equals(route.getId(), mRoute.getId())) { + disconnect(Session.DISCONNECT_REASON_SESSION_DISCONNECTED); + } + } + + @Override public void setRouteOptions(List<RouteOptions> options) throws RemoteException { mRequests.clear(); for (int i = options.size() - 1; i >= 0; i--) { @@ -621,6 +695,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void sendRouteDisconnected(int reason) { + try { + mCb.onRouteDisconnected(mRoute, reason); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in sendRouteDisconnected"); + } + } + public void play() { try { mCb.onPlay(); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 008f9be..78f3b5f 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -24,20 +24,19 @@ import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; import android.media.session.ISession; import android.media.session.ISessionCallback; -import android.media.session.ISessionController; import android.media.session.ISessionManager; -import android.media.session.PlaybackState; import android.media.session.RouteInfo; import android.media.session.RouteOptions; +import android.media.session.Session; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.SparseArray; import com.android.server.SystemService; import com.android.server.Watchdog; @@ -56,16 +55,18 @@ public class MediaSessionService extends SystemService implements Monitor { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final SessionManagerImpl mSessionManagerImpl; - private final MediaRouteProviderWatcher mRouteProviderWatcher; + // private final MediaRouteProviderWatcher mRouteProviderWatcher; private final MediaSessionStack mPriorityStack; - private final ArrayList<MediaSessionRecord> mRecords = new ArrayList<MediaSessionRecord>(); - private final ArrayList<MediaRouteProviderProxy> mProviders - = new ArrayList<MediaRouteProviderProxy>(); + private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); + private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); + // private final ArrayList<MediaRouteProviderProxy> mProviders + // = new ArrayList<MediaRouteProviderProxy>(); private final Object mLock = new Object(); private final Handler mHandler = new Handler(); private MediaSessionRecord mPrioritySession; + private int mCurrentUserId = -1; // Used to keep track of the current request to show routes for a specific // session so we drop late callbacks properly. @@ -77,16 +78,14 @@ public class MediaSessionService extends SystemService implements Monitor { public MediaSessionService(Context context) { super(context); mSessionManagerImpl = new SessionManagerImpl(); - mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback, - mHandler, context.getUserId()); mPriorityStack = new MediaSessionStack(); } @Override public void onStart() { publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); - mRouteProviderWatcher.start(); Watchdog.getInstance().addMonitor(this); + updateUser(); } /** @@ -98,16 +97,28 @@ public class MediaSessionService extends SystemService implements Monitor { public void showRoutePickerForSession(MediaSessionRecord record) { // TODO for now just toggle the route to test (we will only have one // match for now) - if (record.getRoute() != null) { - // For now send null to mean the local route - record.selectRoute(null); - return; - } - mShowRoutesRequestId++; - ArrayList<MediaRouteProviderProxy> providers = mRouteProviderWatcher.getProviders(); - for (int i = providers.size() - 1; i >= 0; i--) { - MediaRouteProviderProxy provider = providers.get(i); - provider.getRoutes(record, mShowRoutesRequestId); + synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session tried to show route picker. Ignoring."); + return; + } + RouteInfo current = record.getRoute(); + UserRecord user = mUserRecords.get(record.getUserId()); + if (current != null) { + // For now send null to mean the local route + MediaRouteProviderProxy proxy = user.getProviderLocked(current.getProvider()); + if (proxy != null) { + proxy.removeSession(record); + } + record.selectRoute(null); + return; + } + ArrayList<MediaRouteProviderProxy> providers = user.getProvidersLocked(); + mShowRoutesRequestId++; + for (int i = providers.size() - 1; i >= 0; i--) { + MediaRouteProviderProxy provider = providers.get(i); + provider.getRoutes(record, mShowRoutesRequestId); + } } } @@ -121,19 +132,31 @@ public class MediaSessionService extends SystemService implements Monitor { public void connectToRoute(MediaSessionRecord session, RouteInfo route, RouteOptions options) { synchronized (mLock) { - MediaRouteProviderProxy proxy = getProviderLocked(route.getProvider()); + if (!mAllSessions.contains(session)) { + Log.d(TAG, "Unknown session attempting to connect to route. Ignoring"); + return; + } + UserRecord user = mUserRecords.get(session.getUserId()); + if (user == null) { + Log.wtf(TAG, "connectToRoute: User " + session.getUserId() + " does not exist."); + return; + } + MediaRouteProviderProxy proxy = user.getProviderLocked(route.getProvider()); if (proxy == null) { Log.w(TAG, "Provider for route " + route.getName() + " does not exist."); return; } RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true); - // TODO make connect an async call to a ThreadPoolExecutor proxy.connectToRoute(session, route, request); } } public void updateSession(MediaSessionRecord record) { synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session updated. Ignoring."); + return; + } mPriorityStack.onSessionStateChange(record); if (record.isSystemPriority()) { if (record.isActive()) { @@ -152,17 +175,48 @@ public class MediaSessionService extends SystemService implements Monitor { public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session changed playback state. Ignoring."); + return; + } mPriorityStack.onPlaystateChange(record, oldState, newState); } } @Override + public void onStartUser(int userHandle) { + updateUser(); + } + + @Override + public void onSwitchUser(int userHandle) { + updateUser(); + } + + @Override + public void onStopUser(int userHandle) { + synchronized (mLock) { + UserRecord user = mUserRecords.get(userHandle); + if (user != null) { + destroyUserLocked(user); + } + } + } + + @Override public void monitor() { synchronized (mLock) { // Check for deadlock } } + protected void enforcePhoneStatePermission(int pid, int uid) { + if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); + } + } + void sessionDied(MediaSessionRecord session) { synchronized (mLock) { destroySessionLocked(session); @@ -175,12 +229,63 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private void updateUser() { + synchronized (mLock) { + int userId = ActivityManager.getCurrentUser(); + if (mCurrentUserId != userId) { + final int oldUserId = mCurrentUserId; + mCurrentUserId = userId; // do this first + + UserRecord oldUser = mUserRecords.get(oldUserId); + if (oldUser != null) { + oldUser.stopLocked(); + } + + UserRecord newUser = getOrCreateUser(userId); + newUser.startLocked(); + } + } + } + + /** + * Stop the user and unbind from everything. + * + * @param user The user to dispose of + */ + private void destroyUserLocked(UserRecord user) { + user.stopLocked(); + user.destroyLocked(); + mUserRecords.remove(user.mUserId); + } + + /* + * When a session is removed several things need to happen. + * 1. We need to remove it from the relevant user. + * 2. We need to remove it from the priority stack. + * 3. We need to remove it from all sessions. + * 4. If this is the system priority session we need to clear it. + * 5. We need to unlink to death from the cb binder + * 6. We need to tell the session to do any final cleanup (onDestroy) + */ private void destroySessionLocked(MediaSessionRecord session) { - mRecords.remove(session); + int userId = session.getUserId(); + UserRecord user = mUserRecords.get(userId); + if (user != null) { + user.removeSessionLocked(session); + } + mPriorityStack.removeSession(session); + mAllSessions.remove(session); if (session == mPrioritySession) { mPrioritySession = null; } + + try { + session.getCallback().asBinder().unlinkToDeath(session, 0); + } catch (Exception e) { + // ignore exceptions while destroying a session. + } + session.onDestroy(); } private void enforcePackageName(String packageName, int uid) { @@ -197,13 +302,6 @@ public class MediaSessionService extends SystemService implements Monitor { throw new IllegalArgumentException("packageName is not owned by the calling process"); } - protected void enforcePhoneStatePermission(int pid, int uid) { - if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); - } - } - /** * Checks a caller's authorization to register an IRemoteControlDisplay. * Authorization is granted if one of the following is true: @@ -271,14 +369,22 @@ public class MediaSessionService extends SystemService implements Monitor { } private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, ISessionCallback cb, String tag) { + String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { synchronized (mLock) { return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); } } + /* + * When a session is created the following things need to happen. + * 1. It's callback binder needs a link to death + * 2. It needs to be added to all sessions. + * 3. It needs to be added to the priority stack. + * 4. It needs to be added to the relevant user record. + */ private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag) { + final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, callerPackageName, cb, tag, this, mHandler); try { @@ -286,17 +392,31 @@ public class MediaSessionService extends SystemService implements Monitor { } catch (RemoteException e) { throw new RuntimeException("Media Session owner died prematurely.", e); } - mRecords.add(session); + + mAllSessions.add(session); mPriorityStack.addSession(session); + + UserRecord user = getOrCreateUser(userId); + user.addSessionLocked(session); + if (DEBUG) { Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); } return session; } + private UserRecord getOrCreateUser(int userId) { + UserRecord user = mUserRecords.get(userId); + if (user == null) { + user = new UserRecord(getContext(), userId); + mUserRecords.put(userId, user); + } + return user; + } + private int findIndexOfSessionForIdLocked(String sessionId) { - for (int i = mRecords.size() - 1; i >= 0; i--) { - MediaSessionRecord session = mRecords.get(i); + for (int i = mAllSessions.size() - 1; i >= 0; i--) { + MediaSessionRecord session = mAllSessions.get(i); if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { return i; } @@ -304,42 +424,11 @@ public class MediaSessionService extends SystemService implements Monitor { return -1; } - private MediaRouteProviderProxy getProviderLocked(String providerId) { - for (int i = mProviders.size() - 1; i >= 0; i--) { - MediaRouteProviderProxy provider = mProviders.get(i); - if (TextUtils.equals(providerId, provider.getId())) { - return provider; - } - } - return null; - } - private boolean isSessionDiscoverable(MediaSessionRecord record) { - // TODO probably want to check more than if it's published. + // TODO probably want to check more than if it's active. return record.isActive(); } - private MediaRouteProviderWatcher.Callback mProviderWatcherCallback - = new MediaRouteProviderWatcher.Callback() { - @Override - public void removeProvider(MediaRouteProviderProxy provider) { - synchronized (mLock) { - mProviders.remove(provider); - provider.setRoutesListener(null); - provider.setInterested(false); - } - } - - @Override - public void addProvider(MediaRouteProviderProxy provider) { - synchronized (mLock) { - mProviders.add(provider); - provider.setRoutesListener(mRoutesCallback); - provider.setInterested(true); - } - } - }; - private MediaRouteProviderProxy.RoutesListener mRoutesCallback = new MediaRouteProviderProxy.RoutesListener() { @Override @@ -350,8 +439,12 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1 && routes != null && routes.size() > 0) { - MediaSessionRecord record = mRecords.get(index); - record.selectRoute(routes.get(0)); + MediaSessionRecord record = mAllSessions.get(index); + RouteInfo route = routes.get(0); + record.selectRoute(route); + UserRecord user = mUserRecords.get(record.getUserId()); + MediaRouteProviderProxy provider = user.getProviderLocked(route.getProvider()); + provider.addSession(record); } } } @@ -362,13 +455,135 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1) { - MediaSessionRecord session = mRecords.get(index); + MediaSessionRecord session = mAllSessions.get(index); session.setRouteConnected(route, options.getConnectionOptions(), connection); } } } }; + /** + * Information about a particular user. The contents of this object is + * guarded by mLock. + */ + final class UserRecord { + private final int mUserId; + private final MediaRouteProviderWatcher mRouteProviderWatcher; + private final ArrayList<MediaRouteProviderProxy> mProviders + = new ArrayList<MediaRouteProviderProxy>(); + private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + + public UserRecord(Context context, int userId) { + mUserId = userId; + mRouteProviderWatcher = new MediaRouteProviderWatcher(context, + mProviderWatcherCallback, mHandler, userId); + } + + public void startLocked() { + mRouteProviderWatcher.start(); + } + + public void stopLocked() { + mRouteProviderWatcher.stop(); + updateInterestLocked(); + } + + public void destroyLocked() { + for (int i = mSessions.size() - 1; i >= 0; i--) { + MediaSessionRecord session = mSessions.get(i); + MediaSessionService.this.destroySessionLocked(session); + if (session.isConnected()) { + session.disconnect(Session.DISCONNECT_REASON_USER_STOPPING); + } + } + } + + public ArrayList<MediaRouteProviderProxy> getProvidersLocked() { + return mProviders; + } + + public ArrayList<MediaSessionRecord> getSessionsLocked() { + return mSessions; + } + + public void addSessionLocked(MediaSessionRecord session) { + mSessions.add(session); + updateInterestLocked(); + } + + public void removeSessionLocked(MediaSessionRecord session) { + mSessions.remove(session); + RouteInfo route = session.getRoute(); + if (route != null) { + MediaRouteProviderProxy provider = getProviderLocked(route.getProvider()); + if (provider != null) { + provider.removeSession(session); + } + } + updateInterestLocked(); + } + + public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "Record for user " + mUserId); + String indent = prefix + " "; + int size = mProviders.size(); + pw.println(indent + size + " Providers:"); + for (int i = 0; i < size; i++) { + mProviders.get(i).dump(pw, indent); + } + pw.println(); + size = mSessions.size(); + pw.println(indent + size + " Sessions:"); + for (int i = 0; i < size; i++) { + // Just print the session info, the full session dump will + // already be in the list of all sessions. + pw.println(indent + mSessions.get(i).getSessionInfo()); + } + } + + public void updateInterestLocked() { + // TODO go through the sessions and build up the set of interfaces + // we're interested in. Update the provider watcher. + // For now, just express interest in all providers for the current + // user + boolean interested = mUserId == mCurrentUserId; + for (int i = mProviders.size() - 1; i >= 0; i--) { + mProviders.get(i).setInterested(interested); + } + } + + private MediaRouteProviderProxy getProviderLocked(String providerId) { + for (int i = mProviders.size() - 1; i >= 0; i--) { + MediaRouteProviderProxy provider = mProviders.get(i); + if (TextUtils.equals(providerId, provider.getId())) { + return provider; + } + } + return null; + } + + private MediaRouteProviderWatcher.Callback mProviderWatcherCallback + = new MediaRouteProviderWatcher.Callback() { + @Override + public void removeProvider(MediaRouteProviderProxy provider) { + synchronized (mLock) { + mProviders.remove(provider); + provider.setRoutesListener(null); + provider.setInterested(false); + } + } + + @Override + public void addProvider(MediaRouteProviderProxy provider) { + synchronized (mLock) { + mProviders.add(provider); + provider.setRoutesListener(mRoutesCallback); + provider.setInterested(true); + } + } + }; + } + class SessionManagerImpl extends ISessionManager.Stub { // TODO add createSessionAsUser, pass user-id to // ActivityManagerNative.handleIncomingUser and stash result for use @@ -447,19 +662,19 @@ public class MediaSessionService extends SystemService implements Monitor { if (mPrioritySession != null) { mPrioritySession.dump(pw, ""); } - int count = mRecords.size(); + int count = mAllSessions.size(); pw.println(count + " Sessions:"); for (int i = 0; i < count; i++) { - mRecords.get(i).dump(pw, ""); + mAllSessions.get(i).dump(pw, ""); pw.println(); } mPriorityStack.dump(pw, ""); - pw.println("Providers:"); - count = mProviders.size(); + pw.println("User Records:"); + count = mUserRecords.size(); for (int i = 0; i < count; i++) { - MediaRouteProviderProxy provider = mProviders.get(i); - provider.dump(pw, ""); + UserRecord user = mUserRecords.get(i); + user.dumpLocked(pw, ""); } } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 1e1818d..f89b14a 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -19,7 +19,6 @@ package com.android.server.media; import android.media.session.PlaybackState; import android.media.session.Session; import android.os.UserHandle; -import android.text.TextUtils; import java.io.PrintWriter; import java.util.ArrayList; @@ -49,6 +48,8 @@ public class MediaSessionStack { private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + private MediaSessionRecord mGlobalPrioritySession; + private MediaSessionRecord mCachedButtonReceiver; private MediaSessionRecord mCachedDefault; private ArrayList<MediaSessionRecord> mCachedActiveList; @@ -61,6 +62,9 @@ public class MediaSessionStack { */ public void addSession(MediaSessionRecord record) { mSessions.add(record); + if ((record.getFlags() & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { + mGlobalPrioritySession = record; + } clearCache(); } @@ -71,6 +75,9 @@ public class MediaSessionStack { */ public void removeSession(MediaSessionRecord record) { mSessions.remove(record); + if (record == mGlobalPrioritySession) { + mGlobalPrioritySession = null; + } clearCache(); } @@ -156,6 +163,9 @@ public class MediaSessionStack { * @return The default media button session or null. */ public MediaSessionRecord getDefaultMediaButtonSession(int userId) { + if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { + return mGlobalPrioritySession; + } if (mCachedButtonReceiver != null) { return mCachedButtonReceiver; } |