From b97b3980f49f17bf027756d8fad8e4ab4e5d1ec5 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 31 May 2019 16:21:59 -0700 Subject: [PATCH] AudioService: hearing aid volume updates for voice vs media Fix interactions between hearing aid volume control and Sound Settings and Slice UIs with the following changes: When receiving volume change, only change the hearing aid volume if the device is hearing aid and the stream type to change matches the stream type returned by AudioService.getHearingAidStreamType(). Modify AudioService.getHearingAidStreamType() to rely on the audio mode OR the activity of the VOICE_COMMUNICATION* usage. Add a listener of audio playback activity to monitor the start and end of the use of VOICE_COMMUNICATION* to reset the hearing aid volume to that of media. Do this only when HEARING_AID device is connected: register/unregister callback for playback activity according to hearing aid connections. Bug: 129163231 Test: use Sound Settings UI to control voice vol outside of call, verify volume is unchanged when listening to media later. Change-Id: I7af9554d04139e1a3929efa53cdcb04e37878571 --- .../android/server/audio/AudioService.java | 76 +++++++++++++++++-- .../server/audio/AudioServiceEvents.java | 46 ++++++++++- 2 files changed, 111 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 7fde0ae8d445f..4c6c728d73144 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -162,6 +162,7 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; /** * The implementation of the volume manager service. @@ -265,6 +266,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26; private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27; private static final int MSG_HDMI_VOLUME_CHECK = 28; + private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -1870,9 +1872,15 @@ public class AudioService extends IAudioService.Stub // Check if volume update should be send to Hearing Aid if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) { - Log.i(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" + newIndex - + " stream=" + streamType); - mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType); + // only modify the hearing aid attenuation when the stream to modify matches + // the one expected by the hearing aid + if (streamType == getHearingAidStreamType()) { + if (DEBUG_VOL) { + Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" + + newIndex + " stream=" + streamType); + } + mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType); + } } // Check if volume update should be sent to Hdmi system audio. @@ -2164,11 +2172,53 @@ public class AudioService extends IAudioService.Stub return AudioSystem.STREAM_VOICE_CALL; case AudioSystem.MODE_NORMAL: default: + // other conditions will influence the stream type choice, read on... break; } + if (mVoiceActive.get()) { + return AudioSystem.STREAM_VOICE_CALL; + } return AudioSystem.STREAM_MUSIC; } + private AtomicBoolean mVoiceActive = new AtomicBoolean(false); + + private final IPlaybackConfigDispatcher mVoiceActivityMonitor = + new IPlaybackConfigDispatcher.Stub() { + @Override + public void dispatchPlaybackConfigChange(List configs, + boolean flush) { + sendMsg(mAudioHandler, MSG_PLAYBACK_CONFIG_CHANGE, SENDMSG_REPLACE, + 0 /*arg1 ignored*/, 0 /*arg2 ignored*/, + configs /*obj*/, 0 /*delay*/); + } + }; + + private void onPlaybackConfigChange(List configs) { + boolean voiceActive = false; + for (AudioPlaybackConfiguration config : configs) { + final int usage = config.getAudioAttributes().getUsage(); + if ((usage == AudioAttributes.USAGE_VOICE_COMMUNICATION + || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) + && config.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + voiceActive = true; + break; + } + } + if (mVoiceActive.getAndSet(voiceActive) != voiceActive) { + updateHearingAidVolumeOnVoiceActivityUpdate(); + } + } + + private void updateHearingAidVolumeOnVoiceActivityUpdate() { + final int streamType = getHearingAidStreamType(); + final int index = getStreamVolume(streamType); + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, + mVoiceActive.get(), streamType, index)); + mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + + } + /** * Manage an audio mode change for audio devices that use an "absolute volume" model, * i.e. the framework sends the full scale signal, and the actual volume for the use case @@ -2203,10 +2253,8 @@ public class AudioService extends IAudioService.Stub // handling of specific interfaces goes here: if ((device & mAbsVolumeMultiModeCaseDevices) == AudioSystem.DEVICE_OUT_HEARING_AID) { final int index = getStreamVolume(streamType); - mModeLogger.log(new AudioEventLogger.StringEvent("setMode to " - + AudioSystem.modeToString(newMode) - + " causes setting HEARING_AID volume to idx:" + index - + " stream:" + AudioSystem.streamToString(streamType))); + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_MODE_CHANGE_HEARING_AID, + newMode, streamType, index)); mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); } } @@ -2272,7 +2320,8 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10); } - if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) { + if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0 + && streamType == getHearingAidStreamType()) { Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index + " stream=" + streamType); mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType); @@ -4348,6 +4397,11 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + " (dis)connection, got " + state); } + if (state == BluetoothProfile.STATE_CONNECTED) { + mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); + } else { + mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); + } mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( device, state, suppressNoisyIntent, musicDevice, "AudioService"); } @@ -4853,6 +4907,7 @@ public class AudioService extends IAudioService.Stub pw.println((mIndexMin + 5) / 10); pw.print(" Max: "); pw.println((mIndexMax + 5) / 10); + pw.print(" streamVolume:"); pw.println(getStreamVolume(mStreamType)); pw.print(" Current: "); for (int i = 0; i < mIndexMap.size(); i++) { if (i > 0) { @@ -5429,6 +5484,11 @@ public class AudioService extends IAudioService.Stub case MSG_HDMI_VOLUME_CHECK: onCheckVolumeCecOnHdmiConnection(msg.arg1, (String) msg.obj); + break; + + case MSG_PLAYBACK_CONFIG_CHANGE: + onPlaybackConfigChange((List) msg.obj); + break; } } } diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index d999217bf29b5..fcd8701f4ccf6 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -95,6 +95,8 @@ public class AudioServiceEvents { static final int VOL_SET_HEARING_AID_VOL = 3; static final int VOL_SET_AVRCP_VOL = 4; static final int VOL_ADJUST_VOL_UID = 5; + static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6; + static final int VOL_MODE_CHANGE_HEARING_AID = 7; final int mOp; final int mStream; @@ -102,6 +104,10 @@ public class AudioServiceEvents { final int mVal2; final String mCaller; + /** used for VOL_ADJUST_VOL_UID, + * VOL_ADJUST_SUGG_VOL, + * VOL_ADJUST_STREAM_VOL, + * VOL_SET_STREAM_VOL */ VolumeEvent(int op, int stream, int val1, int val2, String caller) { mOp = op; mStream = stream; @@ -110,24 +116,46 @@ public class AudioServiceEvents { mCaller = caller; } + /** used for VOL_SET_HEARING_AID_VOL*/ VolumeEvent(int op, int index, int gainDb) { mOp = op; mVal1 = index; mVal2 = gainDb; - //unused + // unused mStream = -1; mCaller = null; } + /** used for VOL_SET_AVRCP_VOL */ VolumeEvent(int op, int index) { mOp = op; mVal1 = index; - //unused + // unused mVal2 = 0; mStream = -1; mCaller = null; } + /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ + VolumeEvent(int op, boolean voiceActive, int stream, int index) { + mOp = op; + mStream = stream; + mVal1 = index; + mVal2 = voiceActive ? 1 : 0; + // unused + mCaller = null; + } + + /** used for VOL_MODE_CHANGE_HEARING_AID */ + VolumeEvent(int op, int mode, int stream, int index) { + mOp = op; + mStream = stream; + mVal1 = index; + mVal2 = mode; + // unused + mCaller = null; + } + @Override public String eventToString() { switch (mOp) { @@ -168,7 +196,19 @@ public class AudioServiceEvents { .append(" flags:0x").append(Integer.toHexString(mVal2)) .append(") from ").append(mCaller) .toString(); - default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); + case VOL_VOICE_ACTIVITY_HEARING_AID: + return new StringBuilder("Voice activity change (") + .append(mVal2 == 1 ? "active" : "inactive") + .append(") causes setting HEARING_AID volume to idx:").append(mVal1) + .append(" stream:").append(AudioSystem.streamToString(mStream)) + .toString(); + case VOL_MODE_CHANGE_HEARING_AID: + return new StringBuilder("setMode(") + .append(AudioSystem.modeToString(mVal2)) + .append(") causes setting HEARING_AID volume to idx:").append(mVal1) + .append(" stream:").append(AudioSystem.streamToString(mStream)) + .toString(); + default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } }