Merge "MediaSessionService: Provide caller information for media key events" into pi-dev

This commit is contained in:
Jaewan Kim
2018-05-10 04:48:35 +00:00
committed by Android (Google) Code Review
10 changed files with 369 additions and 148 deletions

View File

@@ -46,6 +46,8 @@ import java.io.PrintStream;
import java.util.List;
public class Media extends BaseCommand {
// This doesn't belongs to any package. Setting the package name to empty string.
private static final String PACKAGE_NAME = "";
private ISessionManager mSessionService;
/**
@@ -104,7 +106,7 @@ public class Media extends BaseCommand {
private void sendMediaKey(KeyEvent event) {
try {
mSessionService.dispatchMediaKeyEvent(event, false);
mSessionService.dispatchMediaKeyEvent(PACKAGE_NAME, false, event, false);
} catch (RemoteException e) {
}
}
@@ -264,13 +266,13 @@ public class Media extends BaseCommand {
} else if ("q".equals(line) || "quit".equals(line)) {
break;
} else if ("play".equals(line)) {
mController.play("");
mController.play(PACKAGE_NAME);
} else if ("pause".equals(line)) {
mController.pause("");
mController.pause(PACKAGE_NAME);
} else if ("next".equals(line)) {
mController.next("");
mController.next(PACKAGE_NAME);
} else if ("previous".equals(line)) {
mController.previous("");
mController.previous(PACKAGE_NAME);
} else {
System.out.println("Invalid command: " + line);
}

View File

@@ -23,7 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.MediaSessionManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -48,6 +48,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
KeyguardManager mKeyguardManager;
SearchManager mSearchManager;
TelephonyManager mTelephonyManager;
MediaSessionManager mMediaSessionManager;
public PhoneFallbackEventHandler(Context context) {
mContext = context;
@@ -84,8 +85,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
handleVolumeKeyEvent(event);
return true;
}
@@ -216,8 +216,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (!event.isCanceled()) {
MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
handleVolumeKeyEvent(event);
}
return true;
}
@@ -306,12 +305,25 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
return mAudioManager;
}
MediaSessionManager getMediaSessionManager() {
if (mMediaSessionManager == null) {
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
}
return mMediaSessionManager;
}
void sendCloseSystemWindows() {
PhoneWindow.sendCloseSystemWindows(mContext, null);
}
private void handleVolumeKeyEvent(KeyEvent keyEvent) {
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent,
AudioManager.USE_DEFAULT_STREAM_TYPE);
}
private void handleMediaKeyEvent(KeyEvent keyEvent) {
MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
getMediaSessionManager().dispatchMediaKeyEventAsSystemService(keyEvent);
}
private boolean isUserSetupComplete() {

View File

@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.*;
import android.app.ActivityManager;
import android.app.SearchManager;
import android.media.session.MediaSessionManager;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -74,7 +75,6 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaSessionLegacyHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -252,6 +252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private AudioManager mAudioManager;
private KeyguardManager mKeyguardManager;
private MediaSessionManager mMediaSessionManager;
private int mUiOptions = 0;
@@ -1873,22 +1874,10 @@ 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) {
int direction = 0;
switch (keyCode) {
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;
}
mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI);
mMediaController.dispatchVolumeButtonEventAsSystemService(event);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
event, mVolumeControlStreamType, false);
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event,
mVolumeControlStreamType);
}
return true;
}
@@ -1906,7 +1895,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
if (mMediaController != null) {
if (mMediaController.dispatchMediaButtonEvent(event)) {
if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) {
return true;
}
}
@@ -1948,6 +1937,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return mAudioManager;
}
private MediaSessionManager getMediaSessionManager() {
if (mMediaSessionManager == null) {
mMediaSessionManager = (MediaSessionManager) getContext().getSystemService(
Context.MEDIA_SESSION_SERVICE);
}
return mMediaSessionManager;
}
/**
* A key was released and not handled by anything else in the window.
*
@@ -1969,12 +1966,10 @@ 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) {
final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
| AudioManager.FLAG_FROM_KEY;
mMediaController.adjustVolume(0, flags);
mMediaController.dispatchVolumeButtonEventAsSystemService(event);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
event, mVolumeControlStreamType, false);
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(
event, mVolumeControlStreamType);
}
return true;
}
@@ -1983,8 +1978,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// doesn't have one of these. In this case, we execute it here and
// eat the event instead, because we have mVolumeControlStreamType
// and they don't.
MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(
event, AudioManager.USE_DEFAULT_STREAM_TYPE);
return true;
}
// These are all the recognized media key codes in
@@ -2001,7 +1996,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
if (mMediaController != null) {
if (mMediaController.dispatchMediaButtonEvent(event)) {
if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) {
return true;
}
}

View File

@@ -37,7 +37,7 @@ import java.util.List;
*/
interface ISessionController {
void sendCommand(String packageName, String command, in Bundle args, in ResultReceiver cb);
boolean sendMediaButton(String packageName, in KeyEvent mediaButton);
boolean sendMediaButton(String packageName, boolean asSystemService, in KeyEvent mediaButton);
void registerCallbackListener(in ISessionControllerCallback cb);
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
@@ -46,7 +46,7 @@ interface ISessionController {
PendingIntent getLaunchPendingIntent();
long getFlags();
ParcelableVolumeInfo getVolumeAttributes();
void adjustVolume(String packageName, int direction, int flags);
void adjustVolume(String packageName, boolean asSystemService, int direction, int flags);
void setVolumeTo(String packageName, int value, int flags);
// These commands are for the TransportControls

View File

@@ -34,9 +34,11 @@ import android.view.KeyEvent;
interface ISessionManager {
ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
void dispatchVolumeKeyEvent(in KeyEvent keyEvent, int stream, boolean musicOnly);
void dispatchAdjustVolume(int suggestedStream, int delta, int flags);
void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
boolean needWakeLock);
void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
int stream, boolean musicOnly);
void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags);
void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
int userId);
void removeSessionsListener(in IActiveSessionsListener listener);

