Merge "hal: Add support for audio patches in HAL"
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c0e8cf4..986e24a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -835,6 +835,8 @@
 int audio_extn_utils_get_vendor_enhanced_info();
 int audio_extn_utils_get_app_sample_rate_for_device(struct audio_device *adev,
                                     struct audio_usecase *usecase, int snd_device);
+int audio_extn_utils_hash_fn(void *key);
+bool audio_extn_utils_hash_eq(void *key1, void *key2);
 
 #ifdef DS2_DOLBY_DAP_ENABLED
 #define LIB_DS2_DAP_HAL "vendor/lib/libhwdaphal.so"
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index e729bfe..92887f0 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2014 The Android Open Source Project
@@ -3140,3 +3140,13 @@
 
     return size;
 }
+
+int audio_extn_utils_hash_fn(void *key)
+{
+    return (int)key;
+}
+
+bool audio_extn_utils_hash_eq(void *key1, void *key2)
+{
+    return (key1 == key2);
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d5ae055..f70883d 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -94,6 +94,7 @@
 #define PCM_PLAYBACK_VOLUME_MAX 0x2000
 #define DSD_VOLUME_MIN_DB (-110)
 #define INVALID_OUT_VOLUME -1
+#define AUDIO_IO_PORTS_MAX 32
 
 #define RECORD_GAIN_MIN 0.0f
 #define RECORD_GAIN_MAX 1.0f
@@ -666,6 +667,93 @@
     }
 }
 
+static inline bool free_entry(void *key  __unused,
+                              void *value, void *context __unused)
+{
+    free(value);
+    return true;
+}
+
+static inline void free_map(Hashmap *map)
+{
+    if (map) {
+        hashmapForEach(map, free_entry, (void *) NULL);
+        hashmapFree(map);
+    }
+}
+
+static inline void patch_map_remove(struct audio_device *adev,
+                                audio_patch_handle_t patch_handle)
+{
+    if (patch_handle == AUDIO_PATCH_HANDLE_NONE)
+        return;
+
+    pthread_mutex_lock(&adev->lock);
+    struct audio_patch_info *p_info =
+        hashmapGet(adev->patch_map, (void *) (intptr_t) patch_handle);
+    if (p_info) {
+        ALOGV("%s: Remove patch %d", __func__, patch_handle);
+        hashmapRemove(adev->patch_map, (void *) (intptr_t) patch_handle);
+        free(p_info->patch);
+        pthread_mutex_destroy(&p_info->lock);
+        free(p_info);
+    }
+    pthread_mutex_unlock(&adev->lock);
+}
+
+static inline int io_streams_map_insert(struct audio_device *adev,
+                                    struct audio_stream *stream,
+                                    audio_io_handle_t handle,
+                                    audio_patch_handle_t patch_handle)
+{
+    struct audio_stream_info *s_info =
+            (struct audio_stream_info *) calloc(1, sizeof(struct audio_stream_info));
+
+    if (s_info == NULL) {
+        ALOGE("%s: Could not allocate stream info", __func__);
+        return -ENOMEM;
+    }
+    s_info->stream = stream;
+    s_info->patch_handle = patch_handle;
+    pthread_mutex_init(&s_info->lock, (const pthread_mutexattr_t *) NULL);
+
+    pthread_mutex_lock(&adev->lock);
+    struct audio_stream_info *stream_info =
+            hashmapPut(adev->io_streams_map, (void *) (intptr_t) handle, (void *) s_info);
+    pthread_mutex_unlock(&adev->lock);
+    if (stream_info != NULL)
+        free(stream_info);
+    ALOGD("%s: Added stream in io_streams_map with handle %d", __func__, handle);
+    return 0;
+}
+
+static inline void io_streams_map_remove(struct audio_device *adev,
+                                     audio_io_handle_t handle)
+{
+    pthread_mutex_lock(&adev->lock);
+    struct audio_stream_info *s_info =
+            hashmapRemove(adev->io_streams_map, (void *) (intptr_t) handle);
+    pthread_mutex_unlock(&adev->lock);
+    if (s_info == NULL)
+        return;
+    ALOGD("%s: Removed stream with handle %d", __func__, handle);
+    patch_map_remove(adev, s_info->patch_handle);
+    pthread_mutex_destroy(&s_info->lock);
+    free(s_info);
+    return;
+}
+
+static struct audio_patch_info* fetch_patch_info(struct audio_device *adev,
+                                    audio_patch_handle_t handle)
+{
+    struct audio_patch_info *p_info = NULL;
+    pthread_mutex_lock(&adev->lock);
+    p_info = (struct audio_patch_info *)
+                 hashmapGet(adev->patch_map, (void *) (intptr_t) handle);
+    pthread_mutex_unlock(&adev->lock);
+    return p_info;
+}
+
 __attribute__ ((visibility ("default")))
 bool audio_hw_send_gain_dep_calibration(int level) {
     bool ret_val = false;
@@ -4508,18 +4596,206 @@
     return -ENODEV;
 }
 
