From 1a28bfc85d0554f1bcd5f2b99fb22c830f6a36aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Fri, 1 Apr 2022 14:53:26 +0200 Subject: [PATCH] [BUG] AudioService: fix mute/umute of aliased streams. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mute/umute of aliased streams is asymetric according to activation/ de-activation mute events. A toggle mute will mute/unmute all aliased whereas a togglemute/raise will mute all aliased and unmute only the concerned stream of the raise event. A toggle mute and a setStreamVolume will mute all aliased and unmute only the concercend stream of the setVolume request. This CL makes the behavior homogeneous for aliased stream among all activation/deactivation mute events. Test: adb shell am instrument -w com.android.audiopolicytest com.android.audiopolicytest Bug: 260298113 Signed-off-by: François Gaffie Change-Id: I803f9df1c30755b8b5530c4b373c79ea4efd5010 --- .../android/server/audio/AudioService.java | 111 +++++++++++++----- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 524ac1c436937..eabc96fe445ff 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3475,15 +3475,7 @@ public class AudioService extends IAudioService.Stub } else { state = direction == AudioManager.ADJUST_MUTE; } - for (int stream = 0; stream < mStreamStates.length; stream++) { - if (streamTypeAlias == mStreamVolumeAlias[stream]) { - if (!(readCameraSoundForced() - && (mStreamStates[stream].getStreamType() - == AudioSystem.STREAM_SYSTEM_ENFORCED))) { - mStreamStates[stream].mute(state); - } - } - } + muteAliasStreams(streamTypeAlias, state); } else if ((direction == AudioManager.ADJUST_RAISE) && mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias, aliasIndex + step, device, flags)) { @@ -3498,7 +3490,7 @@ public class AudioService extends IAudioService.Stub // Unmute the stream if it was previously muted if (direction == AudioManager.ADJUST_RAISE) { // unmute immediately for volume up - streamState.mute(false); + muteAliasStreams(streamTypeAlias, false); } else if (direction == AudioManager.ADJUST_LOWER) { if (mIsSingleVolume) { sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE, @@ -3624,6 +3616,42 @@ public class AudioService extends IAudioService.Stub sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device); } + /** + * Loops on aliasted stream, update the mute cache attribute of each + * {@see AudioService#VolumeStreamState}, and then apply the change. + * It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream + * and aliases before mute change changed and after. + */ + private void muteAliasStreams(int streamAlias, boolean state) { + synchronized (VolumeStreamState.class) { + List streamsToMute = new ArrayList<>(); + for (int stream = 0; stream < mStreamStates.length; stream++) { + if (streamAlias == mStreamVolumeAlias[stream]) { + if (!(readCameraSoundForced() + && (mStreamStates[stream].getStreamType() + == AudioSystem.STREAM_SYSTEM_ENFORCED))) { + boolean changed = mStreamStates[stream].mute(state, /* apply= */ false); + if (changed) { + streamsToMute.add(stream); + } + } + } + } + streamsToMute.forEach(streamToMute -> { + mStreamStates[streamToMute].doMute(); + broadcastMuteSetting(streamToMute, state); + }); + } + } + + private void broadcastMuteSetting(int streamType, boolean isMuted) { + // Stream mute changed, fire the intent. + Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); + intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted); + sendBroadcastToAll(intent, null /* options */); + } + // Called after a delay when volume down is pressed while muted private void onUnmuteStream(int stream, int flags) { boolean wasMuted; @@ -3720,7 +3748,8 @@ public class AudioService extends IAudioService.Stub // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO) && (getDeviceForStream(stream) == device)) { - mStreamStates[stream].mute(index == 0); + // As adjustStreamVolume with muteAdjust flags mute/unmutes stream and aliased streams. + muteAliasStreams(stream, index == 0); } } @@ -7896,8 +7925,8 @@ public class AudioService extends IAudioService.Stub private int mIndexMinNoPerm; private int mIndexMax; - private boolean mIsMuted; - private boolean mIsMutedInternally; + private boolean mIsMuted = false; + private boolean mIsMutedInternally = false; private String mVolumeIndexSettingName; @NonNull private Set mObservedDeviceSet = new TreeSet<>(); @@ -8355,27 +8384,10 @@ public class AudioService extends IAudioService.Stub public boolean mute(boolean state) { boolean changed = false; synchronized (VolumeStreamState.class) { - if (state != mIsMuted) { - changed = true; - mIsMuted = state; - - // Set the new mute volume. This propagates the values to - // the audio system, otherwise the volume won't be changed - // at the lower level. - sendMsg(mAudioHandler, - MSG_SET_ALL_VOLUMES, - SENDMSG_QUEUE, - 0, - 0, - this, 0); - } + changed = mute(state, true); } if (changed) { - // Stream mute changed, fire the intent. - Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION); - intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType); - intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state); - sendBroadcastToAll(intent, null /* options */); + broadcastMuteSetting(mStreamType, state); } return changed; } @@ -8407,6 +8419,41 @@ public class AudioService extends IAudioService.Stub return mIsMuted || mIsMutedInternally; } + /** + * Mute/unmute the stream + * @param state the new mute state + * @param apply true to propagate to HW, or false just to update the cache. May be needed + * to mute a stream and its aliases as applyAllVolume will force settings to aliases. + * It prevents unnecessary calls to {@see AudioSystem#setStreamVolume} + * @return true if the mute state was changed + */ + public boolean mute(boolean state, boolean apply) { + synchronized (VolumeStreamState.class) { + boolean changed = state != mIsMuted; + if (changed) { + mIsMuted = state; + if (apply) { + doMute(); + } + } + return changed; + } + } + + public void doMute() { + synchronized (VolumeStreamState.class) { + // Set the new mute volume. This propagates the values to + // the audio system, otherwise the volume won't be changed + // at the lower level. + sendMsg(mAudioHandler, + MSG_SET_ALL_VOLUMES, + SENDMSG_QUEUE, + 0, + 0, + this, 0); + } + } + public int getStreamType() { return mStreamType; }