Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f030fe8a authored by Eric Laurent's avatar Eric Laurent
Browse files

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
parent 680f5607
Loading
Loading
Loading
Loading
+107 −13
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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;