From f5ff735faede3239e6cbf7b78d698adb1c8ad12a Mon Sep 17 00:00:00 2001 From: Jaewan Kim Date: Tue, 20 Aug 2019 16:27:11 +0900 Subject: [PATCH] MediaSessionManager: Split abstract class Callback into interfaces This CL splits the MediaSessionManager.Callback (abstract class) with OnMediaKeyEventDispatchedListener and OnMediaKeyEventSessionChangedListener (interfaces with default methods). It's to follow the API guideline and also to help the Bluetooth for minally registering callbacks. (The Bluetooth currently only overrides onAddressedPlayerChanged) Bug: 138648566 Test: Build and run Change-Id: I8607662b00db7fb32cf3f4ee527c52b9fd8a6d7c --- api/system-current.txt | 18 +- ...> IOnMediaKeyEventDispatchedListener.aidl} | 15 +- ...OnMediaKeyEventSessionChangedListener.aidl | 28 ++ .../media/session/ISessionManager.aidl | 11 +- .../media/session/MediaSessionManager.java | 277 +++++++++--------- .../server/media/MediaSessionService.java | 207 ++++++++++--- 6 files changed, 357 insertions(+), 199 deletions(-) rename media/java/android/media/session/{ICallback.aidl => IOnMediaKeyEventDispatchedListener.aidl} (58%) create mode 100644 media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl diff --git a/api/system-current.txt b/api/system-current.txt index 17337338f0f82..f478979d46d1e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3864,18 +3864,20 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { - method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.Callback); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventDispatchedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventSessionChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventSessionChangedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener); method @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, @Nullable android.os.Handler); method @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, @Nullable android.os.Handler); - method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull android.media.session.MediaSessionManager.Callback); } - public abstract static class MediaSessionManager.Callback { - ctor public MediaSessionManager.Callback(); - method public abstract void onAddressedPlayerChanged(android.media.session.MediaSession.Token); - method public abstract void onAddressedPlayerChanged(android.content.ComponentName); - method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token); - method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName); + public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener { + method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @NonNull android.media.session.MediaSession.Token); + } + + public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener { + method public default void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token); } public static interface MediaSessionManager.OnMediaKeyListener { diff --git a/media/java/android/media/session/ICallback.aidl b/media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl similarity index 58% rename from media/java/android/media/session/ICallback.aidl rename to media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl index 322bffa96c9e1..90d913459edc8 100644 --- a/media/java/android/media/session/ICallback.aidl +++ b/media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl @@ -1,4 +1,5 @@ -/* Copyright (C) 2016 The Android Open Source Project +/* + * Copyright 2019 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. @@ -15,21 +16,13 @@ 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, +oneway interface IOnMediaKeyEventDispatchedListener { + void onMediaKeyEventDispatched(in KeyEvent event, in String packageName, 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/IOnMediaKeyEventSessionChangedListener.aidl b/media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl new file mode 100644 index 0000000000000..9566e756162cc --- /dev/null +++ b/media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright 2019 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.media.session.MediaSession; + +/** + * @hide + */ +oneway interface IOnMediaKeyEventSessionChangedListener { + void onMediaKeyEventSessionChanged(in String packageName, + in MediaSession.Token mediaKeyEventSessionToken); +} + diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 01e6ed5e9f35c..c8502a51ee233 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -20,7 +20,8 @@ import android.content.pm.ParceledListSlice; import android.media.IRemoteVolumeController; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; -import android.media.session.ICallback; +import android.media.session.IOnMediaKeyEventDispatchedListener; +import android.media.session.IOnMediaKeyEventSessionChangedListener; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; @@ -62,8 +63,12 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); - void registerCallback(in ICallback callback); - void unregisterCallback(in ICallback callback); + void addOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener); + void removeOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener); + void addOnMediaKeyEventSessionChangedListener( + in IOnMediaKeyEventSessionChangedListener listener); + void removeOnMediaKeyEventSessionChangedListener( + in IOnMediaKeyEventSessionChangedListener listener); void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); void setOnMediaKeyListener(in IOnMediaKeyListener listener); diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 92fb31bd4de85..a89dc5f1d2af5 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -32,7 +32,6 @@ import android.media.MediaSession2; import android.media.Session2Token; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; @@ -78,29 +77,33 @@ public final class MediaSessionManager { */ public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final ISessionManager mService; + private final OnMediaKeyEventDispatchedListenerStub mOnMediaKeyEventDispatchedListenerStub = + new OnMediaKeyEventDispatchedListenerStub(); + private final OnMediaKeyEventSessionChangedListenerStub + mOnMediaKeyEventSessionChangedListenerStub = + new OnMediaKeyEventSessionChangedListenerStub(); private final Object mLock = new Object(); @GuardedBy("mLock") - private final ArrayMap mListeners - = new ArrayMap(); + private final ArrayMap mListeners = + new ArrayMap(); @GuardedBy("mLock") private final ArrayMap mSession2TokensListeners = new ArrayMap<>(); @GuardedBy("mLock") - private final CallbackStub mCbStub = new CallbackStub(); + private final Map + mOnMediaKeyEventDispatchedListeners = new HashMap<>(); @GuardedBy("mLock") - private final Map mCallbacks = new HashMap<>(); + private final Map + mMediaKeyEventSessionChangedCallbacks = new HashMap<>(); @GuardedBy("mLock") - private MediaSession.Token mCurMediaButtonSession; + private String mCurMediaKeyEventSessionPackage; @GuardedBy("mLock") - private ComponentName mCurMediaButtonReceiver; + private MediaSession.Token mCurMediaKeyEventSession; private Context mContext; private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener; private OnMediaKeyListenerImpl mOnMediaKeyListener; - // TODO: Remove mLegacyCallback once Bluetooth app stop calling setCallback() method. - @GuardedBy("mLock") - private Callback mLegacyCallback; /** * @hide @@ -756,89 +759,118 @@ public final class MediaSessionManager { } /** - * 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 - */ - // TODO: Remove this method once Bluetooth app stop calling it. - public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { - if (handler == null) { - handler = new Handler(); - } - synchronized (mLock) { - if (mLegacyCallback != null) { - unregisterCallback(mLegacyCallback); - } - mLegacyCallback = callback; - if (callback != null) { - registerCallback(new HandlerExecutor(handler), callback); - } - } - } - - /** - * Register a {@link Callback}. + * Add a {@link OnMediaKeyEventDispatchedListener}. * * @param executor The executor on which the callback should be invoked - * @param callback A {@link Callback}. + * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) - public void registerCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull Callback callback) { + public void addOnMediaKeyEventDispatchedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnMediaKeyEventDispatchedListener listener) { if (executor == null) { throw new NullPointerException("executor shouldn't be null"); } - if (callback == null) { - throw new NullPointerException("callback shouldn't be null"); + if (listener == null) { + throw new NullPointerException("listener shouldn't be null"); } synchronized (mLock) { try { - mCallbacks.put(callback, executor); - if (mCurMediaButtonSession != null) { - executor.execute( - () -> callback.onAddressedPlayerChanged(mCurMediaButtonSession)); - } else if (mCurMediaButtonReceiver != null) { - executor.execute( - () -> callback.onAddressedPlayerChanged(mCurMediaButtonReceiver)); - } - - if (mCallbacks.size() == 1) { - mService.registerCallback(mCbStub); + mOnMediaKeyEventDispatchedListeners.put(listener, executor); + if (mOnMediaKeyEventDispatchedListeners.size() == 1) { + mService.addOnMediaKeyEventDispatchedListener( + mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { - Log.e(TAG, "Failed to set media key callback", e); + Log.e(TAG, "Failed to set media key listener", e); } } } /** - * Unregister a {@link Callback}. + * Remove a {@link OnMediaKeyEventDispatchedListener}. * - * @param callback A {@link Callback}. + * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) - public void unregisterCallback(@NonNull Callback callback) { - if (callback == null) { - throw new NullPointerException("callback shouldn't be null"); + public void removeOnMediaKeyEventDispatchedListener( + @NonNull OnMediaKeyEventDispatchedListener listener) { + if (listener == null) { + throw new NullPointerException("listener shouldn't be null"); } synchronized (mLock) { try { - mCallbacks.remove(callback); - if (mCallbacks.size() == 0) { - mService.unregisterCallback(mCbStub); + mOnMediaKeyEventDispatchedListeners.remove(listener); + if (mOnMediaKeyEventDispatchedListeners.size() == 0) { + mService.removeOnMediaKeyEventDispatchedListener( + mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { - Log.e(TAG, "Failed to set media key callback", e); + Log.e(TAG, "Failed to set media key event dispatched listener", e); + } + } + } + + /** + * Add a {@link OnMediaKeyEventDispatchedListener}. + * + * @param executor The executor on which the callback should be invoked + * @param listener A {@link OnMediaKeyEventSessionChangedListener}. + * @hide + */ + @SystemApi + @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) + public void addOnMediaKeyEventSessionChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnMediaKeyEventSessionChangedListener listener) { + if (executor == null) { + throw new NullPointerException("executor shouldn't be null"); + } + if (listener == null) { + throw new NullPointerException("listener shouldn't be null"); + } + synchronized (mLock) { + try { + mMediaKeyEventSessionChangedCallbacks.put(listener, executor); + executor.execute( + () -> listener.onMediaKeyEventSessionChanged( + mCurMediaKeyEventSessionPackage, mCurMediaKeyEventSession)); + if (mMediaKeyEventSessionChangedCallbacks.size() == 1) { + mService.addOnMediaKeyEventSessionChangedListener( + mOnMediaKeyEventSessionChangedListenerStub); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to set media key listener", e); + } + } + } + + /** + * Remove a {@link OnMediaKeyEventSessionChangedListener}. + * + * @param listener A {@link OnMediaKeyEventSessionChangedListener}. + * @hide + */ + @SystemApi + @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) + public void removeOnMediaKeyEventSessionChangedListener( + @NonNull OnMediaKeyEventSessionChangedListener listener) { + if (listener == null) { + throw new NullPointerException("listener shouldn't be null"); + } + synchronized (mLock) { + try { + mMediaKeyEventSessionChangedCallbacks.remove(listener); + if (mMediaKeyEventSessionChangedCallbacks.size() == 0) { + mService.removeOnMediaKeyEventSessionChangedListener( + mOnMediaKeyEventSessionChangedListenerStub); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to set media key listener", e); } } } @@ -900,54 +932,46 @@ public final class MediaSessionManager { } /** - * Callbacks for the media session service. - * - *

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. + * Listener to receive when the media session service * @hide */ @SystemApi - public static abstract class Callback { + public interface OnMediaKeyEventDispatchedListener { /** - * Called when a media key event is dispatched to the media session - * through the media session service. + * Called when a media key event is dispatched through the media session service. The + * session token can be {@link null} if the framework has sent the media key event to the + * media button receiver to revive the media app's playback. + * + * the session is dead when , but the framework sent * * @param event Dispatched media key event. - * @param sessionToken The media session's token. + * @param packageName Package + * @param sessionToken The media session's token. Can be {@code null}. */ - public abstract void onMediaKeyEventDispatched(KeyEvent event, - MediaSession.Token sessionToken); + default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName, + @NonNull MediaSession.Token sessionToken) { } + } + /** + * Listener to receive changes in the media key event session, which would receive the media key + * event unless specified. + * @hide + */ + @SystemApi + public interface OnMediaKeyEventSessionChangedListener { /** - * 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. + * Called when the media key session is changed to the given media session. The key event + * session is the media session which would receive key event by default, unless the caller + * has specified the target. + *

+ * The session token can be {@link null} if the media button session is unset. In that case, + * framework would dispatch to the last sessions's media button receiver. * - * @param event Dispatched media key event. - * @param mediaButtonReceiver The media button receiver. + * @param packageName The package name who would receive the media key event. Can be empty. + * @param sessionToken The media session's token. Can be {@code null.} */ - 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 #registerCallback} 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 #registerCallback} if the addressed player exists. - * - * @param mediaButtonReceiver The media button receiver. - */ - public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver); + default void onMediaKeyEventSessionChanged(@NonNull String packageName, + @Nullable MediaSession.Token sessionToken) { } } /** @@ -1149,50 +1173,35 @@ public final class MediaSessionManager { } } - private final class CallbackStub extends ICallback.Stub { + private final class OnMediaKeyEventDispatchedListenerStub + extends IOnMediaKeyEventDispatchedListener.Stub { @Override - public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, + public void onMediaKeyEventDispatched(KeyEvent event, String packageName, MediaSession.Token sessionToken) { synchronized (mLock) { - for (Map.Entry e : mCallbacks.entrySet()) { + for (Map.Entry e + : mOnMediaKeyEventDispatchedListeners.entrySet()) { e.getValue().execute( - () -> e.getKey().onMediaKeyEventDispatched(event, sessionToken)); + () -> e.getKey().onMediaKeyEventDispatched(event, packageName, + sessionToken)); } } } + } + private final class OnMediaKeyEventSessionChangedListenerStub + extends IOnMediaKeyEventSessionChangedListener.Stub { @Override - public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, - ComponentName mediaButtonReceiver) { + public void onMediaKeyEventSessionChanged(String packageName, + MediaSession.Token sessionToken) { synchronized (mLock) { - for (Map.Entry e : mCallbacks.entrySet()) { - e.getValue().execute( - () -> e.getKey().onMediaKeyEventDispatched(event, mediaButtonReceiver)); - } - } - } - - @Override - public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { - synchronized (mLock) { - mCurMediaButtonSession = sessionToken; - mCurMediaButtonReceiver = null; - for (Map.Entry e : mCallbacks.entrySet()) { - e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged(sessionToken)); - } - } - } - - @Override - public void onAddressedPlayerChangedToMediaButtonReceiver( - ComponentName mediaButtonReceiver) { - synchronized (mLock) { - mCurMediaButtonSession = null; - mCurMediaButtonReceiver = mediaButtonReceiver; - for (Map.Entry e : mCallbacks.entrySet()) { - e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged( - mediaButtonReceiver)); + mCurMediaKeyEventSessionPackage = packageName; + mCurMediaKeyEventSession = sessionToken; + for (Map.Entry e + : mMediaKeyEventSessionChangedCallbacks.entrySet()) { + e.getValue().execute(() -> e.getKey().onMediaKeyEventSessionChanged(packageName, + sessionToken)); } } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c05655a6a5de0..49398b93ba5ec 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -47,7 +47,8 @@ import android.media.MediaController2; import android.media.Session2CommandGroup; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; -import android.media.session.ICallback; +import android.media.session.IOnMediaKeyEventDispatchedListener; +import android.media.session.IOnMediaKeyEventSessionChangedListener; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; @@ -749,7 +750,10 @@ public class MediaSessionService extends SystemService implements Monitor { private final int mFullUserId; private final MediaSessionStack mPriorityStack; - private final HashMap mCallbacks = new HashMap<>(); + private final HashMap + mOnMediaKeyEventDispatchedListeners = new HashMap<>(); + private final HashMap + mOnMediaKeyEventSessionChangedListeners = new HashMap<>(); private PendingIntent mLastMediaButtonReceiver; private ComponentName mRestoredMediaButtonReceiver; @@ -795,21 +799,47 @@ public class MediaSessionService extends SystemService implements Monitor { } } - public void registerCallbackLocked(ICallback callback, int uid) { - IBinder cbBinder = callback.asBinder(); - CallbackRecord cr = new CallbackRecord(callback, uid); - mCallbacks.put(cbBinder, cr); + public void addOnMediaKeyEventDispatchedListenerLocked( + IOnMediaKeyEventDispatchedListener listener, int uid) { + IBinder cbBinder = listener.asBinder(); + OnMediaKeyEventDispatchedListenerRecord cr = + new OnMediaKeyEventDispatchedListenerRecord(listener, uid); + mOnMediaKeyEventDispatchedListeners.put(cbBinder, cr); + try { + cbBinder.linkToDeath(cr, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to register listener", e); + mOnMediaKeyEventDispatchedListeners.remove(cbBinder); + } + } + + public void removeOnMediaKeyEventDispatchedListenerLocked( + IOnMediaKeyEventDispatchedListener listener) { + IBinder cbBinder = listener.asBinder(); + OnMediaKeyEventDispatchedListenerRecord cr = + mOnMediaKeyEventDispatchedListeners.remove(cbBinder); + cbBinder.unlinkToDeath(cr, 0); + } + + public void addOnMediaKeyEventSessionChangedListenerLocked( + IOnMediaKeyEventSessionChangedListener listener, int uid) { + IBinder cbBinder = listener.asBinder(); + OnMediaKeyEventSessionChangedListenerRecord cr = + new OnMediaKeyEventSessionChangedListenerRecord(listener, uid); + mOnMediaKeyEventSessionChangedListeners.put(cbBinder, cr); try { cbBinder.linkToDeath(cr, 0); } catch (RemoteException e) { Log.w(TAG, "Failed to register callback", e); - mCallbacks.remove(cbBinder); + mOnMediaKeyEventSessionChangedListeners.remove(cbBinder); } } - public void unregisterCallbackLocked(ICallback callback) { - IBinder cbBinder = callback.asBinder(); - CallbackRecord cr = mCallbacks.remove(cbBinder); + public void removeOnMediaKeyEventSessionChangedListener( + IOnMediaKeyEventSessionChangedListener listener) { + IBinder cbBinder = listener.asBinder(); + OnMediaKeyEventSessionChangedListenerRecord cr = + mOnMediaKeyEventSessionChangedListeners.remove(cbBinder); cbBinder.unlinkToDeath(cr, 0); } @@ -831,8 +861,16 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(indent + "Media key listener: " + mOnMediaKeyListener); pw.println(indent + "Media key listener package: " + getCallingPackageName(mOnMediaKeyListenerUid)); - pw.println(indent + "Callbacks: registered " + mCallbacks.size() + " callback(s)"); - for (CallbackRecord cr : mCallbacks.values()) { + pw.println(indent + "OnMediaKeyEventDispatchedListener: added " + + mOnMediaKeyEventDispatchedListeners.size() + " listener(s)"); + for (OnMediaKeyEventDispatchedListenerRecord cr + : mOnMediaKeyEventDispatchedListeners.values()) { + pw.println(indent + " from " + getCallingPackageName(cr.uid)); + } + pw.println(indent + "OnMediaKeyEventSessionChangedListener: added " + + mOnMediaKeyEventSessionChangedListeners.size() + " listener(s)"); + for (OnMediaKeyEventSessionChangedListenerRecord cr + : mOnMediaKeyEventSessionChangedListeners.values()) { pw.println(indent + " from " + getCallingPackageName(cr.uid)); } pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); @@ -894,19 +932,22 @@ public class MediaSessionService extends SystemService implements Monitor { mFullUserId); } - private void pushAddressedPlayerChangedLocked(ICallback callback) { + private void pushAddressedPlayerChangedLocked( + IOnMediaKeyEventSessionChangedListener callback) { try { MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { - callback.onAddressedPlayerChangedToMediaSession( + callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(), mediaButtonSession.getSessionToken()); } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { - callback.onAddressedPlayerChangedToMediaButtonReceiver( + callback.onMediaKeyEventSessionChanged( mCurrentFullUserRecord.mLastMediaButtonReceiver - .getIntent().getComponent()); + .getIntent().getComponent().getPackageName(), + null); } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { - callback.onAddressedPlayerChangedToMediaButtonReceiver( - mCurrentFullUserRecord.mRestoredMediaButtonReceiver); + callback.onMediaKeyEventSessionChanged( + mCurrentFullUserRecord.mRestoredMediaButtonReceiver.getPackageName(), + null); } } catch (RemoteException e) { Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); @@ -914,7 +955,8 @@ public class MediaSessionService extends SystemService implements Monitor { } private void pushAddressedPlayerChangedLocked() { - for (CallbackRecord cr : mCallbacks.values()) { + for (OnMediaKeyEventSessionChangedListenerRecord cr + : mOnMediaKeyEventSessionChangedListeners.values()) { pushAddressedPlayerChangedLocked(cr.callback); } } @@ -953,11 +995,12 @@ public class MediaSessionService extends SystemService implements Monitor { return COMPONENT_TYPE_BROADCAST; } - final class CallbackRecord implements IBinder.DeathRecipient { - public final ICallback callback; + final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient { + public final IOnMediaKeyEventDispatchedListener callback; public final int uid; - CallbackRecord(ICallback callback, int uid) { + OnMediaKeyEventDispatchedListenerRecord(IOnMediaKeyEventDispatchedListener callback, + int uid) { this.callback = callback; this.uid = uid; } @@ -965,7 +1008,25 @@ public class MediaSessionService extends SystemService implements Monitor { @Override public void binderDied() { synchronized (mLock) { - mCallbacks.remove(callback.asBinder()); + mOnMediaKeyEventDispatchedListeners.remove(callback.asBinder()); + } + } + } + + final class OnMediaKeyEventSessionChangedListenerRecord implements IBinder.DeathRecipient { + public final IOnMediaKeyEventSessionChangedListener callback; + public final int uid; + + OnMediaKeyEventSessionChangedListenerRecord( + IOnMediaKeyEventSessionChangedListener callback, int uid) { + this.callback = callback; + this.uid = uid; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mOnMediaKeyEventSessionChangedListeners.remove(callback.asBinder()); } } } @@ -1348,7 +1409,8 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public void registerCallback(final ICallback callback) { + public void addOnMediaKeyEventDispatchedListener( + final IOnMediaKeyEventDispatchedListener callback) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final int userId = UserHandle.getUserId(uid); @@ -1356,7 +1418,7 @@ public class MediaSessionService extends SystemService implements Monitor { try { if (!hasMediaControlPermission(pid, uid)) { throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" - + " register Callback"); + + " register MediaKeyEventDispatchedCallback"); } synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); @@ -1365,8 +1427,8 @@ public class MediaSessionService extends SystemService implements Monitor { + ", userId=" + userId); return; } - user.registerCallbackLocked(callback, uid); - Log.d(TAG, "The callback (" + callback.asBinder() + user.addOnMediaKeyEventDispatchedListenerLocked(callback, uid); + Log.d(TAG, "The MediaKeyEventDispatchedCallback (" + callback.asBinder() + ") is registered by " + getCallingPackageName(uid)); } } finally { @@ -1375,7 +1437,8 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public void unregisterCallback(final ICallback callback) { + public void removeOnMediaKeyEventDispatchedListener( + final IOnMediaKeyEventDispatchedListener callback) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final int userId = UserHandle.getUserId(uid); @@ -1383,7 +1446,7 @@ public class MediaSessionService extends SystemService implements Monitor { try { if (!hasMediaControlPermission(pid, uid)) { throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" - + " unregister Callback"); + + " unregister MediaKeyEventDispatchedCallback"); } synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); @@ -1392,8 +1455,64 @@ public class MediaSessionService extends SystemService implements Monitor { + ", userId=" + userId); return; } - user.unregisterCallbackLocked(callback); - Log.d(TAG, "The callback (" + callback.asBinder() + user.removeOnMediaKeyEventDispatchedListenerLocked(callback); + Log.d(TAG, "The MediaKeyEventDispatchedCallback (" + callback.asBinder() + + ") is unregistered by " + getCallingPackageName(uid)); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void addOnMediaKeyEventSessionChangedListener( + final IOnMediaKeyEventSessionChangedListener listener) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + final long token = Binder.clearCallingIdentity(); + try { + if (!hasMediaControlPermission(pid, uid)) { + throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" + + " register MediaKeyEventSessionChangedListener"); + } + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can register the listener" + + ", userId=" + userId); + return; + } + user.addOnMediaKeyEventSessionChangedListenerLocked(listener, uid); + Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder() + + ") is registered by " + getCallingPackageName(uid)); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeOnMediaKeyEventSessionChangedListener( + final IOnMediaKeyEventSessionChangedListener callback) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + final long token = Binder.clearCallingIdentity(); + try { + if (!hasMediaControlPermission(pid, uid)) { + throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to" + + " unregister MediaKeyEventSessionChangedListener"); + } + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can unregister the listener" + + ", userId=" + userId); + return; + } + user.removeOnMediaKeyEventSessionChangedListener(callback); + Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + callback.asBinder() + ") is unregistered by " + getCallingPackageName(uid)); } } finally { @@ -2002,10 +2121,10 @@ public class MediaSessionService extends SystemService implements Monitor { needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver); try { - for (FullUserRecord.CallbackRecord cr - : mCurrentFullUserRecord.mCallbacks.values()) { - cr.callback.onMediaKeyEventDispatchedToMediaSession( - keyEvent, session.getSessionToken()); + for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr + : mCurrentFullUserRecord.mOnMediaKeyEventDispatchedListeners.values()) { + cr.callback.onMediaKeyEventDispatched( + keyEvent, session.getPackageName(), session.getSessionToken()); } } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); @@ -2035,10 +2154,11 @@ public class MediaSessionService extends SystemService implements Monitor { ComponentName componentName = mCurrentFullUserRecord .mLastMediaButtonReceiver.getIntent().getComponent(); if (componentName != null) { - for (FullUserRecord.CallbackRecord cr - : mCurrentFullUserRecord.mCallbacks.values()) { - cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, componentName); + for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr + : mCurrentFullUserRecord + .mOnMediaKeyEventDispatchedListeners.values()) { + cr.callback.onMediaKeyEventDispatched(keyEvent, + componentName.getPackageName(), null); } } } else { @@ -2070,10 +2190,11 @@ public class MediaSessionService extends SystemService implements Monitor { Log.w(TAG, "Error sending media button to the restored intent " + receiver + ", type=" + componentType, e); } - for (FullUserRecord.CallbackRecord cr - : mCurrentFullUserRecord.mCallbacks.values()) { - cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, receiver); + for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr + : mCurrentFullUserRecord + .mOnMediaKeyEventDispatchedListeners.values()) { + cr.callback.onMediaKeyEventDispatched(keyEvent, + receiver.getPackageName(), null); } } } catch (CanceledException e) {