summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/session/ISessionManager.aidl4
-rw-r--r--media/java/android/media/session/SessionManager.java38
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java28
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java81
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java36
5 files changed, 138 insertions, 49 deletions
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 7a8c22e..e341647 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -25,6 +25,6 @@ import android.os.Bundle;
* @hide
*/
interface ISessionManager {
- ISession createSession(String packageName, in ISessionCallback cb, String tag);
- List<IBinder> getSessions(in ComponentName compName);
+ ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+ List<IBinder> getSessions(in ComponentName compName, int userId);
} \ No newline at end of file
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java
index fd022fc..1eb3b7a 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -22,6 +22,7 @@ import android.media.session.ISessionManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.util.Log;
@@ -65,10 +66,25 @@ public final class SessionManager {
* @return a {@link Session} for the new session
*/
public Session createSession(String tag) {
+ return createSessionAsUser(tag, UserHandle.myUserId());
+ }
+
+ /**
+ * Creates a new session as the specified user. To create a session as a
+ * user other than your own you must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission.
+ *
+ * @param tag A short name for debugging purposes
+ * @param userId The user id to create the session as.
+ * @return a {@link Session} for the new session
+ * @hide
+ */
+ public Session createSessionAsUser(String tag, int userId) {
try {
Session.CallbackStub cbStub = new Session.CallbackStub();
Session session = new Session(mService
- .createSession(mContext.getPackageName(), cbStub, tag), cbStub);
+ .createSession(mContext.getPackageName(), cbStub, tag, userId), cbStub);
cbStub.setMediaSession(session);
return session;
@@ -91,9 +107,27 @@ public final class SessionManager {
* @return A list of controllers for ongoing sessions
*/
public List<SessionController> getActiveSessions(ComponentName notificationListener) {
+ return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
+ }
+
+ /**
+ * Get active sessions for a specific user. To retrieve actions for a user
+ * other than your own you must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission
+ * in addition to any other requirements. If you are an enabled notification
+ * listener you may only get sessions for the users you are enabled for.
+ *
+ * @param notificationListener The enabled notification listener component.
+ * May be null.
+ * @param userId The user id to fetch sessions for.
+ * @return A list of controllers for ongoing sessions.
+ * @hide
+ */
+ public List<SessionController> getActiveSessionsForUser(ComponentName notificationListener,
+ int userId) {
ArrayList<SessionController> controllers = new ArrayList<SessionController>();
try {
- List<IBinder> binders = mService.getSessions(notificationListener);
+ List<IBinder> binders = mService.getSessions(notificationListener, userId);
for (int i = binders.size() - 1; i >= 0; i--) {
SessionController controller = SessionController.fromBinder(ISessionController.Stub
.asInterface(binders.get(i)));
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 015032b..41ab626 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.app.ActivityManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.routeprovider.RouteRequest;
@@ -42,6 +43,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -80,7 +82,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private final MessageHandler mHandler;
- private final int mPid;
+ private final int mOwnerPid;
+ private final int mOwnerUid;
+ private final int mUserId;
private final SessionInfo mSessionInfo;
private final String mTag;
private final ControllerStub mController;
@@ -110,10 +114,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private boolean mIsActive = false;
- public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
- MediaSessionService service, Handler handler) {
- mPid = pid;
- mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), packageName);
+ public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
+ ISessionCallback cb, String tag, MediaSessionService service, Handler handler) {
+ mOwnerPid = ownerPid;
+ mOwnerUid = ownerUid;
+ mUserId = userId;
+ mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), ownerPackageName);
mTag = tag;
mController = new ControllerStub();
mSession = new SessionStub();
@@ -187,6 +193,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
/**
+ * Get the user id this session was created for.
+ *
+ * @return The user id for this session.
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
* Check if this session has system priorty and should receive media buttons
* before any other sessions.
*
@@ -305,7 +320,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
pw.println(prefix + mTag + " " + this);
final String indent = prefix + " ";
- pw.println(indent + "pid=" + mPid);
+ pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid
+ + ", userId=" + mUserId);
pw.println(indent + "info=" + mSessionInfo.toString());
pw.println(indent + "published=" + mIsActive);
pw.println(indent + "flags=" + mFlags);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index fb858fc..008f9be 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -63,7 +63,6 @@ public class MediaSessionService extends SystemService implements Monitor {
private final ArrayList<MediaRouteProviderProxy> mProviders
= new ArrayList<MediaRouteProviderProxy>();
private final Object mLock = new Object();
- // TODO do we want a separate thread for handling mediasession messages?
private final Handler mHandler = new Handler();
private MediaSessionRecord mPrioritySession;
@@ -72,8 +71,8 @@ public class MediaSessionService extends SystemService implements Monitor {
// session so we drop late callbacks properly.
private int mShowRoutesRequestId = 0;
- // TODO refactor to have per user state. See MediaRouterService for an
- // example
+ // TODO refactor to have per user state for providers. See
+ // MediaRouterService for an example
public MediaSessionService(Context context) {
super(context);
@@ -211,25 +210,42 @@ public class MediaSessionService extends SystemService implements Monitor {
* <ul>
* <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
* permission</li>
- * <li>the caller's listener is one of the enabled notification listeners</li>
+ * <li>the caller's listener is one of the enabled notification listeners
+ * for the caller's user</li>
* </ul>
*/
- private void enforceMediaPermissions(ComponentName compName, int pid, int uid) {
+ private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+ int resolvedUserId) {
if (getContext()
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
- && !isEnabledNotificationListener(compName)) {
+ && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
+ resolvedUserId)) {
throw new SecurityException("Missing permission to control media.");
}
}
- private boolean isEnabledNotificationListener(ComponentName compName) {
+ /**
+ * This checks if the component is an enabled notification listener for the
+ * specified user. Enabled components may only operate on behalf of the user
+ * they're running as.
+ *
+ * @param compName The component that is enabled.
+ * @param userId The user id of the caller.
+ * @param forUserId The user id they're making the request on behalf of.
+ * @return True if the component is enabled, false otherwise
+ */
+ private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+ int forUserId) {
+ if (userId != forUserId) {
+ // You may not access another user's content as an enabled listener.
+ return false;
+ }
if (compName != null) {
- final int currentUser = ActivityManager.getCurrentUser();
final String enabledNotifListeners = Settings.Secure.getStringForUser(
getContext().getContentResolver(),
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
+ userId);
if (enabledNotifListeners != null) {
final String[] components = enabledNotifListeners.split(":");
for (int i = 0; i < components.length; i++) {
@@ -248,23 +264,23 @@ public class MediaSessionService extends SystemService implements Monitor {
}
if (DEBUG) {
Log.d(TAG, "not ok to get sessions, " + compName +
- " is not in list of ENABLED_NOTIFICATION_LISTENERS");
+ " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
}
}
return false;
}
- private MediaSessionRecord createSessionInternal(int pid, String packageName,
- ISessionCallback cb, String tag, boolean forCalls) {
+ private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
+ String callerPackageName, ISessionCallback cb, String tag) {
synchronized (mLock) {
- return createSessionLocked(pid, packageName, cb, tag);
+ return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
}
}
- private MediaSessionRecord createSessionLocked(int pid, String packageName,
- ISessionCallback cb, String tag) {
- final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
- mHandler);
+ 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 {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
@@ -273,7 +289,7 @@ public class MediaSessionService extends SystemService implements Monitor {
mRecords.add(session);
mPriorityStack.addSession(session);
if (DEBUG) {
- Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
+ Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
}
return session;
}
@@ -358,41 +374,50 @@ public class MediaSessionService extends SystemService implements Monitor {
// ActivityManagerNative.handleIncomingUser and stash result for use
// when starting services on that session's behalf.
@Override
- public ISession createSession(String packageName, ISessionCallback cb, String tag)
- throws RemoteException {
+ public ISession createSession(String packageName, ISessionCallback cb, String tag,
+ int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
enforcePackageName(packageName, uid);
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ false /* allowAll */, true /* requireFull */, "createSession", packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
}
- return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder();
+ return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
+ .getSessionBinder();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public List<IBinder> getSessions(ComponentName componentName) {
+ public List<IBinder> getSessions(ComponentName componentName, int userId) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
+ String packageName = null;
if (componentName != null) {
// If they gave us a component name verify they own the
// package
- enforcePackageName(componentName.getPackageName(), uid);
+ packageName = componentName.getPackageName();
+ enforcePackageName(packageName, uid);
}
- // Then check if they have the permissions or their component is
- // allowed
- enforceMediaPermissions(componentName, pid, uid);
+ // Check that they can make calls on behalf of the user and
+ // get the final user id
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
ArrayList<IBinder> binders = new ArrayList<IBinder>();
synchronized (mLock) {
ArrayList<MediaSessionRecord> records = mPriorityStack
- .getActiveSessions();
+ .getActiveSessions(resolvedUserId);
int size = records.size();
for (int i = 0; i < size; i++) {
binders.add(records.get(i).getControllerBinder().asBinder());
@@ -428,7 +453,7 @@ public class MediaSessionService extends SystemService implements Monitor {
mRecords.get(i).dump(pw, "");
pw.println();
}
- mPriorityStack.dumpLocked(pw, "");
+ mPriorityStack.dump(pw, "");
pw.println("Providers:");
count = mProviders.size();
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f9f004d..1e1818d 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -18,6 +18,7 @@ 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;
@@ -104,11 +105,12 @@ public class MediaSessionStack {
* Get the current priority sorted list of active sessions. The most
* important session is at index 0 and the least important at size - 1.
*
+ * @param userId The user to check.
* @return All the active sessions in priority order.
*/
- public ArrayList<MediaSessionRecord> getActiveSessions() {
+ public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
if (mCachedActiveList == null) {
- mCachedActiveList = getPriorityListLocked(true, 0);
+ mCachedActiveList = getPriorityListLocked(true, 0, userId);
}
return mCachedActiveList;
}
@@ -118,13 +120,14 @@ public class MediaSessionStack {
* transport controls. The most important session is at index 0 and the
* least important at size -1.
*
+ * @param userId The user to check.
* @return All the active sessions that handle transport controls in
* priority order.
*/
- public ArrayList<MediaSessionRecord> getTransportControlSessions() {
+ public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) {
if (mCachedTransportControlList == null) {
mCachedTransportControlList = getPriorityListLocked(true,
- Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ Session.FLAG_HANDLES_TRANSPORT_CONTROLS, userId);
}
return mCachedTransportControlList;
}
@@ -132,13 +135,14 @@ public class MediaSessionStack {
/**
* Get the highest priority active session.
*
+ * @param userId The user to check.
* @return The current highest priority session or null.
*/
- public MediaSessionRecord getDefaultSession() {
+ public MediaSessionRecord getDefaultSession(int userId) {
if (mCachedDefault != null) {
return mCachedDefault;
}
- ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0);
+ ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
if (records.size() > 0) {
return records.get(0);
}
@@ -148,22 +152,24 @@ public class MediaSessionStack {
/**
* Get the highest priority session that can handle media buttons.
*
+ * @param userId The user to check.
* @return The default media button session or null.
*/
- public MediaSessionRecord getDefaultMediaButtonSession() {
+ public MediaSessionRecord getDefaultMediaButtonSession(int userId) {
if (mCachedButtonReceiver != null) {
return mCachedButtonReceiver;
}
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
- Session.FLAG_HANDLES_MEDIA_BUTTONS);
+ Session.FLAG_HANDLES_MEDIA_BUTTONS, userId);
if (records.size() > 0) {
mCachedButtonReceiver = records.get(0);
}
return mCachedButtonReceiver;
}
- public void dumpLocked(PrintWriter pw, String prefix) {
- ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0);
+ public void dump(PrintWriter pw, String prefix) {
+ ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
+ UserHandle.USER_ALL);
int count = sortedSessions.size();
pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
String indent = prefix + " ";
@@ -182,9 +188,12 @@ public class MediaSessionStack {
* all sessions.
* @param withFlags Only return sessions with all the specified flags set. 0
* returns all sessions.
+ * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+ * will return sessions for all users.
* @return The priority sorted list of sessions.
*/
- private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) {
+ private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+ int userId) {
ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
int lastLocalIndex = 0;
int lastActiveIndex = 0;
@@ -194,7 +203,12 @@ public class MediaSessionStack {
for (int i = 0; i < size; i++) {
final MediaSessionRecord session = mSessions.get(i);
+ if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+ // Filter out sessions for the wrong user
+ continue;
+ }
if ((session.getFlags() & withFlags) != withFlags) {
+ // Filter out sessions with the wrong flags
continue;
}
if (!session.isActive()) {