Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5ff735f authored by Jaewan Kim's avatar Jaewan Kim
Browse files

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
parent c37f70d6
Loading
Loading
Loading
Loading
+10 −8
Original line number Diff line number Diff line
@@ -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 {
+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.
@@ -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);
}
+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);
}
+8 −3
Original line number Diff line number Diff line
@@ -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);

+131 −122
Original line number Diff line number Diff line
@@ -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<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<>();
    @GuardedBy("mLock")
    private final CallbackStub mCbStub = new CallbackStub();
    private final Map<OnMediaKeyEventDispatchedListener, Executor>
            mOnMediaKeyEventDispatchedListeners = new HashMap<>();
    @GuardedBy("mLock")
    private final Map<Callback, Executor> mCallbacks = new HashMap<>();
    private final Map<OnMediaKeyEventSessionChangedListener, Executor>
            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}.
     * 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
     */
    // 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();
    @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) {
            if (mLegacyCallback != null) {
                unregisterCallback(mLegacyCallback);
            try {
                mOnMediaKeyEventDispatchedListeners.remove(listener);
                if (mOnMediaKeyEventDispatchedListeners.size() == 0) {
                    mService.removeOnMediaKeyEventDispatchedListener(
                            mOnMediaKeyEventDispatchedListenerStub);
                }
            mLegacyCallback = callback;
            if (callback != null) {
                registerCallback(new HandlerExecutor(handler), callback);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to set media key event dispatched listener", e);
            }
        }
    }

    /**
     * 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 OnMediaKeyEventSessionChangedListener}.
     * @hide
     */
    @SystemApi
    @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull Callback callback) {
    public void addOnMediaKeyEventSessionChangedListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnMediaKeyEventSessionChangedListener 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) {
                mMediaKeyEventSessionChangedCallbacks.put(listener, executor);
                executor.execute(
                            () -> callback.onAddressedPlayerChanged(mCurMediaButtonReceiver));
                }

                if (mCallbacks.size() == 1) {
                    mService.registerCallback(mCbStub);
                        () -> 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);
            }
        }
    }

    /**
     * Unregister a {@link Callback}.
     * Remove a {@link OnMediaKeyEventSessionChangedListener}.
     *
     * @param callback A {@link Callback}.
     * @param listener A {@link OnMediaKeyEventSessionChangedListener}.
     * @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 removeOnMediaKeyEventSessionChangedListener(
            @NonNull OnMediaKeyEventSessionChangedListener 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);
                mMediaKeyEventSessionChangedCallbacks.remove(listener);
                if (mMediaKeyEventSessionChangedCallbacks.size() == 0) {
                    mService.removeOnMediaKeyEventSessionChangedListener(
                            mOnMediaKeyEventSessionChangedListenerStub);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to set media key callback", 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.
     *
     * <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
     */
    @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.
         *
         * @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 #registerCallback} 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 #registerCallback} 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) { }
    }

    /**
@@ -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<Callback, Executor> e : mCallbacks.entrySet()) {
                    e.getValue().execute(
                            () -> e.getKey().onMediaKeyEventDispatched(event, sessionToken));
                }
            }
        }

        @Override
        public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
                ComponentName mediaButtonReceiver) {
            synchronized (mLock) {
                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
                for (Map.Entry<OnMediaKeyEventDispatchedListener, Executor> e
                        : mOnMediaKeyEventDispatchedListeners.entrySet()) {
                    e.getValue().execute(
                            () -> e.getKey().onMediaKeyEventDispatched(event, mediaButtonReceiver));
                            () -> e.getKey().onMediaKeyEventDispatched(event, packageName,
                                    sessionToken));
                }
            }
        }

        @Override
        public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) {
            synchronized (mLock) {
                mCurMediaButtonSession = sessionToken;
                mCurMediaButtonReceiver = null;
                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
                    e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged(sessionToken));
                }
            }
    }

    private final class OnMediaKeyEventSessionChangedListenerStub
            extends IOnMediaKeyEventSessionChangedListener.Stub {
        @Override
        public void onAddressedPlayerChangedToMediaButtonReceiver(
                ComponentName mediaButtonReceiver) {
        public void onMediaKeyEventSessionChanged(String packageName,
                MediaSession.Token sessionToken) {
            synchronized (mLock) {
                mCurMediaButtonSession = null;
                mCurMediaButtonReceiver = mediaButtonReceiver;
                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
                    e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged(
                            mediaButtonReceiver));
                mCurMediaKeyEventSessionPackage = packageName;
                mCurMediaKeyEventSession = sessionToken;
                for (Map.Entry<OnMediaKeyEventSessionChangedListener, Executor> e
                        : mMediaKeyEventSessionChangedCallbacks.entrySet()) {
                    e.getValue().execute(() -> e.getKey().onMediaKeyEventSessionChanged(packageName,
                            sessionToken));
                }
            }
        }
Loading