+int route_output_stream(struct stream_out *out,
+                        audio_devices_t devices,
+                        char *address)
+{
+    struct audio_device *adev = out->dev;
+    struct str_parms *addr;
+    int ret = 0;
+    audio_devices_t new_devices = devices;
+    bool bypass_a2dp = false;
+    bool reconfig = false;
+    unsigned long service_interval = 0;
+
+    ALOGD("%s: enter: usecase(%d: %s) devices %x",
+          __func__, out->usecase, use_case_table[out->usecase], devices);
+    addr = str_parms_create_str(address);
+    if (!addr)
+        goto error;
+
+    lock_output_stream(out);
+    pthread_mutex_lock(&adev->lock);
+
+    /*
+     * When HDMI cable is unplugged the music playback is paused and
+     * the policy manager sends routing=0. But the audioflinger continues
+     * to write data until standby time (3sec). As the HDMI core is
+     * turned off, the write gets blocked.
+     * Avoid this by routing audio to speaker until standby.
+     */
+    if ((out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
+            (new_devices == AUDIO_DEVICE_NONE) &&
+            !audio_extn_passthru_is_passthrough_stream(out) &&
+            (platform_get_edid_info(adev->platform) != 0) /* HDMI disconnected */) {
+        new_devices = AUDIO_DEVICE_OUT_SPEAKER;
+    }
+    /*
+     * When A2DP is disconnected the
+     * music playback is paused and the policy manager sends routing=0
+     * But the audioflinger continues to write data until standby time
+     * (3sec). As BT is turned off, the write gets blocked.
+     * Avoid this by routing audio to speaker until standby.
+     */
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+            (new_devices == AUDIO_DEVICE_NONE) &&
+            !audio_extn_a2dp_source_is_ready() &&
+            !adev->bt_sco_on) {
+            new_devices = AUDIO_DEVICE_OUT_SPEAKER;
+    }
+    /*
+     * When USB headset is disconnected the music platback paused
+     * and the policy manager send routing=0. But if the USB is connected
+     * back before the standby time, AFE is not closed and opened
+     * when USB is connected back. So routing to speker will guarantee
+     * AFE reconfiguration and AFE will be opend once USB is connected again
+     */
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_USB) &&
+            (new_devices == AUDIO_DEVICE_NONE) &&
+            !audio_extn_usb_connected(addr))
+        new_devices = AUDIO_DEVICE_OUT_SPEAKER;
+
+    /* To avoid a2dp to sco overlapping / BT device improper state
+     * check with BT lib about a2dp streaming support before routing
+     */
+    if (new_devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+        if (!audio_extn_a2dp_source_is_ready()) {
+            if (new_devices &
+                (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+                //combo usecase just by pass a2dp
+                ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
+                bypass_a2dp = true;
+            } else {
+                ALOGE("%s: A2DP profile is not ready,ignoring routing request", __func__);
+                /* update device to a2dp and don't route as BT returned error
+                 * However it is still possible a2dp routing called because
+                 * of current active device disconnection (like wired headset)
+                 */
+                out->devices = new_devices;
+                pthread_mutex_unlock(&adev->lock);
+                pthread_mutex_unlock(&out->lock);
+                goto error;
+            }
+        }
+    }
+
+
+    // 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_devices) &&
+        (card = get_alive_usb_card(addr)) >= 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 error;
+    }
+
+    /*
+     * select_devices() call below switches all the usecases on the same
+     * backend to the new device. Refer to check_usecases_codec_backend() in
+     * the select_devices(). But how do we undo this?
+     *
+     * For example, music playback is active on headset (deep-buffer usecase)
+     * and if we go to ringtones and select a ringtone, low-latency usecase
+     * will be started on headset+speaker. As we can't enable headset+speaker
+     * and headset devices at the same time, select_devices() switches the music
+     * playback to headset+speaker while starting low-lateny usecase for ringtone.
+     * So when the ringtone playback is completed, how do we undo the same?
+     *
+     * We are relying on the out_set_parameters() call on deep-buffer output,
+     * once the ringtone playback is ended.
+     * NOTE: We should not check if the current devices are same as new devices.
+     *       Because select_devices() must be called to switch back the music
+     *       playback to headset.
+     */
+    if (new_devices != AUDIO_DEVICE_NONE) {
+        bool same_dev = out->devices == new_devices;
+        out->devices = new_devices;
+
+        if (output_drives_call(adev, out)) {
+            if (!voice_is_call_state_active(adev)) {
+                if (adev->mode == AUDIO_MODE_IN_CALL) {
+                    adev->current_call_output = out;
+                    if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
+                        service_interval =
+                            audio_extn_usb_find_service_interval(true, true /*playback*/);
+                        audio_extn_usb_set_service_interval(true /*playback*/,
+                                                            service_interval,
+                                                            &reconfig);
+                        ALOGD("%s, svc_int(%ld),reconfig(%d)",__func__,service_interval, reconfig);
+                    }
+                    ret = voice_start_call(adev);
+                }
+            } else {
+                adev->current_call_output = out;
+                voice_update_devices_for_all_voice_usecases(adev);
+            }
+        }
+
+        if (!out->standby) {
+            if (!same_dev) {
+                ALOGV("update routing change");
+                audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
+                                             adev->perf_lock_opts,
+                                             adev->perf_lock_opts_size);
+                if (adev->adm_on_routing_change)
+                    adev->adm_on_routing_change(adev->adm_data,
+                                                out->handle);
+            }
+            if (!bypass_a2dp) {
+                select_devices(adev, out->usecase);
+            } else {
+                if (new_devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+                    out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+                else
+                    out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                select_devices(adev, out->usecase);
+                out->devices = new_devices;
+            }
+
+            if (!same_dev) {
+                // on device switch force swap, lower functions will make sure
+                // to check if swap is allowed or not.
+                platform_set_swap_channels(adev, true);
+                audio_extn_perf_lock_release(&adev->perf_lock_handle);
+            }
+            if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+                 out->a2dp_compress_mute &&
+                 (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_source_is_ready())) {
+                pthread_mutex_lock(&out->compr_mute_lock);
+                out->a2dp_compress_mute = false;
+                out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+                pthread_mutex_unlock(&out->compr_mute_lock);
+            } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
+                out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&out->lock);
+
+    /*handles device and call state changes*/
+    audio_extn_extspk_update(adev->extspk);
+
+error:
+    if (addr)
+        str_parms_destroy(addr);
+    ALOGV("%s: exit: code(%d)", __func__, ret);
+    return ret;
+}
+
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
     struct stream_out *out = (struct stream_out *)stream;
     struct audio_device *adev = out->dev;
     struct str_parms *parms;
     char value[32];
