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) {