summaryrefslogtreecommitdiff
path: root/hal
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2020-05-20 13:50:49 -0700
committerEric Laurent <elaurent@google.com>2020-05-20 13:50:49 -0700
commitf030fe8ae0683949a9f251673b57bf9f48a309fa (patch)
treee149fb9a335549bb75211c7acdfd99122ddb184a /hal
parent680f560798af7c170e29cd30b37c585117cdc26e (diff)
downloadaudio-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.c120
-rw-r--r--hal/audio_hw.h2
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;