Merge "audio: close dlopen'ed library on error" into oc-mr1-dev
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 b29ab0c..dc1afa1 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -306,6 +306,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);
@@ -1102,6 +1103,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 bdc90bf..ad5d1f1 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1793,6 +1793,15 @@
     /* Must be called after removing the usecase from list */
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
         check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS);
+    else if (out->devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
+        struct listnode *node;
+        struct audio_usecase *usecase;
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)
+                select_devices(adev, usecase->id);
+        }
+    }
 
     ALOGV("%s: exit: status(%d)", __func__, ret);
     return ret;
@@ -2050,16 +2059,13 @@
     return -ENOSYS;
 }
 
-static int out_standby(struct audio_stream *stream)
+/* must be called with out->lock locked */
+static int out_standby_l(struct audio_stream *stream)
 {
     struct stream_out *out = (struct stream_out *)stream;
     struct audio_device *adev = out->dev;
     bool do_stop = true;
 
-    ALOGV("%s: enter: usecase(%d: %s)", __func__,
-          out->usecase, use_case_table[out->usecase]);
-
-    lock_output_stream(out);
     if (!out->standby) {
         if (adev->adm_deregister_stream)
             adev->adm_deregister_stream(adev->adm_data, out->handle);
@@ -2088,6 +2094,18 @@
         }
         pthread_mutex_unlock(&adev->lock);
     }
+    return 0;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+
+    ALOGV("%s: enter: usecase(%d: %s)", __func__,
+          out->usecase, use_case_table[out->usecase]);
+
+    lock_output_stream(out);
+    out_standby_l(stream);
     pthread_mutex_unlock(&out->lock);
     ALOGV("%s: exit", __func__);
     return 0;
@@ -2174,6 +2192,15 @@
     return out == adev->primary_output || out == adev->voice_tx_output;
 }
 
+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;
@@ -2192,7 +2219,19 @@
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
     if (ret >= 0) {
         val = atoi(value);
+
         lock_output_stream(out);
+
+        // The usb driver needs to be closed after usb device disconnection
+        // otherwise audio is no longer played on the new usb devices.
+        // By forcing the stream in standby, the usb stack refcount drops to 0
+        // and the driver is closed.
+        if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD && val == AUDIO_DEVICE_NONE &&
+                audio_is_usb_out_device(out->devices)) {
+            ALOGD("%s() putting the usb device in standby after disconnection", __func__);
+            out_standby_l(&out->stream.common);
+        }
+
         pthread_mutex_lock(&adev->lock);
 
         /*
@@ -2207,6 +2246,21 @@
             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
+        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);
+            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
@@ -2225,7 +2279,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;
@@ -2269,6 +2322,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);
@@ -3124,16 +3178,28 @@
     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
+            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);
+                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);
             }
         }
     }
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index d6b393f..b324b66 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2548,9 +2548,15 @@
         } else if (out_device & AUDIO_DEVICE_OUT_TELEPHONY_TX) {
             snd_device = SND_DEVICE_IN_VOICE_RX;
         } else if (out_device & (AUDIO_DEVICE_OUT_USB_DEVICE|AUDIO_DEVICE_OUT_USB_HEADSET)) {
-          if (audio_extn_usb_is_capture_supported()) {
-            snd_device = SND_DEVICE_IN_VOICE_USB_HEADSET_MIC;
-          }
+            if (audio_extn_usb_is_capture_supported()) {
+              snd_device = SND_DEVICE_IN_VOICE_USB_HEADSET_MIC;
+            } else if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode) {
+                if (my_data->source_mic_type & SOURCE_DUAL_MIC) {
+                    snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC;
+                } else {
+                    snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
+                }
+            }
         }
     } else if (source == AUDIO_SOURCE_CAMCORDER) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
@@ -2632,8 +2638,12 @@
         }
     } else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION ||
                mode == AUDIO_MODE_IN_COMMUNICATION) {
-        if (out_device & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE))
+        if (out_device & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+            out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+            (out_device & (AUDIO_DEVICE_OUT_USB_DEVICE | AUDIO_DEVICE_OUT_USB_HEADSET) &&
+                !audio_extn_usb_is_capture_supported())) {
             in_device = AUDIO_DEVICE_IN_BACK_MIC;
+        }
         if (adev->active_input) {
             if (adev->active_input->enable_aec &&
                     adev->active_input->enable_ns) {