Loading api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -3693,8 +3693,18 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull android.media.session.MediaSessionManager.Callback, @Nullable android.os.Handler); 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); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull android.media.session.MediaSessionManager.Callback); } public abstract static class MediaSessionManager.Callback { ctor public MediaSessionManager.Callback(); method public abstract void onAddressedPlayerChanged(android.media.session.MediaSession.Token); method public abstract void onAddressedPlayerChanged(android.content.ComponentName); method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token); method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName); } public static interface MediaSessionManager.OnMediaKeyListener { Loading media/java/android/media/session/ISessionManager.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,8 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(in ICallback callback); void registerCallback(in ICallback callback); void unregisterCallback(in ICallback callback); void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); void setOnMediaKeyListener(in IOnMediaKeyListener listener); Loading media/java/android/media/session/MediaSessionManager.java +103 −42 Original line number Diff line number Diff line Loading @@ -46,7 +46,9 @@ 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; /** Loading @@ -72,6 +74,7 @@ public final class MediaSessionManager { * @hide */ public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final ISessionManager mService; private final Object mLock = new Object(); @GuardedBy("mLock") Loading @@ -80,13 +83,21 @@ public final class MediaSessionManager { @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; @GuardedBy("mLock") private final CallbackStub mCbStub = new CallbackStub(); @GuardedBy("mLock") private final Map<Callback, Handler> mCallbacks = new HashMap<>(); @GuardedBy("mLock") private MediaSession.Token mCurMediaButtonSession; @GuardedBy("mLock") private ComponentName mCurMediaButtonReceiver; private Context mContext; private CallbackImpl mCallback; private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener; private OnMediaKeyListenerImpl mOnMediaKeyListener; // TODO: Remove mLegacyCallback once Bluetooth app stop calling setCallback() method. @GuardedBy("mLock") private Callback mLegacyCallback; /** * @hide Loading Loading @@ -752,18 +763,71 @@ public final class MediaSessionManager { * if the callback should be invoked on the calling thread's looper. * @hide */ // TODO: Remove this method once Bluetooth app stop calling it. public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { synchronized (mLock) { try { if (mLegacyCallback != null) { unregisterCallback(mLegacyCallback); } mLegacyCallback = callback; if (callback != null) { registerCallback(callback, handler); } } } /** * Register a {@link Callback}. * * @param callback A {@link Callback}. * @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 */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { if (callback == null) { mCallback = null; mService.setCallback(null); } else { throw new NullPointerException("callback shouldn't be null"); } synchronized (mLock) { try { if (handler == null) { handler = new Handler(); } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); mCallbacks.put(callback, handler); if (mCurMediaButtonSession != null) { handler.post(() -> callback.onAddressedPlayerChanged(mCurMediaButtonSession)); } else if (mCurMediaButtonReceiver != null) { handler.post(() -> callback.onAddressedPlayerChanged(mCurMediaButtonReceiver)); } if (mCallbacks.size() == 1) { mService.registerCallback(mCbStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); } } } /** * Unregister a {@link Callback}. * * @param callback A {@link Callback}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull Callback callback) { if (callback == null) { throw new NullPointerException("callback shouldn't be null"); } synchronized (mLock) { try { mCallbacks.remove(callback); if (mCallbacks.size() == 0) { mService.unregisterCallback(mCbStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); Loading Loading @@ -835,6 +899,7 @@ public final class MediaSessionManager { * receive media key events. * @hide */ @SystemApi public static abstract class Callback { /** * Called when a media key event is dispatched to the media session Loading @@ -861,7 +926,7 @@ public final class MediaSessionManager { /** * 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. * {@link #registerCallback} if the addressed player exists. * * @param sessionToken The media session's token. */ Loading @@ -870,7 +935,7 @@ public final class MediaSessionManager { /** * 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. * {@link #registerCallback} if the addressed player exists. * * @param mediaButtonReceiver The media button receiver. */ Loading Loading @@ -1076,56 +1141,52 @@ 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 CallbackStub extends ICallback.Stub { @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); synchronized (mLock) { for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post( () -> e.getKey().onMediaKeyEventDispatched(event, sessionToken)); } } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); synchronized (mLock) { for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post( () -> e.getKey().onMediaKeyEventDispatched(event, mediaButtonReceiver)); } } }); } @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); synchronized (mLock) { mCurMediaButtonSession = sessionToken; mCurMediaButtonReceiver = null; for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post(() -> e.getKey().onAddressedPlayerChanged(sessionToken)); } } }); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); synchronized (mLock) { mCurMediaButtonSession = null; mCurMediaButtonReceiver = mediaButtonReceiver; for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post(() -> e.getKey().onAddressedPlayerChanged( mediaButtonReceiver)); } } }); } } } services/core/java/com/android/server/media/MediaSessionService.java +112 −60 Original line number Diff line number Diff line Loading @@ -92,6 +92,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; /** Loading Loading @@ -748,6 +749,8 @@ public class MediaSessionService extends SystemService implements Monitor { private final int mFullUserId; private final MediaSessionStack mPriorityStack; private final HashMap<IBinder, CallbackRecord> mCallbacks = new HashMap<>(); private PendingIntent mLastMediaButtonReceiver; private ComponentName mRestoredMediaButtonReceiver; private int mRestoredMediaButtonReceiverComponentType; Loading @@ -761,7 +764,6 @@ public class MediaSessionService extends SystemService implements Monitor { private IOnMediaKeyListener mOnMediaKeyListener; private int mOnMediaKeyListenerUid; private ICallback mCallback; FullUserRecord(int fullUserId) { mFullUserId = fullUserId; Loading Loading @@ -793,6 +795,24 @@ public class MediaSessionService extends SystemService implements Monitor { } } public void registerCallbackLocked(ICallback callback, int uid) { IBinder cbBinder = callback.asBinder(); CallbackRecord cr = new CallbackRecord(callback, uid); mCallbacks.put(cbBinder, cr); try { cbBinder.linkToDeath(cr, 0); } catch (RemoteException e) { Log.w(TAG, "Failed to register callback", e); mCallbacks.remove(cbBinder); } } public void unregisterCallbackLocked(ICallback callback) { IBinder cbBinder = callback.asBinder(); CallbackRecord cr = mCallbacks.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. Loading @@ -811,7 +831,10 @@ 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 + "Callbacks: registered " + mCallbacks.size() + " callback(s)"); for (CallbackRecord cr : mCallbacks.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: " Loading Loading @@ -871,21 +894,18 @@ public class MediaSessionService extends SystemService implements Monitor { mFullUserId); } private void pushAddressedPlayerChangedLocked() { if (mCallback == null) { return; } private void pushAddressedPlayerChangedLocked(ICallback callback) { try { MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { mCallback.onAddressedPlayerChangedToMediaSession( callback.onAddressedPlayerChangedToMediaSession( mediaButtonSession.getSessionToken()); } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { mCallback.onAddressedPlayerChangedToMediaButtonReceiver( callback.onAddressedPlayerChangedToMediaButtonReceiver( mCurrentFullUserRecord.mLastMediaButtonReceiver .getIntent().getComponent()); } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { mCallback.onAddressedPlayerChangedToMediaButtonReceiver( callback.onAddressedPlayerChangedToMediaButtonReceiver( mCurrentFullUserRecord.mRestoredMediaButtonReceiver); } } catch (RemoteException e) { Loading @@ -893,6 +913,12 @@ public class MediaSessionService extends SystemService implements Monitor { } } private void pushAddressedPlayerChangedLocked() { for (CallbackRecord cr : mCallbacks.values()) { pushAddressedPlayerChangedLocked(cr.callback); } } private MediaSessionRecord getMediaButtonSessionLocked() { return isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); Loading Loading @@ -926,6 +952,23 @@ public class MediaSessionService extends SystemService implements Monitor { // Pick legacy behavior for BroadcastReceiver or unknown. return COMPONENT_TYPE_BROADCAST; } final class CallbackRecord implements IBinder.DeathRecipient { public final ICallback callback; public final int uid; CallbackRecord(ICallback callback, int uid) { this.callback = callback; this.uid = uid; } @Override public void binderDied() { synchronized (mLock) { mCallbacks.remove(callback.asBinder()); } } } } final class SessionsListenerRecord implements IBinder.DeathRecipient { Loading Loading @@ -1305,44 +1348,53 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override public void setCallback(ICallback callback) { public void registerCallback(final ICallback 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 Callback"); } 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) { return; user.registerCallbackLocked(callback, uid); Log.d(TAG, "The callback (" + callback.asBinder() + ") is registered by " + getCallingPackageName(uid)); } try { user.mCallback.asBinder().linkToDeath( new IBinder.DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { user.mCallback = null; } finally { Binder.restoreCallingIdentity(token); } } }, 0); user.pushAddressedPlayerChangedLocked(); } catch (RemoteException e) { Log.w(TAG, "Failed to set callback", e); user.mCallback = null; @Override public void unregisterCallback(final ICallback 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 Callback"); } 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; } user.unregisterCallbackLocked(callback); Log.d(TAG, "The callback (" + callback.asBinder() + ") is unregistered by " + getCallingPackageName(uid)); } } finally { Binder.restoreCallingIdentity(token); Loading Loading @@ -1771,6 +1823,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. Loading @@ -1781,8 +1834,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); } Loading @@ -1808,13 +1861,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. Loading @@ -1823,11 +1870,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; } Loading @@ -1845,7 +1896,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; Loading Loading @@ -1950,14 +2001,15 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaSession( keyEvent, session.getSessionToken()); } } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } } } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { if (needWakeLock) { Loading @@ -1980,12 +2032,12 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, componentName); } } Loading Loading @@ -2018,9 +2070,9 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, receiver); } } Loading Loading
api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -3693,8 +3693,18 @@ package android.media.audiopolicy { package android.media.session { public final class MediaSessionManager { method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull android.media.session.MediaSessionManager.Callback, @Nullable android.os.Handler); 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); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull android.media.session.MediaSessionManager.Callback); } public abstract static class MediaSessionManager.Callback { ctor public MediaSessionManager.Callback(); method public abstract void onAddressedPlayerChanged(android.media.session.MediaSession.Token); method public abstract void onAddressedPlayerChanged(android.content.ComponentName); method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token); method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName); } public static interface MediaSessionManager.OnMediaKeyListener { Loading
media/java/android/media/session/ISessionManager.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,8 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(in ICallback callback); void registerCallback(in ICallback callback); void unregisterCallback(in ICallback callback); void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); void setOnMediaKeyListener(in IOnMediaKeyListener listener); Loading
media/java/android/media/session/MediaSessionManager.java +103 −42 Original line number Diff line number Diff line Loading @@ -46,7 +46,9 @@ 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; /** Loading @@ -72,6 +74,7 @@ public final class MediaSessionManager { * @hide */ public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final ISessionManager mService; private final Object mLock = new Object(); @GuardedBy("mLock") Loading @@ -80,13 +83,21 @@ public final class MediaSessionManager { @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; @GuardedBy("mLock") private final CallbackStub mCbStub = new CallbackStub(); @GuardedBy("mLock") private final Map<Callback, Handler> mCallbacks = new HashMap<>(); @GuardedBy("mLock") private MediaSession.Token mCurMediaButtonSession; @GuardedBy("mLock") private ComponentName mCurMediaButtonReceiver; private Context mContext; private CallbackImpl mCallback; private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener; private OnMediaKeyListenerImpl mOnMediaKeyListener; // TODO: Remove mLegacyCallback once Bluetooth app stop calling setCallback() method. @GuardedBy("mLock") private Callback mLegacyCallback; /** * @hide Loading Loading @@ -752,18 +763,71 @@ public final class MediaSessionManager { * if the callback should be invoked on the calling thread's looper. * @hide */ // TODO: Remove this method once Bluetooth app stop calling it. public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { synchronized (mLock) { try { if (mLegacyCallback != null) { unregisterCallback(mLegacyCallback); } mLegacyCallback = callback; if (callback != null) { registerCallback(callback, handler); } } } /** * Register a {@link Callback}. * * @param callback A {@link Callback}. * @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 */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { if (callback == null) { mCallback = null; mService.setCallback(null); } else { throw new NullPointerException("callback shouldn't be null"); } synchronized (mLock) { try { if (handler == null) { handler = new Handler(); } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); mCallbacks.put(callback, handler); if (mCurMediaButtonSession != null) { handler.post(() -> callback.onAddressedPlayerChanged(mCurMediaButtonSession)); } else if (mCurMediaButtonReceiver != null) { handler.post(() -> callback.onAddressedPlayerChanged(mCurMediaButtonReceiver)); } if (mCallbacks.size() == 1) { mService.registerCallback(mCbStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); } } } /** * Unregister a {@link Callback}. * * @param callback A {@link Callback}. * @hide */ @SystemApi @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull Callback callback) { if (callback == null) { throw new NullPointerException("callback shouldn't be null"); } synchronized (mLock) { try { mCallbacks.remove(callback); if (mCallbacks.size() == 0) { mService.unregisterCallback(mCbStub); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); Loading Loading @@ -835,6 +899,7 @@ public final class MediaSessionManager { * receive media key events. * @hide */ @SystemApi public static abstract class Callback { /** * Called when a media key event is dispatched to the media session Loading @@ -861,7 +926,7 @@ public final class MediaSessionManager { /** * 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. * {@link #registerCallback} if the addressed player exists. * * @param sessionToken The media session's token. */ Loading @@ -870,7 +935,7 @@ public final class MediaSessionManager { /** * 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. * {@link #registerCallback} if the addressed player exists. * * @param mediaButtonReceiver The media button receiver. */ Loading Loading @@ -1076,56 +1141,52 @@ 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 CallbackStub extends ICallback.Stub { @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); synchronized (mLock) { for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post( () -> e.getKey().onMediaKeyEventDispatched(event, sessionToken)); } } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); synchronized (mLock) { for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post( () -> e.getKey().onMediaKeyEventDispatched(event, mediaButtonReceiver)); } } }); } @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); synchronized (mLock) { mCurMediaButtonSession = sessionToken; mCurMediaButtonReceiver = null; for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post(() -> e.getKey().onAddressedPlayerChanged(sessionToken)); } } }); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); synchronized (mLock) { mCurMediaButtonSession = null; mCurMediaButtonReceiver = mediaButtonReceiver; for (Map.Entry<Callback, Handler> e : mCallbacks.entrySet()) { e.getValue().post(() -> e.getKey().onAddressedPlayerChanged( mediaButtonReceiver)); } } }); } } }
services/core/java/com/android/server/media/MediaSessionService.java +112 −60 Original line number Diff line number Diff line Loading @@ -92,6 +92,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; /** Loading Loading @@ -748,6 +749,8 @@ public class MediaSessionService extends SystemService implements Monitor { private final int mFullUserId; private final MediaSessionStack mPriorityStack; private final HashMap<IBinder, CallbackRecord> mCallbacks = new HashMap<>(); private PendingIntent mLastMediaButtonReceiver; private ComponentName mRestoredMediaButtonReceiver; private int mRestoredMediaButtonReceiverComponentType; Loading @@ -761,7 +764,6 @@ public class MediaSessionService extends SystemService implements Monitor { private IOnMediaKeyListener mOnMediaKeyListener; private int mOnMediaKeyListenerUid; private ICallback mCallback; FullUserRecord(int fullUserId) { mFullUserId = fullUserId; Loading Loading @@ -793,6 +795,24 @@ public class MediaSessionService extends SystemService implements Monitor { } } public void registerCallbackLocked(ICallback callback, int uid) { IBinder cbBinder = callback.asBinder(); CallbackRecord cr = new CallbackRecord(callback, uid); mCallbacks.put(cbBinder, cr); try { cbBinder.linkToDeath(cr, 0); } catch (RemoteException e) { Log.w(TAG, "Failed to register callback", e); mCallbacks.remove(cbBinder); } } public void unregisterCallbackLocked(ICallback callback) { IBinder cbBinder = callback.asBinder(); CallbackRecord cr = mCallbacks.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. Loading @@ -811,7 +831,10 @@ 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 + "Callbacks: registered " + mCallbacks.size() + " callback(s)"); for (CallbackRecord cr : mCallbacks.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: " Loading Loading @@ -871,21 +894,18 @@ public class MediaSessionService extends SystemService implements Monitor { mFullUserId); } private void pushAddressedPlayerChangedLocked() { if (mCallback == null) { return; } private void pushAddressedPlayerChangedLocked(ICallback callback) { try { MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { mCallback.onAddressedPlayerChangedToMediaSession( callback.onAddressedPlayerChangedToMediaSession( mediaButtonSession.getSessionToken()); } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { mCallback.onAddressedPlayerChangedToMediaButtonReceiver( callback.onAddressedPlayerChangedToMediaButtonReceiver( mCurrentFullUserRecord.mLastMediaButtonReceiver .getIntent().getComponent()); } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { mCallback.onAddressedPlayerChangedToMediaButtonReceiver( callback.onAddressedPlayerChangedToMediaButtonReceiver( mCurrentFullUserRecord.mRestoredMediaButtonReceiver); } } catch (RemoteException e) { Loading @@ -893,6 +913,12 @@ public class MediaSessionService extends SystemService implements Monitor { } } private void pushAddressedPlayerChangedLocked() { for (CallbackRecord cr : mCallbacks.values()) { pushAddressedPlayerChangedLocked(cr.callback); } } private MediaSessionRecord getMediaButtonSessionLocked() { return isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); Loading Loading @@ -926,6 +952,23 @@ public class MediaSessionService extends SystemService implements Monitor { // Pick legacy behavior for BroadcastReceiver or unknown. return COMPONENT_TYPE_BROADCAST; } final class CallbackRecord implements IBinder.DeathRecipient { public final ICallback callback; public final int uid; CallbackRecord(ICallback callback, int uid) { this.callback = callback; this.uid = uid; } @Override public void binderDied() { synchronized (mLock) { mCallbacks.remove(callback.asBinder()); } } } } final class SessionsListenerRecord implements IBinder.DeathRecipient { Loading Loading @@ -1305,44 +1348,53 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override public void setCallback(ICallback callback) { public void registerCallback(final ICallback 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 Callback"); } 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) { return; user.registerCallbackLocked(callback, uid); Log.d(TAG, "The callback (" + callback.asBinder() + ") is registered by " + getCallingPackageName(uid)); } try { user.mCallback.asBinder().linkToDeath( new IBinder.DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { user.mCallback = null; } finally { Binder.restoreCallingIdentity(token); } } }, 0); user.pushAddressedPlayerChangedLocked(); } catch (RemoteException e) { Log.w(TAG, "Failed to set callback", e); user.mCallback = null; @Override public void unregisterCallback(final ICallback 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 Callback"); } 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; } user.unregisterCallbackLocked(callback); Log.d(TAG, "The callback (" + callback.asBinder() + ") is unregistered by " + getCallingPackageName(uid)); } } finally { Binder.restoreCallingIdentity(token); Loading Loading @@ -1771,6 +1823,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. Loading @@ -1781,8 +1834,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); } Loading @@ -1808,13 +1861,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. Loading @@ -1823,11 +1870,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; } Loading @@ -1845,7 +1896,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; Loading Loading @@ -1950,14 +2001,15 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaSession( keyEvent, session.getSessionToken()); } } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } } } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { if (needWakeLock) { Loading @@ -1980,12 +2032,12 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, componentName); } } Loading Loading @@ -2018,9 +2070,9 @@ 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( for (FullUserRecord.CallbackRecord cr : mCurrentFullUserRecord.mCallbacks.values()) { cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, receiver); } } Loading