View File

@@ -126,6 +126,27 @@ public final class MediaController {
* @return true if the event was sent to the session, false otherwise.
*/
public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
return dispatchMediButtonEventInternal(false, keyEvent);
}
/**
* Dispatches the media button event as system service to the session. This only effects the
* {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
* check done by the system service.
* <p>
* 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 dispatchMediButtonEventInternal(true, keyEvent);
}
private boolean dispatchMediButtonEventInternal(boolean asSystemService,
@NonNull KeyEvent keyEvent) {
if (keyEvent == null) {
throw new IllegalArgumentException("KeyEvent may not be null");
}
@@ -133,13 +154,60 @@ public final class MediaController {
return false;
}
try {
return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent);
return mSessionBinder.sendMediaButton(mContext.getPackageName(), asSystemService,
keyEvent);
} catch (RemoteException e) {
// System is dead. =(
}
return false;
}
/**
* Dispatches the volume button event as system service to the session. This only effects the
* {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
* check done by the system service.
* <p>
* 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 {
mSessionBinder.adjustVolume(mContext.getPackageName(), true, direction,
AudioManager.FLAG_SHOW_UI);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
}
case KeyEvent.ACTION_UP: {
final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
| AudioManager.FLAG_FROM_KEY;
try {
mSessionBinder.adjustVolume(mContext.getPackageName(), true, 0, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
}
}
}
/**
* Get the current playback state for this session.
*
@@ -322,7 +390,7 @@ public final class MediaController {
*/
public void adjustVolume(int direction, int flags) {
try {
mSessionBinder.adjustVolume(mContext.getPackageName(), direction, flags);
mSessionBinder.adjustVolume(mContext.getPackageName(), false, direction, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
}

View File

@@ -122,6 +122,15 @@ public final class MediaSession {
FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
public @interface SessionFlags { }
private static final String EXTRA_KEY_CALLING_PACKAGE =
"android.media.session.extra.CALLING_PACKAGE";
private static final String EXTRA_KEY_CALLING_PID =
"android.media.session.extra.CALLING_PID";
private static final String EXTRA_KEY_CALLING_UID =
"android.media.session.extra.CALLING_UID";
private static final String EXTRA_KEY_ORIGINAL_BUNDLE =
"android.media.session.extra.ORIGINAL_BUNDLE";
private final Object mLock = new Object();
private final int mMaxBitmapSize;
@@ -520,11 +529,15 @@ public final class MediaSession {
* @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
*/
public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
return createRemoteUserInfo(getCurrentData());
}
private @NonNull Bundle getCurrentData() {
if (mCallback == null || mCallback.mCurrentData == null) {
throw new IllegalStateException(
"This should be called inside of MediaSession.Callback methods");
}
return mCallback.mCurrentControllerInfo;
return mCallback.mCurrentData;
}
/**
@@ -556,7 +569,7 @@ public final class MediaSession {
*/
public String getCallingPackage() {
if (mCallback != null) {
return mCallback.mCurrentControllerInfo.getPackageName();
return createRemoteUserInfo(mCallback.mCurrentData).getPackageName();
}
return null;
}
@@ -658,6 +671,57 @@ public final class MediaSession {
}
}
/**
* Creates the extra bundle that includes the caller information.
*
* @return An extraBundle that contains caller information
*/
private static Bundle createExtraBundle(String packageName, int pid, int uid) {
return createExtraBundle(packageName, pid, uid, null);
}
/**
* Creates the extra bundle that includes the caller information.
*
* @param originalBundle bundle
* @return An extraBundle that contains caller information
*/
private static Bundle createExtraBundle(String packageName, int pid, int uid,
Bundle originalBundle) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_KEY_CALLING_PACKAGE, packageName);
bundle.putInt(EXTRA_KEY_CALLING_PID, pid);
bundle.putInt(EXTRA_KEY_CALLING_UID, uid);
if (originalBundle != null) {
bundle.putBundle(EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle);
}
return bundle;
}
/**
* Creates the {@link RemoteUserInfo} from the extra bundle created by
* {@link #createExtraBundle}.
*
* @param extraBundle that previously created by createExtraBundle()
* @return a RemoteUserInfo
*/
private static RemoteUserInfo createRemoteUserInfo(Bundle extraBundle) {
return new RemoteUserInfo(
extraBundle.getString(EXTRA_KEY_CALLING_PACKAGE),
extraBundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID),
extraBundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID));
}
/**
* Gets the original bundle from the extra bundle created by {@link #createExtraBundle}.
*
* @param extraBundle that previously created by createExtraBundle()
* @return a Bundle
*/
private static Bundle getOriginalBundle(Bundle extraBundle) {
return extraBundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE);
}
/**
* Return true if this is considered an active playback state.
*
@@ -755,9 +819,6 @@ public final class MediaSession {
private MediaSession mSession;
private CallbackMessageHandler mHandler;
private boolean mMediaPlayPauseKeyPending;
private String mCallingPackage;
private int mCallingPid;
private int mCallingUid;
public Callback() {
}
@@ -811,8 +872,9 @@ public final class MediaSession {
}
} else {
mMediaPlayPauseKeyPending = true;
mHandler.sendEmptyMessageDelayed(CallbackMessageHandler
.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
mHandler.postDelayed(CallbackMessageHandler
.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
mSession.getCurrentData(),
ViewConfiguration.getDoubleTapTimeout());
}
return true;
@@ -1242,22 +1304,6 @@ public final class MediaSession {
session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid));
}
}
private Bundle createExtraBundle(String packageName, int pid, int uid) {
return createExtraBundle(packageName, pid, uid, null);
}
private Bundle createExtraBundle(String packageName, int pid, int uid,
Bundle originalBundle) {
Bundle bundle = new Bundle();
bundle.putString(CallbackMessageHandler.EXTRA_KEY_CALLING_PACKAGE, packageName);
bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_PID, pid);
bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_UID, uid);
if (originalBundle != null) {
bundle.putBundle(CallbackMessageHandler.EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle);
}
return bundle;
}
}
/**
@@ -1379,15 +1425,6 @@ public final class MediaSession {
private class CallbackMessageHandler extends Handler {
private static final String EXTRA_KEY_CALLING_PACKAGE =
"android.media.session.extra.CALLING_PACKAGE";
private static final String EXTRA_KEY_CALLING_PID =
"android.media.session.extra.CALLING_PID";
private static final String EXTRA_KEY_CALLING_UID =
"android.media.session.extra.CALLING_UID";
private static final String EXTRA_KEY_ORIGINAL_BUNDLE =
"android.media.session.extra.ORIGINAL_BUNDLE";
private static final int MSG_COMMAND = 1;
private static final int MSG_MEDIA_BUTTON = 2;
private static final int MSG_PREPARE = 3;
@@ -1413,8 +1450,7 @@ public final class MediaSession {
private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
private MediaSession.Callback mCallback;
private RemoteUserInfo mCurrentControllerInfo;
private Bundle mCurrentData;
public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
super(looper, null, true);
@@ -1422,22 +1458,25 @@ public final class MediaSession {
mCallback.mHandler = this;
}
public void post(int what, Object obj, Bundle bundle) {
public void post(int what, Object obj, Bundle data) {
Message msg = obtainMessage(what, obj);
msg.setData(bundle);
msg.setData(data);
msg.sendToTarget();
}
public void postDelayed(int what, Bundle data, long delayMs) {
Message msg = obtainMessage(what);
msg.setData(data);
sendMessageDelayed(msg, delayMs);
}
@Override
public void handleMessage(Message msg) {
VolumeProvider vp;
Bundle bundle = msg.getData();
Bundle originalBundle = bundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE);
Bundle data = msg.getData();
Bundle originalBundle = getOriginalBundle(data);
mCurrentControllerInfo = new RemoteUserInfo(
bundle.getString(EXTRA_KEY_CALLING_PACKAGE),
bundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID),
bundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID));
mCurrentData = data;
switch (msg.what) {
case MSG_COMMAND:
@@ -1521,7 +1560,7 @@ public final class MediaSession {
mCallback.handleMediaPlayPauseKeySingleTapIfPending();
break;
}
mCurrentControllerInfo = null;
mCurrentData = null;
}
}
}

View File

@@ -300,8 +300,28 @@ public final class MediaSessionManager {
* @hide
*/
public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) {
dispatchMediaKeyEventInternal(false, keyEvent, needWakeLock);
}
/**
* Send a media key event as system component. The receiver will be selected automatically.
* <p>
* Should be only called by the {@link com.android.internal.policy.PhoneWindow} or
* {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key
* from the hardware devices.
*
* @param keyEvent The KeyEvent to send.
* @hide
*/
public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) {
dispatchMediaKeyEventInternal(true, keyEvent, false);
}
private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
boolean needWakeLock) {
try {
mService.dispatchMediaKeyEvent(keyEvent, needWakeLock);
mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
needWakeLock);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send key event.", e);
}
@@ -311,12 +331,33 @@ public final class MediaSessionManager {
* Send a volume key event. The receiver will be selected automatically.
*
* @param keyEvent The volume KeyEvent to send.
* @param needWakeLock True if a wake lock should be held while sending the key.
* @hide
*/
public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly) {
dispatchVolumeKeyEventInternal(false, keyEvent, stream, musicOnly);
}
/**
* Dispatches the volume button event as system service to the session. This only effects the
* {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
* check done by the system service.
* <p>
* Should be only called by the {@link com.android.internal.policy.PhoneWindow} or
* {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key
* from the hardware devices.
*
* @param keyEvent The KeyEvent to send.
* @hide
*/
public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) {
dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false);
}
private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
int stream, boolean musicOnly) {
try {
mService.dispatchVolumeKeyEvent(keyEvent, stream, musicOnly);
mService.dispatchVolumeKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
stream, musicOnly);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send volume key event.", e);
}
@@ -336,7 +377,8 @@ public final class MediaSessionManager {
*/
public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
try {
mService.dispatchAdjustVolume(suggestedStream, direction, flags);
mService.dispatchAdjustVolume(mContext.getPackageName(), suggestedStream, direction,
flags);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send adjust volume.", e);
}

View File

@@ -45,6 +45,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
@@ -82,6 +83,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private final SessionStub mSession;
private final SessionCb mSessionCb;
private final MediaSessionService mService;
private final Context mContext;
private final Object mLock = new Object();
private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders =
@@ -126,8 +128,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mSession = new SessionStub();
mSessionCb = new SessionCb(cb);
mService = service;
mContext = mService.getContext();
mHandler = new MessageHandler(handlerLooper);
mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
@@ -232,12 +235,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
* @param packageName The package that made the original volume request.
* @param pid The pid that made the original volume request.
* @param uid The uid that made the original volume request.
* @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. This helps sessions to distinguish
* between the key injection by the app and key events from the hardware devices.
* Should be used only when the volume key events aren't handled by foreground
* activity. {@code false} otherwise to tell session about the real caller.
* @param direction The direction to adjust volume in.
* @param flags Any of the flags from {@link AudioManager}.
* @param useSuggested True to use adjustSuggestedStreamVolume instead of
*/
public void adjustVolume(String packageName, int pid, int uid, int direction, int flags,
boolean useSuggested) {
public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService,
int direction, int flags, boolean useSuggested) {
int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
@@ -258,7 +266,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
Log.w(TAG, "Muting remote playback is not supported");
return;
}
mSessionCb.adjustVolume(packageName, pid, uid, direction);
mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction);
int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
mOptimisticVolume = volumeBefore + direction;
@@ -418,9 +426,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
return mSessionCb.mCb;
}
public void sendMediaButton(String packageName, int pid, int uid, KeyEvent ke, int sequenceId,
ResultReceiver cb) {
mSessionCb.sendMediaButton(packageName, pid, uid, ke, sequenceId, cb);
public void sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, cb);
}
public void dump(PrintWriter pw, String prefix) {
@@ -698,11 +706,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
private String getPackageName(int uid) {
Context context = mService.getContext();
if (context == null) {
return null;
}
String[] packages = context.getPackageManager().getPackagesForUid(uid);
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
return packages[0];
}
@@ -907,12 +911,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mCb = cb;
}
public boolean sendMediaButton(String packageName, int pid, int uid, KeyEvent keyEvent,
int sequenceId, ResultReceiver cb) {
public boolean sendMediaButton(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
try {
mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb);
if (asSystemService) {
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, mediaButtonIntent, sequenceId, cb);
} else {
mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb);
}
return true;
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
@@ -1079,9 +1088,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
public void adjustVolume(String packageName, int pid, int uid, int direction) {
public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService,
int direction) {
try {
mCb.onAdjustVolume(packageName, pid, uid, direction);
if (asSystemService) {
mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, direction);
} else {
mCb.onAdjustVolume(packageName, pid, uid, direction);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in adjustVolume.", e);
}
@@ -1105,9 +1120,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
public boolean sendMediaButton(String packageName, KeyEvent mediaButtonIntent) {
public boolean sendMediaButton(String packageName, boolean asSystemService,
KeyEvent mediaButtonIntent) {
return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), mediaButtonIntent, 0, null);
Binder.getCallingUid(), asSystemService, mediaButtonIntent, 0, null);
}
@Override
@@ -1188,13 +1204,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
public void adjustVolume(String packageName, int direction, int flags) {
public void adjustVolume(String packageName, boolean asSystemService, int direction,
int flags) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
MediaSessionRecord.this.adjustVolume(packageName, pid, uid, direction, flags,
false /* useSuggested */);
MediaSessionRecord.this.adjustVolume(packageName, pid, uid, asSystemService,
direction, flags, false /* useSuggested */);
} finally {
Binder.restoreCallingIdentity(token);
}

