summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorRoboErik <epastern@google.com>2014-05-13 10:13:04 -0700
committerRoboErik <epastern@google.com>2014-05-14 13:19:48 -0700
commit4646d288821d62fdfe481be67d8b7fed7d7eabd8 (patch)
treeac21a45a6ad5829708056e375dd530f107224dd7 /services
parent515396a6b5ee3eab57fed87ee0f4aa63783e2e61 (diff)
downloadframeworks_base-4646d288821d62fdfe481be67d8b7fed7d7eabd8.zip
frameworks_base-4646d288821d62fdfe481be67d8b7fed7d7eabd8.tar.gz
frameworks_base-4646d288821d62fdfe481be67d8b7fed7d7eabd8.tar.bz2
Add UserRecords to separate user interactions
Each user record maintains the list of sessions and providers that are running under that user. Lifecycle for providers has been modified to stop discovery when the user is no longer current but keep the binder connection open so long as there's a session that has selected a route from that provider. When a user is stopped all providers on that user will be unbound even if they were still in use. Change-Id: Iadf1efded3415f7ecf384d3a73513883de9c86b0
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/media/MediaRouteProviderProxy.java29
-rw-r--r--services/core/java/com/android/server/media/MediaRouteProviderWatcher.java12
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java106
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java365
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java12
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;
}