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
Signed-off-by: Kevin Rocard <krocard@google.com>
(cherry picked from commit f9f241e45e5a76458e5f5596bca495317981ca3a)

-Routing to usb was rejected due to incorrect card number

Fixes regression introduced by: f9f241e45e5a76458e5f5596bca495317981ca3a

The card number is updated by adev_set_parameters,
that is called after out_set_parameters.
As a result, out_set_parameters was using the wrong card number to check
for the card presence (in an attempt to ignore rerouting to an unplug
card).
Thus all routing to USB we denied.

Bug: 64532887
Test: Plug and unplug usb headset (skylab) during Photo playback,
      Youtube, Play music and voice call.
Signed-off-by: Kevin Rocard <krocard@google.com>
(cherry picked from commit 1e02c88b8fa620f7a968413b21daae9cfce04c2a)

Change-Id: I148a0b35067703e6ba3ca3414d3afe9cf6718dc0
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index f29ca30..f1f45c0 100755
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -221,6 +221,7 @@
 #define audio_extn_usb_get_max_bit_width(p)                            (0)
 #define audio_extn_usb_get_sup_sample_rates(t, s, l)                   (0)
 #define audio_extn_usb_is_tunnel_supported()                           (0)
+#define audio_extn_usb_alive(adev)                                     (false)
 #else
 void audio_extn_usb_init(void *adev);
 void audio_extn_usb_deinit();
@@ -238,6 +239,7 @@
 int audio_extn_usb_get_max_bit_width(bool playback);
 int audio_extn_usb_get_sup_sample_rates(int type, uint32_t *sr, uint32_t l);
 bool audio_extn_usb_is_tunnel_supported();
+bool audio_extn_usb_alive(int card);
 #endif
 
 #ifndef SPLIT_A2DP_ENABLED
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
index 8fa47a8..af8cc89 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -1133,6 +1133,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 1a8f0a1..06b702d 100755
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -3573,6 +3573,15 @@
     return;
 }
 
+static int get_alive_usb_card(struct str_parms* parms) {
+    int card;
+    if ((str_parms_get_int(parms, "card", &card) >= 0) &&
+        !audio_extn_usb_alive(card)) {
+        return card;
+    }
+    return -ENODEV;
+}
+
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
     struct stream_out *out = (struct stream_out *)stream;
@@ -3640,6 +3649,22 @@
                 }
             }
         }
+
+        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
+        int card;
+        if (audio_is_usb_out_device(new_dev) &&
+            (card = get_alive_usb_card(parms)) >= 0) {
+
+            ALOGW("out_set_parameters() ignoring rerouting to non existing USB card %d", card);
+            pthread_mutex_unlock(&adev->lock);
+            pthread_mutex_unlock(&out->lock);
+            ret = -ENOSYS;
+            goto routing_fail;
+        }
+
         /*
          * select_devices() call below switches all the usecases on the same
          * backend to the new device. Refer to check_usecases_codec_backend() in
@@ -3714,6 +3739,7 @@
         pthread_mutex_unlock(&adev->lock);
         pthread_mutex_unlock(&out->lock);
     }
+    routing_fail:
 
     if (out == adev->primary_output) {
         pthread_mutex_lock(&adev->lock);
@@ -5125,15 +5151,29 @@
     err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
     if (err >= 0) {
         val = atoi(value);
-        if (((int)in->device != val) && (val != 0)) {
-            in->device = val;
-            /* If recording is in progress, change the tx device to new device */
-            if (!in->standby && !in->is_st_session) {
-                ALOGV("update input routing change");
-                if (adev->adm_on_routing_change)
+        if (((int)in->device != val) && (val != 0) && audio_is_input_device(val) ) {
+
+            // Workaround: If routing to an non existing usb device, fail gracefully
+            // The routing request will otherwise block during 10 second
+            int card;
+            if (audio_is_usb_in_device(val) &&
+                (card = get_alive_usb_card(parms)) >= 0) {
+
+                ALOGW("in_set_parameters() ignoring rerouting to non existing USB card %d", card);
+                ret = -ENOSYS;
+            } else {
+
+                in->device = val;
+                /* If recording is in progress, change the tx device to new device */
+                if (!in->standby && !in->is_st_session) {
+                    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);
-                ret = select_devices(adev, in->usecase);
+                        ret = select_devices(adev, in->usecase);
+                    }
+                }
             }
         }
     }