diff --git a/api/system-current.txt b/api/system-current.txt index 00676c815f357..d3d9c220f12ad 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3609,7 +3609,7 @@ package android.media.session { public abstract static class ControllerLink.ControllerStub { ctor public ControllerLink.ControllerStub(); - method public void adjustVolume(@NonNull String, @NonNull String, @NonNull android.media.session.ControllerCallbackLink, boolean, int, int); + method public void adjustVolume(@NonNull String, @NonNull String, @NonNull android.media.session.ControllerCallbackLink, int, int); method public void fastForward(@NonNull String, @NonNull android.media.session.ControllerCallbackLink); method @Nullable public android.os.Bundle getExtras(); method public long getFlags(); @@ -3639,7 +3639,7 @@ package android.media.session { method public void seekTo(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, long); method public void sendCommand(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.ResultReceiver); method public void sendCustomAction(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle); - method public boolean sendMediaButton(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, boolean, @NonNull android.view.KeyEvent); + method public boolean sendMediaButton(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.view.KeyEvent); method public void setVolumeTo(@NonNull String, @NonNull String, @NonNull android.media.session.ControllerCallbackLink, int, int); method public void skipToQueueItem(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, long); method public void stop(@NonNull String, @NonNull android.media.session.ControllerCallbackLink); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index d8ee643e27f01..69efb2b10af06 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -41,7 +41,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Color; @@ -1885,7 +1884,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If we have a session send it the volume command, otherwise // use the suggested stream. if (mMediaController != null) { - mMediaController.dispatchVolumeButtonEventAsSystemService(event); + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService( + mMediaController.getSessionToken(), event); } else { getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event, mVolumeControlStreamType); @@ -1906,7 +1906,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { if (mMediaController != null) { - if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) { + if (getMediaSessionManager().dispatchMediaKeyEventAsSystemService( + mMediaController.getSessionToken(), event)) { return true; } } @@ -1977,7 +1978,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If we have a session send it the volume command, otherwise // use the suggested stream. if (mMediaController != null) { - mMediaController.dispatchVolumeButtonEventAsSystemService(event); + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService( + mMediaController.getSessionToken(), event); } else { getMediaSessionManager().dispatchVolumeKeyEventAsSystemService( event, mVolumeControlStreamType); @@ -2007,7 +2009,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { if (mMediaController != null) { - if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) { + if (getMediaSessionManager().dispatchMediaKeyEventAsSystemService( + mMediaController.getSessionToken(), event)) { return true; } } diff --git a/media/apex/java/android/media/session/ControllerLink.java b/media/apex/java/android/media/session/ControllerLink.java index 937df20949f08..f60ec000f2d2d 100644 --- a/media/apex/java/android/media/session/ControllerLink.java +++ b/media/apex/java/android/media/session/ControllerLink.java @@ -34,6 +34,7 @@ import android.os.ResultReceiver; import android.view.KeyEvent; import java.util.List; +import java.util.Objects; /** * Handles incoming commands from {@link MediaController}. @@ -96,15 +97,12 @@ public final class ControllerLink implements Parcelable { * * @param packageName the package name of the controller * @param caller the {@link ControllerCallbackLink} of the controller - * @param asSystemService whether this event should be considered as from system service * @param mediaButton the media button key event */ boolean sendMediaButton(@NonNull String packageName, - @NonNull ControllerCallbackLink caller, boolean asSystemService, - @NonNull KeyEvent mediaButton) { + @NonNull ControllerCallbackLink caller, @NonNull KeyEvent mediaButton) { try { - return mISessionController.sendMediaButton(packageName, caller, asSystemService, - mediaButton); + return mISessionController.sendMediaButton(packageName, caller, mediaButton); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -202,16 +200,14 @@ public final class ControllerLink implements Parcelable { * @param packageName the package name of the controller * @param opPackageName the op package name of this request * @param caller the {@link ControllerCallbackLink} of the controller - * @param asSystemService whether this event should be considered as from system service * @param direction the direction to adjust the volume in * @param flags the flags with this volume change request */ void adjustVolume(@NonNull String packageName, @NonNull String opPackageName, - @NonNull ControllerCallbackLink caller, boolean asSystemService, int direction, + @NonNull ControllerCallbackLink caller, int direction, int flags) { try { - mISessionController.adjustVolume(packageName, opPackageName, caller, asSystemService, - direction, flags); + mISessionController.adjustVolume(packageName, opPackageName, caller, direction, flags); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -603,6 +599,23 @@ public final class ControllerLink implements Parcelable { dest.writeStrongBinder(mISessionController.asBinder()); } + @Override + public int hashCode() { + return mISessionController.asBinder().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ControllerLink)) { + return false; + } + ControllerLink other = (ControllerLink) obj; + return Objects.equals(getBinder(), other.getBinder()); + } + /** * Class for Stub implementation */ @@ -614,8 +627,7 @@ public final class ControllerLink implements Parcelable { /** Stub method for ISessionController.sendMediaButton */ public boolean sendMediaButton(@NonNull String packageName, - @NonNull ControllerCallbackLink caller, boolean asSystemService, - @NonNull KeyEvent mediaButton) { + @NonNull ControllerCallbackLink caller, @NonNull KeyEvent mediaButton) { return false; } @@ -659,8 +671,7 @@ public final class ControllerLink implements Parcelable { /** Stub method for ISessionController.adjustVolume */ public void adjustVolume(@NonNull String packageName, @NonNull String opPackageName, - @NonNull ControllerCallbackLink caller, boolean asSystemService, int direction, - int flags) { + @NonNull ControllerCallbackLink caller, int direction, int flags) { } /** Stub method for ISessionController.setVolumeTo */ @@ -801,9 +812,8 @@ public final class ControllerLink implements Parcelable { @Override public boolean sendMediaButton(String packageName, ControllerCallbackLink caller, - boolean asSystemService, KeyEvent mediaButton) { - return mControllerStub.sendMediaButton(packageName, caller, asSystemService, - mediaButton); + KeyEvent mediaButton) { + return mControllerStub.sendMediaButton(packageName, caller, mediaButton); } @Override @@ -843,10 +853,8 @@ public final class ControllerLink implements Parcelable { @Override public void adjustVolume(String packageName, String opPackageName, - ControllerCallbackLink caller, boolean asSystemService, int direction, - int flags) { - mControllerStub.adjustVolume(packageName, opPackageName, caller, asSystemService, - direction, flags); + ControllerCallbackLink caller, int direction, int flags) { + mControllerStub.adjustVolume(packageName, opPackageName, caller, direction, flags); } @Override diff --git a/media/apex/java/android/media/session/ISessionController.aidl b/media/apex/java/android/media/session/ISessionController.aidl index a3439a1a8deb6..e697c65e11c0d 100644 --- a/media/apex/java/android/media/session/ISessionController.aidl +++ b/media/apex/java/android/media/session/ISessionController.aidl @@ -39,7 +39,7 @@ interface ISessionController { void sendCommand(String packageName, in ControllerCallbackLink caller, String command, in Bundle args, in ResultReceiver cb); boolean sendMediaButton(String packageName, in ControllerCallbackLink caller, - boolean asSystemService, in KeyEvent mediaButton); + in KeyEvent mediaButton); void registerCallback(String packageName, in ControllerCallbackLink cb); void unregisterCallback(in ControllerCallbackLink cb); String getPackageName(); @@ -48,8 +48,7 @@ interface ISessionController { long getFlags(); MediaController.PlaybackInfo getVolumeAttributes(); void adjustVolume(String packageName, String opPackageName, - in ControllerCallbackLink caller, boolean asSystemService, int direction, - int flags); + in ControllerCallbackLink caller, int direction, int flags); void setVolumeTo(String packageName, String opPackageName, in ControllerCallbackLink caller, int value, int flags); diff --git a/media/apex/java/android/media/session/MediaController.java b/media/apex/java/android/media/session/MediaController.java index 79389a8b529c0..1333ab097219a 100644 --- a/media/apex/java/android/media/session/MediaController.java +++ b/media/apex/java/android/media/session/MediaController.java @@ -123,25 +123,6 @@ public final class MediaController { * @return true if the event was sent to the session, false otherwise. */ public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { - return dispatchMediaButtonEventInternal(false, keyEvent); - } - - /** - * Dispatches the media button event as system service to the session. - *

- * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the - * foreground activity didn't consume the key from the hardware devices. - * - * @param keyEvent media key event - * @return {@code true} if the event was sent to the session, {@code false} otherwise - * @hide - */ - public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { - return dispatchMediaButtonEventInternal(true, keyEvent); - } - - private boolean dispatchMediaButtonEventInternal(boolean asSystemService, - @NonNull KeyEvent keyEvent) { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } @@ -149,67 +130,13 @@ public final class MediaController { return false; } try { - return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub, - asSystemService, keyEvent); + return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub, keyEvent); } catch (RuntimeException e) { // System is dead. =( } return false; } - /** - * Dispatches the volume button event as system service to the session. - *

- * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the - * foreground activity didn't consume the key from the hardware devices. - * - * @param keyEvent volume key event - * @hide - */ - public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { - switch (keyEvent.getAction()) { - case KeyEvent.ACTION_DOWN: { - int direction = 0; - switch (keyEvent.getKeyCode()) { - case KeyEvent.KEYCODE_VOLUME_UP: - direction = AudioManager.ADJUST_RAISE; - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - direction = AudioManager.ADJUST_LOWER; - break; - case KeyEvent.KEYCODE_VOLUME_MUTE: - direction = AudioManager.ADJUST_TOGGLE_MUTE; - break; - } - try { - // Note: Need both package name and OP package name. Package name is used for - // RemoteUserInfo, and OP package name is used for AudioService's internal - // AppOpsManager usages. - mSessionBinder.adjustVolume(mContext.getPackageName(), - mContext.getOpPackageName(), mCbStub, true, direction, - AudioManager.FLAG_SHOW_UI); - } catch (RuntimeException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy", e); - } - break; - } - - case KeyEvent.ACTION_UP: { - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE - | AudioManager.FLAG_FROM_KEY; - try { - // Note: Need both package name and OP package name. Package name is used for - // RemoteUserInfo, and OP package name is used for AudioService's internal - // AppOpsManager usages. - mSessionBinder.adjustVolume(mContext.getPackageName(), - mContext.getOpPackageName(), mCbStub, true, 0, flags); - } catch (RuntimeException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy", e); - } - } - } - } - /** * Get the current playback state for this session. * @@ -394,7 +321,7 @@ public final class MediaController { // RemoteUserInfo, and OP package name is used for AudioService's internal // AppOpsManager usages. mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(), - mCbStub, false, direction, flags); + mCbStub, direction, flags); } catch (RuntimeException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); } diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index e3608086395ea..edaa55dad0e56 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -42,8 +42,12 @@ interface ISessionManager { List getSession2Tokens(int userId); void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, boolean needWakeLock); + boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName, + in MediaSession.Token sessionToken, in KeyEvent keyEvent); void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService, in KeyEvent keyEvent, int stream, boolean musicOnly); + void dispatchVolumeKeyEventToSessionAsSystemService(String packageName, String opPackageName, + in MediaSession.Token sessionToken, in KeyEvent keyEvent); void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream, int delta, int flags); void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 8f4a3ad9a8217..ca3346c9a8c47 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.ResultReceiver; import android.service.media.MediaBrowserService; import android.text.TextUtils; @@ -43,6 +44,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.Objects; /** * Allows interaction with media controllers, volume keys, media buttons, and @@ -423,15 +425,22 @@ public final class MediaSession { */ public static final class Token implements Parcelable { - private ControllerLink mControllerLink; + private final int mUid; + private final ControllerLink mControllerLink; /** * @hide */ public Token(ControllerLink controllerLink) { + mUid = Process.myUid(); mControllerLink = controllerLink; } + Token(Parcel in) { + mUid = in.readInt(); + mControllerLink = in.readParcelable(null); + } + @Override public int describeContents() { return 0; @@ -439,13 +448,14 @@ public final class MediaSession { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mUid); dest.writeParcelable(mControllerLink, flags); } @Override public int hashCode() { final int prime = 31; - int result = 1; + int result = mUid; result = prime * result + ((mControllerLink == null) ? 0 : mControllerLink.getBinder().hashCode()); return result; @@ -460,14 +470,18 @@ public final class MediaSession { if (getClass() != obj.getClass()) return false; Token other = (Token) obj; - if (mControllerLink == null) { - if (other.mControllerLink != null) { - return false; - } - } else if (!mControllerLink.getBinder().equals(other.mControllerLink.getBinder())) { + if (mUid != other.mUid) { return false; } - return true; + return Objects.equals(mControllerLink, other.mControllerLink); + } + + /** + * Gets the UID of this token. + * @hide + */ + public int getUid() { + return mUid; } /** @@ -483,8 +497,7 @@ public final class MediaSession { new Parcelable.Creator() { @Override public Token createFromParcel(Parcel in) { - ControllerLink link = in.readParcelable(null); - return new Token(link); + return new Token(in); } @Override diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 756386736aecc..bd8eaddd4a028 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -507,6 +507,37 @@ public final class MediaSessionManager { } } + /** + * Dispatches the media button event as system service to the session. + *

+ * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param sessionToken session token + * @param keyEvent media key event + * @return {@code true} if the event was sent to the session, {@code false} otherwise + * @hide + */ + public boolean dispatchMediaKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, + @NonNull KeyEvent keyEvent) { + if (sessionToken == null) { + throw new IllegalArgumentException("sessionToken shouldn't be null"); + } + if (keyEvent == null) { + throw new IllegalArgumentException("keyEvent shouldn't be null"); + } + if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { + return false; + } + try { + return mService.dispatchMediaKeyEventToSessionAsSystemService(mContext.getPackageName(), + sessionToken, keyEvent); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send key event.", e); + } + return false; + } + /** * Send a volume key event. The receiver will be selected automatically. * @@ -543,6 +574,32 @@ public final class MediaSessionManager { } } + /** + * Dispatches the volume key event as system service to the session. + *

+ * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param sessionToken sessionToken + * @param keyEvent volume key event + * @hide + */ + public void dispatchVolumeKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, + @NonNull KeyEvent keyEvent) { + if (sessionToken == null) { + throw new IllegalArgumentException("sessionToken shouldn't be null"); + } + if (keyEvent == null) { + throw new IllegalArgumentException("keyEvent shouldn't be null"); + } + try { + mService.dispatchVolumeKeyEventToSessionAsSystemService(mContext.getPackageName(), + mContext.getOpPackageName(), sessionToken, keyEvent); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling dispatchVolumeKeyEventAsSystemService", e); + } + } + /** * Dispatch an adjust volume request to the system. It will be sent to the * most relevant audio stream or media session. The direction must be one of diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index af790f25564c5..b6ef180f4b597 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -283,6 +283,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { Log.w(TAG, "Muting remote playback is not supported"); return; } + if (DEBUG) { + Log.w(TAG, "adjusting volume, pkg=" + packageName + ", asSystemService=" + + asSystemService + ", dir=" + direction); + } mSessionCb.adjustVolume(packageName, pid, uid, caller, asSystemService, direction); int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); @@ -456,9 +460,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return mSessionCb.mCb; } - public void sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + /** + * Sends media button. + * + * @param packageName caller package name + * @param pid caller pid + * @param uid caller uid + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. + * @param ke key events + * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. + * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is + * needed. + * @return {@code true} if the attempt to send media button was successfuly. + * {@code false} otherwise. + */ + public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb) { - mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, cb); + return mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, + cb); } public void dump(PrintWriter pw, String prefix) { @@ -492,6 +512,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { final String callingOpPackageName, final int callingPid, final int callingUid, final boolean asSystemService, final boolean useSuggested, final int previousFlagPlaySound) { + if (DEBUG) { + Log.w(TAG, "adjusting local volume, stream=" + stream + ", dir=" + direction + + ", asSystemService=" + asSystemService + ", useSuggested=" + useSuggested); + } // Must use opPackageName for adjusting volumes with UID. final String opPackageName; final int uid; @@ -1223,9 +1247,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public boolean sendMediaButton(String packageName, ControllerCallbackLink cb, - boolean asSystemService, KeyEvent keyEvent) { + KeyEvent keyEvent) { return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(), - Binder.getCallingUid(), cb, asSystemService, keyEvent); + Binder.getCallingUid(), cb, false, keyEvent); } @Override @@ -1292,14 +1316,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void adjustVolume(String packageName, String opPackageName, - ControllerCallbackLink caller, boolean asSystemService, int direction, - int flags) { + ControllerCallbackLink caller, int direction, int flags) { int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { MediaSessionRecord.this.adjustVolume(packageName, opPackageName, pid, uid, caller, - asSystemService, direction, flags, false /* useSuggested */); + false, direction, flags, false /* useSuggested */); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 94de49e729378..409060e348417 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -707,6 +707,14 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { return mUserRecords.get(fullUserId); } + private MediaSessionRecord getMediaSessionRecordLocked(MediaSession.Token sessionToken) { + FullUserRecord user = getFullUserRecordLocked(UserHandle.getUserId(sessionToken.getUid())); + if (user != null) { + return user.mPriorityStack.getMediaSessionRecord(sessionToken); + } + return null; + } + /** * Information about a full user and its corresponding managed profiles. * @@ -1271,6 +1279,34 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { } } + @Override + public boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName, + MediaSession.Token sessionToken, KeyEvent keyEvent) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken); + if (record == null) { + if (DEBUG) { + Log.d(TAG, "Failed to find session to dispatch key event."); + } + return false; + } + if (DEBUG) { + Log.d(TAG, "dispatchMediaKeyEventToSessionAsSystemService, pkg=" + + packageName + ", pid=" + pid + ", uid=" + uid + ", sessionToken=" + + sessionToken + ", event=" + keyEvent + ", session=" + record); + } + return record.sendMediaButton(packageName, pid, uid, true /* asSystemService */, + keyEvent, 0, null); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public void setCallback(ICallback callback) { final int pid = Binder.getCallingPid(); @@ -1576,6 +1612,62 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { } } + @Override + public void dispatchVolumeKeyEventToSessionAsSystemService(String packageName, + String opPackageName, MediaSession.Token sessionToken, KeyEvent keyEvent) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + MediaSessionRecord record = getMediaSessionRecordLocked(sessionToken); + if (record == null) { + if (DEBUG) { + Log.d(TAG, "Failed to find session to dispatch key event."); + } + return; + } + if (DEBUG) { + Log.d(TAG, "dispatchVolumeKeyEventToSessionAsSystemService, pkg=" + + packageName + ", opPkg=" + opPackageName + ", pid=" + pid + + ", uid=" + uid + ", sessionToken=" + sessionToken + ", event=" + + keyEvent + ", session=" + record); + } + switch (keyEvent.getAction()) { + case KeyEvent.ACTION_DOWN: { + int direction = 0; + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_VOLUME_UP: + direction = AudioManager.ADJUST_RAISE; + break; + case KeyEvent.KEYCODE_VOLUME_DOWN: + direction = AudioManager.ADJUST_LOWER; + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + direction = AudioManager.ADJUST_TOGGLE_MUTE; + break; + } + record.adjustVolume(packageName, opPackageName, pid, uid, + null /* caller */, true /* asSystemService */, direction, + AudioManager.FLAG_SHOW_UI, false /* useSuggested */); + break; + } + + case KeyEvent.ACTION_UP: { + final int flags = + AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE + | AudioManager.FLAG_FROM_KEY; + record.adjustVolume(packageName, opPackageName, pid, uid, + null /* caller */, true /* asSystemService */, 0, + flags, false /* useSuggested */); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream, int delta, int flags) { diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 719ec362e6e8c..9ba50ee25cb63 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -28,6 +28,7 @@ import android.util.SparseArray; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Keeps track of media sessions and their priority for notifications, media @@ -135,6 +136,21 @@ class MediaSessionStack { return mSessions.contains(record); } + /** + * Gets the {@link MediaSessionRecord} with the {@link MediaSession.Token}. + * + * @param sessionToken session token + * @return the MediaSessionRecord. Can be {@code null} if the session is gone meanwhile. + */ + public MediaSessionRecord getMediaSessionRecord(MediaSession.Token sessionToken) { + for (MediaSessionRecord record : mSessions) { + if (Objects.equals(record.getControllerLink(), sessionToken.getControllerLink())) { + return record; + } + } + return null; + } + /** * Notify the priority tracker that a session's playback state changed. *