Merge "Add callback for AVRCP 1.6 support"

This commit is contained in:
Jaewan Kim
2017-02-28 06:19:46 +00:00
committed by Android (Google) Code Review
5 changed files with 289 additions and 25 deletions

View File

@@ -441,6 +441,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/IOnMediaKeyListener.aidl \
media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl \
media/java/android/media/session/ISession.aidl \

View File

@@ -0,0 +1,35 @@
/* 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);
}

View File

@@ -18,8 +18,9 @@ package android.media.session;
import android.content.ComponentName;
import android.media.IRemoteVolumeController;
import android.media.session.IActiveSessionsListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.os.Bundle;
@@ -45,7 +46,7 @@ interface ISessionManager {
// For PhoneWindowManager to precheck media keys
boolean isGlobalPriorityActive();
void setCallback(in ICallback callback);
void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
void setOnMediaKeyListener(in IOnMediaKeyListener listener);
}

View File

@@ -71,6 +71,7 @@ public final class MediaSessionManager {
private Context mContext;
private CallbackImpl mCallback;
private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener;
private OnMediaKeyListenerImpl mOnMediaKeyListener;
@@ -410,6 +411,36 @@ public final class MediaSessionManager {
}
}
/**
* 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}.
@@ -448,6 +479,56 @@ public final class MediaSessionManager {
boolean onMediaKey(KeyEvent event);
}
/**
* 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;
@@ -546,4 +627,57 @@ 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;
}
@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);
}
});
}
}
}

View File

@@ -37,6 +37,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.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
@@ -107,6 +108,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.
@@ -486,6 +488,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()));
@@ -517,6 +520,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());
@@ -838,6 +887,44 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
@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 {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
final int pid = Binder.getCallingPid();
@@ -1217,24 +1304,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
// 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;
}
}
if (DEBUG) {
Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
+ useNotPlayingSessions);
}
MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
mCurrentUserIdList, useNotPlayingSessions);
MediaSessionRecord session = getMediaButtonSessionLocked();
if (session != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to " + session);
@@ -1247,6 +1317,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) {
@@ -1263,26 +1341,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_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_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;
}