Merge changes from topic "msm_callback"
am: 7e8dffc582
Change-Id: I41d84015fdc917f4ca61fbaa71b740bfaccc37b4
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user