diff options
7 files changed, 136 insertions, 31 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 543836b..ee9044e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -138,6 +138,17 @@ public class AudioManager { public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"; /** + * @hide Broadcast intent when a stream mute state changes. + * Includes the stream that changed and the new mute state + * + * @see #EXTRA_VOLUME_STREAM_TYPE + * @see #EXTRA_STREAM_VOLUME_MUTED + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String STREAM_MUTE_CHANGED_ACTION = + "android.media.STREAM_MUTE_CHANGED_ACTION"; + + /** * @hide Broadcast intent when the master volume changes. * Includes the new volume * @@ -221,6 +232,13 @@ public class AudioManager { "android.media.EXTRA_MASTER_VOLUME_MUTED"; /** + * @hide The new stream volume mute state for the stream mute changed intent. + * Value is boolean + */ + public static final String EXTRA_STREAM_VOLUME_MUTED = + "android.media.EXTRA_STREAM_VOLUME_MUTED"; + + /** * Broadcast Action: Wired Headset plugged in or unplugged. * * You <em>cannot</em> receive this through components declared @@ -728,11 +746,7 @@ public class AudioManager { break; case KeyEvent.KEYCODE_VOLUME_MUTE: if (event.getRepeatCount() == 0) { - if (mUseMasterVolume) { - setMasterMute(!isMasterMute()); - } else { - // TODO: Actually handle MUTE. - } + MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); } break; } @@ -763,6 +777,9 @@ public class AudioManager { } mVolumeKeyUpTime = SystemClock.uptimeMillis(); break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); + break; } } diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index d9586bc..616bdd1 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -15,6 +15,8 @@ */ package android.media; +import android.os.IBinder; + import com.android.server.LocalServices; /** @@ -39,6 +41,9 @@ public abstract class AudioManagerInternal { public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage, int uid); + public abstract void setMasterMuteForUid(boolean state, int flags, String callingPackage, + IBinder cb, int uid); + public abstract void setRingerModeDelegate(RingerModeDelegate delegate); public abstract int getRingerModeInternal(); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 9a3ec42..f1d6a0a 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -1517,12 +1517,20 @@ public class AudioService extends IAudioService.Stub { if (mUseFixedVolume) { return; } + if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { + streamType = getActiveStreamType(streamType); + } if (isStreamAffectedByMute(streamType)) { if (streamType == AudioSystem.STREAM_MUSIC) { setSystemAudioMute(state); } mStreamStates[streamType].mute(cb, state); + + Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); + intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state); + sendBroadcastToAll(intent); } } @@ -1544,6 +1552,9 @@ public class AudioService extends IAudioService.Stub { /** get stream mute state. */ public boolean isStreamMute(int streamType) { + if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { + streamType = getActiveStreamType(streamType); + } synchronized (VolumeStreamState.class) { return mStreamStates[streamType].isMuted_syncVSS(); } @@ -1651,11 +1662,16 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#setMasterMute(boolean, int) */ public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) { + setMasterMuteInternal(state, flags, callingPackage, cb, Binder.getCallingUid()); + } + + private void setMasterMuteInternal(boolean state, int flags, String callingPackage, IBinder cb, + int uid) { if (mUseFixedVolume) { return; } - if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(), - callingPackage) != AppOpsManager.MODE_ALLOWED) { + if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage) + != AppOpsManager.MODE_ALLOWED) { return; } if (state != AudioSystem.getMasterMute()) { @@ -1665,6 +1681,10 @@ public class AudioService extends IAudioService.Stub { sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1 : 0, UserHandle.getCallingUserId(), null, PERSIST_DELAY); sendMasterMuteUpdate(state, flags); + + Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, state); + sendBroadcastToAll(intent); } } @@ -5781,6 +5801,12 @@ public class AudioService extends IAudioService.Stub { public void setRingerModeInternal(int ringerMode, String caller) { AudioService.this.setRingerModeInternal(ringerMode, caller); } + + @Override + public void setMasterMuteForUid(boolean state, int flags, String callingPackage, IBinder cb, + int uid) { + setMasterMuteInternal(state, flags, callingPackage, cb, uid); + } } //========================================================================================== diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java index b37ee6e..4b9a929 100644 --- a/media/java/android/media/session/MediaSessionLegacyHelper.java +++ b/media/java/android/media/session/MediaSessionLegacyHelper.java @@ -190,6 +190,7 @@ public class MediaSessionLegacyHelper { boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; int direction = 0; + boolean isMute = false; switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_VOLUME_UP: direction = AudioManager.ADJUST_RAISE; @@ -198,15 +199,11 @@ public class MediaSessionLegacyHelper { direction = AudioManager.ADJUST_LOWER; break; case KeyEvent.KEYCODE_VOLUME_MUTE: - // TODO + isMute = true; break; } - if ((down || up) && direction != 0) { + if (down || up) { int flags; - // If this is action up we want to send a beep for non-music events - if (up) { - direction = 0; - } if (musicOnly) { // This flag is used when the screen is off to only affect // active media @@ -219,9 +216,23 @@ public class MediaSessionLegacyHelper { flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; } } - - mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, - direction, flags); + if (direction != 0) { + // If this is action up we want to send a beep for non-music events + if (up) { + direction = 0; + } + mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, + direction, flags); + } else if (isMute) { + if (down) { + // We need to send two volume events on down, one to mute + // and one to show the UI + mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, + MediaSessionManager.DIRECTION_MUTE, flags); + } + mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, + 0 /* direction, causes UI to show on down */, flags); + } } } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index b4fff8f..a4ef851 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -59,6 +59,14 @@ public final class MediaSessionManager { private Context mContext; /** + * Special flag for sending the mute key to dispatchAdjustVolume used by the + * system. + * + * @hide + */ + public static final int DIRECTION_MUTE = -99; + + /** * @hide */ public MediaSessionManager(Context context) { diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 9440697..2c61d2c 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -35,6 +35,7 @@ import android.media.session.ISessionControllerCallback; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; import android.media.AudioAttributes; @@ -92,6 +93,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final MediaSessionService mService; private final boolean mUseMasterVolume; + private final IBinder mICallback = new Binder(); private final Object mLock = new Object(); private final ArrayList<ISessionControllerCallback> mControllerCallbacks = new ArrayList<ISessionControllerCallback>(); @@ -245,6 +247,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { flags &= ~AudioManager.FLAG_PLAY_SOUND; } + boolean isMute = direction == MediaSessionManager.DIRECTION_MUTE; if (direction > 1) { direction = 1; } else if (direction < -1) { @@ -254,29 +257,52 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { if (mUseMasterVolume) { // If this device only uses master volume and playback is local // just adjust the master volume and return. - mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, uid); + if (isMute) { + mAudioManagerInternal.setMasterMuteForUid(!mAudioManager.isMasterMute(), + flags, packageName, mICallback, uid); + } else { + mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, + uid); + } return; } int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); if (useSuggested) { if (AudioSystem.isStreamActive(stream, 0)) { - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, - flags, packageName, uid); + if (isMute) { + mAudioManager.setStreamMute(stream, !mAudioManager.isStreamMute(stream)); + } else { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, + flags, packageName, uid); + } } else { flags |= previousFlagPlaySound; - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( - AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName, - uid); + if (isMute) { + mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE, + !mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE)); + } else { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( + AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName, + uid); + } } } else { - mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, - packageName, uid); + if (isMute) { + mAudioManager.setStreamMute(stream, !mAudioManager.isStreamMute(stream)); + } else { + mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, + packageName, uid); + } } } else { if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { // Nothing to do, the volume cannot be changed return; } + if (isMute) { + Log.w(TAG, "Muting remote playback is not supported"); + return; + } mSessionCb.adjustVolume(direction); int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index f11a3f9..b4ec607 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -40,6 +40,7 @@ import android.media.session.ISessionCallback; import android.media.session.ISessionManager; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -588,6 +589,8 @@ public class MediaSessionService extends SystemService implements Monitor { "android.media.AudioService.WAKELOCK_ACQUIRED"; private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number + private final IBinder mICallback = new Binder(); + private boolean mVoiceButtonDown = false; private boolean mVoiceButtonHandled = false; @@ -720,8 +723,7 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) - throws RemoteException { + public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); @@ -828,11 +830,21 @@ public class MediaSessionService extends SystemService implements Monitor { } try { if (mUseMasterVolume) { - mAudioService.adjustMasterVolume(direction, flags, - getContext().getOpPackageName()); + if (direction == MediaSessionManager.DIRECTION_MUTE) { + mAudioService.setMasterMute(!mAudioService.isMasterMute(), flags, + getContext().getOpPackageName(), mICallback); + } else { + mAudioService.adjustMasterVolume(direction, flags, + getContext().getOpPackageName()); + } } else { - mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, - getContext().getOpPackageName()); + if (direction == MediaSessionManager.DIRECTION_MUTE) { + mAudioService.setStreamMute(suggestedStream, + !mAudioService.isStreamMute(suggestedStream), mICallback); + } else { + mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, + flags, getContext().getOpPackageName()); + } } } catch (RemoteException e) { Log.e(TAG, "Error adjusting default volume.", e); @@ -841,7 +853,7 @@ public class MediaSessionService extends SystemService implements Monitor { session.adjustVolume(direction, flags, getContext().getPackageName(), UserHandle.myUserId(), true); if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE - && mRvc != null) { + && mRvc != null && direction != MediaSessionManager.DIRECTION_MUTE) { try { mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); } catch (Exception e) { |