Loading api/system-current.txt +12 −0 Original line number Diff line number Diff line Loading @@ -3883,10 +3883,22 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { 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); } 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 { method public boolean onMediaKey(android.view.KeyEvent); } Loading media/java/android/media/session/ICallback.aidl→media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl +4 −11 Original line number Diff line number Diff line /* 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. Loading @@ -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); } media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * 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); } media/java/android/media/session/ISessionManager.aidl +8 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -62,7 +63,12 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(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); Loading media/java/android/media/session/MediaSessionManager.java +178 −100 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.session; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -46,8 +47,11 @@ import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; /** * Provides support for interacting with {@link MediaSession media sessions} Loading @@ -72,19 +76,32 @@ public final class MediaSessionManager { * @hide */ 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<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; @GuardedBy("mLock") private final Map<OnMediaKeyEventDispatchedListener, Executor> mOnMediaKeyEventDispatchedListeners = new HashMap<>(); @GuardedBy("mLock") private final Map<OnMediaKeyEventSessionChangedListener, Executor> mMediaKeyEventSessionChangedCallbacks = new HashMap<>(); @GuardedBy("mLock") private String mCurMediaKeyEventSessionPackage; @GuardedBy("mLock") private MediaSession.Token mCurMediaKeyEventSession; private Context mContext; private CallbackImpl mCallback; private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener; private OnMediaKeyListenerImpl mOnMediaKeyListener; Loading Loading @@ -742,31 +759,118 @@ public final class MediaSessionManager { } /** * Set a {@link Callback}. * Add a {@link OnMediaKeyEventDispatchedListener}. * * <p>System can only have a single callback, and the callback can only be set by * Bluetooth service process. * @param executor The executor on which the callback should be invoked * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventDispatchedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnMediaKeyEventDispatchedListener 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 { mOnMediaKeyEventDispatchedListeners.put(listener, executor); if (mOnMediaKeyEventDispatchedListeners.size() == 1) { mService.addOnMediaKeyEventDispatchedListener( mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key listener", e); } } } /** * Remove a {@link OnMediaKeyEventDispatchedListener}. * * @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. * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener( @NonNull OnMediaKeyEventDispatchedListener listener) { if (listener == null) { throw new NullPointerException("listener shouldn't be null"); } synchronized (mLock) { try { if (callback == null) { mCallback = null; mService.setCallback(null); } else { if (handler == null) { handler = new Handler(); mOnMediaKeyEventDispatchedListeners.remove(listener); if (mOnMediaKeyEventDispatchedListeners.size() == 0) { mService.removeOnMediaKeyEventDispatchedListener( mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key event dispatched listener", e); } } } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); /** * 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 callback", 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); } } } Loading Loading @@ -828,53 +932,46 @@ public final class MediaSessionManager { } /** * Callbacks for the media session service. * * <p>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 */ public static abstract class Callback { @SystemApi 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. * * @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. * <p>MediaSessionService may broadcast key events to the media button receiver * when reviving playback after the media session is released. * the session is dead when , but the framework sent * * @param event Dispatched media key event. * @param mediaButtonReceiver The media button receiver. * @param packageName Package * @param sessionToken The media session's token. Can be {@code null}. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, ComponentName mediaButtonReceiver); default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName, @NonNull MediaSession.Token sessionToken) { } } /** * Called when the addressed player is changed to a media session. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param sessionToken The media session's token. * Listener to receive changes in the media key event session, which would receive the media key * event unless specified. * @hide */ public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken); @SystemApi public interface OnMediaKeyEventSessionChangedListener { /** * Called when the addressed player is changed to the media button receiver. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * 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. * <p> * 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 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 onAddressedPlayerChanged(ComponentName mediaButtonReceiver); default void onMediaKeyEventSessionChanged(@NonNull String packageName, @Nullable MediaSession.Token sessionToken) { } } /** Loading Loading @@ -1076,56 +1173,37 @@ public final class MediaSessionManager { } } 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; } private final class OnMediaKeyEventDispatchedListenerStub extends IOnMediaKeyEventDispatchedListener.Stub { @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, public void onMediaKeyEventDispatched(KeyEvent event, String packageName, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); synchronized (mLock) { for (Map.Entry<OnMediaKeyEventDispatchedListener, Executor> e : mOnMediaKeyEventDispatchedListeners.entrySet()) { e.getValue().execute( () -> e.getKey().onMediaKeyEventDispatched(event, packageName, sessionToken)); } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); } }); } private final class OnMediaKeyEventSessionChangedListenerStub extends IOnMediaKeyEventSessionChangedListener.Stub { @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); } }); public void onMediaKeyEventSessionChanged(String packageName, MediaSession.Token sessionToken) { synchronized (mLock) { mCurMediaKeyEventSessionPackage = packageName; mCurMediaKeyEventSession = sessionToken; for (Map.Entry<OnMediaKeyEventSessionChangedListener, Executor> e : mMediaKeyEventSessionChangedCallbacks.entrySet()) { e.getValue().execute(() -> e.getKey().onMediaKeyEventSessionChanged(packageName, sessionToken)); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); } }); } } } Loading
api/system-current.txt +12 −0 Original line number Diff line number Diff line Loading @@ -3883,10 +3883,22 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { 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); } 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 { method public boolean onMediaKey(android.view.KeyEvent); } Loading
media/java/android/media/session/ICallback.aidl→media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl +4 −11 Original line number Diff line number Diff line /* 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. Loading @@ -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); }
media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * 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); }
media/java/android/media/session/ISessionManager.aidl +8 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -62,7 +63,12 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(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); Loading
media/java/android/media/session/MediaSessionManager.java +178 −100 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.session; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -46,8 +47,11 @@ import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; /** * Provides support for interacting with {@link MediaSession media sessions} Loading @@ -72,19 +76,32 @@ public final class MediaSessionManager { * @hide */ 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<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; @GuardedBy("mLock") private final Map<OnMediaKeyEventDispatchedListener, Executor> mOnMediaKeyEventDispatchedListeners = new HashMap<>(); @GuardedBy("mLock") private final Map<OnMediaKeyEventSessionChangedListener, Executor> mMediaKeyEventSessionChangedCallbacks = new HashMap<>(); @GuardedBy("mLock") private String mCurMediaKeyEventSessionPackage; @GuardedBy("mLock") private MediaSession.Token mCurMediaKeyEventSession; private Context mContext; private CallbackImpl mCallback; private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener; private OnMediaKeyListenerImpl mOnMediaKeyListener; Loading Loading @@ -742,31 +759,118 @@ public final class MediaSessionManager { } /** * Set a {@link Callback}. * Add a {@link OnMediaKeyEventDispatchedListener}. * * <p>System can only have a single callback, and the callback can only be set by * Bluetooth service process. * @param executor The executor on which the callback should be invoked * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventDispatchedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnMediaKeyEventDispatchedListener 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 { mOnMediaKeyEventDispatchedListeners.put(listener, executor); if (mOnMediaKeyEventDispatchedListeners.size() == 1) { mService.addOnMediaKeyEventDispatchedListener( mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key listener", e); } } } /** * Remove a {@link OnMediaKeyEventDispatchedListener}. * * @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. * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener( @NonNull OnMediaKeyEventDispatchedListener listener) { if (listener == null) { throw new NullPointerException("listener shouldn't be null"); } synchronized (mLock) { try { if (callback == null) { mCallback = null; mService.setCallback(null); } else { if (handler == null) { handler = new Handler(); mOnMediaKeyEventDispatchedListeners.remove(listener); if (mOnMediaKeyEventDispatchedListeners.size() == 0) { mService.removeOnMediaKeyEventDispatchedListener( mOnMediaKeyEventDispatchedListenerStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key event dispatched listener", e); } } } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); /** * 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 callback", 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); } } } Loading Loading @@ -828,53 +932,46 @@ public final class MediaSessionManager { } /** * Callbacks for the media session service. * * <p>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 */ public static abstract class Callback { @SystemApi 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. * * @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. * <p>MediaSessionService may broadcast key events to the media button receiver * when reviving playback after the media session is released. * the session is dead when , but the framework sent * * @param event Dispatched media key event. * @param mediaButtonReceiver The media button receiver. * @param packageName Package * @param sessionToken The media session's token. Can be {@code null}. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, ComponentName mediaButtonReceiver); default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName, @NonNull MediaSession.Token sessionToken) { } } /** * Called when the addressed player is changed to a media session. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param sessionToken The media session's token. * Listener to receive changes in the media key event session, which would receive the media key * event unless specified. * @hide */ public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken); @SystemApi public interface OnMediaKeyEventSessionChangedListener { /** * Called when the addressed player is changed to the media button receiver. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * 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. * <p> * 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 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 onAddressedPlayerChanged(ComponentName mediaButtonReceiver); default void onMediaKeyEventSessionChanged(@NonNull String packageName, @Nullable MediaSession.Token sessionToken) { } } /** Loading Loading @@ -1076,56 +1173,37 @@ public final class MediaSessionManager { } } 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; } private final class OnMediaKeyEventDispatchedListenerStub extends IOnMediaKeyEventDispatchedListener.Stub { @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, public void onMediaKeyEventDispatched(KeyEvent event, String packageName, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); synchronized (mLock) { for (Map.Entry<OnMediaKeyEventDispatchedListener, Executor> e : mOnMediaKeyEventDispatchedListeners.entrySet()) { e.getValue().execute( () -> e.getKey().onMediaKeyEventDispatched(event, packageName, sessionToken)); } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); } }); } private final class OnMediaKeyEventSessionChangedListenerStub extends IOnMediaKeyEventSessionChangedListener.Stub { @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); } }); public void onMediaKeyEventSessionChanged(String packageName, MediaSession.Token sessionToken) { synchronized (mLock) { mCurMediaKeyEventSessionPackage = packageName; mCurMediaKeyEventSession = sessionToken; for (Map.Entry<OnMediaKeyEventSessionChangedListener, Executor> e : mMediaKeyEventSessionChangedCallbacks.entrySet()) { e.getValue().execute(() -> e.getKey().onMediaKeyEventSessionChanged(packageName, sessionToken)); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); } }); } } }