summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--media/java/android/media/Controller2Link.java53
-rw-r--r--media/java/android/media/IMediaController2.aidl7
-rw-r--r--media/java/android/media/MediaConstants.java1
-rw-r--r--media/java/android/media/MediaController2.java150
-rw-r--r--media/java/android/media/MediaSession2.java43
6 files changed, 185 insertions, 73 deletions
diff --git a/api/current.txt b/api/current.txt
index 40264045d4d7..ea132de14292 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24566,6 +24566,7 @@ package android.media {
ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
method public void cancelSessionCommand(java.lang.Object);
method public void close();
+ method public boolean isPlaybackActive();
method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle);
}
@@ -24574,6 +24575,7 @@ package android.media {
method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup);
method public void onDisconnected(android.media.MediaController2);
+ method public void onPlaybackActiveChanged(android.media.MediaController2, boolean);
method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle);
}
@@ -25915,7 +25917,9 @@ package android.media {
method public void close();
method public java.lang.String getSessionId();
method public android.media.Session2Token getSessionToken();
+ method public boolean isPlaybackActive();
method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+ method public void setPlaybackActive(boolean);
}
public static final class MediaSession2.Builder {
diff --git a/media/java/android/media/Controller2Link.java b/media/java/android/media/Controller2Link.java
index a62db5f1fb20..d11f7769ee5e 100644
--- a/media/java/android/media/Controller2Link.java
+++ b/media/java/android/media/Controller2Link.java
@@ -16,6 +16,7 @@
package android.media;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -102,6 +103,15 @@ public final class Controller2Link implements Parcelable {
}
}
+ /** Interface method for IMediaController2.notifyPlaybackActiveChanged */
+ public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
+ try {
+ mIController.notifyPlaybackActiveChanged(seq, playbackActive);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/** Interface method for IMediaController2.sendSessionCommand */
public void sendSessionCommand(int seq, Session2Command command, Bundle args,
ResultReceiver resultReceiver) {
@@ -135,6 +145,11 @@ public final class Controller2Link implements Parcelable {
mController.onDisconnected(seq);
}
+ /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */
+ public void onPlaybackActiveChanged(int seq, boolean playbackActive) {
+ mController.onPlaybackActiveChanged(seq, playbackActive);
+ }
+
/** Stub implementation for IMediaController2.sendSessionCommand */
public void onSessionCommand(int seq, Session2Command command, Bundle args,
ResultReceiver resultReceiver) {
@@ -149,23 +164,53 @@ public final class Controller2Link implements Parcelable {
private class Controller2Stub extends IMediaController2.Stub {
@Override
public void notifyConnected(int seq, Bundle connectionResult) {
- Controller2Link.this.onConnected(seq, connectionResult);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Controller2Link.this.onConnected(seq, connectionResult);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void notifyDisconnected(int seq) {
- Controller2Link.this.onDisconnected(seq);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Controller2Link.this.onDisconnected(seq);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void sendSessionCommand(int seq, Session2Command command, Bundle args,
ResultReceiver resultReceiver) {
- Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void cancelSessionCommand(int seq) {
- Controller2Link.this.onCancelCommand(seq);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Controller2Link.this.onCancelCommand(seq);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
}
diff --git a/media/java/android/media/IMediaController2.aidl b/media/java/android/media/IMediaController2.aidl
index ca5394f504cb..42c6e70529ec 100644
--- a/media/java/android/media/IMediaController2.aidl
+++ b/media/java/android/media/IMediaController2.aidl
@@ -31,8 +31,9 @@ import android.media.Session2Command;
oneway interface IMediaController2 {
void notifyConnected(int seq, in Bundle connectionResult) = 0;
void notifyDisconnected(int seq) = 1;
+ void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2;
void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
- in ResultReceiver resultReceiver) = 2;
- void cancelSessionCommand(int seq) = 3;
- // Next Id : 4
+ in ResultReceiver resultReceiver) = 3;
+ void cancelSessionCommand(int seq) = 4;
+ // Next Id : 5
}
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
index 5a5747ae4bab..65b6f55a068a 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/java/android/media/MediaConstants.java
@@ -26,6 +26,7 @@ class MediaConstants {
// Bundle key for Parcelable
static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
+ static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
private MediaConstants() {
}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 165ea412333e..7c5335b71627 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,6 +19,7 @@ package android.media;
import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
+import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
import static android.media.MediaConstants.KEY_SESSION2LINK;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
@@ -30,7 +31,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -82,6 +82,8 @@ public class MediaController2 implements AutoCloseable {
private ArrayMap<ResultReceiver, Integer> mPendingCommands;
//@GuardedBy("mLock")
private ArraySet<Integer> mRequestedCommandSeqNumbers;
+ //@GuardedBy("mLock")
+ private boolean mPlaybackActive;
/**
* Create a {@link MediaController2} from the {@link Session2Token}.
@@ -160,6 +162,18 @@ public class MediaController2 implements AutoCloseable {
}
/**
+ * Returns whether the session's playback is active.
+ *
+ * @return {@code true} if playback active. {@code false} otherwise.
+ * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean)
+ */
+ public boolean isPlaybackActive() {
+ synchronized (mLock) {
+ return mPlaybackActive;
+ }
+ }
+
+ /**
* Sends a session command to the session
* <p>
* @param command the session command
@@ -221,89 +235,82 @@ public class MediaController2 implements AutoCloseable {
// Called by Controller2Link.onConnected
void onConnected(int seq, Bundle connectionResult) {
- final long token = Binder.clearCallingIdentity();
- try {
- Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
- Session2CommandGroup allowedCommands =
- connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
- if (DEBUG) {
- Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
- + ", allowedCommands=" + allowedCommands);
- }
- if (sessionBinder == null || allowedCommands == null) {
- // Connection rejected.
- close();
- return;
- }
- synchronized (mLock) {
- mSessionBinder = sessionBinder;
- mAllowedCommands = allowedCommands;
- // Implementation for the local binder is no-op,
- // so can be used without worrying about deadlock.
- sessionBinder.linkToDeath(mDeathRecipient, 0);
- mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
- mSessionToken.getPackageName(), sessionBinder);
- }
- mCallbackExecutor.execute(() -> {
- mCallback.onConnected(MediaController2.this, allowedCommands);
- });
- } finally {
- Binder.restoreCallingIdentity(token);
+ Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
+ Session2CommandGroup allowedCommands =
+ connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
+ boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
+ if (DEBUG) {
+ Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
+ + ", allowedCommands=" + allowedCommands);
+ }
+ if (sessionBinder == null || allowedCommands == null) {
+ // Connection rejected.
+ close();
+ return;
+ }
+ synchronized (mLock) {
+ mSessionBinder = sessionBinder;
+ mAllowedCommands = allowedCommands;
+ mPlaybackActive = playbackActive;
+
+ // Implementation for the local binder is no-op,
+ // so can be used without worrying about deadlock.
+ sessionBinder.linkToDeath(mDeathRecipient, 0);
+ mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
+ mSessionToken.getPackageName(), sessionBinder);
}
+ mCallbackExecutor.execute(() -> {
+ mCallback.onConnected(MediaController2.this, allowedCommands);
+ });
}
// Called by Controller2Link.onDisconnected
void onDisconnected(int seq) {
- final long token = Binder.clearCallingIdentity();
- try {
- // close() will call mCallback.onDisconnected
- close();
- } finally {
- Binder.restoreCallingIdentity(token);
+ // close() will call mCallback.onDisconnected
+ close();
+ }
+
+ // Called by Controller2Link.onPlaybackActiveChanged
+ void onPlaybackActiveChanged(int seq, boolean playbackActive) {
+ synchronized (mLock) {
+ mPlaybackActive = playbackActive;
}
+ mCallbackExecutor.execute(() -> {
+ mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive);
+ });
}
// Called by Controller2Link.onSessionCommand
void onSessionCommand(int seq, Session2Command command, Bundle args,
@Nullable ResultReceiver resultReceiver) {
- final long token = Binder.clearCallingIdentity();
- try {
+ synchronized (mLock) {
+ mRequestedCommandSeqNumbers.add(seq);
+ }
+ mCallbackExecutor.execute(() -> {
+ boolean isCanceled;
synchronized (mLock) {
- mRequestedCommandSeqNumbers.add(seq);
+ isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
}
- mCallbackExecutor.execute(() -> {
- boolean isCanceled;
- synchronized (mLock) {
- isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
- }
- if (isCanceled) {
- resultReceiver.send(RESULT_INFO_SKIPPED, null);
- return;
- }
- Session2Command.Result result = mCallback.onSessionCommand(
- MediaController2.this, command, args);
- if (resultReceiver != null) {
- if (result == null) {
- throw new RuntimeException("onSessionCommand shouldn't return null");
- } else {
- resultReceiver.send(result.getResultCode(), result.getResultData());
- }
+ if (isCanceled) {
+ resultReceiver.send(RESULT_INFO_SKIPPED, null);
+ return;
+ }
+ Session2Command.Result result = mCallback.onSessionCommand(
+ MediaController2.this, command, args);
+ if (resultReceiver != null) {
+ if (result == null) {
+ throw new RuntimeException("onSessionCommand shouldn't return null");
+ } else {
+ resultReceiver.send(result.getResultCode(), result.getResultData());
}
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ }
+ });
}
// Called by Controller2Link.onSessionCommand
void onCancelCommand(int seq) {
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- mRequestedCommandSeqNumbers.remove(seq);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
+ synchronized (mLock) {
+ mRequestedCommandSeqNumbers.remove(seq);
}
}
@@ -393,6 +400,17 @@ public class MediaController2 implements AutoCloseable {
public void onDisconnected(@NonNull MediaController2 controller) {}
/**
+ * Called when the playback of the session's playback activeness is changed.
+ *
+ * @param controller the controller for this event
+ * @param playbackActive {@code true} if the session's playback is active.
+ * {@code false} otherwise.
+ * @see MediaController2#isPlaybackActive()
+ */
+ public void onPlaybackActiveChanged(@NonNull MediaController2 controller,
+ boolean playbackActive) {}
+
+ /**
* Called when the connected session sent a session command.
*
* @param controller the controller for this event
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 04999cace5e0..3adac7295fff 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -19,6 +19,7 @@ package android.media;
import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
+import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
import static android.media.MediaConstants.KEY_SESSION2LINK;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
@@ -87,6 +88,8 @@ public class MediaSession2 implements AutoCloseable {
//@GuardedBy("mLock")
private boolean mClosed;
+ //@GuardedBy("mLock")
+ private boolean mPlaybackActive;
MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
@NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
@@ -215,6 +218,35 @@ public class MediaSession2 implements AutoCloseable {
controller.cancelSessionCommand(token);
}
+ /**
+ * Sets whether the playback is active (i.e. playing something)
+ *
+ * @param playbackActive {@code true} if the playback active, {@code false} otherwise.
+ **/
+ public void setPlaybackActive(boolean playbackActive) {
+ synchronized (mLock) {
+ if (mPlaybackActive == playbackActive) {
+ return;
+ }
+ mPlaybackActive = playbackActive;
+ }
+ List<ControllerInfo> controllerInfos = getConnectedControllers();
+ for (ControllerInfo controller : controllerInfos) {
+ controller.notifyPlaybackActiveChanged(playbackActive);
+ }
+ }
+
+ /**
+ * Returns whehther the playback is active (i.e. playing something)
+ *
+ * @return {@code true} if the playback active, {@code false} otherwise.
+ */
+ public boolean isPlaybackActive() {
+ synchronized (mLock) {
+ return mPlaybackActive;
+ }
+ }
+
boolean isClosed() {
synchronized (mLock) {
return mClosed;
@@ -275,6 +307,7 @@ public class MediaSession2 implements AutoCloseable {
connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
controllerInfo.mAllowedCommands);
+ connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
// Double check if session is still there, because close() can be called in
// another thread.
@@ -591,6 +624,16 @@ public class MediaSession2 implements AutoCloseable {
}
}
+ void notifyPlaybackActiveChanged(boolean playbackActive) {
+ if (mControllerBinder == null) return;
+
+ try {
+ mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive);
+ } catch (RuntimeException e) {
+ // Controller may be died prematurely.
+ }
+ }
+
void sendSessionCommand(Session2Command command, Bundle args,
ResultReceiver resultReceiver) {
if (mControllerBinder == null) return;