Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8548472 authored by RoboErik's avatar RoboErik Committed by Android Git Automerger
Browse files

am 5ba476c3: Merge "Make sessions aware of user id"

* commit '5ba476c37f997a2dfde4f0b18d77a19e7451ed55':
  Make sessions aware of user id
parents bd2db3c7 f33e9982
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+36 −2
Original line number Diff line number Diff line
@@ -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)));
+22 −6
Original line number Diff line number Diff line
@@ -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();
@@ -186,6 +192,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        return (mFlags & flag) != 0;
    }

    /**
     * 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);
+53 −28
Original line number Diff line number Diff line
@@ -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();
+25 −11
Original line number Diff line number Diff line
@@ -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()) {