diff --git a/Android.mk b/Android.mk index 1aaa09a40ffde..77ab10bfe94da 100644 --- a/Android.mk +++ b/Android.mk @@ -404,6 +404,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/projection/IMediaProjectionManager.aidl \ media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \ media/java/android/media/session/IActiveSessionsListener.aidl \ + media/java/android/media/session/ICallback.aidl \ media/java/android/media/session/ISessionController.aidl \ media/java/android/media/session/ISessionControllerCallback.aidl \ media/java/android/media/session/ISession.aidl \ diff --git a/media/java/android/media/session/ICallback.aidl b/media/java/android/media/session/ICallback.aidl new file mode 100644 index 0000000000000..bb8a3cc9b1390 --- /dev/null +++ b/media/java/android/media/session/ICallback.aidl @@ -0,0 +1,34 @@ +/* Copyright (C) 2016 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.app.PendingIntent; +import android.content.ComponentName; +import android.media.session.MediaSession; +import android.view.KeyEvent; + +/** + * @hide + */ +oneway interface ICallback { + void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event, + in MediaSession.Token sessionToken); + void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event, + in ComponentName mediaButtonReceiver); + + void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken); + void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver); +} diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index bb59e5b4be35f..575e7d784a3f5 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -18,6 +18,7 @@ package android.media.session; import android.content.ComponentName; import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; +import android.media.session.ICallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; @@ -41,4 +42,6 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); -} \ No newline at end of file + + void setCallback(in ICallback callback); +} diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 2364a13e63855..31e60da7fb7ad 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -57,6 +57,8 @@ public final class MediaSessionManager { private Context mContext; + private CallbackImpl mCallback; + /** * @hide */ @@ -312,6 +314,36 @@ public final class MediaSessionManager { return false; } + /** + * Set a {@link Callback}. + * + *
System can only have a single callback, and the callback can only be set by
+ * Bluetooth service process.
+ *
+ * @param callback A {@link Callback}. {@code null} to reset.
+ * @param handler The handler on which the callback should be invoked, or {@code null}
+ * if the callback should be invoked on the calling thread's looper.
+ * @hide
+ */
+ public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
+ synchronized (mLock) {
+ try {
+ if (callback == null) {
+ mCallback = null;
+ mService.setCallback(null);
+ } else {
+ if (handler == null) {
+ handler = new Handler();
+ }
+ mCallback = new CallbackImpl(callback, handler);
+ mService.setCallback(mCallback);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set media key callback", e);
+ }
+ }
+ }
+
/**
* Listens for changes to the list of active sessions. This can be added
* using {@link #addOnActiveSessionsChangedListener}.
@@ -320,6 +352,56 @@ public final class MediaSessionManager {
public void onActiveSessionsChanged(@Nullable List Called when a media key event is dispatched or the addressed player is changed.
+ * The addressed player is either the media session or the media button receiver that will
+ * receive media key events.
+ * @hide
+ */
+ public static abstract class Callback {
+ /**
+ * Called when a media key event is dispatched to the media session
+ * through the media session service.
+ *
+ * @param event Dispatched media key event.
+ * @param sessionToken The media session's token.
+ */
+ public abstract void onMediaKeyEventDispatched(KeyEvent event,
+ MediaSession.Token sessionToken);
+
+ /**
+ * Called when a media key event is dispatched to the media button receiver
+ * through the media session service.
+ * MediaSessionService may broadcast key events to the media button receiver
+ * when reviving playback after the media session is released.
+ *
+ * @param event Dispatched media key event.
+ * @param mediaButtonReceiver The media button receiver.
+ */
+ public abstract void onMediaKeyEventDispatched(KeyEvent event,
+ ComponentName mediaButtonReceiver);
+
+ /**
+ * Called when the addressed player is changed to a media session.
+ * One of the {@ #onAddressedPlayerChanged} will be also called immediately after
+ * {@link #setCallback} if the addressed player exists.
+ *
+ * @param sessionToken The media session's token.
+ */
+ public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken);
+
+ /**
+ * Called when the addressed player is changed to the media button receiver.
+ * One of the {@ #onAddressedPlayerChanged} will be also called immediately after
+ * {@link #setCallback} if the addressed player exists.
+ *
+ * @param mediaButtonReceiver The media button receiver.
+ */
+ public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
+ }
+
private static final class SessionsChangedWrapper {
private Context mContext;
private OnActiveSessionsChangedListener mListener;
@@ -360,4 +442,57 @@ public final class MediaSessionManager {
mHandler = null;
}
}
+
+ private static final class CallbackImpl extends ICallback.Stub {
+ private final Callback mCallback;
+ private final Handler mHandler;
+
+ public CallbackImpl(@NonNull Callback callback, @NonNull Handler handler) {
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event,
+ MediaSession.Token sessionToken) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onMediaKeyEventDispatched(event, sessionToken);
+ }
+ });
+ }
+
+ @Override
+ public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
+ ComponentName mediaButtonReceiver) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver);
+ }
+ });
+ }
+
+ @Override
+ public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onAddressedPlayerChanged(sessionToken);
+ }
+ });
+ }
+
+ @Override
+ public void onAddressedPlayerChangedToMediaButtonReceiver(
+ ComponentName mediaButtonReceiver) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onAddressedPlayerChanged(mediaButtonReceiver);
+ }
+ });
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 4c58ffd5dc833..cc41060a55eb4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -36,6 +36,7 @@ import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
import android.media.session.IActiveSessionsListener;
+import android.media.session.ICallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
@@ -101,6 +102,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private AudioManagerInternal mAudioManagerInternal;
private ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver;
+ private ICallback mCallback;
// List of user IDs running in the foreground.
// Multiple users can be in the foreground if the work profile is on.
@@ -485,6 +487,7 @@ public class MediaSessionService extends SystemService implements Monitor {
if (size > 0 && records.get(0).isPlaybackActive(false)) {
rememberMediaButtonReceiverLocked(records.get(0));
}
+ pushAddressedPlayerChangedLocked();
ArrayList