Merge changes I87f551a7,Icb8dc76c,I66fbf585,I0c9506e7 into lmp-dev
* changes:
Add context to MediaController constructor
Add getPackageName to MediaController
Expose a way to set a PendingIntent for restarting playback
Finish plumbing for launch intents in sessions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index abec4c8..fff8a85 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -213,6 +213,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework.* $(PRODUCT_OUT)/system/framework2.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/docs/api-stubs-timestamp)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 5b9ea82..b39f200 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14144,10 +14144,10 @@
method public void loadSoundEffects();
method public void playSoundEffect(int);
method public void playSoundEffect(int, float);
- method public void registerMediaButtonEventReceiver(android.content.ComponentName);
- method public void registerMediaButtonEventReceiver(android.app.PendingIntent);
- method public void registerRemoteControlClient(android.media.RemoteControlClient);
- method public boolean registerRemoteController(android.media.RemoteController);
+ method public deprecated void registerMediaButtonEventReceiver(android.content.ComponentName);
+ method public deprecated void registerMediaButtonEventReceiver(android.app.PendingIntent);
+ method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
+ method public deprecated boolean registerRemoteController(android.media.RemoteController);
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
method public deprecated void setBluetoothA2dpOn(boolean);
method public void setBluetoothScoOn(boolean);
@@ -14166,10 +14166,10 @@
method public void startBluetoothSco();
method public void stopBluetoothSco();
method public void unloadSoundEffects();
- method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
- method public void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
- method public void unregisterRemoteControlClient(android.media.RemoteControlClient);
- method public void unregisterRemoteController(android.media.RemoteController);
+ method public deprecated void unregisterMediaButtonEventReceiver(android.content.ComponentName);
+ method public deprecated void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
+ method public deprecated void unregisterRemoteControlClient(android.media.RemoteControlClient);
+ method public deprecated void unregisterRemoteController(android.media.RemoteController);
field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
@@ -16578,14 +16578,16 @@
package android.media.session {
public final class MediaController {
- ctor public MediaController(android.media.session.MediaSession.Token);
+ ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void addCallback(android.media.session.MediaController.Callback);
method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
method public void adjustVolume(int, int);
method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public long getFlags();
+ method public android.app.PendingIntent getLaunchActivity();
method public android.media.MediaMetadata getMetadata();
+ method public java.lang.String getPackageName();
method public android.media.session.PlaybackState getPlaybackState();
method public java.util.List<android.media.session.MediaSession.Track> getQueue();
method public int getRatingType();
@@ -16639,6 +16641,7 @@
method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback, android.os.Handler);
+ method public android.media.session.MediaController getController();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isActive();
method public void release();
@@ -16648,7 +16651,8 @@
method public void setActive(boolean);
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
- method public void setLaunchPendingIntent(android.app.PendingIntent);
+ method public void setLaunchActivity(android.app.PendingIntent);
+ method public void setMediaButtonReceiver(android.app.PendingIntent);
method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
@@ -16711,10 +16715,11 @@
public final class MediaSessionManager {
method public void addActiveSessionsListener(android.media.session.MediaSessionManager.SessionListener, android.content.ComponentName);
method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+ method public void removeActiveSessionsListener(android.media.session.MediaSessionManager.SessionListener);
}
public static abstract class MediaSessionManager.SessionListener {
- ctor public MediaSessionManager.SessionListener();
+ ctor public MediaSessionManager.SessionListener(android.content.Context);
method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
}
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index c771f65..7757b91c 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -19,16 +19,17 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.session.ISessionController;
+import android.media.session.ISessionControllerCallback;
import android.media.session.ISessionManager;
import android.media.session.MediaController;
-import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -62,14 +63,14 @@
"usage: media [subcommand] [options]\n" +
" media dispatch KEY\n" +
" media list-sessions\n" +
- " media monitor <sessionId>\n" +
+ " media monitor <tag>\n" +
"\n" +
"media dispatch: dispatch a media key to the system.\n" +
" KEY may be: play, pause, play-pause, mute, headsethook,\n" +
" stop, next, previous, rewind, record, fast-forword.\n" +
"media list-sessions: print a list of the current sessions.\n" +
"media monitor: monitor updates to the specified session.\n" +
- " Use the sessionId from list-sessions.\n"
+ " Use the tag from list-sessions.\n"
);
}
@@ -114,13 +115,16 @@
List<IBinder> sessions = mSessionService
.getSessions(null, ActivityManager.getCurrentUser());
for (IBinder session : sessions) {
- MediaController controller = new MediaController(ISessionController.Stub
- .asInterface(session));
- if (controller != null && controller.getSessionInfo().getId().equals(id)) {
- ControllerMonitor monitor = new ControllerMonitor(controller);
- monitor.run();
- success = true;
- break;
+ ISessionController controller = ISessionController.Stub.asInterface(session);
+ try {
+ if (controller != null && id.equals(controller.getTag())) {
+ ControllerMonitor monitor = new ControllerMonitor(controller);
+ monitor.run();
+ success = true;
+ break;
+ }
+ } catch (RemoteException e) {
+ // ignore
}
}
} catch (Exception e) {
@@ -168,14 +172,14 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
- class ControllerMonitor extends MediaController.Callback {
- private final MediaController mController;
+ class ControllerMonitor extends ISessionControllerCallback.Stub {
+ private final ISessionController mController;
- public ControllerMonitor(MediaController controller) {
+ public ControllerMonitor(ISessionController controller) {
mController = controller;
}
@Override
- public void onSessionEvent(String event, Bundle extras) {
+ public void onEvent(String event, Bundle extras) {
System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
}
@@ -191,9 +195,33 @@
System.out.println("onMetadataChanged " + mmString);
}
+ @Override
+ public void onQueueChanged(ParceledListSlice queue) throws RemoteException {
+ System.out.println("onQueueChanged, size=" + queue.getList().size());
+ }
+
+ @Override
+ public void onQueueTitleChanged(CharSequence title) throws RemoteException {
+ System.out.println("onQueueTitleChange " + title);
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) throws RemoteException {
+ System.out.println("onExtrasChanged " + extras);
+ }
+
+ @Override
+ public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
+ System.out.println("onVolumeInfoChanged " + info);
+ }
+
void printUsageMessage() {
- System.out.println("V2Monitoring session " + mController.getSessionInfo().getId()
- + "... available commands:");
+ try {
+ System.out.println("V2Monitoring session " + mController.getTag()
+ + "... available commands:");
+ } catch (RemoteException e) {
+ System.out.println("Error trying to monitor session!");
+ }
System.out.println("(q)uit: finish monitoring");
}
@@ -202,7 +230,11 @@
HandlerThread cbThread = new HandlerThread("MediaCb") {
@Override
protected void onLooperPrepared() {
- mController.addCallback(ControllerMonitor.this);
+ try {
+ mController.registerCallbackListener(ControllerMonitor.this);
+ } catch (RemoteException e) {
+ System.out.println("Error registering monitor callback");
+ }
}
};
cbThread.start();
@@ -234,7 +266,7 @@
} finally {
cbThread.getLooper().quit();
try {
- mController.removeCallback(this);
+ mController.unregisterCallbackListener(this);
} catch (Exception e) {
// ignoring
}
@@ -248,12 +280,15 @@
List<IBinder> sessions = mSessionService
.getSessions(null, ActivityManager.getCurrentUser());
for (IBinder session : sessions) {
- MediaController controller = new MediaController(ISessionController.Stub
- .asInterface(session));
+
+ ISessionController controller = ISessionController.Stub.asInterface(session);
if (controller != null) {
- MediaSessionInfo info = controller.getSessionInfo();
- System.out.println(" id=" + info.getId() + ", package="
- + info.getPackageName());
+ try {
+ System.out.println(" tag=" + controller.getTag()
+ + ", package=" + controller.getPackageName());
+ } catch (RemoteException e) {
+ // ignore
+ }
}
}
} catch (Exception e) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 20ac8e9..1116127 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -27,7 +27,10 @@
import android.media.RemoteController.OnClientUpdateListener;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.media.session.MediaSessionLegacyHelper;
+import android.media.session.MediaSessionManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -2218,7 +2221,9 @@
* that will receive the media button intent. This broadcast receiver must be declared
* in the application manifest. The package of the component must match that of
* the context you're registering from.
+ * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
*/
+ @Deprecated
public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
if (eventReceiver == null) {
return;
@@ -2244,9 +2249,12 @@
* you know you will continue running for the full time until unregistering the
* PendingIntent.
* @param eventReceiver target that will receive media button intents. The PendingIntent
- * will be sent as-is when a media button action occurs, with {@link Intent#EXTRA_KEY_EVENT}
- * added and holding the key code of the media button that was pressed.
+ * will be sent an {@link Intent#ACTION_MEDIA_BUTTON} event when a media button action
+ * occurs, with {@link Intent#EXTRA_KEY_EVENT} added and holding the key code of the
+ * media button that was pressed.
+ * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
*/
+ @Deprecated
public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) {
if (eventReceiver == null) {
return;
@@ -2271,7 +2279,9 @@
* Unregister the receiver of MEDIA_BUTTON intents.
* @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
* that was registered with {@link #registerMediaButtonEventReceiver(ComponentName)}.
+ * @deprecated Use {@link MediaSession} instead.
*/
+ @Deprecated
public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
if (eventReceiver == null) {
return;
@@ -2289,7 +2299,9 @@
* Unregister the receiver of MEDIA_BUTTON intents.
* @param eventReceiver same PendingIntent that was registed with
* {@link #registerMediaButtonEventReceiver(PendingIntent)}.
+ * @deprecated Use {@link MediaSession} instead.
*/
+ @Deprecated
public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) {
if (eventReceiver == null) {
return;
@@ -2311,7 +2323,9 @@
* @param rcClient The remote control client from which remote controls will receive
* information to display.
* @see RemoteControlClient
+ * @deprecated Use {@link MediaSession} instead.
*/
+ @Deprecated
public void registerRemoteControlClient(RemoteControlClient rcClient) {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
@@ -2324,7 +2338,9 @@
* remote controls.
* @param rcClient The remote control client to unregister.
* @see #registerRemoteControlClient(RemoteControlClient)
+ * @deprecated Use {@link MediaSession} instead.
*/
+ @Deprecated
public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
@@ -2342,7 +2358,11 @@
* @param rctlr the object to register.
* @return true if the {@link RemoteController} was successfully registered, false if an
* error occurred, due to an internal system error, or insufficient permissions.
+ * @deprecated Use
+ * {@link MediaSessionManager#addActiveSessionsListener(android.media.session.MediaSessionManager.SessionListener, ComponentName)}
+ * and {@link MediaController} instead.
*/
+ @Deprecated
public boolean registerRemoteController(RemoteController rctlr) {
if (rctlr == null) {
return false;
@@ -2355,7 +2375,11 @@
* Unregisters a {@link RemoteController}, causing it to no longer receive media metadata and
* playback state information, and no longer be capable of controlling playback.
* @param rctlr the object to unregister.
+ * @deprecated Use
+ * {@link MediaSessionManager#removeActiveSessionsListener(android.media.session.MediaSessionManager.SessionListener)}
+ * instead.
*/
+ @Deprecated
public void unregisterRemoteController(RemoteController rctlr) {
if (rctlr == null) {
return;
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 5d2f3bd..3f7ebce 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.format.Time;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
@@ -73,7 +74,8 @@
public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
/**
- * The date the media was created or published as TODO determine format.
+ * The date the media was created or published. The format is unspecified
+ * but RFC 3339 is recommended.
*/
public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 75a8952..0aaaf46 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -74,8 +74,7 @@
private MetadataEditor mMetadataEditor;
private MediaSessionManager mSessionManager;
- private MediaSessionManager.SessionListener mSessionListener
- = new TopTransportSessionListener();
+ private MediaSessionManager.SessionListener mSessionListener;
private MediaController.Callback mSessionCb = new MediaControllerCallback();
/**
@@ -141,6 +140,7 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mSessionManager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ mSessionListener = new TopTransportSessionListener(context);
if (ActivityManager.isLowRamDeviceStatic()) {
mMaxBitmapDimension = MAX_BITMAP_DIMENSION;
@@ -214,7 +214,7 @@
public String getRemoteControlClientPackageName() {
if (USE_SESSIONS) {
synchronized (mInfoLock) {
- return mCurrentSession != null ? mCurrentSession.getSessionInfo().getPackageName()
+ return mCurrentSession != null ? mCurrentSession.getPackageName()
: null;
}
} else {
@@ -711,6 +711,11 @@
* currently tracked session if it has changed.
*/
private class TopTransportSessionListener extends MediaSessionManager.SessionListener {
+
+ public TopTransportSessionListener(Context context) {
+ super(context);
+ }
+
@Override
public void onActiveSessionsChanged(List<MediaController> controllers) {
int size = controllers.size();
@@ -980,8 +985,8 @@
0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */);
}
} else if (mCurrentSession == null
- || !controller.getSessionInfo().getId()
- .equals(mCurrentSession.getSessionInfo().getId())) {
+ || !controller.getSessionToken()
+ .equals(mCurrentSession.getSessionToken())) {
if (mCurrentSession != null) {
mCurrentSession.removeCallback(mSessionCb);
}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 2c190b7..af3b72e 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,7 +15,7 @@
package android.media.session;
-import android.content.ComponentName;
+import android.app.PendingIntent;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
@@ -36,7 +36,8 @@
void setFlags(int flags);
void setActive(boolean active);
void setMediaRouter(in IMediaRouter router);
- void setMediaButtonReceiver(in ComponentName mbr);
+ void setMediaButtonReceiver(in PendingIntent mbr);
+ void setLaunchPendingIntent(in PendingIntent pi);
void destroy();
// These commands are for the TransportPerformer
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index d20b0ad5..3518458 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -15,6 +15,7 @@
package android.media.session;
+import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
@@ -22,7 +23,6 @@
import android.media.routing.IMediaRouterDelegate;
import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISessionControllerCallback;
-import android.media.session.MediaSessionInfo;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
@@ -43,7 +43,9 @@
void registerCallbackListener(in ISessionControllerCallback cb);
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
- MediaSessionInfo getSessionInfo();
+ String getPackageName();
+ String getTag();
+ PendingIntent getLaunchPendingIntent();
long getFlags();
ParcelableVolumeInfo getVolumeAttributes();
void adjustVolume(int direction, int flags);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 89c61c8..382579c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -65,12 +67,14 @@
private final ISessionController mSessionBinder;
private final MediaSession.Token mToken;
+ private final Context mContext;
private final CallbackStub mCbStub = new CallbackStub(this);
private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
private final Object mLock = new Object();
private boolean mCbRegistered = false;
- private MediaSessionInfo mInfo;
+ private String mPackageName;
+ private String mTag;
private final TransportControls mTransportControls;
@@ -80,22 +84,27 @@
*
* @hide
*/
- public MediaController(ISessionController sessionBinder) {
+ public MediaController(Context context, ISessionController sessionBinder) {
if (sessionBinder == null) {
throw new IllegalArgumentException("Session token cannot be null");
}
+ if (context == null) {
+ throw new IllegalArgumentException("Context cannot be null");
+ }
mSessionBinder = sessionBinder;
mTransportControls = new TransportControls();
mToken = new MediaSession.Token(sessionBinder);
+ mContext = context;
}
/**
* Create a new MediaController from a session's token.
*
+ * @param context The caller's context.
* @param token The token for the session.
*/
- public MediaController(@NonNull MediaSession.Token token) {
- this(token.getBinder());
+ public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
+ this(context, token.getBinder());
}
/**
@@ -241,6 +250,21 @@
}
/**
+ * Get an intent for launching UI associated with this session if one
+ * exists.
+ *
+ * @return A {@link PendingIntent} to launch UI or null.
+ */
+ public @Nullable PendingIntent getLaunchActivity() {
+ try {
+ return mSessionBinder.getLaunchPendingIntent();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getPendingIntent.", e);
+ }
+ return null;
+ }
+
+ /**
* Get the token for the session this is connected to.
*
* @return The token for the connected session.
@@ -355,20 +379,36 @@
}
/**
- * Get the info for the session this controller is connected to.
+ * Get the session owner's package name.
*
- * @return The session info for the connected session.
- * @hide
+ * @return The package name of of the session owner.
*/
- public MediaSessionInfo getSessionInfo() {
- if (mInfo == null) {
+ public String getPackageName() {
+ if (mPackageName == null) {
try {
- mInfo = mSessionBinder.getSessionInfo();
+ mPackageName = mSessionBinder.getPackageName();
} catch (RemoteException e) {
- Log.e(TAG, "Error in getSessionInfo.", e);
+ Log.d(TAG, "Dead object in getPackageName.", e);
}
}
- return mInfo;
+ return mPackageName;
+ }
+
+ /**
+ * Get the session's tag for debugging purposes.
+ *
+ * @return The session's tag.
+ * @hide
+ */
+ public String getTag() {
+ if (mTag == null) {
+ try {
+ mTag = mSessionBinder.getTag();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Dead object in getTag.", e);
+ }
+ }
+ return mTag;
}
/*
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 866910d..cf8e3dd 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -119,6 +120,7 @@
private final Object mLock = new Object();
private final MediaSession.Token mSessionToken;
+ private final MediaController mController;
private final ISession mBinder;
private final CallbackStub mCbStub;
@@ -168,6 +170,7 @@
try {
mBinder = manager.createSession(mCbStub, tag, userId);
mSessionToken = new Token(mBinder.getController());
+ mController = new MediaController(context, mSessionToken);
} catch (RemoteException e) {
throw new RuntimeException("Remote error creating session.", e);
}
@@ -222,12 +225,17 @@
/**
* Set an intent for launching UI for this Session. This can be used as a
- * quick link to an ongoing media screen.
+ * quick link to an ongoing media screen. The intent should be for an
+ * activity that may be started using {@link Activity#startActivity(Intent)}.
*
* @param pi The intent to launch to show UI for this Session.
*/
- public void setLaunchPendingIntent(@Nullable PendingIntent pi) {
- // TODO
+ public void setLaunchActivity(@Nullable PendingIntent pi) {
+ try {
+ mBinder.setLaunchPendingIntent(pi);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e);
+ }
}
/**
@@ -248,13 +256,14 @@
}
/**
- * Set a media button event receiver component to use to restart playback
- * after an app has been stopped.
+ * Set a pending intent for your media button receiver to allow restarting
+ * playback after the session has been stopped. If your app is started in
+ * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
+ * the pending intent.
*
- * @param mbr The receiver component to send the media button event to.
- * @hide
+ * @param mbr The {@link PendingIntent} to send the media button event to.
*/
- public void setMediaButtonReceiver(@Nullable ComponentName mbr) {
+ public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) {
@@ -402,6 +411,16 @@
}
/**
+ * Get a controller for this session. This is a convenience method to avoid
+ * having to cache your own controller in process.
+ *
+ * @return A controller for this session.
+ */
+ public @NonNull MediaController getController() {
+ return mController;
+ }
+
+ /**
* Add a callback to receive transport controls on, such as play, rewind, or
* fast forward.
*
@@ -708,6 +727,7 @@
* the session.
*/
public static final class Token implements Parcelable {
+
private ISessionController mBinder;
/**
@@ -727,6 +747,31 @@
dest.writeStrongBinder(mBinder.asBinder());
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mBinder == null) ? 0 : mBinder.asBinder().hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Token other = (Token) obj;
+ if (mBinder == null) {
+ if (other.mBinder != null)
+ return false;
+ } else if (!mBinder.asBinder().equals(other.mBinder.asBinder()))
+ return false;
+ return true;
+ }
+
ISessionController getBinder() {
return mBinder;
}
diff --git a/media/java/android/media/session/MediaSessionInfo.aidl b/media/java/android/media/session/MediaSessionInfo.aidl
deleted file mode 100644
index 63dca9a..0000000
--- a/media/java/android/media/session/MediaSessionInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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 android.media.session;
-
-parcelable MediaSessionInfo;
diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
deleted file mode 100644
index 4dc1c09..0000000
--- a/media/java/android/media/session/MediaSessionInfo.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 android.media.session;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Information about a media session, including the owner's package name.
- *
- * @hide
- */
-public final class MediaSessionInfo implements Parcelable {
- private final String mId;
- private final String mPackageName;
- private final int mPid;
-
- /**
- * @hide
- */
- public MediaSessionInfo(String id, String packageName, int pid) {
- mId = id;
- mPackageName = packageName;
- mPid = pid;
- }
-
- private MediaSessionInfo(Parcel in) {
- mId = in.readString();
- mPackageName = in.readString();
- mPid = in.readInt();
- }
-
- /**
- * Get the package name of the owner of this session.
- *
- * @return The owner's package name
- */
- public String getPackageName() {
- return mPackageName;
- }
-
- /**
- * Get the unique id for this session.
- *
- * @return The id for the session.
- */
- public String getId() {
- return mId;
- }
-
- public int getPid() {
- return mPid;
- }
-
- @Override
- public String toString() {
- return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + ", pid=" + mPid + "}";
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mId);
- dest.writeString(mPackageName);
- dest.writeInt(mPid);
- }
-
- public static final Parcelable.Creator<MediaSessionInfo> CREATOR
- = new Parcelable.Creator<MediaSessionInfo>() {
- @Override
- public MediaSessionInfo createFromParcel(Parcel in) {
- return new MediaSessionInfo(in);
- }
-
- @Override
- public MediaSessionInfo[] newArray(int size) {
- return new MediaSessionInfo[size];
- }
- };
-}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index a6963cf..f075ded 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -304,7 +304,7 @@
holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
- holder.mSession.setMediaButtonReceiver(mbrComponent);
+ holder.mSession.setMediaButtonReceiver(pi);
if (DEBUG) {
Log.d(TAG, "addMediaButtonListener added " + pi);
}
@@ -369,7 +369,7 @@
SessionHolder holder = mSessions.get(pi);
if (holder == null && createIfMissing) {
MediaSession session;
- session = new MediaSession(mContext, TAG);
+ session = new MediaSession(mContext, TAG + "-" + pi.getCreatorPackage());
session.setActive(true);
holder = new SessionHolder(session, pi);
mSessions.put(pi, holder);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 824b397..84983b9 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -116,7 +116,7 @@
List<IBinder> binders = mService.getSessions(notificationListener, userId);
int size = binders.size();
for (int i = 0; i < size; i++) {
- MediaController controller = new MediaController(ISessionController.Stub
+ MediaController controller = new MediaController(mContext, ISessionController.Stub
.asInterface(binders.get(i)));
controllers.add(controller);
}
@@ -175,7 +175,6 @@
* Stop receiving active sessions updates on the specified listener.
*
* @param listener The listener to remove.
- * @hide
*/
public void removeActiveSessionsListener(@NonNull SessionListener listener) {
if (listener == null) {
@@ -253,6 +252,11 @@
* using {@link #addActiveSessionsListener}.
*/
public static abstract class SessionListener {
+ private final Context mContext;
+
+ public SessionListener(Context context) {
+ mContext = context;
+ }
/**
* Called when the list of active sessions has changed. This can be due
* to a session being added or removed or the order of sessions
@@ -271,7 +275,7 @@
ArrayList<MediaController> controllers = new ArrayList<MediaController>();
int size = tokens.size();
for (int i = 0; i < size; i++) {
- controllers.add(new MediaController(tokens.get(i)));
+ controllers.add(new MediaController(mContext, tokens.get(i)));
}
SessionListener.this.onActiveSessionsChanged(controllers);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2123bd9..044f69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1634,7 +1634,7 @@
final MediaSession.Token token = entry.notification.getNotification().extras
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
if (token != null) {
- controller = new MediaController(token);
+ controller = new MediaController(mContext, token);
if (controller != null) {
// we've got a live one, here
mediaNotification = entry;
@@ -1664,7 +1664,7 @@
continue;
default:
// now to see if we have one like this
- final String pkg = aController.getSessionInfo().getPackageName();
+ final String pkg = aController.getPackageName();
for (int i = 0; i < N; i++) {
final Entry entry = mNotificationData.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 9d050f9..cc351f9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -195,7 +195,7 @@
@Override
public void remoteVolumeChanged(ISessionController binder, int flags)
throws RemoteException {
- MediaController controller = new MediaController(binder);
+ MediaController controller = new MediaController(mContext, binder);
mPanel.postRemoteVolumeChanged(controller, flags);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index e9df507..2f1bd60 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -33,14 +34,9 @@
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaController;
import android.media.session.MediaSession;
-import android.media.session.MediaSessionInfo;
import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.Rating;
-import android.media.VolumeProvider;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -85,7 +81,7 @@
private final int mOwnerPid;
private final int mOwnerUid;
private final int mUserId;
- private final MediaSessionInfo mSessionInfo;
+ private final String mPackageName;
private final String mTag;
private final ControllerStub mController;
private final SessionStub mSession;
@@ -98,7 +94,8 @@
private long mFlags;
private IMediaRouter mMediaRouter;
- private ComponentName mMediaButtonReceiver;
+ private PendingIntent mMediaButtonReceiver;
+ private PendingIntent mLaunchIntent;
// TransportPerformer fields
@@ -129,8 +126,7 @@
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
- mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName,
- ownerPid);
+ mPackageName = ownerPackageName;
mTag = tag;
mController = new ControllerStub();
mSession = new SessionStub();
@@ -164,11 +160,25 @@
*
* @return Info that identifies this session.
*/
- public MediaSessionInfo getSessionInfo() {
- return mSessionInfo;
+ public String getPackageName() {
+ return mPackageName;
}
- public ComponentName getMediaButtonReceiver() {
+ /**
+ * Get the tag for the session.
+ *
+ * @return The session's tag.
+ */
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Get the intent the app set for their media button receiver.
+ *
+ * @return The pending intent set by the app or null.
+ */
+ public PendingIntent getMediaButtonReceiver() {
return mMediaButtonReceiver;
}
@@ -402,9 +412,10 @@
pw.println(prefix + mTag + " " + this);
final String indent = prefix + " ";
+ // We print the hashcode for matching with the list in user records
pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid
+ ", userId=" + mUserId);
- pw.println(indent + "info=" + mSessionInfo.toString());
+ pw.println(indent + "package=" + mPackageName);
pw.println(indent + "active=" + mIsActive);
pw.println(indent + "flags=" + mFlags);
pw.println(indent + "rating type=" + mRatingType);
@@ -413,6 +424,11 @@
pw.println(indent + "metadata:" + getShortMetadataString());
}
+ @Override
+ public String toString() {
+ return mPackageName + "/" + mTag;
+ }
+
private String getShortMetadataString() {
int fields = mMetadata == null ? 0 : mMetadata.size();
String title = mMetadata == null ? null : mMetadata
@@ -646,8 +662,13 @@
}
@Override
- public void setMediaButtonReceiver(ComponentName mbr) {
- mMediaButtonReceiver = mbr;
+ public void setMediaButtonReceiver(PendingIntent pi) {
+ mMediaButtonReceiver = pi;
+ }
+
+ @Override
+ public void setLaunchPendingIntent(PendingIntent pi) {
+ mLaunchIntent = pi;
}
@Override
@@ -916,8 +937,18 @@
}
@Override
- public MediaSessionInfo getSessionInfo() {
- return mSessionInfo;
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public PendingIntent getLaunchPendingIntent() {
+ return mLaunchIntent;
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b0ccd62..92644ce 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -20,6 +20,8 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -407,16 +409,6 @@
return user;
}
- private int findIndexOfSessionForIdLocked(String sessionId) {
- for (int i = mAllSessions.size() - 1; i >= 0; i--) {
- MediaSessionRecord session = mAllSessions.get(i);
- if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
- return i;
- }
- }
- return -1;
- }
-
private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
if (mSessionsListeners.get(i).mListener == listener) {
@@ -436,7 +428,7 @@
List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
int size = records.size();
if (size > 0) {
- persistMediaButtonReceiverLocked(records.get(0));
+ rememberMediaButtonReceiverLocked(records.get(0));
}
ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
for (int i = 0; i < size; i++) {
@@ -469,13 +461,11 @@
}
}
- private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
- ComponentName receiver = record.getMediaButtonReceiver();
- if (receiver != null) {
- Settings.System.putStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER,
- receiver == null ? "" : receiver.flattenToString(),
- UserHandle.USER_CURRENT);
+ private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
+ PendingIntent receiver = record.getMediaButtonReceiver();
+ UserRecord user = mUserRecords.get(record.getUserId());
+ if (receiver != null && user != null) {
+ user.mLastMediaButtonReceiver = receiver;
}
}
@@ -486,6 +476,7 @@
final class UserRecord {
private final int mUserId;
private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
+ private PendingIntent mLastMediaButtonReceiver;
public UserRecord(Context context, int userId) {
mUserId = userId;
@@ -521,12 +512,13 @@
public void dumpLocked(PrintWriter pw, String prefix) {
pw.println(prefix + "Record for user " + mUserId);
String indent = prefix + " ";
+ pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
int size = mSessions.size();
pw.println(indent + size + " Sessions:");
for (int i = 0; i < size; i++) {
- // Just print the session info, the full session dump will
+ // Just print the short version, the full session dump will
// already be in the list of all sessions.
- pw.println(indent + mSessions.get(i).getSessionInfo());
+ pw.println(indent + mSessions.get(i).toString());
}
}
}
@@ -767,9 +759,9 @@
private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
MediaSessionRecord session) {
if (DEBUG) {
- String sessionInfo = session == null ? null : session.getSessionInfo().toString();
- Log.d(TAG, "Adjusting session " + sessionInfo + " by " + direction + ". flags=" + flags
- + ", suggestedStream=" + suggestedStream);
+ String description = session == null ? null : session.toString();
+ Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
+ + flags + ", suggestedStream=" + suggestedStream);
}
if (session == null) {
@@ -832,7 +824,7 @@
MediaSessionRecord session) {
if (session != null) {
if (DEBUG) {
- Log.d(TAG, "Sending media key to " + session.getSessionInfo());
+ Log.d(TAG, "Sending media key to " + session.toString());
}
if (needWakeLock) {
mKeyEventReceiver.aquireWakeLockLocked();
@@ -843,21 +835,43 @@
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mKeyEventReceiver);
} else {
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
+ // Launch the last PendingIntent we had with priority
+ int userId = ActivityManager.getCurrentUser();
+ UserRecord user = mUserRecords.get(userId);
+ if (user.mLastMediaButtonReceiver != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Sending media key to last known PendingIntent");
+ }
+ if (needWakeLock) {
+ mKeyEventReceiver.aquireWakeLockLocked();
+ }
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ user.mLastMediaButtonReceiver.send(getContext(),
+ needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+ mediaButtonIntent, mKeyEventReceiver, null);
+ } catch (CanceledException e) {
+ Log.i(TAG, "Error sending key event to media button receiver "
+ + user.mLastMediaButtonReceiver, e);
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Sending media key ordered broadcast");
+ }
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ }
+ // Fallback to legacy behavior
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ if (needWakeLock) {
+ keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
+ WAKELOCK_RELEASE_ON_FINISHED);
+ }
+ getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
}
- if (DEBUG) {
- Log.d(TAG, "Sending media key ordered broadcast");
- }
- // Fallback to legacy behavior
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
- WAKELOCK_RELEASE_ON_FINISHED);
- }
- getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
}
}
@@ -904,7 +918,8 @@
private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
- class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable {
+ class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
+ PendingIntent.OnFinished {
private final Handler mHandler;
private int mRefCount = 0;
private int mLastTimeoutId = 0;
@@ -963,6 +978,12 @@
mMediaEventWakeLock.release();
mHandler.removeCallbacks(this);
}
+
+ @Override
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ onReceiveResult(resultCode, null);
+ }
};
BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {