diff options
18 files changed, 346 insertions, 245 deletions
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/framew $(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 @@ package android.media { 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 @@ package android.media { 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.routing { 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 @@ package android.media.session { 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 @@ package android.media.session { 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 @@ package android.media.session { 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..7757b91 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 @@ package com.android.commands.media; 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 @@ public class Media extends BaseCommand { "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 @@ public class Media extends BaseCommand { 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 @@ public class Media extends BaseCommand { 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 @@ public class Media extends BaseCommand { 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 @@ public class Media extends BaseCommand { 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 @@ public class Media extends BaseCommand { } finally { cbThread.getLooper().quit(); try { - mController.removeCallback(this); + mController.unregisterCallbackListener(this); } catch (Exception e) { // ignoring } @@ -248,12 +280,15 @@ public class Media extends BaseCommand { 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.content.Intent; 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * @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 @@ public class AudioManager { * 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 @@ public class AudioManager { * @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 @@ public class AudioManager { * 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.graphics.Bitmap; 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 final class MediaMetadata implements Parcelable { 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 @@ import java.util.List; 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 @@ import java.util.List; 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 @@ import java.util.List; 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 @@ import java.util.List; * 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 @@ import java.util.List; 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 @@ interface ISession { 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 d20b0ad..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.Rating; 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 @@ interface ISessionController { 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 @@ package android.media.session; 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 @@ public final class MediaController { 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 @@ public final class MediaController { * * @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 @@ public final class MediaController { } /** + * 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 @@ public final class MediaController { } /** - * Get the info for the session this controller is connected to. + * Get the session owner's package name. + * + * @return The package name of of the session owner. + */ + public String getPackageName() { + if (mPackageName == null) { + try { + mPackageName = mSessionBinder.getPackageName(); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in getPackageName.", e); + } + } + return mPackageName; + } + + /** + * Get the session's tag for debugging purposes. * - * @return The session info for the connected session. + * @return The session's tag. * @hide */ - public MediaSessionInfo getSessionInfo() { - if (mInfo == null) { + public String getTag() { + if (mTag == null) { try { - mInfo = mSessionBinder.getSessionInfo(); + mTag = mSessionBinder.getTag(); } catch (RemoteException e) { - Log.e(TAG, "Error in getSessionInfo.", e); + Log.d(TAG, "Dead object in getTag.", e); } } - return mInfo; + 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 @@ package android.media.session; 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 @@ public final class MediaSession { 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 @@ public final class MediaSession { 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 @@ public final class MediaSession { /** * 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 @@ public final class MediaSession { } /** - * 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 @@ public final class MediaSession { } /** + * 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 @@ public final class MediaSession { * the session. */ public static final class Token implements Parcelable { + private ISessionController mBinder; /** @@ -727,6 +747,31 @@ public final class MediaSession { 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 @@ public class MediaSessionLegacyHelper { 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 @@ public class MediaSessionLegacyHelper { 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 @@ public final class MediaSessionManager { 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 @@ public final class MediaSessionManager { * 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 @@ public final class MediaSessionManager { * 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 @@ public final class MediaSessionManager { 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class VolumeUI extends SystemUI { @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.ISessionController; 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private long mFlags; private IMediaRouter mMediaRouter; - private ComponentName mMediaButtonReceiver; + private PendingIntent mMediaButtonReceiver; + private PendingIntent mLaunchIntent; // TransportPerformer fields @@ -129,8 +126,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * * @return Info that identifies this session. */ - public MediaSessionInfo getSessionInfo() { - return mSessionInfo; + public String getPackageName() { + return mPackageName; + } + + /** + * Get the tag for the session. + * + * @return The session's tag. + */ + public String getTag() { + return mTag; } - public ComponentName getMediaButtonReceiver() { + /** + * 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { 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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @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 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @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.Manifest; 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { } } - 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver); } else { - if (needWakeLock) { - mMediaEventWakeLock.acquire(); - } - 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); + // 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); } - getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, - null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null); } } @@ -904,7 +918,8 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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() { |