-    int ret = 0, val = 0, err;
+    int ret = 0, err;
     int ext_controller = -1;
     int ext_stream = -1;
-    bool bypass_a2dp = false;
-    bool reconfig = false;
-    unsigned long service_interval = 0;
 
     ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
           __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -4537,182 +4813,6 @@
               out->extconn.cs.stream);
     }
 
-    err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
-    if (err >= 0) {
-        val = atoi(value);
-        lock_output_stream(out);
-        pthread_mutex_lock(&adev->lock);
-
-        /*
-         * When HDMI cable is unplugged the music playback is paused and
-         * the policy manager sends routing=0. But the audioflinger continues
-         * to write data until standby time (3sec). As the HDMI core is
-         * turned off, the write gets blocked.
-         * Avoid this by routing audio to speaker until standby.
-         */
-        if ((out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
-                (val == AUDIO_DEVICE_NONE) &&
-                !audio_extn_passthru_is_passthrough_stream(out) &&
-                (platform_get_edid_info_v2(adev->platform,
-                                           out->extconn.cs.controller,
-                                           out->extconn.cs.stream) != 0)) {
-            out->extconn.cs.controller = out->extconn.cs.stream = -1;
-            val = AUDIO_DEVICE_OUT_SPEAKER;
-        }
-        /*
-         * When A2DP is disconnected the
-         * music playback is paused and the policy manager sends routing=0
-         * But the audioflinger continues to write data until standby time
-         * (3sec). As BT is turned off, the write gets blocked.
-         * Avoid this by routing audio to speaker until standby.
-         */
-        if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
-                (val == AUDIO_DEVICE_NONE) &&
-                !audio_extn_a2dp_source_is_ready() &&
-                !adev->bt_sco_on) {
-                val = AUDIO_DEVICE_OUT_SPEAKER;
-        }
-        /*
-        * When USB headset is disconnected the music platback paused
-        * and the policy manager send routing=0. But if the USB is connected
-        * back before the standby time, AFE is not closed and opened
-        * when USB is connected back. So routing to speker will guarantee
-        * AFE reconfiguration and AFE will be opend once USB is connected again
-        */
-        if ((out->devices & AUDIO_DEVICE_OUT_ALL_USB) &&
-                (val == AUDIO_DEVICE_NONE) &&
-                 !audio_extn_usb_connected(parms)) {
-                 val = AUDIO_DEVICE_OUT_SPEAKER;
-         }
-        /* To avoid a2dp to sco overlapping / BT device improper state
-         * check with BT lib about a2dp streaming support before routing
-         */
-        if (val & AUDIO_DEVICE_OUT_ALL_A2DP) {
-            if (!audio_extn_a2dp_source_is_ready()) {
-                if (val &
-                    (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-                    //combo usecase just by pass a2dp
-                    ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
-                    bypass_a2dp = true;
-                } else {
-                    ALOGE("%s: A2DP profile is not ready,ignoring routing request", __func__);
-                    /* update device to a2dp and don't route as BT returned error
-                     * However it is still possible a2dp routing called because
-                     * of current active device disconnection (like wired headset)
-                     */
-                    out->devices = val;
-                    pthread_mutex_unlock(&out->lock);
-                    pthread_mutex_unlock(&adev->lock);
-                    goto error;
-                }
-            }
-        }
-
-        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
-         * the select_devices(). But how do we undo this?
-         *
-         * For example, music playback is active on headset (deep-buffer usecase)
-         * and if we go to ringtones and select a ringtone, low-latency usecase
-         * will be started on headset+speaker. As we can't enable headset+speaker
-         * and headset devices at the same time, select_devices() switches the music
-         * playback to headset+speaker while starting low-lateny usecase for ringtone.
-         * So when the ringtone playback is completed, how do we undo the same?
-         *
-         * We are relying on the out_set_parameters() call on deep-buffer output,
-         * once the ringtone playback is ended.
-         * NOTE: We should not check if the current devices are same as new devices.
-         *       Because select_devices() must be called to switch back the music
-         *       playback to headset.
-         */
-        if (val != 0) {
-            audio_devices_t new_dev = val;
-            bool same_dev = out->devices == new_dev;
-            out->devices = new_dev;
-
-            if (output_drives_call(adev, out)) {
-                if (!voice_is_call_state_active(adev)) {
-                    if (adev->mode == AUDIO_MODE_IN_CALL) {
-                        adev->current_call_output = out;
-                        if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
-                            service_interval = audio_extn_usb_find_service_interval(true, true /*playback*/);
-                            audio_extn_usb_set_service_interval(true /*playback*/,
-                                                                service_interval,
-                                                                &reconfig);
-                            ALOGD("%s, svc_int(%ld),reconfig(%d)",__func__,service_interval, reconfig);
-                         }
-                         ret = voice_start_call(adev);
-                    }
-                } else {
-                    adev->current_call_output = out;
-                    voice_update_devices_for_all_voice_usecases(adev);
-                }
-            }
-
-            if (!out->standby) {
-                if (!same_dev) {
-                    ALOGV("update routing change");
-                    audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
-                                                 adev->perf_lock_opts,
-                                                 adev->perf_lock_opts_size);
-                    if (adev->adm_on_routing_change)
-                        adev->adm_on_routing_change(adev->adm_data,
-                                                    out->handle);
-                }
-                if (!bypass_a2dp) {
-                    select_devices(adev, out->usecase);
-                } else {
-                    if (new_dev & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
-                        out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-                    else
-                        out->devices = AUDIO_DEVICE_OUT_SPEAKER;
-                    select_devices(adev, out->usecase);
-                    out->devices = new_dev;
-                }
-
-                if (!same_dev) {
-                    // on device switch force swap, lower functions will make sure
-                    // to check if swap is allowed or not.
-                    platform_set_swap_channels(adev, true);
-                    audio_extn_perf_lock_release(&adev->perf_lock_handle);
-                }
-                if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
-                    out->a2dp_compress_mute &&
-                    (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_source_is_ready())) {
-                    pthread_mutex_lock(&out->compr_mute_lock);
-                    out->a2dp_compress_mute = false;
-                    out_set_compr_volume(&out->stream, out->volume_l, out->volume_r);
-                    pthread_mutex_unlock(&out->compr_mute_lock);
-                } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
-                    out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
-                }
-            }
-        }
-
-        pthread_mutex_unlock(&adev->lock);
-        pthread_mutex_unlock(&out->lock);
-
-        /*handles device and call state changes*/
-        audio_extn_extspk_update(adev->extspk);
-    }
-    routing_fail:
-
     if (out == adev->primary_output) {
         pthread_mutex_lock(&adev->lock);
         audio_extn_set_parameters(adev, parms);
@@ -6482,13 +6582,78 @@
     return;
 }
 
+int route_input_stream(struct stream_in *in,
+                       audio_devices_t devices,
+                       char *address,
+                       audio_source_t source)
+{
+    struct audio_device *adev = in->dev;
+    int ret = 0;
+
+    lock_input_stream(in);
+    pthread_mutex_lock(&adev->lock);
+
+    /* no audio source uses val == 0 */
+    if ((in->source != source) && (source != AUDIO_SOURCE_DEFAULT)) {
+        in->source = source;
+        if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) &&
+            (in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
+            (voice_extn_compress_voip_is_format_supported(in->format)) &&
+            (in->config.rate == 8000 || in->config.rate == 16000 ||
+             in->config.rate == 32000 || in->config.rate == 48000 ) &&
+            (audio_channel_count_from_in_mask(in->channel_mask) == 1)) {
+            ret = voice_extn_compress_voip_open_input_stream(in);
+            if (ret != 0) {
+                ALOGE("%s: Compress voip input cannot be opened, error:%d",
+                      __func__, ret);
+            }
+        }
+    }
+
+    if ((in->device != devices) && (devices != AUDIO_DEVICE_NONE) &&
+          audio_is_input_device(devices)) {
+        // Workaround: If routing to an non existing usb device, fail gracefully
+        // The routing request will otherwise block during 10 second
+        int card;
+        struct str_parms *addr = str_parms_create_str(address);
+
+        if (!addr)
+            return ret;
+        if (audio_is_usb_in_device(devices) &&
+            (card = get_alive_usb_card(addr)) >= 0) {
+            ALOGW("%s: ignoring rerouting to non existing USB card %d", __func__, card);
+            ret = -ENOSYS;
+        } else {
+            in->device = devices;
+            /* 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);
+                    if (in->usecase == USECASE_AUDIO_RECORD_LOW_LATENCY)
+                        adev->adm_routing_changed = true;
+                }
+            }
+        }
+        str_parms_destroy(addr);
+    }
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&in->lock);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
 static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
     struct stream_in *in = (struct stream_in *)stream;
     struct audio_device *adev = in->dev;
     struct str_parms *parms;
     char value[32];
-    int ret = 0, val = 0, err;
+    int ret = 0;
 
     ALOGD("%s: enter: kvpairs=%s", __func__, kvpairs);
     parms = str_parms_create_str(kvpairs);
@@ -6498,61 +6663,8 @@
     lock_input_stream(in);
     pthread_mutex_lock(&adev->lock);
 
-    err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
-    if (err >= 0) {
-        val = atoi(value);
-        /* no audio source uses val == 0 */
-        if ((in->source != val) && (val != 0)) {
-            in->source = val;
-            if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) &&
-                (in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
-                (voice_extn_compress_voip_is_format_supported(in->format)) &&
-                (in->config.rate == 8000 || in->config.rate == 16000 ||
-                 in->config.rate == 32000 || in->config.rate == 48000 ) &&
-                (audio_channel_count_from_in_mask(in->channel_mask) == 1)) {
-                err = voice_extn_compress_voip_open_input_stream(in);
-                if (err != 0) {
-                    ALOGE("%s: Compress voip input cannot be opened, error:%d",
-                          __func__, err);
-                }
-            }
-        }
-    }
-
-    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) && 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);
-                        if (in->usecase == USECASE_AUDIO_RECORD_LOW_LATENCY)
-                            adev->adm_routing_changed = true;
-                    }
-                }
-            }
-        }
-    }
-
-    err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
-    if (err >= 0) {
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
+    if (ret >= 0) {
         strlcpy(in->profile, value, sizeof(in->profile));
         ALOGV("updating stream profile with value '%s'", in->profile);
         audio_extn_utils_update_stream_input_app_type_cfg(adev->platform,
@@ -8112,6 +8224,11 @@
         }
     }
 
+    ret = io_streams_map_insert(adev, &out->stream.common,
+                            out->handle, AUDIO_PATCH_HANDLE_NONE);
+    if (ret != 0)
+        goto error_open;
+
     streams_output_ctxt_t *out_ctxt = (streams_output_ctxt_t *)
         calloc(1, sizeof(streams_output_ctxt_t));
     if (out_ctxt == NULL) {
@@ -8146,6 +8263,8 @@
 
     ALOGD("%s: enter:stream_handle(%s)",__func__, use_case_table[out->usecase]);
 
+    io_streams_map_remove(adev, out->handle);
+
     // must deregister from sndmonitor first to prevent races
     // between the callback and close_stream
     audio_extn_snd_mon_unregister_listener(out);
@@ -9177,6 +9296,11 @@
 
     *stream_in = &in->stream;
 
+    ret = io_streams_map_insert(adev, &in->stream.common,
+                            handle, AUDIO_PATCH_HANDLE_NONE);
+    if (ret != 0)
+        goto err_open;
+
     streams_input_ctxt_t *in_ctxt = (streams_input_ctxt_t *)
         calloc(1, sizeof(streams_input_ctxt_t));
     if (in_ctxt == NULL) {
@@ -9213,6 +9337,8 @@
 
     ALOGD("%s: enter:stream_handle(%p)",__func__, in);
 
+    io_streams_map_remove(adev, in->capture_handle);
+
     /* must deregister from sndmonitor first to prevent races
      * between the callback and close_stream
      */
@@ -9410,6 +9536,50 @@
     return 0;
 }
 
+int update_patch(unsigned int num_sources,
+                 const struct audio_port_config *sources,
+                 unsigned int num_sinks,
+                 const struct audio_port_config *sinks,
+                 audio_patch_handle_t handle,
+                 struct audio_patch_info *p_info,
+                 patch_type_t patch_type, bool new_patch)
+{
+    ALOGD("%s: enter", __func__);
+
+    if (p_info == NULL) {
+        ALOGE("%s: Invalid patch pointer", __func__);
+        return -EINVAL;
+    }
+
+    if (new_patch) {
+        p_info->patch = (struct audio_patch *) calloc(1, sizeof(struct audio_patch));
+        if (p_info->patch == NULL) {
+            ALOGE("%s: Could not allocate patch", __func__);
+            return -ENOMEM;
+        }
+    }
+
+    p_info->patch->id = handle;
+    p_info->patch->num_sources = num_sources;
+    p_info->patch->num_sinks = num_sinks;
+
+    for (int i = 0; i < num_sources; i++)
+        p_info->patch->sources[i] = sources[i];
+    for (int i = 0; i < num_sinks; i++)
+        p_info->patch->sinks[i] = sinks[i];
+
+    p_info->patch_type = patch_type;
+    return 0;
+}
+
+audio_patch_handle_t generate_patch_handle()
+{
+    static audio_patch_handle_t patch_handle = AUDIO_PATCH_HANDLE_NONE;
+    if (++patch_handle < 0)
+        patch_handle = AUDIO_PATCH_HANDLE_NONE + 1;
+    return patch_handle;
+}
+
 int adev_create_audio_patch(struct audio_hw_device *dev,
                             unsigned int num_sources,
                             const struct audio_port_config *sources,
@@ -9417,15 +9587,169 @@
                             const struct audio_port_config *sinks,
                             audio_patch_handle_t *handle)
 {
-    int ret;
+    int ret = 0;
+    struct audio_device *adev = (struct audio_device *)dev;
+    struct audio_patch_info *p_info = NULL;
+    patch_type_t patch_type = PATCH_NONE;
+    audio_io_handle_t io_handle = AUDIO_IO_HANDLE_NONE;
+    audio_source_t input_source = AUDIO_SOURCE_DEFAULT;
+    struct audio_stream_info *s_info = NULL;
+    struct audio_stream *stream = NULL;
+    audio_devices_t device_type = AUDIO_DEVICE_NONE;
+    bool new_patch = false;
+    char addr[AUDIO_DEVICE_MAX_ADDRESS_LEN];
 
-    ret = audio_extn_hw_loopback_create_audio_patch(dev,
+    ALOGD("%s: enter: num sources %d, num_sinks %d, handle %d", __func__,
+           num_sources, num_sinks, *handle);
+
+    if (num_sources == 0 || num_sources > AUDIO_PATCH_PORTS_MAX ||
+        num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        ALOGE("%s: Invalid patch arguments", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (num_sources > 1) {
+        ALOGE("%s: Multiple sources are not supported", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (sources == NULL || sinks == NULL) {
+        ALOGE("%s: Invalid sources or sinks port config", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    ALOGV("%s: source role %d, source type %d", __func__,
+           sources[0].type, sources[0].role);
+
+    // Populate source/sink information and fetch stream info
+    switch (sources[0].type) {
+        case AUDIO_PORT_TYPE_DEVICE: // Patch for audio capture or loopback
+            device_type = sources[0].ext.device.type;
+            strlcpy(&addr[0], &sources[0].ext.device.address[0], AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            if (sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+                patch_type = PATCH_CAPTURE;
+                io_handle = sinks[0].ext.mix.handle;
+                input_source = sinks[0].ext.mix.usecase.source;
+                ALOGV("%s: Capture patch from device %x to mix %d",
+                       __func__, device_type, io_handle);
+            } else {
+                // Device to device patch is not implemented.
+                // This space will need changes if audio HAL
+                // handles device to device patches in the future.
+                patch_type = PATCH_DEVICE_LOOPBACK;
+            }
+            break;
+        case AUDIO_PORT_TYPE_MIX: // Patch for audio playback
+            io_handle = sources[0].ext.mix.handle;
+            for (int i = 0; i < num_sinks; i++) {
+                if (sinks[i].type == AUDIO_PORT_TYPE_MIX) {
+                    ALOGE("%s: mix to mix patches are not supported", __func__);
+                    ret = -EINVAL;
+                    goto done;
+                }
+                device_type |= sinks[i].ext.device.type;
+                strlcpy(&addr[0], &sinks[i].ext.device.address[0], AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            }
+            patch_type = PATCH_PLAYBACK;
+            ALOGV("%s: Playback patch from mix handle %d to device %x",
+                   __func__, io_handle, device_type);
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+    }
+
+    pthread_mutex_lock(&adev->lock);
+    s_info = hashmapGet(adev->io_streams_map, (void *) (intptr_t) io_handle);
+    pthread_mutex_unlock(&adev->lock);
+    if (s_info == NULL) {
+        ALOGE("%s: Failed to obtain stream info", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+    ALOGV("%s: Fetched stream info with io_handle %d", __func__, io_handle);
+
+    pthread_mutex_lock(&s_info->lock);
+    // Generate patch info and update patch
+    if (*handle == AUDIO_PATCH_HANDLE_NONE) {
+        if (s_info->patch_handle != AUDIO_PATCH_HANDLE_NONE) {
+            // Use patch handle cached in s_info to update patch
+            *handle = s_info->patch_handle;
+            p_info = fetch_patch_info(adev, *handle);
+            if (p_info == NULL) {
+                ALOGE("%s: Unable to fetch patch for stream patch handle %d",
+                      __func__, *handle);
+                pthread_mutex_unlock(&s_info->lock);
+                ret = -EINVAL;
+                goto done;
+            }
+        } else {
+            *handle = generate_patch_handle();
+            p_info = (struct audio_patch_info *) calloc(1, sizeof(struct audio_patch_info));
+            if (p_info == NULL) {
+                ALOGE("%s: Failed to allocate memory", __func__);
+                pthread_mutex_unlock(&s_info->lock);
+                ret = -ENOMEM;
+                goto done;
+            }
+            new_patch = true;
+            pthread_mutex_init(&p_info->lock, (const pthread_mutexattr_t *) NULL);
+            s_info->patch_handle = *handle;
+        }
+    } else {
+        p_info = fetch_patch_info(adev, *handle);
+        if (p_info == NULL) {
+            ALOGE("%s: Unable to fetch patch for received patch handle %d",
+                  __func__, *handle);
+            pthread_mutex_unlock(&s_info->lock);
+            ret = -EINVAL;
+            goto done;
+        }
+        s_info->patch_handle = *handle;
+    }
+    pthread_mutex_lock(&p_info->lock);
+    update_patch(num_sources, sources, num_sinks, sinks,
+                 *handle, p_info, patch_type, new_patch);
+    stream = s_info->stream;
+
+    // Update routing for stream
+    if (stream != NULL) {
+        if (p_info->patch_type == PATCH_PLAYBACK)
+            ret = route_output_stream((struct stream_out *) stream, device_type, &addr[0]);
+        else if (p_info->patch_type == PATCH_CAPTURE)
+            ret = route_input_stream((struct stream_in *) stream,
+                                     device_type, &addr[0], input_source);
+    }
+
+    if (ret < 0) {
+        s_info->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+        ALOGE("%s: Stream routing failed for io_handle %d", __func__, io_handle);
+        pthread_mutex_unlock(&p_info->lock);
+        pthread_mutex_unlock(&s_info->lock);
+        goto done;
+    }
+
+    // Add new patch to patch map
+    if (!ret && new_patch) {
+        pthread_mutex_lock(&adev->lock);
+        hashmapPut(adev->patch_map, (void *) (intptr_t) *handle, (void *) p_info);
+        pthread_mutex_unlock(&adev->lock);
+        ALOGD("%s: Added a new patch with handle %d", __func__, *handle);
+    }
+
+    pthread_mutex_unlock(&p_info->lock);
+    pthread_mutex_unlock(&s_info->lock);
+done:
+    audio_extn_hw_loopback_create_audio_patch(dev,
                                         num_sources,
                                         sources,
                                         num_sinks,
                                         sinks,
                                         handle);
-    ret |= audio_extn_auto_hal_create_audio_patch(dev,
+    audio_extn_auto_hal_create_audio_patch(dev,
                                         num_sources,
                                         sources,
                                         num_sinks,
@@ -9437,10 +9761,81 @@
 int adev_release_audio_patch(struct audio_hw_device *dev,
                            audio_patch_handle_t handle)
 {
-    int ret;
+    struct audio_device *adev = (struct audio_device *) dev;
+    int ret = 0;
+    char *addr = "";
+    audio_source_t input_source = AUDIO_SOURCE_DEFAULT;
 
-    ret = audio_extn_hw_loopback_release_audio_patch(dev, handle);
-    ret |= audio_extn_auto_hal_release_audio_patch(dev, handle);
+    if (handle == AUDIO_PATCH_HANDLE_NONE) {
+        ALOGE("%s: Invalid patch handle %d", __func__, handle);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    ALOGD("%s: Remove patch with handle %d", __func__, handle);
+    struct audio_patch_info *p_info = fetch_patch_info(adev, handle);
+    if (p_info == NULL) {
+        ALOGE("%s: Patch info not found with handle %d", __func__, handle);
+        ret = -EINVAL;
+        goto done;
+    }
+    pthread_mutex_lock(&p_info->lock);
+    struct audio_patch *patch = p_info->patch;
+    if (patch == NULL) {
+        ALOGE("%s: Patch not found for handle %d", __func__, handle);
+        ret = -EINVAL;
+        pthread_mutex_unlock(&p_info->lock);
+        goto done;
+    }
+    pthread_mutex_unlock(&p_info->lock);
+    audio_io_handle_t io_handle = AUDIO_IO_HANDLE_NONE;
+    switch (patch->sources[0].type) {
+        case AUDIO_PORT_TYPE_MIX:
+            io_handle = patch->sources[0].ext.mix.handle;
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            io_handle = patch->sinks[0].ext.mix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+    }
+
+    // Remove patch and reset patch handle in stream info
+    pthread_mutex_lock(&adev->lock);
+    struct audio_stream_info *s_info =
+        hashmapGet(adev->io_streams_map, (void *) (intptr_t) io_handle);
+    pthread_mutex_unlock(&adev->lock);
+    if (s_info == NULL) {
+        ALOGE("%s: stream for io_handle %d is not available", __func__, io_handle);
+        goto done;
+    }
+    pthread_mutex_lock(&s_info->lock);
+    s_info->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+    struct audio_stream *stream = s_info->stream;
+
+    pthread_mutex_lock(&p_info->lock);
+    if (stream != NULL) {
+        if (p_info->patch_type == PATCH_PLAYBACK)
+            ret = route_output_stream((struct stream_out *)stream, AUDIO_DEVICE_NONE, addr);
+        else if (p_info->patch_type == PATCH_CAPTURE)
+            ret = route_input_stream((struct stream_in *)stream,
+                                      AUDIO_DEVICE_NONE, addr, input_source);
+    }
+
+    if (ret < 0)
+        ALOGW("%s: Stream routing failed for io_handle %d", __func__, io_handle);
+
+    pthread_mutex_unlock(&p_info->lock);
+    pthread_mutex_unlock(&s_info->lock);
+
+    // Remove patch entry from map
+    patch_map_remove(adev, handle);
+done:
+    audio_extn_hw_loopback_release_audio_patch(dev, handle);
+    audio_extn_auto_hal_release_audio_patch(dev, handle);
+
+    ALOGV("%s: Successfully released patch %d", __func__, handle);
     return ret;
 }
 
@@ -9516,6 +9911,8 @@
         if(adev->ext_hw_plugin)
             audio_extn_ext_hw_plugin_deinit(adev->ext_hw_plugin);
         audio_extn_auto_hal_deinit();
+        free_map(adev->patch_map);
+        free_map(adev->io_streams_map);
         free(device);
         adev = NULL;
     }
@@ -9703,7 +10100,7 @@
 #endif
 
     /* default audio HAL major version */
-    uint32_t maj_version = 2;
+    uint32_t maj_version = 3;
     if(property_get("vendor.audio.hal.maj.version", value, NULL))
         maj_version = atoi(value);
 
@@ -9754,6 +10151,20 @@
     list_init(&adev->active_outputs_list);
     list_init(&adev->audio_patch_record_list);
     adev->audio_patch_index = 0;
+    adev->io_streams_map = hashmapCreate(AUDIO_IO_PORTS_MAX, audio_extn_utils_hash_fn,
+                                         audio_extn_utils_hash_eq);
+    if (!adev->io_streams_map) {
+        ALOGE("%s: Could not create io streams map", __func__);
+        ret = -ENOMEM;
+        goto adev_open_err;
+    }
+    adev->patch_map = hashmapCreate(AUDIO_PATCH_PORTS_MAX, audio_extn_utils_hash_fn,
+                                    audio_extn_utils_hash_eq);
+    if (!adev->patch_map) {
+        ALOGE("%s: Could not create audio patch map", __func__);
+        ret = -ENOMEM;
+        goto adev_open_err;
+    }
     adev->cur_wfd_channels = 2;
     adev->offload_usecases_state = 0;
     adev->pcm_record_uc_state = 0;
@@ -9769,27 +10180,17 @@
     /* Loads platform specific libraries dynamically */
     adev->platform = platform_init(adev);
     if (!adev->platform) {
-        pthread_mutex_destroy(&adev->lock);
-        free(adev->snd_dev_ref_cnt);
-        free(adev);
-        adev = NULL;
         ALOGE("%s: Failed to init platform data, aborting.", __func__);
-        *device = NULL;
-        pthread_mutex_unlock(&adev_init_lock);
-        return -EINVAL;
+        ret = -EINVAL;
+        goto adev_open_err;
     }
 
     adev->extspk = audio_extn_extspk_init(adev);
     if (audio_extn_qap_is_enabled()) {
         ret = audio_extn_qap_init(adev);
         if (ret < 0) {
-            pthread_mutex_destroy(&adev->lock);
-            free(adev);
-            adev = NULL;
             ALOGE("%s: Failed to init platform data, aborting.", __func__);
-            *device = NULL;
-            pthread_mutex_unlock(&adev_init_lock);
-            return ret;
+            goto adev_open_err;
         }
         adev->device.open_output_stream = audio_extn_qap_open_output_stream;
         adev->device.close_output_stream = audio_extn_qap_close_output_stream;
@@ -9798,13 +10199,8 @@
     if (audio_extn_qaf_is_enabled()) {
         ret = audio_extn_qaf_init(adev);
         if (ret < 0) {
-            pthread_mutex_destroy(&adev->lock);
-            free(adev);
-            adev = NULL;
             ALOGE("%s: Failed to init platform data, aborting.", __func__);
-            *device = NULL;
-            pthread_mutex_unlock(&adev_init_lock);
-            return ret;
+            goto adev_open_err;
         }
 
         adev->device.open_output_stream = audio_extn_qaf_open_output_stream;
@@ -10003,6 +10399,18 @@
 
     ALOGV("%s: exit", __func__);
     return 0;
+
+adev_open_err:
+    free_map(adev->patch_map);
+    free_map(adev->io_streams_map);
+    if (adev->snd_dev_ref_cnt)
+        free(adev->snd_dev_ref_cnt);
+    pthread_mutex_destroy(&adev->lock);
+    free(adev);
+    adev = NULL;
+    *device = NULL;
+    pthread_mutex_unlock(&adev_init_lock);
+    return ret;
 }
 
 static struct hw_module_methods_t hal_module_methods = {
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 3998d43..5604977 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -41,6 +41,7 @@
 #include <stdlib.h>
 #include <cutils/str_parms.h>
 #include <cutils/list.h>
+#include <cutils/hashmap.h>
 #include <hardware/audio.h>
 #include <tinyalsa/asoundlib.h>
 #include <tinycompress/tinycompress.h>
@@ -336,6 +337,7 @@
     audio_devices_t devices;
     unsigned int bit_width;
 };
+
 struct stream_inout {
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
     pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
@@ -519,6 +521,25 @@
     USECASE_TYPE_MAX
 } usecase_type_t;
 
+typedef enum {
+    PATCH_NONE = -1,
+    PATCH_PLAYBACK,
+    PATCH_CAPTURE,
+    PATCH_DEVICE_LOOPBACK
+} patch_type_t;
+
+struct audio_patch_info {
+    struct audio_patch *patch;
+    patch_type_t patch_type;
+    pthread_mutex_t lock;
+};
+
+struct audio_stream_info {
+    struct audio_stream *stream;
+    audio_patch_handle_t patch_handle;
+    pthread_mutex_t lock;
+};
+
 union stream_ptr {
     struct stream_in *in;
     struct stream_out *out;
@@ -697,6 +718,8 @@
     bool adm_routing_changed;
     struct listnode audio_patch_record_list;
     unsigned int audio_patch_index;
+    Hashmap *patch_map;
+    Hashmap *io_streams_map;
 };
 
 struct audio_patch_record {
@@ -774,6 +797,14 @@
         return false;
 }
 
+int route_output_stream(struct stream_out *stream,
+                               audio_devices_t devices,
+                               char *address);
+int route_input_stream(struct stream_in *stream,
+                              audio_devices_t devices,
+                              char *address,
+                              audio_source_t source);
+
 /*
  * NOTE: when multiple mutexes have to be acquired, always take the
  * stream_in or stream_out mutex first, followed by the audio_device mutex.