Expose a way to set a PendingIntent for restarting playback
This is to allow apps to set a PendingIntent to restart playback. This
is not persisted across reboots but will allow music to start playing
again for the life of the system. Only the most recent priority app with
a PI set will be cached. This also deprecates methods in AudioManager
that do this and unhides the unregister method in MediaSessionManager.
Change-Id: I66fbf5856333468d8cb8a3022809778ba00d426e
diff --git a/api/current.txt b/api/current.txt
index 9b211c2..f5a4973 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14142,10 +14142,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);
@@ -14164,10 +14164,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";
@@ -16643,6 +16643,7 @@
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
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);
@@ -16705,6 +16706,7 @@
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 {
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/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 0b8b0d4..af3b72e 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,7 +16,6 @@
package android.media.session;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
@@ -37,7 +36,7 @@
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();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 5833492..a878633 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -254,13 +254,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) {
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index a6963cf..8ea2039 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);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 824b397..ca41965 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -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) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 4ceb213..d0bcfd408 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -99,7 +99,7 @@
private long mFlags;
private IMediaRouter mMediaRouter;
- private ComponentName mMediaButtonReceiver;
+ private PendingIntent mMediaButtonReceiver;
private PendingIntent mLaunchIntent;
// TransportPerformer fields
@@ -170,7 +170,12 @@
return mSessionInfo;
}
- 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;
}
@@ -648,8 +653,8 @@
}
@Override
- public void setMediaButtonReceiver(ComponentName mbr) {
- mMediaButtonReceiver = mbr;
+ public void setMediaButtonReceiver(PendingIntent pi) {
+ mMediaButtonReceiver = pi;
}
@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..ce2899d 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,6 +512,7 @@
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++) {
@@ -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() {