View File

@@ -1093,13 +1093,21 @@ public class MediaSessionService extends SystemService implements Monitor {
* registered listeners, or if there was none, broadcast an
* ACTION_MEDIA_BUTTON intent to the rest of the system.
*
* @param packageName The caller package
* @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. This helps sessions to
* distinguish between the key injection by the app and key events from the
* hardware devices. Should be used only when the volume key events aren't handled
* by foreground activity. {@code false} otherwise to tell session about the real
* caller.
* @param keyEvent a non-null KeyEvent whose key code is one of the
* supported media buttons
* @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
* while this key event is dispatched.
*/
@Override
public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
KeyEvent keyEvent, boolean needWakeLock) {
if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
Log.w(TAG, "Attempted to dispatch null or non-media key event.");
return;
@@ -1110,7 +1118,8 @@ public class MediaSessionService extends SystemService implements Monitor {
final long token = Binder.clearCallingIdentity();
try {
if (DEBUG) {
Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
+ ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
+ keyEvent);
}
if (!isUserSetupComplete()) {
@@ -1137,7 +1146,8 @@ public class MediaSessionService extends SystemService implements Monitor {
}
try {
mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
new MediaKeyListenerResultReceiver(packageName, pid, uid,
asSystemService, keyEvent, needWakeLock));
return;
} catch (RemoteException e) {
Log.w(TAG, "Failed to send " + keyEvent
@@ -1146,9 +1156,11 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
handleVoiceKeyEventLocked(keyEvent, needWakeLock);
handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock);
} else {
dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock);
}
}
} finally {
@@ -1324,6 +1336,13 @@ public class MediaSessionService extends SystemService implements Monitor {
* there's no active global priority session, long-pressess will be sent to the
* long-press listener instead of adjusting volume.
*
* @param packageName The caller package.
* @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. This helps sessions to
* distinguish between the key injection by the app and key events from the
* hardware devices. Should be used only when the volume key events aren't handled
* by foreground activity. {@code false} otherwise to tell session about the real
* caller.
* @param keyEvent a non-null KeyEvent whose key code is one of the
* {@link KeyEvent#KEYCODE_VOLUME_UP},
* {@link KeyEvent#KEYCODE_VOLUME_DOWN},
@@ -1332,7 +1351,8 @@ public class MediaSessionService extends SystemService implements Monitor {
* @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
*/
@Override
public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,
KeyEvent keyEvent, int stream, boolean musicOnly) {
if (keyEvent == null ||
(keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
@@ -1346,15 +1366,16 @@ public class MediaSessionService extends SystemService implements Monitor {
final long token = Binder.clearCallingIdentity();
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
+ keyEvent);
Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
+ uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
}
try {
synchronized (mLock) {
if (isGlobalPriorityActiveLocked()
|| mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, stream, musicOnly);
} else {
// TODO: Consider the case when both volume up and down keys are pressed
// at the same time.
@@ -1387,11 +1408,12 @@ public class MediaSessionService extends SystemService implements Monitor {
&& mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
.getDownTime() == keyEvent.getDownTime()) {
// Short-press. Should change volume.
dispatchVolumeKeyEventLocked(
dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
mCurrentFullUserRecord.mInitialDownVolumeStream,
mCurrentFullUserRecord.mInitialDownMusicOnly);
dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, stream, musicOnly);
} else {
dispatchVolumeKeyLongPressLocked(keyEvent);
}
@@ -1403,8 +1425,8 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
private void dispatchVolumeKeyEventLocked(
KeyEvent keyEvent, int stream, boolean musicOnly) {
private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
int direction = 0;
@@ -1438,21 +1460,27 @@ public class MediaSessionService extends SystemService implements Monitor {
if (up) {
direction = 0;
}
dispatchAdjustVolumeLocked(stream, direction, flags);
dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
direction, flags);
} else if (isMute) {
if (down && keyEvent.getRepeatCount() == 0) {
dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream,
AudioManager.ADJUST_TOGGLE_MUTE, flags);
}
}
}
}
@Override
public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta,
int flags) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
dispatchAdjustVolumeLocked(packageName, pid, uid, false,
suggestedStream, delta, flags);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1777,7 +1805,8 @@ public class MediaSessionService extends SystemService implements Monitor {
return false;
}
private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,
boolean asSystemService, int suggestedStream, int direction, int flags) {
MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
@@ -1822,12 +1851,13 @@ public class MediaSessionService extends SystemService implements Monitor {
}
});
} else {
session.adjustVolume(getContext().getPackageName(), Process.myPid(),
Process.SYSTEM_UID, direction, flags, true);
session.adjustVolume(packageName, pid, uid, asSystemService,
direction, flags, true);
}
}
private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
int action = keyEvent.getAction();
boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
if (action == KeyEvent.ACTION_DOWN) {
@@ -1844,14 +1874,17 @@ 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);
dispatchMediaKeyEventLocked(keyEvent, needWakeLock);
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
downEvent, needWakeLock);
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock);
}
}
}
}
private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
if (session != null) {
if (DEBUG_KEY_EVENT) {
@@ -1861,17 +1894,13 @@ public class MediaSessionService extends SystemService implements Monitor {
mKeyEventReceiver.aquireWakeLockLocked();
}
// If we don't need a wakelock use -1 as the id so we won't release it later.
session.sendMediaButton(getContext().getPackageName(),
Process.myPid(),
Process.SYSTEM_UID,
keyEvent,
session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mKeyEventReceiver);
if (mCurrentFullUserRecord.mCallback != null) {
try {
mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
keyEvent,
new MediaSession.Token(session.getControllerBinder()));
keyEvent, new MediaSession.Token(session.getControllerBinder()));
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
@@ -1884,6 +1913,10 @@ public class MediaSessionService extends SystemService implements Monitor {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
// TODO: Find a way to also send PID/UID in secure way.
String callerPackageName =
(asSystemService) ? getContext().getPackageName() : packageName;
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
try {
if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
@@ -1984,13 +2017,22 @@ public class MediaSessionService extends SystemService implements Monitor {
}
private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
private KeyEvent mKeyEvent;
private boolean mNeedWakeLock;
private final String mPackageName;
private final int mPid;
private final int mUid;
private final boolean mAsSystemService;
private final KeyEvent mKeyEvent;
private final boolean mNeedWakeLock;
private boolean mHandled;
private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
super(mHandler);
mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
mPackageName = packageName;
mPid = pid;
mUid = uid;
mAsSystemService = asSystemService;
mKeyEvent = keyEvent;
mNeedWakeLock = needWakeLock;
}
@@ -2020,9 +2062,11 @@ public class MediaSessionService extends SystemService implements Monitor {
synchronized (mLock) {
if (!isGlobalPriorityActiveLocked()
&& isVoiceKey(mKeyEvent.getKeyCode())) {
handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock);
handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock);
} else {
dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock);
dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock);
}
}
}