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

Commit a8f95146 authored by RoboErik's avatar RoboErik
Browse files

Add a class for managing Session priority

Priority is given first to the system priorty session, then to
active local sessions, then to active remote sessions, then to
the rest of the sessions. Ordering within categories is by whoever
last performed an action we associate with the user.

The stack has methods for getting filtered sets of this priority.

This also:
-Changes publish to setActive(boolean)
-Adds a flag for handling media buttons.
-Adds a flag for transport controls instead of enabling once.
-Unhides the setFlags API.
-Updates the legacy helper to use the flags.

Change-Id: I6ebeb27410de1b24149fd6e1785613ac444f0774
parent e7880d8e
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -15448,12 +15448,15 @@ package android.media.session {
    method public void disconnect(android.media.session.RouteInfo);
    method public android.media.session.SessionToken getSessionToken();
    method public android.media.session.TransportPerformer getTransportPerformer();
    method public void publish();
    method public boolean isActive();
    method public void release();
    method public void removeCallback(android.media.session.Session.Callback);
    method public void sendEvent(java.lang.String, android.os.Bundle);
    method public void setActive(boolean);
    method public void setFlags(int);
    method public void setRouteOptions(java.util.List<android.media.session.RouteOptions>);
    method public android.media.session.TransportPerformer setTransportPerformerEnabled();
    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
  }
  public static abstract class Session.Callback {
+2 −3
Original line number Diff line number Diff line
@@ -31,9 +31,8 @@ import android.os.ResultReceiver;
interface ISession {
    void sendEvent(String event, in Bundle data);
    ISessionController getController();
    void setTransportPerformerEnabled();
    void setFlags(long flags);
    void publish();
    void setFlags(int flags);
    void setActive(boolean active);
    void destroy();

    // These commands are for setting up and communicating with routes
+11 −2
Original line number Diff line number Diff line
@@ -79,6 +79,8 @@ public class MediaSessionLegacyHelper {
        }
        performer.addListener(listener, mHandler);
        holder.mRccListener = listener;
        holder.mFlags |= Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
        holder.mSession.setFlags(holder.mFlags);
        holder.update();
    }

@@ -87,6 +89,8 @@ public class MediaSessionLegacyHelper {
        if (holder != null && holder.mRccListener != null) {
            holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
            holder.mRccListener = null;
            holder.mFlags &= ~Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
            holder.mSession.setFlags(holder.mFlags);
            holder.update();
        }
    }
@@ -99,6 +103,8 @@ public class MediaSessionLegacyHelper {
            return;
        }
        holder.mMediaButtonListener = new MediaButtonListener(pi, context);
        holder.mFlags |= Session.FLAG_HANDLES_MEDIA_BUTTONS;
        holder.mSession.setFlags(holder.mFlags);
        holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
    }

@@ -106,6 +112,9 @@ public class MediaSessionLegacyHelper {
        SessionHolder holder = getHolder(pi, false);
        if (holder != null && holder.mMediaButtonListener != null) {
            holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
            holder.mFlags &= ~Session.FLAG_HANDLES_MEDIA_BUTTONS;
            holder.mSession.setFlags(holder.mFlags);
            holder.mMediaButtonListener = null;
            holder.update();
        }
    }
