Merge "Add a class for managing Session priority"
This commit is contained in:
@@ -15475,12 +15475,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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
* 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)}.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ 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.os.Binder;
|
||||
@@ -56,9 +57,9 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
|
||||
private final SessionManagerImpl mSessionManagerImpl;
|
||||
private final MediaRouteProviderWatcher mRouteProviderWatcher;
|
||||
private final MediaSessionStack mPriorityStack;
|
||||
|
||||
private final ArrayList<MediaSessionRecord> mSessions
|
||||
= new ArrayList<MediaSessionRecord>();
|
||||
private final ArrayList<MediaSessionRecord> mRecords = new ArrayList<MediaSessionRecord>();
|
||||
private final ArrayList<MediaRouteProviderProxy> mProviders
|
||||
= new ArrayList<MediaRouteProviderProxy>();
|
||||
private final Object mLock = new Object();
|
||||
@@ -79,6 +80,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
mSessionManagerImpl = new SessionManagerImpl();
|
||||
mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback,
|
||||
mHandler, context.getUserId());
|
||||
mPriorityStack = new MediaSessionStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,17 +133,30 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
}
|
||||
|
||||
public void publishSession(MediaSessionRecord record) {
|
||||
public void updateSession(MediaSessionRecord record) {
|
||||
synchronized (mLock) {
|
||||
mPriorityStack.onSessionStateChange(record);
|
||||
if (record.isSystemPriority()) {
|
||||
if (mPrioritySession != null) {
|
||||
Log.w(TAG, "Replacing existing priority session with a new session");
|
||||
if (record.isActive()) {
|
||||
if (mPrioritySession != null) {
|
||||
Log.w(TAG, "Replacing existing priority session with a new session");
|
||||
}
|
||||
mPrioritySession = record;
|
||||
} else {
|
||||
if (mPrioritySession == record) {
|
||||
mPrioritySession = null;
|
||||
}
|
||||
}
|
||||
mPrioritySession = record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
|
||||
synchronized (mLock) {
|
||||
mPriorityStack.onPlaystateChange(record, oldState, newState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitor() {
|
||||
synchronized (mLock) {
|
||||
@@ -162,7 +177,8 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
}
|
||||
|
||||
private void destroySessionLocked(MediaSessionRecord session) {
|
||||
mSessions.remove(session);
|
||||
mRecords.remove(session);
|
||||
mPriorityStack.removeSession(session);
|
||||
if (session == mPrioritySession) {
|
||||
mPrioritySession = null;
|
||||
}
|
||||
@@ -254,13 +270,24 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException("Media Session owner died prematurely.", e);
|
||||
}
|
||||
mSessions.add(session);
|
||||
mRecords.add(session);
|
||||
mPriorityStack.addSession(session);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
private int findIndexOfSessionForIdLocked(String sessionId) {
|
||||
for (int i = mRecords.size() - 1; i >= 0; i--) {
|
||||
MediaSessionRecord session = mRecords.get(i);
|
||||
if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private MediaRouteProviderProxy getProviderLocked(String providerId) {
|
||||
for (int i = mProviders.size() - 1; i >= 0; i--) {
|
||||
MediaRouteProviderProxy provider = mProviders.get(i);
|
||||
@@ -271,19 +298,9 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findIndexOfSessionForIdLocked(String sessionId) {
|
||||
for (int i = mSessions.size() - 1; i >= 0; i--) {
|
||||
MediaSessionRecord session = mSessions.get(i);
|
||||
if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean isSessionDiscoverable(MediaSessionRecord record) {
|
||||
// TODO probably want to check more than if it's published.
|
||||
return record.isPublished();
|
||||
return record.isActive();
|
||||
}
|
||||
|
||||
private MediaRouteProviderWatcher.Callback mProviderWatcherCallback
|
||||
@@ -317,7 +334,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
synchronized (mLock) {
|
||||
int index = findIndexOfSessionForIdLocked(sessionId);
|
||||
if (index != -1 && routes != null && routes.size() > 0) {
|
||||
MediaSessionRecord record = mSessions.get(index);
|
||||
MediaSessionRecord record = mRecords.get(index);
|
||||
record.selectRoute(routes.get(0));
|
||||
}
|
||||
}
|
||||
@@ -329,7 +346,7 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
synchronized (mLock) {
|
||||
int index = findIndexOfSessionForIdLocked(sessionId);
|
||||
if (index != -1) {
|
||||
MediaSessionRecord session = mSessions.get(index);
|
||||
MediaSessionRecord session = mRecords.get(index);
|
||||
session.setRouteConnected(route, options.getConnectionOptions(), connection);
|
||||
}
|
||||
}
|
||||
@@ -359,7 +376,6 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
|
||||
@Override
|
||||
public List<IBinder> getSessions(ComponentName componentName) {
|
||||
|
||||
final int pid = Binder.getCallingPid();
|
||||
final int uid = Binder.getCallingUid();
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
@@ -375,11 +391,11 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
enforceMediaPermissions(componentName, pid, uid);
|
||||
ArrayList<IBinder> binders = new ArrayList<IBinder>();
|
||||
synchronized (mLock) {
|
||||
for (int i = mSessions.size() - 1; i >= 0; i--) {
|
||||
MediaSessionRecord record = mSessions.get(i);
|
||||
if (isSessionDiscoverable(record)) {
|
||||
binders.add(record.getControllerBinder().asBinder());
|
||||
}
|
||||
ArrayList<MediaSessionRecord> records = mPriorityStack
|
||||
.getActiveSessions();
|
||||
int size = records.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
binders.add(records.get(i).getControllerBinder().asBinder());
|
||||
}
|
||||
}
|
||||
return binders;
|
||||
@@ -406,13 +422,14 @@ public class MediaSessionService extends SystemService implements Monitor {
|
||||
if (mPrioritySession != null) {
|
||||
mPrioritySession.dump(pw, "");
|
||||
}
|
||||
int count = mSessions.size();
|
||||
pw.println("Sessions - have " + count + " states:");
|
||||
int count = mRecords.size();
|
||||
pw.println(count + " Sessions:");
|
||||
for (int i = 0; i < count; i++) {
|
||||
MediaSessionRecord record = mSessions.get(i);
|
||||
mRecords.get(i).dump(pw, "");
|
||||
pw.println();
|
||||
record.dump(pw, "");
|
||||
}
|
||||
mPriorityStack.dumpLocked(pw, "");
|
||||
|
||||
pw.println("Providers:");
|
||||
count = mProviders.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.media;
|
||||
|
||||
import android.media.session.PlaybackState;
|
||||
import android.media.session.Session;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Keeps track of media sessions and their priority for notifications, media
|
||||
* button routing, etc.
|
||||
*/
|
||||
public class MediaSessionStack {
|
||||
/**
|
||||
* These are states that usually indicate the user took an action and should
|
||||
* bump priority regardless of the old state.
|
||||
*/
|
||||
private static final int[] ALWAYS_PRIORITY_STATES = {
|
||||
PlaybackState.PLAYSTATE_FAST_FORWARDING,
|
||||
PlaybackState.PLAYSTATE_REWINDING,
|
||||
PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
|
||||
PlaybackState.PLAYSTATE_SKIPPING_FORWARDS };
|
||||
/**
|
||||
* These are states that usually indicate the user took an action if they
|
||||
* were entered from a non-priority state.
|
||||
*/
|
||||
private static final int[] TRANSITION_PRIORITY_STATES = {
|
||||
PlaybackState.PLAYSTATE_BUFFERING,
|
||||
PlaybackState.PLAYSTATE_CONNECTING,
|
||||
PlaybackState.PLAYSTATE_PLAYING };
|
||||
|
||||
private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
|
||||
|
||||
private MediaSessionRecord mCachedButtonReceiver;
|
||||
private MediaSessionRecord mCachedDefault;
|
||||
private ArrayList<MediaSessionRecord> mCachedActiveList;
|
||||
private ArrayList<MediaSessionRecord> mCachedTransportControlList;
|
||||
|
||||
/**
|
||||
* Add a record to the priority tracker.
|
||||
*
|
||||
* @param record The record to add.
|
||||
*/
|
||||
public void addSession(MediaSessionRecord record) {
|
||||
mSessions.add(record);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a record from the priority tracker.
|
||||
*
|
||||
* @param record The record to remove.
|
||||
*/
|
||||
public void removeSession(MediaSessionRecord record) {
|
||||
mSessions.remove(record);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the priority tracker that a session's state changed.
|
||||
*
|
||||
* @param record The record that changed.
|
||||
* @param oldState Its old playback state.
|
||||
* @param newState Its new playback state.
|
||||
*/
|
||||
public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
|
||||
if (shouldUpdatePriority(oldState, newState)) {
|
||||
mSessions.remove(record);
|
||||
mSessions.add(0, record);
|
||||
clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any stack changes that need to occur in response to a session
|
||||
* state change. TODO add the old and new session state as params
|
||||
*
|
||||
* @param record The record that changed.
|
||||
*/
|
||||
public void onSessionStateChange(MediaSessionRecord record) {
|
||||
// For now just clear the cache. Eventually we'll selectively clear
|
||||
// depending on what changed.
|
||||
clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current priority sorted list of active sessions. The most
|
||||
* important session is at index 0 and the least important at size - 1.
|
||||
*
|
||||
* @return All the active sessions in priority order.
|
||||
*/
|
||||
public ArrayList<MediaSessionRecord> getActiveSessions() {
|
||||
if (mCachedActiveList == null) {
|
||||
mCachedActiveList = getPriorityListLocked(true, 0);
|
||||
}
|
||||
return mCachedActiveList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current priority sorted list of active sessions that use
|
||||
* transport controls. The most important session is at index 0 and the
|
||||
* least important at size -1.
|
||||
*
|
||||
* @return All the active sessions that handle transport controls in
|
||||
* priority order.
|
||||
*/
|
||||
public ArrayList<MediaSessionRecord> getTransportControlSessions() {
|
||||
if (mCachedTransportControlList == null) {
|
||||
mCachedTransportControlList = getPriorityListLocked(true,
|
||||
Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
}
|
||||
return mCachedTransportControlList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the highest priority active session.
|
||||
*
|
||||
* @return The current highest priority session or null.
|
||||
*/
|
||||
public MediaSessionRecord getDefaultSession() {
|
||||
if (mCachedDefault != null) {
|
||||
return mCachedDefault;
|
||||
}
|
||||
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0);
|
||||
if (records.size() > 0) {
|
||||
return records.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the highest priority session that can handle media buttons.
|
||||
*
|
||||
* @return The default media button session or null.
|
||||
*/
|
||||
public MediaSessionRecord getDefaultMediaButtonSession() {
|
||||
if (mCachedButtonReceiver != null) {
|
||||
return mCachedButtonReceiver;
|
||||
}
|
||||
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
|
||||
Session.FLAG_HANDLES_MEDIA_BUTTONS);
|
||||
if (records.size() > 0) {
|
||||
mCachedButtonReceiver = records.get(0);
|
||||
}
|
||||
return mCachedButtonReceiver;
|
||||
}
|
||||
|
||||
public void dumpLocked(PrintWriter pw, String prefix) {
|
||||
ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0);
|
||||
int count = sortedSessions.size();
|
||||
pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
|
||||
String indent = prefix + " ";
|
||||
for (int i = 0; i < count; i++) {
|
||||
MediaSessionRecord record = sortedSessions.get(i);
|
||||
record.dump(pw, indent);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a priority sorted list of sessions. Can filter to only return active
|
||||
* sessions or sessions with specific flags.
|
||||
*
|
||||
* @param activeOnly True to only return active sessions, false to return
|
||||
* all sessions.
|
||||
* @param withFlags Only return sessions with all the specified flags set. 0
|
||||
* returns all sessions.
|
||||
* @return The priority sorted list of sessions.
|
||||
*/
|
||||
private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) {
|
||||
ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
|
||||
int lastLocalIndex = 0;
|
||||
int lastActiveIndex = 0;
|
||||
int lastPublishedIndex = 0;
|
||||
|
||||
int size = mSessions.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final MediaSessionRecord session = mSessions.get(i);
|
||||
|
||||
if ((session.getFlags() & withFlags) != withFlags) {
|
||||
continue;
|
||||
}
|
||||
if (!session.isActive()) {
|
||||
if (!activeOnly) {
|
||||
// If we're getting unpublished as well always put them at
|
||||
// the end
|
||||
result.add(session);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (session.isSystemPriority()) {
|
||||
// System priority sessions are special and always go at the
|
||||
// front. We expect there to only be one of these at a time.
|
||||
result.add(0, session);
|
||||
lastLocalIndex++;
|
||||
lastActiveIndex++;
|
||||
lastPublishedIndex++;
|
||||
} else if (session.isPlaybackActive()) {
|
||||
// TODO replace getRoute() == null with real local route check
|
||||
if(session.getRoute() == null) {
|
||||
// Active local sessions get top priority
|
||||
result.add(lastLocalIndex, session);
|
||||
lastLocalIndex++;
|
||||
lastActiveIndex++;
|
||||
lastPublishedIndex++;
|
||||
} else {
|
||||
// Then active remote sessions
|
||||
result.add(lastActiveIndex, session);
|
||||
lastActiveIndex++;
|
||||
lastPublishedIndex++;
|
||||
}
|
||||
} else {
|
||||
// inactive sessions go at the end in order of whoever last did
|
||||
// something.
|
||||
result.add(lastPublishedIndex, session);
|
||||
lastPublishedIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean shouldUpdatePriority(int oldState, int newState) {
|
||||
if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
|
||||
return true;
|
||||
}
|
||||
if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
|
||||
&& containsState(newState, TRANSITION_PRIORITY_STATES)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsState(int state, int[] states) {
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (states[i] == state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void clearCache() {
|
||||
mCachedDefault = null;
|
||||
mCachedButtonReceiver = null;
|
||||
mCachedActiveList = null;
|
||||
mCachedTransportControlList = null;
|
||||
}
|
||||
}
|
||||
@@ -84,11 +84,12 @@ public class PlayerSession {
|
||||
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
|
||||
mSession = man.createSession("OneMedia");
|
||||
mSession.addCallback(mCallback);
|
||||
mPerformer = mSession.setTransportPerformerEnabled();
|
||||
mPerformer = mSession.getTransportPerformer();
|
||||
mPerformer.addListener(new TransportListener());
|
||||
mPerformer.setPlaybackState(mPlaybackState);
|
||||
mSession.setFlags(Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
mSession.setRouteOptions(mRouteOptions);
|
||||
mSession.publish();
|
||||
mSession.setActive(true);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
|
||||
Reference in New Issue
Block a user