Merge "Add a class for managing Session priority"

This commit is contained in:
RoboErik
2014-05-07 17:26:28 +00:00
committed by Android (Google) Code Review
8 changed files with 467 additions and 108 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
/**

View File

@@ -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;
}
}

View File

@@ -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++) {

View File

@@ -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;
}
}

View File

@@ -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() {