@@ -114,8 +123,7 @@ public class MediaSessionLegacyHelper {
        SessionHolder holder = mSessions.get(pi);
        if (holder == null && createIfMissing) {
            Session session = mSessionManager.createSession(TAG);
            session.setTransportPerformerEnabled();
            session.publish();
            session.setActive(true);
            holder = new SessionHolder(session, pi);
            mSessions.put(pi, holder);
        }
@@ -194,6 +202,7 @@ public class MediaSessionLegacyHelper {
        public final PendingIntent mPi;
        public MediaButtonListener mMediaButtonListener;
        public TransportPerformer.Listener mRccListener;
        public int mFlags;

        public SessionHolder(Session session, PendingIntent pi) {
            mSession = session;
+44 −47
Original line number Diff line number Diff line
@@ -64,6 +64,19 @@ import java.util.List;
public final class Session {
    private static final String TAG = "Session";

    /**
     * Set this flag on the session to indicate that it can handle media button
     * events.
     */
    public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;

    /**
     * Set this flag on the session to indicate that it handles commands through
     * the {@link TransportPerformer}. The performer can be retrieved by calling
     * {@link #getTransportPerformer()}.
     */
    public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;

    /**
     * System only flag for a session that needs to have priority over all other
     * sessions. This flag ensures this session will receive media button events
@@ -71,7 +84,7 @@ public final class Session {
     *
     * @hide
     */
    public static final long FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 32;
    public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;

    private static final int MSG_MEDIA_BUTTON = 1;
    private static final int MSG_COMMAND = 2;
@@ -96,7 +109,7 @@ public final class Session {
    private TransportPerformer mPerformer;
    private Route mRoute;

    private boolean mPublished = false;;
    private boolean mActive = false;;

    /**
     * @hide
@@ -111,6 +124,7 @@ public final class Session {
            throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
        }
        mSessionToken = new SessionToken(controllerBinder);
        mPerformer = new TransportPerformer(mBinder);
    }

    /**
@@ -158,52 +172,23 @@ public final class Session {
    }

    /**
     * Start using a TransportPerformer with this media session. This must be
     * called before calling publish and cannot be called more than once.
     * Calling this will allow MediaControllers to retrieve a
     * TransportController.
     *
     * @see TransportController
     * @return The TransportPerformer created for this session
     */
    public TransportPerformer setTransportPerformerEnabled() {
        if (mPerformer != null) {
            throw new IllegalStateException("setTransportPerformer can only be called once.");
        }
        if (mPublished) {
            throw new IllegalStateException("setTransportPerformer cannot be called after publish");
        }

        mPerformer = new TransportPerformer(mBinder);
        try {
            mBinder.setTransportPerformerEnabled();
        } catch (RemoteException e) {
            Log.wtf(TAG, "Failure in setTransportPerformerEnabled.", e);
        }
        return mPerformer;
    }

    /**
     * Retrieves the TransportPerformer used by this session. If called before
     * {@link #setTransportPerformerEnabled} null will be returned.
     * Retrieves the {@link TransportPerformer} for this session. To receive
     * commands through the performer you must also set the
     * {@link #FLAG_HANDLES_TRANSPORT_CONTROLS} flag using
     * {@link #setFlags(int)}.
     *
     * @return The TransportPerformer associated with this session or null
     * @return The performer associated with this session.
     */
    public TransportPerformer getTransportPerformer() {
        return mPerformer;
    }

    /**
     * Set any flags for the session. This cannot be called after calling
     * {@link #publish()}.
     * Set any flags for the session.
     *
     * @param flags The flags to set for this session.
     * @hide remove hide once we have non-system flags
     */
    public void setFlags(long flags) {
        if (mPublished) {
            throw new IllegalStateException("setFlags may not be called after publish");
        }
    public void setFlags(int flags) {
        try {
            mBinder.setFlags(flags);
        } catch (RemoteException e) {
@@ -212,20 +197,32 @@ public final class Session {
    }

    /**
     * Call after you have finished setting up the session. This will make it
     * available to listeners and begin pushing updates to MediaControllers.
     * This can only be called once.
     * Set if this session is currently active and ready to receive commands. If
     * set to false your session's controller may not be discoverable. You must
     * set the session to active before it can start receiving media button
     * events or transport commands.
     *
     * @param active Whether this session is active or not.
     */
    public void publish() {
        if (mPublished) {
            throw new RuntimeException("publish() may only be called once.");
    public void setActive(boolean active) {
        if (mActive == active) {
            return;
        }
        try {
            mBinder.publish();
            mBinder.setActive(active);
            mActive = active;
        } catch (RemoteException e) {
            Log.wtf(TAG, "Failure in publish.", e);
            Log.wtf(TAG, "Failure in setActive.", e);
        }
        mPublished = true;
    }

    /**
     * Get the current active state of this session.
     *
     * @return True if the session is active, false otherwise.
     */
    public boolean isActive() {
        return mActive;
    }

    /**
+87 −21
Original line number Diff line number Diff line
@@ -60,6 +60,24 @@ import java.util.UUID;
public class MediaSessionRecord implements IBinder.DeathRecipient {
    private static final String TAG = "MediaSessionRecord";

    /**
     * These are the playback states that count as currently active.
     */
    private static final int[] ACTIVE_STATES = {
            PlaybackState.PLAYSTATE_FAST_FORWARDING,
            PlaybackState.PLAYSTATE_REWINDING,
            PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
            PlaybackState.PLAYSTATE_SKIPPING_FORWARDS,
            PlaybackState.PLAYSTATE_BUFFERING,
            PlaybackState.PLAYSTATE_CONNECTING,
            PlaybackState.PLAYSTATE_PLAYING };

    /**
     * The length of time a session will still be considered active after
     * pausing in ms.
     */
    private static final int ACTIVE_BUFFER = 30000;

    private final MessageHandler mHandler;

    private final int mPid;
@@ -75,7 +93,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
            new ArrayList<ISessionControllerCallback>();
    private final ArrayList<RouteRequest> mRequests = new ArrayList<RouteRequest>();

    private boolean mTransportPerformerEnabled = false;
    private RouteInfo mRoute;
    private RouteOptions mRequest;
    private RouteConnectionRecord mConnection;
@@ -88,9 +105,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
    private MediaMetadata mMetadata;
    private PlaybackState mPlaybackState;
    private int mRatingType;
    private long mLastActiveTime;
    // End TransportPerformer fields

    private boolean mIsPublished = false;
    private boolean mIsActive = false;

    public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
            MediaSessionService service, Handler handler) {
@@ -158,6 +176,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        return mFlags;
    }

    /**
     * Check if this session has the specified flag.
     *
     * @param flag The flag to check.
     * @return True if this session has that flag set, false otherwise.
     */
    public boolean hasFlag(int flag) {
        return (mFlags & flag) != 0;
    }

    /**
     * Check if this session has system priorty and should receive media buttons
     * before any other sessions.
@@ -236,12 +264,36 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
    }

    /**
     * Check if this session has been published by the app yet.
     * Check if this session has been set to active by the app.
     *
     * @return True if it has been published, false otherwise.
     * @return True if the session is active, false otherwise.
     */
    public boolean isPublished() {
        return mIsPublished;
    public boolean isActive() {
        return mIsActive;
    }

    /**
     * Check if the session is currently performing playback. This will also
     * return true if the session was recently paused.
     *
     * @return True if the session is performing playback, false otherwise.
     */
    public boolean isPlaybackActive() {
        int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
        if (isActiveState(state)) {
            return true;
        }
        if (state == mPlaybackState.PLAYSTATE_PAUSED) {
            long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
            if (inactiveTime < ACTIVE_BUFFER) {
                return true;
            }
        }
        return false;
    }

    public boolean isTransportControlEnabled() {
        return hasFlag(Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
    }

    @Override
@@ -255,11 +307,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        final String indent = prefix + "  ";
        pw.println(indent + "pid=" + mPid);
        pw.println(indent + "info=" + mSessionInfo.toString());
        pw.println(indent + "published=" + mIsPublished);
        pw.println(indent + "transport controls enabled=" + mTransportPerformerEnabled);
        pw.println(indent + "published=" + mIsActive);
        pw.println(indent + "flags=" + mFlags);
        pw.println(indent + "rating type=" + mRatingType);
        pw.println(indent + "controllers: " + mControllerCallbacks.size());
        pw.println(indent + "state=" + mPlaybackState.toString());
        pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString()));
        pw.println(indent + "metadata:" + getShortMetadataString());
        pw.println(indent + "route requests {");
        int size = mRequests.size();
@@ -272,6 +324,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        pw.println(indent + "params=" + (mRequest == null ? null : mRequest.toString()));
    }

    private boolean isActiveState(int state) {
        for (int i = 0; i < ACTIVE_STATES.length; i++) {
            if (ACTIVE_STATES[i] == state) {
                return true;
            }
        }
        return false;
    }

    private String getShortMetadataString() {
        int fields = mMetadata == null ? 0 : mMetadata.size();
        String title = mMetadata == null ? null : mMetadata
@@ -414,26 +475,21 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        }

        @Override
        public void publish() {
            mIsPublished = true;
            mService.publishSession(MediaSessionRecord.this);
        }
        @Override
        public void setTransportPerformerEnabled() {
            mTransportPerformerEnabled = true;
        public void setActive(boolean active) {
            mIsActive = active;
            mService.updateSession(MediaSessionRecord.this);
            mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
        }

        @Override
        public void setFlags(long flags) {
        public void setFlags(int flags) {
            if ((flags & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
                int pid = getCallingPid();
                int uid = getCallingUid();
                mService.enforcePhoneStatePermission(pid, uid);
            }
            if (mIsPublished) {
                throw new IllegalStateException("Cannot set flags after publishing session.");
            }
            mFlags = flags;
            mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
        }

        @Override
@@ -444,7 +500,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {

        @Override
        public void setPlaybackState(PlaybackState state) {
            int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
            int newState = state == null ? 0 : state.getState();
            if (isActiveState(oldState) && newState == PlaybackState.PLAYSTATE_PAUSED) {
                mLastActiveTime = SystemClock.elapsedRealtime();
            }
            mPlaybackState = state;
            mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState);
            mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
        }

@@ -708,7 +770,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {

        @Override
        public boolean isTransportControlEnabled() {
            return mTransportPerformerEnabled;
            return MediaSessionRecord.this.isTransportControlEnabled();
        }

        @Override
@@ -724,6 +786,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        private static final int MSG_SEND_EVENT = 4;
        private static final int MSG_UPDATE_ROUTE_FILTERS = 5;
        private static final int MSG_SEND_COMMAND = 6;
        private static final int MSG_UPDATE_SESSION_STATE = 7;

        public MessageHandler(Looper looper) {
            super(looper);
@@ -748,6 +811,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
                            (Pair<RouteCommand, ResultReceiver>) msg.obj;
                    pushRouteCommand(cmd.first, cmd.second);
                    break;
                case MSG_UPDATE_SESSION_STATE:
                    // TODO add session state
                    break;
            }
        }

Loading