Merge changes from topic "msm_callback"

am: 7e8dffc582

Change-Id: I41d84015fdc917f4ca61fbaa71b740bfaccc37b4
This commit is contained in:
Jaewan Kim
2019-12-16 19:03:00 -08:00
committed by android-build-merger
6 changed files with 470 additions and 180 deletions

View File

@@ -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);
}

View File

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

View File

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

View File

@@ -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,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);

View File

@@ -16,6 +16,7 @@
package android.media.session;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -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}
@@ -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;
@@ -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 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 executor The executor on which the callback should be invoked
* @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 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 {
if (callback == null) {
mCallback = null;
mService.setCallback(null);
} else {
if (handler == null) {
handler = new Handler();
}
mCallback = new CallbackImpl(callback, handler);
mService.setCallback(mCallback);
mOnMediaKeyEventDispatchedListeners.put(listener, executor);
if (mOnMediaKeyEventDispatchedListeners.size() == 1) {
mService.addOnMediaKeyEventDispatchedListener(
mOnMediaKeyEventDispatchedListenerStub);
}
} 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 OnMediaKeyEventDispatchedListener}.
*
* @param listener A {@link OnMediaKeyEventDispatchedListener}.
* @hide
*/
@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 {
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);
}
}
}
/**
* 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 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);
}
}
}
@@ -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.
*
* the session is dead when , but the framework sent
*
* @param event Dispatched media key event.
* @param sessionToken The media session's token.
* @param packageName Package
* @param sessionToken The media session's token. Can be {@code null}.
*/
public abstract void onMediaKeyEventDispatched(KeyEvent event,
MediaSession.Token sessionToken);
default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
@NonNull MediaSession.Token sessionToken) { }
}
/**
* Listener to receive changes in the media key event session, which would receive the media key
* event unless specified.
* @hide
*/
@SystemApi
public interface OnMediaKeyEventSessionChangedListener {
/**
* 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.
* 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 event Dispatched media key event.
* @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 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);
default void onMediaKeyEventSessionChanged(@NonNull String packageName,
@Nullable MediaSession.Token sessionToken) { }
}
/**
@@ -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));
}
});
}
}
}
private final class OnMediaKeyEventSessionChangedListenerStub
extends IOnMediaKeyEventSessionChangedListener.Stub {
@Override
public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
ComponentName mediaButtonReceiver) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver);
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 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

@@ -47,7 +47,8 @@ import android.media.MediaController2;
import android.media.Session2CommandGroup;
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;
@@ -92,6 +93,7 @@ import com.android.server.Watchdog.Monitor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
@@ -748,6 +750,11 @@ public class MediaSessionService extends SystemService implements Monitor {
private final int mFullUserId;
private final MediaSessionStack mPriorityStack;
private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord>
mOnMediaKeyEventDispatchedListeners = new HashMap<>();
private final HashMap<IBinder, OnMediaKeyEventSessionChangedListenerRecord>
mOnMediaKeyEventSessionChangedListeners = new HashMap<>();
private PendingIntent mLastMediaButtonReceiver;
private ComponentName mRestoredMediaButtonReceiver;
private int mRestoredMediaButtonReceiverComponentType;
@@ -761,7 +768,6 @@ public class MediaSessionService extends SystemService implements Monitor {
private IOnMediaKeyListener mOnMediaKeyListener;
private int mOnMediaKeyListenerUid;
private ICallback mCallback;
FullUserRecord(int fullUserId) {
mFullUserId = fullUserId;
@@ -793,6 +799,50 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
public void addOnMediaKeyEventDispatchedListenerLocked(
IOnMediaKeyEventDispatchedListener listener, int uid) {
IBinder cbBinder = listener.asBinder();
OnMediaKeyEventDispatchedListenerRecord cr =
new OnMediaKeyEventDispatchedListenerRecord(listener, uid);
mOnMediaKeyEventDispatchedListeners.put(cbBinder, cr);
try {
cbBinder.linkToDeath(cr, 0);
} catch (RemoteException e) {
Log.w(TAG, "Failed to register listener", e);
mOnMediaKeyEventDispatchedListeners.remove(cbBinder);
}
}
public void removeOnMediaKeyEventDispatchedListenerLocked(
IOnMediaKeyEventDispatchedListener listener) {
IBinder cbBinder = listener.asBinder();
OnMediaKeyEventDispatchedListenerRecord cr =
mOnMediaKeyEventDispatchedListeners.remove(cbBinder);
cbBinder.unlinkToDeath(cr, 0);
}
public void addOnMediaKeyEventSessionChangedListenerLocked(
IOnMediaKeyEventSessionChangedListener listener, int uid) {
IBinder cbBinder = listener.asBinder();
OnMediaKeyEventSessionChangedListenerRecord cr =
new OnMediaKeyEventSessionChangedListenerRecord(listener, uid);
mOnMediaKeyEventSessionChangedListeners.put(cbBinder, cr);
try {
cbBinder.linkToDeath(cr, 0);
} catch (RemoteException e) {
Log.w(TAG, "Failed to register callback", e);
mOnMediaKeyEventSessionChangedListeners.remove(cbBinder);
}
}
public void removeOnMediaKeyEventSessionChangedListener(
IOnMediaKeyEventSessionChangedListener listener) {
IBinder cbBinder = listener.asBinder();
OnMediaKeyEventSessionChangedListenerRecord cr =
mOnMediaKeyEventSessionChangedListeners.remove(cbBinder);
cbBinder.unlinkToDeath(cr, 0);
}
public void dumpLocked(PrintWriter pw, String prefix) {
pw.print(prefix + "Record for full_user=" + mFullUserId);
// Dump managed profile user ids associated with this user.
@@ -811,7 +861,18 @@ public class MediaSessionService extends SystemService implements Monitor {
pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
pw.println(indent + "Media key listener package: "
+ getCallingPackageName(mOnMediaKeyListenerUid));
pw.println(indent + "Callback: " + mCallback);
pw.println(indent + "OnMediaKeyEventDispatchedListener: added "
+ mOnMediaKeyEventDispatchedListeners.size() + " listener(s)");
for (OnMediaKeyEventDispatchedListenerRecord cr
: mOnMediaKeyEventDispatchedListeners.values()) {
pw.println(indent + " from " + getCallingPackageName(cr.uid));
}
pw.println(indent + "OnMediaKeyEventSessionChangedListener: added "
+ mOnMediaKeyEventSessionChangedListeners.size() + " listener(s)");
for (OnMediaKeyEventSessionChangedListenerRecord cr
: mOnMediaKeyEventSessionChangedListeners.values()) {
pw.println(indent + " from " + getCallingPackageName(cr.uid));
}
pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
@@ -871,28 +932,35 @@ public class MediaSessionService extends SystemService implements Monitor {
mFullUserId);
}
private void pushAddressedPlayerChangedLocked() {
if (mCallback == null) {
return;
}
private void pushAddressedPlayerChangedLocked(
IOnMediaKeyEventSessionChangedListener callback) {
try {
MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
if (mediaButtonSession != null) {
mCallback.onAddressedPlayerChangedToMediaSession(
callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(),
mediaButtonSession.getSessionToken());
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
callback.onMediaKeyEventSessionChanged(
mCurrentFullUserRecord.mLastMediaButtonReceiver
.getIntent().getComponent());
.getIntent().getComponent().getPackageName(),
null);
} else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
callback.onMediaKeyEventSessionChanged(
mCurrentFullUserRecord.mRestoredMediaButtonReceiver.getPackageName(),
null);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
}
}
private void pushAddressedPlayerChangedLocked() {
for (OnMediaKeyEventSessionChangedListenerRecord cr
: mOnMediaKeyEventSessionChangedListeners.values()) {
pushAddressedPlayerChangedLocked(cr.callback);
}
}
private MediaSessionRecord getMediaButtonSessionLocked() {
return isGlobalPriorityActiveLocked()
? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
@@ -926,6 +994,42 @@ public class MediaSessionService extends SystemService implements Monitor {
// Pick legacy behavior for BroadcastReceiver or unknown.
return COMPONENT_TYPE_BROADCAST;
}
final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient {
public final IOnMediaKeyEventDispatchedListener callback;
public final int uid;
OnMediaKeyEventDispatchedListenerRecord(IOnMediaKeyEventDispatchedListener callback,
int uid) {
this.callback = callback;
this.uid = uid;
}
@Override
public void binderDied() {
synchronized (mLock) {
mOnMediaKeyEventDispatchedListeners.remove(callback.asBinder());
}
}
}
final class OnMediaKeyEventSessionChangedListenerRecord implements IBinder.DeathRecipient {
public final IOnMediaKeyEventSessionChangedListener callback;
public final int uid;
OnMediaKeyEventSessionChangedListenerRecord(
IOnMediaKeyEventSessionChangedListener callback, int uid) {
this.callback = callback;
this.uid = uid;
}
@Override
public void binderDied() {
synchronized (mLock) {
mOnMediaKeyEventSessionChangedListeners.remove(callback.asBinder());
}
}
}
}
final class SessionsListenerRecord implements IBinder.DeathRecipient {
@@ -1305,44 +1409,111 @@ public class MediaSessionService extends SystemService implements Monitor {
}
@Override
public void setCallback(ICallback callback) {
public void addOnMediaKeyEventDispatchedListener(
final IOnMediaKeyEventDispatchedListener callback) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
throw new SecurityException("Only Bluetooth service processes can set"
+ " Callback");
if (!hasMediaControlPermission(pid, uid)) {
throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+ " register MediaKeyEventDispatchedCallback");
}
synchronized (mLock) {
int userId = UserHandle.getUserId(uid);
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can set the callback"
Log.w(TAG, "Only the full user can register the callback"
+ ", userId=" + userId);
return;
}
user.mCallback = callback;
Log.d(TAG, "The callback " + user.mCallback
+ " is set by " + getCallingPackageName(uid));
if (user.mCallback == null) {
user.addOnMediaKeyEventDispatchedListenerLocked(callback, uid);
Log.d(TAG, "The MediaKeyEventDispatchedCallback (" + callback.asBinder()
+ ") is registered by " + getCallingPackageName(uid));
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void removeOnMediaKeyEventDispatchedListener(
final IOnMediaKeyEventDispatchedListener callback) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+ " unregister MediaKeyEventDispatchedCallback");
}
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can unregister the callback"
+ ", userId=" + userId);
return;
}
try {
user.mCallback.asBinder().linkToDeath(
new IBinder.DeathRecipient() {
@Override
public void binderDied() {
synchronized (mLock) {
user.mCallback = null;
}
}
}, 0);
user.pushAddressedPlayerChangedLocked();
} catch (RemoteException e) {
Log.w(TAG, "Failed to set callback", e);
user.mCallback = null;
user.removeOnMediaKeyEventDispatchedListenerLocked(callback);
Log.d(TAG, "The MediaKeyEventDispatchedCallback (" + callback.asBinder()
+ ") is unregistered by " + getCallingPackageName(uid));
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void addOnMediaKeyEventSessionChangedListener(
final IOnMediaKeyEventSessionChangedListener listener) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+ " register MediaKeyEventSessionChangedListener");
}
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can register the listener"
+ ", userId=" + userId);
return;
}
user.addOnMediaKeyEventSessionChangedListenerLocked(listener, uid);
Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder()
+ ") is registered by " + getCallingPackageName(uid));
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void removeOnMediaKeyEventSessionChangedListener(
final IOnMediaKeyEventSessionChangedListener callback) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
if (!hasMediaControlPermission(pid, uid)) {
throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+ " unregister MediaKeyEventSessionChangedListener");
}
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
Log.w(TAG, "Only the full user can unregister the listener"
+ ", userId=" + userId);
return;
}
user.removeOnMediaKeyEventSessionChangedListener(callback);
Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + callback.asBinder()
+ ") is unregistered by " + getCallingPackageName(uid));
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1771,6 +1942,7 @@ public class MediaSessionService extends SystemService implements Monitor {
public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
throws RemoteException {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
// Don't perform sanity check between controllerPackageName and controllerUid.
@@ -1781,8 +1953,8 @@ public class MediaSessionService extends SystemService implements Monitor {
// Note that we can use Context#getOpPackageName() instead of
// Context#getPackageName() for getting package name that matches with the PID/UID,
// but it doesn't tell which package has created the MediaController, so useless.
return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
controllerPid, controllerUid);
return hasMediaControlPermission(controllerPid, controllerUid)
|| hasEnabledNotificationListener(userId, controllerPackageName);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1808,13 +1980,7 @@ public class MediaSessionService extends SystemService implements Monitor {
return resolvedUserId;
}
private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
int pid, int uid) throws RemoteException {
// Allow API calls from the System UI and Settings
if (hasStatusBarServicePermission(pid, uid)) {
return true;
}
private boolean hasMediaControlPermission(int pid, int uid) {
// Check if it's system server or has MEDIA_CONTENT_CONTROL.
// Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
// check here.
@@ -1823,11 +1989,15 @@ public class MediaSessionService extends SystemService implements Monitor {
== PackageManager.PERMISSION_GRANTED) {
return true;
} else if (DEBUG) {
Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
}
return false;
}
private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
throws RemoteException {
// You may not access another user's content as an enabled listener.
final int userId = UserHandle.getUserId(uid);
final int userId = UserHandle.getUserId(resolvedUserId);
if (resolvedUserId != userId) {
return false;
}
@@ -1845,7 +2015,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
if (DEBUG) {
Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled "
+ "notification listener");
}
return false;
@@ -1950,13 +2120,14 @@ public class MediaSessionService extends SystemService implements Monitor {
session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mKeyEventReceiver);
if (mCurrentFullUserRecord.mCallback != null) {
try {
mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
keyEvent, session.getSessionToken());
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
try {
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord.mOnMediaKeyEventDispatchedListeners.values()) {
cr.callback.onMediaKeyEventDispatched(
keyEvent, session.getPackageName(), session.getSessionToken());
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
|| mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
@@ -1980,13 +2151,14 @@ public class MediaSessionService extends SystemService implements Monitor {
receiver.send(mContext,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mediaButtonIntent, mKeyEventReceiver, mHandler);
if (mCurrentFullUserRecord.mCallback != null) {
ComponentName componentName = mCurrentFullUserRecord
.mLastMediaButtonReceiver.getIntent().getComponent();
if (componentName != null) {
mCurrentFullUserRecord.mCallback
.onMediaKeyEventDispatchedToMediaButtonReceiver(
keyEvent, componentName);
ComponentName componentName = mCurrentFullUserRecord
.mLastMediaButtonReceiver.getIntent().getComponent();
if (componentName != null) {
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord
.mOnMediaKeyEventDispatchedListeners.values()) {
cr.callback.onMediaKeyEventDispatched(keyEvent,
componentName.getPackageName(), null);
}
}
} else {
@@ -2018,10 +2190,11 @@ public class MediaSessionService extends SystemService implements Monitor {
Log.w(TAG, "Error sending media button to the restored intent "
+ receiver + ", type=" + componentType, e);
}
if (mCurrentFullUserRecord.mCallback != null) {
mCurrentFullUserRecord.mCallback
.onMediaKeyEventDispatchedToMediaButtonReceiver(
keyEvent, receiver);
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord
.mOnMediaKeyEventDispatchedListeners.values()) {
cr.callback.onMediaKeyEventDispatched(keyEvent,
receiver.getPackageName(), null);
}
}
} catch (CanceledException e) {