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

Commit 58b5789c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add callback for AVRCP 1.6 support"

parents fe358c6a c8d846ff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -404,6 +404,7 @@ LOCAL_SRC_FILES += \
	media/java/android/media/projection/IMediaProjectionManager.aidl \
	media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \
	media/java/android/media/session/IActiveSessionsListener.aidl \
	media/java/android/media/session/ICallback.aidl \
	media/java/android/media/session/ISessionController.aidl \
	media/java/android/media/session/ISessionControllerCallback.aidl \
	media/java/android/media/session/ISession.aidl \
+34 −0
Original line number Diff line number Diff line
/* Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media.session;

import android.app.PendingIntent;
import android.content.ComponentName;
import android.media.session.MediaSession;
import android.view.KeyEvent;

/**
 * @hide
 */
oneway interface ICallback {
    void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event,
            in MediaSession.Token sessionToken);
    void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event,
            in ComponentName mediaButtonReceiver);

    void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken);
    void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver);
}
+4 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.media.session;
import android.content.ComponentName;
import android.media.IRemoteVolumeController;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.os.Bundle;
@@ -41,4 +42,6 @@ interface ISessionManager {

    // For PhoneWindowManager to precheck media keys
    boolean isGlobalPriorityActive();

    void setCallback(in ICallback callback);
}
+135 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ public final class MediaSessionManager {

    private Context mContext;

    private CallbackImpl mCallback;

    /**
     * @hide
     */
@@ -312,6 +314,36 @@ public final class MediaSessionManager {
        return false;
    }

    /**
     * Set a {@link Callback}.
     *
     * <p>System can only have a single callback, and the callback can only be set by
     * Bluetooth service process.
     *
     * @param callback A {@link Callback}. {@code null} to reset.
     * @param handler The handler on which the callback should be invoked, or {@code null}
     *            if the callback should be invoked on the calling thread's looper.
     * @hide
     */
    public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
        synchronized (mLock) {
            try {
                if (callback == null) {
                    mCallback = null;
                    mService.setCallback(null);
                } else {
                    if (handler == null) {
                        handler = new Handler();
                    }
                    mCallback = new CallbackImpl(callback, handler);
                    mService.setCallback(mCallback);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to set media key callback", e);
            }
        }
    }

    /**
     * Listens for changes to the list of active sessions. This can be added
     * using {@link #addOnActiveSessionsChangedListener}.
@@ -320,6 +352,56 @@ public final class MediaSessionManager {
        public void onActiveSessionsChanged(@Nullable List<MediaController> controllers);
    }

    /**
     * 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.
     * @hide
     */
    public static abstract class Callback {
        /**
         * Called when a media key event is dispatched to the media session
         * through the media session service.
         *
         * @param event Dispatched media key event.
         * @param sessionToken The media session's token.
         */
        public abstract void onMediaKeyEventDispatched(KeyEvent event,
                MediaSession.Token sessionToken);

        /**
         * Called when a media key event is dispatched to the media button receiver
         * through the media session service.
         * <p>MediaSessionService may broadcast key events to the media button receiver
         * when reviving playback after the media session is released.
         *
         * @param event Dispatched media key event.
         * @param mediaButtonReceiver The media button receiver.
         */
        public abstract void onMediaKeyEventDispatched(KeyEvent event,
                ComponentName mediaButtonReceiver);

        /**
         * Called when the addressed player is changed to a media session.
         * <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.
         */
        public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken);

        /**
         * 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.
         *
         * @param mediaButtonReceiver The media button receiver.
         */
        public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
    }

    private static final class SessionsChangedWrapper {
        private Context mContext;
        private OnActiveSessionsChangedListener mListener;
@@ -360,4 +442,57 @@ public final class MediaSessionManager {
            mHandler = null;
        }
    }

    private static final class CallbackImpl extends ICallback.Stub {
        private final Callback mCallback;
        private final Handler mHandler;

        public CallbackImpl(@NonNull Callback callback, @NonNull Handler handler) {
            mCallback = callback;
            mHandler = handler;
        }

        @Override
        public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event,
                MediaSession.Token sessionToken) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mCallback.onMediaKeyEventDispatched(event, sessionToken);
                }
            });
        }

        @Override
        public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
                ComponentName mediaButtonReceiver) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver);
                }
            });
        }

        @Override
        public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mCallback.onAddressedPlayerChanged(sessionToken);
                }
            });
        }

        @Override
        public void onAddressedPlayerChangedToMediaButtonReceiver(
                ComponentName mediaButtonReceiver) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mCallback.onAddressedPlayerChanged(mediaButtonReceiver);
                }
            });
        }
    }
}
+131 −20
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
@@ -101,6 +102,7 @@ public class MediaSessionService extends SystemService implements Monitor {
    private AudioManagerInternal mAudioManagerInternal;
    private ContentResolver mContentResolver;
    private SettingsObserver mSettingsObserver;
    private ICallback mCallback;

    // List of user IDs running in the foreground.
    // Multiple users can be in the foreground if the work profile is on.
@@ -485,6 +487,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            if (size > 0 && records.get(0).isPlaybackActive(false)) {
                rememberMediaButtonReceiverLocked(records.get(0));
            }
            pushAddressedPlayerChangedLocked();
            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
            for (int i = 0; i < size; i++) {
                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
@@ -516,6 +519,52 @@ public class MediaSessionService extends SystemService implements Monitor {
        }
    }

    private MediaSessionRecord getMediaButtonSessionLocked() {
        // If we don't have a media button receiver to fall back on
        // include non-playing sessions for dispatching.
        boolean useNotPlayingSessions = true;
        for (int userId : mCurrentUserIdList) {
            UserRecord ur = mUserRecords.get(userId);
            if (ur.mLastMediaButtonReceiver != null
                    || ur.mRestoredMediaButtonReceiver != null) {
                useNotPlayingSessions = false;
                break;
            }
        }
        return mPriorityStack.getDefaultMediaButtonSession(
                mCurrentUserIdList, useNotPlayingSessions);
    }

    private void pushAddressedPlayerChangedLocked() {
        if (mCallback == null) {
            return;
        }
        try {
            MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
            if (mediaButtonSession != null) {
                mCallback.onAddressedPlayerChangedToMediaSession(
                        new MediaSession.Token(mediaButtonSession.getControllerBinder()));
            } else {
                for (int userId : mCurrentUserIdList) {
                    UserRecord user = mUserRecords.get(userId);
                    if (user.mLastMediaButtonReceiver == null
                            && user.mRestoredMediaButtonReceiver == null) {
                        continue;
                    }
                    ComponentName componentName = user.mLastMediaButtonReceiver != null
                            ? user.mLastMediaButtonReceiver.getIntent().getComponent()
                            : user.mRestoredMediaButtonReceiver;
                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(componentName);
                    return;
                }
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
        }
    }

    // Remember media button receiver and keep it in the persistent storage.
    // This should be called whenever there's no media session to receive media button event.
    private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
        PendingIntent receiver = record.getMediaButtonReceiver();
        UserRecord user = mUserRecords.get(record.getUserId());
@@ -530,6 +579,14 @@ public class MediaSessionService extends SystemService implements Monitor {
        }
    }

    private String getCallingPackageName(int uid) {
        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
            if (packages != null && packages.length > 0) {
                return packages[0];
            }
        return "";
    }

    /**
     * Information about a particular user. The contents of this object is
     * guarded by mLock.
@@ -792,12 +849,48 @@ public class MediaSessionService extends SystemService implements Monitor {
                        Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
                                + useNotPlayingSessions);
                    }
                    MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
                            mCurrentUserIdList, useNotPlayingSessions);
                    if (isVoiceKey(keyEvent.getKeyCode())) {
                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
                        handleVoiceKeyEventLocked(keyEvent, needWakeLock);
                    } else {
                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setCallback(ICallback callback) {
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            final long token = Binder.clearCallingIdentity();
            try {
                if (uid != Process.BLUETOOTH_UID) {
                    throw new SecurityException("Only Bluetooth service processes can set"
                            + " Callback");
                }
                synchronized (mLock) {
                    Log.d(TAG, "Callback + " + mCallback
                            + " is set by " + getCallingPackageName(uid));
                    mCallback = callback;
                    if (mCallback == null) {
                        return;
                    }
                    try {
                        mCallback.asBinder().linkToDeath(
                                new IBinder.DeathRecipient() {
                                    @Override
                                    public void binderDied() {
                                        synchronized (mLock) {
                                            mCallback = null;
                                        }
                                    }
                                }, 0);
                        pushAddressedPlayerChangedLocked();
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to set callback", e);
                        mCallback = null;
                    }
                }
            } finally {
@@ -805,6 +898,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            }
        }


        @Override
        public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
            final long token = Binder.clearCallingIdentity();
@@ -932,13 +1026,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            }
        }

        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
                MediaSessionRecord session) {
            if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
                // If the phone app has priority just give it the event
                dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
                return;
            }
        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
            int action = keyEvent.getAction();
            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
            if (action == KeyEvent.ACTION_DOWN) {
@@ -955,15 +1043,15 @@ public class MediaSessionService extends SystemService implements Monitor {
                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
                        // Resend the down then send this event through
                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
                        dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
                        dispatchMediaKeyEventLocked(downEvent, needWakeLock);
                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
                    }
                }
            }
        }

        private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
                MediaSessionRecord session) {
        private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
            MediaSessionRecord session = getMediaButtonSessionLocked();
            if (session != null) {
                if (DEBUG_MEDIA_KEY_EVENT) {
                    Log.d(TAG, "Sending " + keyEvent + " to " + session);
@@ -977,6 +1065,14 @@ public class MediaSessionService extends SystemService implements Monitor {
                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
                        mKeyEventReceiver, Process.SYSTEM_UID,
                        getContext().getPackageName());
                if (mCallback != null) {
                    try {
                        mCallback.onMediaKeyEventDispatchedToMediaSession(keyEvent,
                                new MediaSession.Token(session.getControllerBinder()));
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to send callback", e);
                    }
                }
            } else {
                // Launch the last PendingIntent we had with priority
                for (int userId : mCurrentUserIdList) {
@@ -993,26 +1089,41 @@ public class MediaSessionService extends SystemService implements Monitor {
                    mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                    try {
                        if (user.mLastMediaButtonReceiver != null) {
                            PendingIntent receiver = user.mLastMediaButtonReceiver;
                            if (DEBUG_MEDIA_KEY_EVENT) {
                                Log.d(TAG, "Sending " + keyEvent
                                        + " to the last known pendingIntent "
                                        + user.mLastMediaButtonReceiver);
                                        + " to the last known pendingIntent " + receiver);
                            }
                            user.mLastMediaButtonReceiver.send(getContext(),
                            receiver.send(getContext(),
                                    needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
                                    mediaButtonIntent, mKeyEventReceiver, mHandler);
                            if (mCallback != null) {
                                ComponentName componentName =
                                        user.mLastMediaButtonReceiver.getIntent().getComponent();
                                if (componentName != null) {
                                    mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver(
                                            keyEvent, componentName);
                                }
                            }
                        } else {
                            ComponentName receiver = user.mRestoredMediaButtonReceiver;
                            if (DEBUG_MEDIA_KEY_EVENT) {
                                Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
                                        + user.mRestoredMediaButtonReceiver);
                                        + receiver);
                            }
                            mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
                            mediaButtonIntent.setComponent(receiver);
                            getContext().sendBroadcastAsUser(mediaButtonIntent,
                                    UserHandle.of(userId));
                            if (mCallback != null) {
                                mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver(
                                        keyEvent, receiver);
                            }
                        }
                    } catch (CanceledException e) {
                        Log.i(TAG, "Error sending key event to media button receiver "
                                + user.mLastMediaButtonReceiver, e);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to send callback", e);
                    }
                    return;
                }