Add callback for AVRCP 1.6 support
Bug: 33828042 Test: Build Change-Id: Iaf5cecfa38065cfeed096929952559d7cb2e248b
This commit is contained in:
committed by
Andre Eisenbach
parent
fe358c6adf
commit
c8d846ffef
@@ -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
media/java/android/media/session/ICallback.aidl
Normal file
34
media/java/android/media/session/ICallback.aidl
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,10 @@ 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 {
|
||||
@@ -805,6 +860,45 @@ 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 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user