Loading Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -404,6 +404,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/projection/IMediaProjectionManager.aidl \ media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \ media/java/android/media/session/IActiveSessionsListener.aidl \ media/java/android/media/session/ICallback.aidl \ media/java/android/media/session/ISessionController.aidl \ media/java/android/media/session/ISessionControllerCallback.aidl \ media/java/android/media/session/ISession.aidl \ Loading media/java/android/media/session/ICallback.aidl 0 → 100644 +34 −0 Original line number Diff line number Diff line /* Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.session; import android.app.PendingIntent; import android.content.ComponentName; import android.media.session.MediaSession; import android.view.KeyEvent; /** * @hide */ oneway interface ICallback { void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event, in MediaSession.Token sessionToken); void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event, in ComponentName mediaButtonReceiver); void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken); void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver); } media/java/android/media/session/ISessionManager.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media.session; import android.content.ComponentName; import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; Loading @@ -41,4 +42,6 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(in ICallback callback); } media/java/android/media/session/MediaSessionManager.java +135 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ public final class MediaSessionManager { private Context mContext; private CallbackImpl mCallback; /** * @hide */ Loading Loading @@ -312,6 +314,36 @@ public final class MediaSessionManager { return false; } /** * Set a {@link Callback}. * * <p>System can only have a single callback, and the callback can only be set by * Bluetooth service process. * * @param callback A {@link Callback}. {@code null} to reset. * @param handler The handler on which the callback should be invoked, or {@code null} * if the callback should be invoked on the calling thread's looper. * @hide */ public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { synchronized (mLock) { try { if (callback == null) { mCallback = null; mService.setCallback(null); } else { if (handler == null) { handler = new Handler(); } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); } } } /** * Listens for changes to the list of active sessions. This can be added * using {@link #addOnActiveSessionsChangedListener}. Loading @@ -320,6 +352,56 @@ public final class MediaSessionManager { public void onActiveSessionsChanged(@Nullable List<MediaController> controllers); } /** * Callbacks for the media session service. * * <p>Called when a media key event is dispatched or the addressed player is changed. * The addressed player is either the media session or the media button receiver that will * receive media key events. * @hide */ public static abstract class Callback { /** * Called when a media key event is dispatched to the media session * through the media session service. * * @param event Dispatched media key event. * @param sessionToken The media session's token. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, MediaSession.Token sessionToken); /** * Called when a media key event is dispatched to the media button receiver * through the media session service. * <p>MediaSessionService may broadcast key events to the media button receiver * when reviving playback after the media session is released. * * @param event Dispatched media key event. * @param mediaButtonReceiver The media button receiver. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, ComponentName mediaButtonReceiver); /** * Called when the addressed player is changed to a media session. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param sessionToken The media session's token. */ public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken); /** * Called when the addressed player is changed to the media button receiver. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param mediaButtonReceiver The media button receiver. */ public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver); } private static final class SessionsChangedWrapper { private Context mContext; private OnActiveSessionsChangedListener mListener; Loading Loading @@ -360,4 +442,57 @@ public final class MediaSessionManager { mHandler = null; } } private static final class CallbackImpl extends ICallback.Stub { private final Callback mCallback; private final Handler mHandler; public CallbackImpl(@NonNull Callback callback, @NonNull Handler handler) { mCallback = callback; mHandler = handler; } @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); } }); } @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); } }); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); } }); } } } services/core/java/com/android/server/media/MediaSessionService.java +131 −20 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.media.session.ISessionManager; Loading Loading @@ -101,6 +102,7 @@ public class MediaSessionService extends SystemService implements Monitor { private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; private ICallback mCallback; // List of user IDs running in the foreground. // Multiple users can be in the foreground if the work profile is on. Loading Loading @@ -485,6 +487,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (size > 0 && records.get(0).isPlaybackActive(false)) { rememberMediaButtonReceiverLocked(records.get(0)); } pushAddressedPlayerChangedLocked(); ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); for (int i = 0; i < size; i++) { tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); Loading Loading @@ -516,6 +519,52 @@ public class MediaSessionService extends SystemService implements Monitor { } } private MediaSessionRecord getMediaButtonSessionLocked() { // If we don't have a media button receiver to fall back on // include non-playing sessions for dispatching. boolean useNotPlayingSessions = true; for (int userId : mCurrentUserIdList) { UserRecord ur = mUserRecords.get(userId); if (ur.mLastMediaButtonReceiver != null || ur.mRestoredMediaButtonReceiver != null) { useNotPlayingSessions = false; break; } } return mPriorityStack.getDefaultMediaButtonSession( mCurrentUserIdList, useNotPlayingSessions); } private void pushAddressedPlayerChangedLocked() { if (mCallback == null) { return; } try { MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { mCallback.onAddressedPlayerChangedToMediaSession( new MediaSession.Token(mediaButtonSession.getControllerBinder())); } else { for (int userId : mCurrentUserIdList) { UserRecord user = mUserRecords.get(userId); if (user.mLastMediaButtonReceiver == null && user.mRestoredMediaButtonReceiver == null) { continue; } ComponentName componentName = user.mLastMediaButtonReceiver != null ? user.mLastMediaButtonReceiver.getIntent().getComponent() : user.mRestoredMediaButtonReceiver; mCallback.onAddressedPlayerChangedToMediaButtonReceiver(componentName); return; } } } catch (RemoteException e) { Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); } } // Remember media button receiver and keep it in the persistent storage. // This should be called whenever there's no media session to receive media button event. private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { PendingIntent receiver = record.getMediaButtonReceiver(); UserRecord user = mUserRecords.get(record.getUserId()); Loading @@ -530,6 +579,14 @@ public class MediaSessionService extends SystemService implements Monitor { } } private String getCallingPackageName(int uid) { String[] packages = getContext().getPackageManager().getPackagesForUid(uid); if (packages != null && packages.length > 0) { return packages[0]; } return ""; } /** * Information about a particular user. The contents of this object is * guarded by mLock. Loading Loading @@ -792,12 +849,48 @@ public class MediaSessionService extends SystemService implements Monitor { Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions=" + useNotPlayingSessions); } MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession( mCurrentUserIdList, useNotPlayingSessions); if (isVoiceKey(keyEvent.getKeyCode())) { handleVoiceKeyEventLocked(keyEvent, needWakeLock, session); handleVoiceKeyEventLocked(keyEvent, needWakeLock); } else { dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); dispatchMediaKeyEventLocked(keyEvent, needWakeLock); } } } finally { Binder.restoreCallingIdentity(token); } } @Override public void setCallback(ICallback callback) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { if (uid != Process.BLUETOOTH_UID) { throw new SecurityException("Only Bluetooth service processes can set" + " Callback"); } synchronized (mLock) { Log.d(TAG, "Callback + " + mCallback + " is set by " + getCallingPackageName(uid)); mCallback = callback; if (mCallback == null) { return; } try { mCallback.asBinder().linkToDeath( new IBinder.DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { mCallback = null; } } }, 0); pushAddressedPlayerChangedLocked(); } catch (RemoteException e) { Log.w(TAG, "Failed to set callback", e); mCallback = null; } } } finally { Loading @@ -805,6 +898,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } @Override public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) { final long token = Binder.clearCallingIdentity(); Loading Loading @@ -932,13 +1026,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session) { if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { // If the phone app has priority just give it the event dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); return; } private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { int action = keyEvent.getAction(); boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; if (action == KeyEvent.ACTION_DOWN) { Loading @@ -955,15 +1043,15 @@ public class MediaSessionService extends SystemService implements Monitor { if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { // Resend the down then send this event through KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); dispatchMediaKeyEventLocked(downEvent, needWakeLock, session); dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); dispatchMediaKeyEventLocked(downEvent, needWakeLock); dispatchMediaKeyEventLocked(keyEvent, needWakeLock); } } } } private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session) { private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { MediaSessionRecord session = getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to " + session); Loading @@ -977,6 +1065,14 @@ public class MediaSessionService extends SystemService implements Monitor { needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver, Process.SYSTEM_UID, getContext().getPackageName()); if (mCallback != null) { try { mCallback.onMediaKeyEventDispatchedToMediaSession(keyEvent, new MediaSession.Token(session.getControllerBinder())); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } } } else { // Launch the last PendingIntent we had with priority for (int userId : mCurrentUserIdList) { Loading @@ -993,26 +1089,41 @@ public class MediaSessionService extends SystemService implements Monitor { mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); try { if (user.mLastMediaButtonReceiver != null) { PendingIntent receiver = user.mLastMediaButtonReceiver; if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to the last known pendingIntent " + user.mLastMediaButtonReceiver); + " to the last known pendingIntent " + receiver); } user.mLastMediaButtonReceiver.send(getContext(), receiver.send(getContext(), needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mediaButtonIntent, mKeyEventReceiver, mHandler); if (mCallback != null) { ComponentName componentName = user.mLastMediaButtonReceiver.getIntent().getComponent(); if (componentName != null) { mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, componentName); } } } else { ComponentName receiver = user.mRestoredMediaButtonReceiver; if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to the restored intent " + user.mRestoredMediaButtonReceiver); + receiver); } mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver); mediaButtonIntent.setComponent(receiver); getContext().sendBroadcastAsUser(mediaButtonIntent, UserHandle.of(userId)); if (mCallback != null) { mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, receiver); } } } catch (CanceledException e) { Log.i(TAG, "Error sending key event to media button receiver " + user.mLastMediaButtonReceiver, e); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } return; } Loading Loading
Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -404,6 +404,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/projection/IMediaProjectionManager.aidl \ media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \ media/java/android/media/session/IActiveSessionsListener.aidl \ media/java/android/media/session/ICallback.aidl \ media/java/android/media/session/ISessionController.aidl \ media/java/android/media/session/ISessionControllerCallback.aidl \ media/java/android/media/session/ISession.aidl \ Loading
media/java/android/media/session/ICallback.aidl 0 → 100644 +34 −0 Original line number Diff line number Diff line /* Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.session; import android.app.PendingIntent; import android.content.ComponentName; import android.media.session.MediaSession; import android.view.KeyEvent; /** * @hide */ oneway interface ICallback { void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event, in MediaSession.Token sessionToken); void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event, in ComponentName mediaButtonReceiver); void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken); void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver); }
media/java/android/media/session/ISessionManager.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media.session; import android.content.ComponentName; import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; Loading @@ -41,4 +42,6 @@ interface ISessionManager { // For PhoneWindowManager to precheck media keys boolean isGlobalPriorityActive(); void setCallback(in ICallback callback); }
media/java/android/media/session/MediaSessionManager.java +135 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ public final class MediaSessionManager { private Context mContext; private CallbackImpl mCallback; /** * @hide */ Loading Loading @@ -312,6 +314,36 @@ public final class MediaSessionManager { return false; } /** * Set a {@link Callback}. * * <p>System can only have a single callback, and the callback can only be set by * Bluetooth service process. * * @param callback A {@link Callback}. {@code null} to reset. * @param handler The handler on which the callback should be invoked, or {@code null} * if the callback should be invoked on the calling thread's looper. * @hide */ public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { synchronized (mLock) { try { if (callback == null) { mCallback = null; mService.setCallback(null); } else { if (handler == null) { handler = new Handler(); } mCallback = new CallbackImpl(callback, handler); mService.setCallback(mCallback); } } catch (RemoteException e) { Log.e(TAG, "Failed to set media key callback", e); } } } /** * Listens for changes to the list of active sessions. This can be added * using {@link #addOnActiveSessionsChangedListener}. Loading @@ -320,6 +352,56 @@ public final class MediaSessionManager { public void onActiveSessionsChanged(@Nullable List<MediaController> controllers); } /** * Callbacks for the media session service. * * <p>Called when a media key event is dispatched or the addressed player is changed. * The addressed player is either the media session or the media button receiver that will * receive media key events. * @hide */ public static abstract class Callback { /** * Called when a media key event is dispatched to the media session * through the media session service. * * @param event Dispatched media key event. * @param sessionToken The media session's token. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, MediaSession.Token sessionToken); /** * Called when a media key event is dispatched to the media button receiver * through the media session service. * <p>MediaSessionService may broadcast key events to the media button receiver * when reviving playback after the media session is released. * * @param event Dispatched media key event. * @param mediaButtonReceiver The media button receiver. */ public abstract void onMediaKeyEventDispatched(KeyEvent event, ComponentName mediaButtonReceiver); /** * Called when the addressed player is changed to a media session. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param sessionToken The media session's token. */ public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken); /** * Called when the addressed player is changed to the media button receiver. * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after * {@link #setCallback} if the addressed player exists. * * @param mediaButtonReceiver The media button receiver. */ public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver); } private static final class SessionsChangedWrapper { private Context mContext; private OnActiveSessionsChangedListener mListener; Loading Loading @@ -360,4 +442,57 @@ public final class MediaSessionManager { mHandler = null; } } private static final class CallbackImpl extends ICallback.Stub { private final Callback mCallback; private final Handler mHandler; public CallbackImpl(@NonNull Callback callback, @NonNull Handler handler) { mCallback = callback; mHandler = handler; } @Override public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event, MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, sessionToken); } }); } @Override public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event, ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onMediaKeyEventDispatched(event, mediaButtonReceiver); } }); } @Override public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(sessionToken); } }); } @Override public void onAddressedPlayerChangedToMediaButtonReceiver( ComponentName mediaButtonReceiver) { mHandler.post(new Runnable() { @Override public void run() { mCallback.onAddressedPlayerChanged(mediaButtonReceiver); } }); } } }
services/core/java/com/android/server/media/MediaSessionService.java +131 −20 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.media.session.ISessionManager; Loading Loading @@ -101,6 +102,7 @@ public class MediaSessionService extends SystemService implements Monitor { private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; private ICallback mCallback; // List of user IDs running in the foreground. // Multiple users can be in the foreground if the work profile is on. Loading Loading @@ -485,6 +487,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (size > 0 && records.get(0).isPlaybackActive(false)) { rememberMediaButtonReceiverLocked(records.get(0)); } pushAddressedPlayerChangedLocked(); ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); for (int i = 0; i < size; i++) { tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); Loading Loading @@ -516,6 +519,52 @@ public class MediaSessionService extends SystemService implements Monitor { } } private MediaSessionRecord getMediaButtonSessionLocked() { // If we don't have a media button receiver to fall back on // include non-playing sessions for dispatching. boolean useNotPlayingSessions = true; for (int userId : mCurrentUserIdList) { UserRecord ur = mUserRecords.get(userId); if (ur.mLastMediaButtonReceiver != null || ur.mRestoredMediaButtonReceiver != null) { useNotPlayingSessions = false; break; } } return mPriorityStack.getDefaultMediaButtonSession( mCurrentUserIdList, useNotPlayingSessions); } private void pushAddressedPlayerChangedLocked() { if (mCallback == null) { return; } try { MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { mCallback.onAddressedPlayerChangedToMediaSession( new MediaSession.Token(mediaButtonSession.getControllerBinder())); } else { for (int userId : mCurrentUserIdList) { UserRecord user = mUserRecords.get(userId); if (user.mLastMediaButtonReceiver == null && user.mRestoredMediaButtonReceiver == null) { continue; } ComponentName componentName = user.mLastMediaButtonReceiver != null ? user.mLastMediaButtonReceiver.getIntent().getComponent() : user.mRestoredMediaButtonReceiver; mCallback.onAddressedPlayerChangedToMediaButtonReceiver(componentName); return; } } } catch (RemoteException e) { Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); } } // Remember media button receiver and keep it in the persistent storage. // This should be called whenever there's no media session to receive media button event. private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { PendingIntent receiver = record.getMediaButtonReceiver(); UserRecord user = mUserRecords.get(record.getUserId()); Loading @@ -530,6 +579,14 @@ public class MediaSessionService extends SystemService implements Monitor { } } private String getCallingPackageName(int uid) { String[] packages = getContext().getPackageManager().getPackagesForUid(uid); if (packages != null && packages.length > 0) { return packages[0]; } return ""; } /** * Information about a particular user. The contents of this object is * guarded by mLock. Loading Loading @@ -792,12 +849,48 @@ public class MediaSessionService extends SystemService implements Monitor { Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions=" + useNotPlayingSessions); } MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession( mCurrentUserIdList, useNotPlayingSessions); if (isVoiceKey(keyEvent.getKeyCode())) { handleVoiceKeyEventLocked(keyEvent, needWakeLock, session); handleVoiceKeyEventLocked(keyEvent, needWakeLock); } else { dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); dispatchMediaKeyEventLocked(keyEvent, needWakeLock); } } } finally { Binder.restoreCallingIdentity(token); } } @Override public void setCallback(ICallback callback) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { if (uid != Process.BLUETOOTH_UID) { throw new SecurityException("Only Bluetooth service processes can set" + " Callback"); } synchronized (mLock) { Log.d(TAG, "Callback + " + mCallback + " is set by " + getCallingPackageName(uid)); mCallback = callback; if (mCallback == null) { return; } try { mCallback.asBinder().linkToDeath( new IBinder.DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { mCallback = null; } } }, 0); pushAddressedPlayerChangedLocked(); } catch (RemoteException e) { Log.w(TAG, "Failed to set callback", e); mCallback = null; } } } finally { Loading @@ -805,6 +898,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } @Override public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) { final long token = Binder.clearCallingIdentity(); Loading Loading @@ -932,13 +1026,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session) { if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { // If the phone app has priority just give it the event dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); return; } private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { int action = keyEvent.getAction(); boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; if (action == KeyEvent.ACTION_DOWN) { Loading @@ -955,15 +1043,15 @@ public class MediaSessionService extends SystemService implements Monitor { if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { // Resend the down then send this event through KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); dispatchMediaKeyEventLocked(downEvent, needWakeLock, session); dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session); dispatchMediaKeyEventLocked(downEvent, needWakeLock); dispatchMediaKeyEventLocked(keyEvent, needWakeLock); } } } } private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session) { private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { MediaSessionRecord session = getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to " + session); Loading @@ -977,6 +1065,14 @@ public class MediaSessionService extends SystemService implements Monitor { needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver, Process.SYSTEM_UID, getContext().getPackageName()); if (mCallback != null) { try { mCallback.onMediaKeyEventDispatchedToMediaSession(keyEvent, new MediaSession.Token(session.getControllerBinder())); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } } } else { // Launch the last PendingIntent we had with priority for (int userId : mCurrentUserIdList) { Loading @@ -993,26 +1089,41 @@ public class MediaSessionService extends SystemService implements Monitor { mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); try { if (user.mLastMediaButtonReceiver != null) { PendingIntent receiver = user.mLastMediaButtonReceiver; if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to the last known pendingIntent " + user.mLastMediaButtonReceiver); + " to the last known pendingIntent " + receiver); } user.mLastMediaButtonReceiver.send(getContext(), receiver.send(getContext(), needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mediaButtonIntent, mKeyEventReceiver, mHandler); if (mCallback != null) { ComponentName componentName = user.mLastMediaButtonReceiver.getIntent().getComponent(); if (componentName != null) { mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, componentName); } } } else { ComponentName receiver = user.mRestoredMediaButtonReceiver; if (DEBUG_MEDIA_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to the restored intent " + user.mRestoredMediaButtonReceiver); + receiver); } mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver); mediaButtonIntent.setComponent(receiver); getContext().sendBroadcastAsUser(mediaButtonIntent, UserHandle.of(userId)); if (mCallback != null) { mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( keyEvent, receiver); } } } catch (CanceledException e) { Log.i(TAG, "Error sending key event to media button receiver " + user.mLastMediaButtonReceiver, e); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } return; } Loading