Workaround: Do not send command to a disconnected usb subsystem
After usb headset disconnection,
the audio policy receives two separate messages warning it of a
usb recoding device and usb playback device disconnection.
If the recording message is handled before the playback one,
the policy transiently switches from headset to headphone then speaker.
Thus temporally rerouting existing streams to a device that no longer exists.
This rerouting causes all usb commands and audio writes to freeze (for 2sec).
Delaying the subsequent routing to speaker by ~8sec.
If the unnecessary routing was to be removed this workaround might not
be necessary.
Taking the problem the other way around, an other fix would
be for the usb drivers to fail immediately if no
usb is present. This fix is targeted for the next release.
In the meantime, this commit patches the primary hal to intercept
routing requests towards a disconnected usb device
and immediately return an error.
Test: disconnect usb headset while playing video in Photo
Bug: 62815577
Change-Id: Ica8305db04f923a2120be7ec9944e99e64a9c75a
Signed-off-by: Kevin Rocard <krocard@google.com>
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 36a8ba5..c7d6768 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -82,6 +82,7 @@
#define audio_extn_usb_get_max_channels(dir) (0)
#define audio_extn_usb_get_max_bit_width(dir) (0)
#define audio_extn_usb_sup_sample_rates(t, s, l) (0)
+#define audio_extn_usb_alive(adev) (false)
#else
void audio_extn_usb_init(void *adev);
void audio_extn_usb_deinit();
@@ -98,6 +99,7 @@
int audio_extn_usb_get_max_channels(bool is_playback);
int audio_extn_usb_get_max_bit_width(bool is_playback);
int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
+bool audio_extn_usb_alive(int card);
#endif
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
index 4b81ae2..743ba58 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -305,6 +305,7 @@
goto done;
}
+ // TODO: figure up if this wait is needed any more
while (tries--) {
if (access(path, F_OK) < 0) {
ALOGW("stream %s doesn't exist retrying\n", path);
@@ -1101,6 +1102,13 @@
return;
}
+bool audio_extn_usb_alive(int card) {
+ char path[PATH_MAX] = {0};
+ // snprintf should never fail
+ (void) snprintf(path, sizeof(path), "/proc/asound/card%u/stream0", card);
+ return access(path, F_OK) == 0;
+}
+
void audio_extn_usb_init(void *adev)
{
if (usbmod == NULL) {
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index fd32e77..5b5a434 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -2217,6 +2217,19 @@
val = AUDIO_DEVICE_OUT_SPEAKER;
}
+ audio_devices_t new_dev = val;
+
+ // Workaround: If routing to an non existing usb device, fail gracefully
+ // The routing request will otherwise block during 10 second
+ if (audio_is_usb_out_device(new_dev) && !audio_extn_usb_alive(adev->snd_card)) {
+ ALOGW("out_set_parameters() ignoring rerouting to non existing USB card %d",
+ adev->snd_card);
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&out->lock);
+ status = -ENOSYS;
+ goto routing_fail;
+ }
+
/*
* select_devices() call below switches all the usecases on the same
* backend to the new device. Refer to check_and_route_playback_usecases() in
@@ -2235,7 +2248,6 @@
* Because select_devices() must be called to switch back the music
* playback to headset.
*/
- audio_devices_t new_dev = val;
if (new_dev != AUDIO_DEVICE_NONE) {
bool same_dev = out->devices == new_dev;
out->devices = new_dev;
@@ -2279,6 +2291,7 @@
/*handles device and call state changes*/
audio_extn_extspk_update(adev->extspk);
}
+ routing_fail:
if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
parse_compress_metadata(out, parms);
@@ -3125,16 +3138,26 @@
if (ret >= 0) {
val = atoi(value);
if (((int)in->device != val) && (val != 0) && audio_is_input_device(val) ) {
- in->device = val;
- /* If recording is in progress, change the tx device to new device */
- if (!in->standby) {
- ALOGV("update input routing change");
- // inform adm before actual routing to prevent glitches.
- if (adev->adm_on_routing_change) {
- adev->adm_on_routing_change(adev->adm_data,
- in->capture_handle);
+
+ // Workaround: If routing to an non existing usb device, fail gracefully
+ // The routing request will otherwise block during 10 second
+ if (audio_is_usb_in_device(val) && !audio_extn_usb_alive(adev->snd_card)) {
+ ALOGW("in_set_parameters() ignoring rerouting to non existing USB card %d",
+ adev->snd_card);
+ status = -ENOSYS;
+ } else {
+
+ in->device = val;
+ /* If recording is in progress, change the tx device to new device */
+ if (!in->standby) {
+ ALOGV("update input routing change");
+ // inform adm before actual routing to prevent glitches.
+ if (adev->adm_on_routing_change) {
+ adev->adm_on_routing_change(adev->adm_data,
+ in->capture_handle);
+ }
+ select_devices(adev, in->usecase);
}
- select_devices(adev, in->usecase);
}
}
}