diff options
author | Eric Laurent <elaurent@google.com> | 2020-05-20 13:50:49 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2020-05-20 13:50:49 -0700 |
commit | f030fe8ae0683949a9f251673b57bf9f48a309fa (patch) | |
tree | e149fb9a335549bb75211c7acdfd99122ddb184a /hal | |
parent | 680f560798af7c170e29cd30b37c585117cdc26e (diff) | |
download | audio-f030fe8ae0683949a9f251673b57bf9f48a309fa.tar.gz |
audio hal: fix audio leak when disconnecting A2DP sink
When A2DP sink is disconnected, the A2DP offload path may be
disabled before the audio framework pauses the output stream, in
which case the HAL forces the audio route to speaker.
When this happens, the front end must be muted to avoid audio
leaking over speaker path potentially at full volume if
A2DP absolute volume is enabled.
Bug: 156044862
Test: repro steps in the bug.
Change-Id: I90de8729ac862ca794dbdcf85fc1fa9dfabbf23b
Diffstat (limited to 'hal')
-rw-r--r-- | hal/audio_hw.c | 120 | ||||
-rw-r--r-- | hal/audio_hw.h | 2 |
2 files changed, 109 insertions, 13 deletions
diff --git a/hal/audio_hw.c b/hal/audio_hw.c index 035b14e..0f868ca 100644 --- a/hal/audio_hw.c +++ b/hal/audio_hw.c @@ -72,6 +72,8 @@ /* treat as unsigned Q1.13 */ #define APP_TYPE_GAIN_DEFAULT 0x2000 #define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000 +#define PCM_PLAYBACK_VOLUME_MAX 0x2000 +#define INVALID_OUT_VOLUME -1 /* treat as unsigned Q1.13 */ #define VOIP_PLAYBACK_VOLUME_MAX 0x2000 @@ -359,7 +361,8 @@ static unsigned int audio_device_ref_count; static int last_known_cal_step = -1 ; static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore); -static int set_compr_volume(struct audio_stream_out *stream, float left, float right); +static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right); +static int out_set_pcm_volume(struct audio_stream_out *stream, float left, float right); static int in_set_microphone_direction(const struct audio_stream_in *stream, audio_microphone_direction_t dir); @@ -2602,6 +2605,11 @@ int start_output_stream(struct stream_out *out) goto error_open; } } + if ((out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY + || out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER + || out->usecase == USECASE_AUDIO_PLAYBACK_ULL)) { + out_set_pcm_volume(&out->stream, out->volume_l, out->volume_r); + } } register_out_stream(out); @@ -2969,6 +2977,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) bool select_new_device = false; int status = 0; bool bypass_a2dp = false; + bool forced_speaker_fallback = false; ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s", __func__, out->usecase, use_case_table[out->usecase], kvpairs); @@ -2990,6 +2999,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) out_standby_l(&out->stream.common); } val = AUDIO_DEVICE_OUT_SPEAKER; + forced_speaker_fallback = true; } pthread_mutex_lock(&adev->lock); @@ -3004,6 +3014,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) if (out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL && val == AUDIO_DEVICE_NONE) { val = AUDIO_DEVICE_OUT_SPEAKER; + forced_speaker_fallback = true; } /* @@ -3018,6 +3029,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) !audio_extn_a2dp_is_ready() && !adev->bt_sco_on) { val = AUDIO_DEVICE_OUT_SPEAKER; + forced_speaker_fallback = true; } /* To avoid a2dp to sco overlapping / BT device improper state @@ -3094,6 +3106,42 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) } if (!out->standby) { + int volume_delay_us = 0; + if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + pthread_mutex_lock(&out->compr_mute_lock); + if (out->a2dp_compress_mute && + (!(new_dev & AUDIO_DEVICE_OUT_ALL_A2DP) || + audio_extn_a2dp_is_ready())) { + out->a2dp_compress_mute = false; + } + float volume_l = out->volume_l; + float volume_r = out->volume_r; + if (out->a2dp_compress_mute || forced_speaker_fallback) { + volume_l = 0.0; + volume_r = 0.0; + } + if (volume_l != out->applied_volume_l || volume_r != out->applied_volume_r) + volume_delay_us = COMPRESS_OFFLOAD_PLAYBACK_LATENCY * 2000; + + out_set_compr_volume(&out->stream, volume_l, volume_r); + pthread_mutex_unlock(&out->compr_mute_lock); + } else if (out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY || + out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER || + out->usecase == USECASE_AUDIO_PLAYBACK_ULL) { + float volume_l = out->volume_l; + float volume_r = out->volume_r; + if (forced_speaker_fallback) { + volume_l = 0.0; + volume_r = 0.0; + } + if (volume_l != out->applied_volume_l || volume_r != out->applied_volume_r) + volume_delay_us = (int)platform_render_latency(out) * 2; + + out_set_pcm_volume(&out->stream, volume_l, volume_r); + } + if (volume_delay_us > 0) + usleep(volume_delay_us * 2); + if (!same_dev) { ALOGV("update routing change"); // inform adm before actual routing to prevent glitches. @@ -3120,14 +3168,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) if (!same_dev) platform_set_swap_channels(adev, true); - if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && - out->a2dp_compress_mute && - (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_is_ready())) { - pthread_mutex_lock(&out->compr_mute_lock); - out->a2dp_compress_mute = false; - set_compr_volume(&out->stream, out->volume_l, out->volume_r); - pthread_mutex_unlock(&out->compr_mute_lock); - } + } } @@ -3299,7 +3340,7 @@ static uint32_t out_get_latency(const struct audio_stream_out *stream) return latency; } -static int set_compr_volume(struct audio_stream_out *stream, float left, +static int out_set_compr_volume(struct audio_stream_out *stream, float left, float right) { struct stream_out *out = (struct stream_out *)stream; @@ -3310,6 +3351,9 @@ static int set_compr_volume(struct audio_stream_out *stream, float left, int pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK); + if (left == out->applied_volume_l && right == out->applied_volume_r) + return 0; + snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "Compress Playback %d Volume", pcm_device_id); ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); @@ -3324,9 +3368,49 @@ static int set_compr_volume(struct audio_stream_out *stream, float left, volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX); mixer_ctl_set_array(ctl, volume, sizeof(volume) / sizeof(volume[0])); + out->applied_volume_l = left; + out->applied_volume_r = right; return 0; } +static int out_set_pcm_volume(struct audio_stream_out *stream, float left, + float right) +{ + struct stream_out *out = (struct stream_out *)stream; + + if (left == out->applied_volume_l && right == out->applied_volume_r) + return 0; + + /* Volume control for pcm playback */ + if (left != right) { + return -EINVAL; + } else { + char mixer_ctl_name[128]; + struct audio_device *adev = out->dev; + struct mixer_ctl *ctl; + int pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK); + snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "Playback %d Volume", pcm_device_id); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s : Could not get ctl for mixer cmd - %s", __func__, mixer_ctl_name); + return -EINVAL; + } + + int volume = (int) (left * PCM_PLAYBACK_VOLUME_MAX); + int ret = mixer_ctl_set_value(ctl, 0, volume); + if (ret < 0) { + ALOGE("%s: Could not set ctl, error:%d ", __func__, ret); + return -EINVAL; + } + + ALOGV("%s : Pcm set volume value %d left %f", __func__, volume, left); + + out->applied_volume_l = left; + out->applied_volume_r = right; + return 0; + } +} + static int out_set_volume(struct audio_stream_out *stream, float left, float right) { @@ -3341,7 +3425,7 @@ static int out_set_volume(struct audio_stream_out *stream, float left, pthread_mutex_lock(&out->compr_mute_lock); ALOGV("%s: compress mute %d", __func__, out->a2dp_compress_mute); if (!out->a2dp_compress_mute) - ret = set_compr_volume(stream, left, right); + ret = out_set_compr_volume(stream, left, right); out->volume_l = left; out->volume_r = right; pthread_mutex_unlock(&out->compr_mute_lock); @@ -5293,6 +5377,13 @@ static int adev_open_output_stream(struct audio_hw_device *dev, } } + if (out->usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY || + out->usecase == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER || + out->usecase == USECASE_AUDIO_PLAYBACK_ULL) { + out->volume_l = 1.0; + out->volume_r = 1.0; + } + if (config->sample_rate == 0) { out->sample_rate = out->config.rate; } else { @@ -5375,6 +5466,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev, } pthread_mutex_unlock(&adev->lock); + out->applied_volume_l = INVALID_OUT_VOLUME; + out->applied_volume_r = INVALID_OUT_VOLUME; + out->stream.common.get_sample_rate = out_get_sample_rate; out->stream.common.set_sample_rate = out_set_sample_rate; out->stream.common.get_buffer_size = out_get_buffer_size; @@ -6387,7 +6481,7 @@ static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *ou if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && (out->a2dp_compress_mute)) { out->a2dp_compress_mute = false; - set_compr_volume(&out->stream, out->volume_l, out->volume_r); + out_set_compr_volume(&out->stream, out->volume_l, out->volume_r); } pthread_mutex_unlock(&out->compr_mute_lock); } @@ -6404,7 +6498,7 @@ static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *ou right_p = out->volume_r; if (out->offload_state == OFFLOAD_STATE_PLAYING) compress_pause(out->compr); - set_compr_volume(&out->stream, 0.0f, 0.0f); + out_set_compr_volume(&out->stream, 0.0f, 0.0f); out->a2dp_compress_mute = true; select_devices(adev, out->usecase); if (out->offload_state == OFFLOAD_STATE_PLAYING) diff --git a/hal/audio_hw.h b/hal/audio_hw.h index 15cfa60..e980e88 100644 --- a/hal/audio_hw.h +++ b/hal/audio_hw.h @@ -256,6 +256,8 @@ struct stream_out { bool a2dp_compress_mute; float volume_l; float volume_r; + float applied_volume_l; + float applied_volume_r; error_log_t *error_log; |