am c7ed6f69: Merge "Minimum work to make volume handling work with sessions" into lmp-preview-dev

* commit 'c7ed6f69d474f23df89d36ed94ff0062daa8dcbc':
  Minimum work to make volume handling work with sessions
This commit is contained in:
RoboErik
2014-06-02 17:15:14 +00:00
committed by Android Git Automerger
11 changed files with 326 additions and 13 deletions

View File

@@ -644,7 +644,12 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
service.adjustVolume(direction, flags, mContext.getOpPackageName());
if (USE_SESSIONS) {
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
} else {
service.adjustVolume(direction, flags, mContext.getOpPackageName());
}
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -674,8 +679,13 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
mContext.getOpPackageName());
if (USE_SESSIONS) {
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
} else {
service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
mContext.getOpPackageName());
}
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);

View File

@@ -47,4 +47,8 @@ interface ISession {
void setMetadata(in MediaMetadata metadata);
void setPlaybackState(in PlaybackState state);
void setRatingType(int type);
// These commands relate to volume handling
void configureVolumeHandling(int type, int arg1, int arg2);
void setCurrentVolume(int currentVolume);
}

View File

@@ -45,4 +45,8 @@ oneway interface ISessionCallback {
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
// These callbacks are for volume handling
void onAdjustVolumeBy(int delta);
void onSetVolumeTo(int value);
}

View File

@@ -29,4 +29,5 @@ interface ISessionManager {
ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
}

View File

@@ -124,9 +124,21 @@ public final class MediaSession {
*/
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
private static final String KEY_COMMAND = "command";
private static final String KEY_EXTRAS = "extras";
private static final String KEY_CALLBACK = "callback";
/**
* The session uses local playback. Used for configuring volume handling
* with the system.
*
* @hide
*/
public static final int VOLUME_TYPE_LOCAL = 1;
/**
* The session uses remote playback. Used for configuring volume handling
* with the system.
*
* @hide
*/
public static final int VOLUME_TYPE_REMOTE = 2;
private final Object mLock = new Object();
@@ -143,6 +155,7 @@ public final class MediaSession {
= new ArrayMap<String, RouteInterface.EventListener>();
private Route mRoute;
private RemoteVolumeProvider mVolumeProvider;
private boolean mActive = false;;
@@ -242,7 +255,11 @@ public final class MediaSession {
* @param stream The {@link AudioManager} stream this session is playing on.
*/
public void setPlaybackToLocal(int stream) {
// TODO
try {
mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
}
}
/**
@@ -259,7 +276,14 @@ public final class MediaSession {
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
// TODO
mVolumeProvider = volumeProvider;
try {
mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
volumeProvider.getMaxVolume());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
}
}
/**
@@ -942,6 +966,26 @@ public final class MediaSession {
}
/*
* (non-Javadoc)
* @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
*/
@Override
public void onAdjustVolumeBy(int delta) throws RemoteException {
// TODO(epastern): Auto-generated method stub
}
/*
* (non-Javadoc)
* @see android.media.session.ISessionCallback#onSetVolumeTo(int)
*/
@Override
public void onSetVolumeTo(int value) throws RemoteException {
// TODO(epastern): Auto-generated method stub
}
}
private class CallbackMessageHandler extends Handler {

View File

@@ -76,6 +76,13 @@ public class MediaSessionLegacyHelper {
}
}
public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
mSessionManager.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
if (DEBUG) {
Log.d(TAG, "dispatched volume adjustment");
}
}
public void addRccListener(PendingIntent pi,
MediaSession.TransportControlsCallback listener) {
if (pi == null) {

View File

@@ -166,4 +166,22 @@ public final class MediaSessionManager {
Log.e(TAG, "Failed to send key event.", e);
}
}
/**
* Dispatch an adjust volume request to the system. It will be routed to the
* most relevant stream/session.
*
* @param suggestedStream The stream to fall back to if there isn't a
* relevant stream
* @param delta The amount to adjust the volume by.
* @param flags Any flags to include with the volume change.
* @hide
*/
public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) {
try {
mService.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send adjust volume.", e);
}
}
}

View File

@@ -25,6 +25,7 @@ import android.media.session.ISessionControllerCallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.MediaController;
import android.media.session.RemoteVolumeProvider;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@ import android.media.session.MediaSession;
import android.media.session.MediaSessionInfo;
import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Bundle;
@@ -112,6 +114,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private long mLastActiveTime;
// End TransportPerformer fields
// Volume handling fields
private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
private int mAudioStream = AudioManager.STREAM_MUSIC;
private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
// End volume handling fields
private boolean mIsActive = false;
private boolean mDestroyed = false;
@@ -247,6 +257,27 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mSessionCb.sendRouteEvent(event);
}
/**
* Send a volume adjustment to the session owner.
*
* @param delta The amount to adjust the volume by.
*/
public void adjustVolumeBy(int delta) {
if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
return;
}
mSessionCb.adjustVolumeBy(delta);
}
public void setVolumeTo(int value) {
if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
return;
}
mSessionCb.setVolumeTo(value);
}
/**
* Set the connection to use for the selected route and notify the app it is
* now connected.
@@ -294,14 +325,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
* Check if the session is currently performing playback. This will also
* return true if the session was recently paused.
*
* @param includeRecentlyActive True if playback that was recently paused
* should count, false if it shouldn't.
* @return True if the session is performing playback, false otherwise.
*/
public boolean isPlaybackActive() {
public boolean isPlaybackActive(boolean includeRecentlyActive) {
int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
if (isActiveState(state)) {
return true;
}
if (state == mPlaybackState.STATE_PAUSED) {
if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
if (inactiveTime < ACTIVE_BUFFER) {
return true;
@@ -310,6 +343,54 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
return false;
}
/**
* Get the type of playback, either local or remote.
*
* @return The current type of playback.
*/
public int getPlaybackType() {
return mPlaybackType;
}
/**
* Get the local audio stream being used. Only valid if playback type is
* local.
*
* @return The audio stream the session is using.
*/
public int getAudioStream() {
return mAudioStream;
}
/**
* Get the type of volume control. Only valid if playback type is remote.
*
* @return The volume control type being used.
*/
public int getVolumeControl() {
return mVolumeControlType;
}
/**
* Get the max volume that can be set. Only valid if playback type is
* remote.
*
* @return The max volume that can be set.
*/
public int getMaxVolume() {
return mMaxVolume;
}
/**
* Get the current volume for this session. Only valid if playback type is
* remote.
*
* @return The current volume of the remote playback.
*/
public int getCurrentVolume() {
return mCurrentVolume;
}
/**
* @return True if this session is currently connected to a route.
*/
@@ -640,6 +721,40 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mRequests.add(request);
}
}
@Override
public void setCurrentVolume(int volume) {
mCurrentVolume = volume;
}
@Override
public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
switch(type) {
case MediaSession.VOLUME_TYPE_LOCAL:
mPlaybackType = type;
int audioStream = arg1;
if (isValidStream(audioStream)) {
mAudioStream = audioStream;
} else {
Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
mAudioStream = AudioManager.STREAM_MUSIC;
}
break;
case MediaSession.VOLUME_TYPE_REMOTE:
mPlaybackType = type;
mVolumeControlType = arg1;
mMaxVolume = arg2;
break;
default:
throw new IllegalArgumentException("Volume handling type " + type
+ " not recognized.");
}
}
private boolean isValidStream(int stream) {
return stream >= AudioManager.STREAM_VOICE_CALL
&& stream <= AudioManager.STREAM_NOTIFICATION;
}
}
class SessionCb {
@@ -780,6 +895,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
Slog.e(TAG, "Remote failure in rate.", e);
}
}
public void adjustVolumeBy(int delta) {
try {
mCb.onAdjustVolumeBy(delta);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
}
}
public void setVolumeTo(int value) {
try {
mCb.onSetVolumeTo(value);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
}
}
}
class ControllerStub extends ISessionController.Stub {

View File

@@ -26,6 +26,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.routeprovider.RouteRequest;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -40,6 +42,7 @@ import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.speech.RecognizerIntent;
@@ -79,6 +82,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private final PowerManager.WakeLock mMediaEventWakeLock;
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -105,6 +109,12 @@ public class MediaSessionService extends SystemService implements Monitor {
updateUser();
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
}
private IAudioService getAudioService() {
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
return IAudioService.Stub.asInterface(b);
}
/**
@@ -702,6 +712,23 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
@Override
public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags)
throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
MediaSessionRecord session = mPriorityStack
.getDefaultVolumeSession(mCurrentUserId);
dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
@@ -737,6 +764,49 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
MediaSessionRecord session) {
int direction = 0;
int steps = delta;
if (delta > 0) {
direction = 1;
} else if (delta < 0) {
direction = -1;
steps = -delta;
}
if (DEBUG) {
String sessionInfo = session == null ? null : session.getSessionInfo().toString();
Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
+ ", suggestedStream=" + suggestedStream);
}
if (session == null) {
for (int i = 0; i < steps; i++) {
try {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
flags, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
}
}
} else {
if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
for (int i = 0; i < steps; i++) {
try {
mAudioService.adjustSuggestedStreamVolume(direction,
session.getAudioStream(), flags,
getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting volume for stream "
+ session.getAudioStream(), e);
}
}
} else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
session.adjustVolumeBy(delta);
}
}
}
private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
MediaSessionRecord session) {
if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {

View File

@@ -52,6 +52,7 @@ public class MediaSessionStack {
private MediaSessionRecord mCachedButtonReceiver;
private MediaSessionRecord mCachedDefault;
private MediaSessionRecord mCachedVolumeDefault;
private ArrayList<MediaSessionRecord> mCachedActiveList;
private ArrayList<MediaSessionRecord> mCachedTransportControlList;
@@ -93,6 +94,9 @@ public class MediaSessionStack {
mSessions.remove(record);
mSessions.add(0, record);
clearCache();
} else if (newState == PlaybackState.STATE_PAUSED) {
// Just clear the volume cache in this case
mCachedVolumeDefault = null;
}
}
@@ -177,6 +181,25 @@ public class MediaSessionStack {
return mCachedButtonReceiver;
}
public MediaSessionRecord getDefaultVolumeSession(int userId) {
if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
return mGlobalPrioritySession;
}
if (mCachedVolumeDefault != null) {
return mCachedVolumeDefault;
}
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
int size = records.size();
for (int i = 0; i < size; i++) {
MediaSessionRecord record = records.get(i);
if (record.isPlaybackActive(false)) {
mCachedVolumeDefault = record;
return record;
}
}
return null;
}
public void dump(PrintWriter pw, String prefix) {
ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
UserHandle.USER_ALL);
@@ -237,7 +260,7 @@ public class MediaSessionStack {
lastLocalIndex++;
lastActiveIndex++;
lastPublishedIndex++;
} else if (session.isPlaybackActive()) {
} else if (session.isPlaybackActive(true)) {
// TODO replace getRoute() == null with real local route check
if(session.getRoute() == null) {
// Active local sessions get top priority
@@ -284,6 +307,7 @@ public class MediaSessionStack {
private void clearCache() {
mCachedDefault = null;
mCachedVolumeDefault = null;
mCachedButtonReceiver = null;
mCachedActiveList = null;
mCachedTransportControlList = null;

View File

@@ -82,7 +82,7 @@ public class PlayerSession {
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
mSession = man.createSession("OneMedia");
mSession.addCallback(mCallback);
mSession.addTransportControlsCallback(new TransportListener());
mSession.addTransportControlsCallback(new TransportCallback());
mSession.setPlaybackState(mPlaybackState);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setRouteOptions(mRouteOptions);
@@ -255,7 +255,7 @@ public class PlayerSession {
}
}
private class TransportListener extends MediaSession.TransportControlsCallback {
private class TransportCallback extends MediaSession.TransportControlsCallback {
@Override
public void onPlay() {
mRenderer.onPlay();