hal: add support for HAL API 3.0 to utilize AudioPatch

Add audio HAL version property to support audio HAL
API 3.0 for audio patch and audio port configuration.
Add basic device and  mixer routing support in audio
patch as audio stream set_parameters for ROUTING and
INPUT_SOURCE will be replaced with audio_path in
AudioFlinger for audio HAL API 3.0 and above.

Change-Id: I0a16982ff5115b3475ae302e8653f02657437e28
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index d97c2b9..079d33a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -1064,22 +1064,22 @@
                                      const struct audio_port_config *sinks __unused,
                                      audio_patch_handle_t *handle __unused)
 {
-    return -ENOSYS;
+    return 0;
 }
 static int __unused audio_extn_hw_loopback_release_audio_patch(struct audio_hw_device *dev __unused,
                                              audio_patch_handle_t handle __unused)
 {
-    return -ENOSYS;
+    return 0;
 }
 static int __unused audio_extn_hw_loopback_set_audio_port_config(struct audio_hw_device *dev __unused,
                                     const struct audio_port_config *config __unused)
 {
-    return -ENOSYS;
+    return 0;
 }
 static int __unused audio_extn_hw_loopback_get_audio_port(struct audio_hw_device *dev __unused,
                                     struct audio_port *port_in __unused)
 {
-    return -ENOSYS;
+    return 0;
 }
 static int __unused audio_extn_hw_loopback_set_param_data(audio_patch_handle_t handle __unused,
                                                audio_extn_loopback_param_id param_id __unused,
@@ -1151,11 +1151,22 @@
 #define audio_extn_auto_hal_deinit()                  (0)
 #define audio_extn_auto_hal_enable_hostless()         (0)
 #define audio_extn_auto_hal_disable_hostless()        (0)
+#define audio_extn_auto_hal_create_audio_patch(dev, num_sources,\
+    sources, num_sinks, sinks, handle) (0)
+#define audio_extn_auto_hal_release_audio_patch(dev, handle) (0)
 #else
 int32_t audio_extn_auto_hal_init(struct audio_device *adev);
 void audio_extn_auto_hal_deinit(void);
 int32_t audio_extn_auto_hal_enable_hostless(void);
 void audio_extn_auto_hal_disable_hostless(void);
+int audio_extn_auto_hal_create_audio_patch(struct audio_hw_device *dev,
+                                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);
+int audio_extn_auto_hal_release_audio_patch(struct audio_hw_device *dev,
+                                audio_patch_handle_t handle);
 #endif
 
 #ifndef EXT_HW_PLUGIN_ENABLED
diff --git a/hal/audio_extn/auto_hal.c b/hal/audio_extn/auto_hal.c
index 9f88d86..f57e885 100644
--- a/hal/audio_extn/auto_hal.c
+++ b/hal/audio_extn/auto_hal.c
@@ -158,6 +158,158 @@
     }
 }
 
+#define MAX_SOURCE_PORTS_PER_PATCH 1
+#define MAX_SINK_PORTS_PER_PATCH 1
+
+int audio_extn_auto_hal_create_audio_patch(struct audio_hw_device *dev,
+                                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_device *adev = (struct audio_device *)dev;
+    int ret = 0;
+    char *str = NULL;
+    struct str_parms *parms = NULL;
+    char *address = NULL;
+
+    ALOGV("%s: enter", __func__);
+
+    if (!dev || !sources || !sinks || !handle ) {
+        ALOGE("%s: null audio patch parameters", __func__);
+        return -EINVAL;
+    }
+
+    /* Port configuration check & validation */
+    if (num_sources > MAX_SOURCE_PORTS_PER_PATCH ||
+         num_sinks > MAX_SINK_PORTS_PER_PATCH) {
+         ALOGE("%s: invalid audio patch parameters, sources %d sinks %d ",
+                 __func__, num_sources, num_sources);
+         return -EINVAL;
+    }
+
+    /* Release patch if valid handle */
+    if (*handle != AUDIO_PATCH_HANDLE_NONE) {
+        ret = audio_extn_auto_hal_release_audio_patch(dev,
+                        *handle);
+        if (ret) {
+            ALOGE("%s: failed to release audio patch 0x%x", __func__, *handle);
+            return ret;
+        }
+        *handle = AUDIO_PATCH_HANDLE_NONE;
+    }
+
+    /* No validation on num of sources and sinks to allow patch with
+     * multiple sinks being created, but only the first source and
+     * sink are used to create patch.
+     *
+     * Stream set_parameters for AUDIO_PARAMETER_STREAM_ROUTING and
+     * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is replaced with audio_patch
+     * callback in audioflinger for AUDIO_DEVICE_API_VERSION_3_0 and above.
+     * Need to handle device routing notification in audio HAL for
+     *   Capture:  DEVICE -> MIX
+     *   Playback: MIX -> DEVICE
+     * For DEVICE -> DEVICE patch type, it refers to routing from/to external
+     * codec/amplifier and allow Android streams to be mixed at the H/W level.
+     */
+    if ((sources->type == AUDIO_PORT_TYPE_DEVICE) &&
+        (sinks->type == AUDIO_PORT_TYPE_MIX)) {
+        pthread_mutex_lock(&adev->lock);
+        streams_input_ctxt_t *in_ctxt = in_get_stream(adev,
+                        sinks->ext.mix.handle);
+        if (!in_ctxt) {
+            ALOGE("%s, failed to find input stream", __func__);
+            ret = -EINVAL;
+        }
+        pthread_mutex_unlock(&adev->lock);
+        if(ret)
+            return ret;
+
+        if (strcmp(sources->ext.device.address, "") != 0) {
+            address = audio_device_address_to_parameter(
+                                                sources->ext.device.type,
+                                                sources->ext.device.address);
+        } else {
+            address = (char *)calloc(1, 1);
+        }
+        parms = str_parms_create_str(address);
+        if (!parms) {
+            ALOGE("%s: failed to allocate mem for parms", __func__);
+            ret = -ENOMEM;
+            goto error;
+        }
+        str_parms_add_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                        (int)sources->ext.device.type);
+        str_parms_add_int(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                        (int)sinks->ext.mix.usecase.source);
+        str = str_parms_to_str(parms);
+        in_ctxt->input->stream.common.set_parameters(
+                        (struct audio_stream *)in_ctxt->input, str);
+    } else if ((sources->type == AUDIO_PORT_TYPE_MIX) &&
+            (sinks->type == AUDIO_PORT_TYPE_DEVICE)) {
+        pthread_mutex_lock(&adev->lock);
+        streams_output_ctxt_t *out_ctxt = out_get_stream(adev,
+            sources->ext.mix.handle);
+        if (!out_ctxt) {
+            ALOGE("%s, failed to find output stream", __func__);
+            ret = -EINVAL;
+        }
+        pthread_mutex_unlock(&adev->lock);
+        if(ret)
+            return ret;
+
+        if (strcmp(sinks->ext.device.address, "") != 0) {
+            address = audio_device_address_to_parameter(
+                                                sinks->ext.device.type,
+                                                sinks->ext.device.address);
+        } else {
+            address = (char *)calloc(1, 1);
+        }
+        parms = str_parms_create_str(address);
+        if (!parms) {
+            ALOGE("%s: failed to allocate mem for parms", __func__);
+            ret = -ENOMEM;
+            goto error;
+        }
+        str_parms_add_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                        (int)sinks->ext.device.type);
+        str = str_parms_to_str(parms);
+        out_ctxt->output->stream.common.set_parameters(
+                        (struct audio_stream *)out_ctxt->output, str);
+    } else {
+        ALOGW("%s: create device -> device audio patch", __func__);
+    }
+
+error:
+    if (parms)
+        str_parms_destroy(parms);
+    if (address)
+        free(address);
+    ALOGV("%s: exit: handle 0x%x", __func__, *handle);
+    return ret;
+}
+
+int audio_extn_auto_hal_release_audio_patch(struct audio_hw_device *dev,
+                                audio_patch_handle_t handle)
+{
+    int ret = 0;
+
+    ALOGV("%s: enter: handle 0x%x", __func__, handle);
+
+    if (!dev) {
+        ALOGE("%s: null audio patch parameters", __func__);
+        return -EINVAL;
+    }
+
+    if (handle != AUDIO_PATCH_HANDLE_NONE) {
+        ALOGW("%s: release device -> device audio patch", __func__);
+    }
+
+    ALOGV("%s: exit", __func__);
+    return ret;
+}
+
 int32_t audio_extn_auto_hal_init(struct audio_device *adev)
 {
     int32_t ret = 0;
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 006c5a2..cae3e3b 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -6251,6 +6251,38 @@
     return add_remove_audio_effect(stream, effect, false);
 }
 
+streams_input_ctxt_t *in_get_stream(struct audio_device *dev,
+                                  audio_io_handle_t input)
+{
+    struct listnode *node;
+
+    list_for_each(node, &dev->active_inputs_list) {
+        streams_input_ctxt_t *in_ctxt = node_to_item(node,
+                                                     streams_input_ctxt_t,
+                                                     list);
+        if (in_ctxt->input->capture_handle == input) {
+            return in_ctxt;
+        }
+    }
+    return NULL;
+}
+
+streams_output_ctxt_t *out_get_stream(struct audio_device *dev,
+                                  audio_io_handle_t output)
+{
+    struct listnode *node;
+
+    list_for_each(node, &dev->active_outputs_list) {
+        streams_output_ctxt_t *out_ctxt = node_to_item(node,
+                                                     streams_output_ctxt_t,
+                                                     list);
+        if (out_ctxt->output->handle == output) {
+            return out_ctxt;
+        }
+    }
+    return NULL;
+}
+
 static int in_stop(const struct audio_stream_in* stream)
 {
     struct stream_in *in = (struct stream_in *)stream;
@@ -7220,6 +7252,20 @@
             out->ip_hdlr_handle = NULL;
         }
     }
+
+    streams_output_ctxt_t *out_ctxt = (streams_output_ctxt_t *)
+        calloc(1, sizeof(streams_output_ctxt_t));
+    if (out_ctxt == NULL) {
+        ALOGE("%s fail to allocate output ctxt", __func__);
+        ret = -ENOMEM;
+        goto error_open;
+    }
+    out_ctxt->output = out;
+
+    pthread_mutex_lock(&adev->lock);
+    list_add_tail(&adev->active_outputs_list, &out_ctxt->list);
+    pthread_mutex_unlock(&adev->lock);
+
     ALOGV("%s: exit", __func__);
     return 0;
 
@@ -7298,7 +7344,17 @@
 
     pthread_cond_destroy(&out->cond);
     pthread_mutex_destroy(&out->lock);
+
+    pthread_mutex_lock(&adev->lock);
+    streams_output_ctxt_t *out_ctxt = out_get_stream(adev, out->handle);
+    if (out_ctxt != NULL) {
+        list_remove(&out_ctxt->list);
+        free(out_ctxt);
+    } else {
+        ALOGW("%s, output stream already closed", __func__);
+    }
     free(stream);
+    pthread_mutex_unlock(&adev->lock);
     ALOGV("%s: exit", __func__);
 }
 
@@ -8130,6 +8186,20 @@
     stream_app_type_cfg_init(&in->app_type_cfg);
 
     *stream_in = &in->stream;
+
+    streams_input_ctxt_t *in_ctxt = (streams_input_ctxt_t *)
+        calloc(1, sizeof(streams_input_ctxt_t));
+    if (in_ctxt == NULL) {
+        ALOGE("%s fail to allocate input ctxt", __func__);
+        ret = -ENOMEM;
+        goto err_open;
+    }
+    in_ctxt->input = in;
+
+    pthread_mutex_lock(&adev->lock);
+    list_add_tail(&adev->active_inputs_list, &in_ctxt->list);
+    pthread_mutex_unlock(&adev->lock);
+
     ALOGV("%s: exit", __func__);
     return ret;
 
@@ -8205,6 +8275,13 @@
         ALOGV("%s: sound trigger pcm stop lab", __func__);
         audio_extn_sound_trigger_stop_lab(in);
     }
+    streams_input_ctxt_t *in_ctxt = in_get_stream(adev, in->capture_handle);
+    if (in_ctxt != NULL) {
+        list_remove(&in_ctxt->list);
+        free(in_ctxt);
+    } else {
+        ALOGW("%s, input stream already closed", __func__);
+    }
     free(stream);
     pthread_mutex_unlock(&adev->lock);
     return;
@@ -8349,21 +8426,31 @@
                             const struct audio_port_config *sinks,
                             audio_patch_handle_t *handle)
 {
+    int ret;
 
-
-     return audio_extn_hw_loopback_create_audio_patch(dev,
-                                         num_sources,
-                                         sources,
-                                         num_sinks,
-                                         sinks,
-                                         handle);
-
+    ret = audio_extn_hw_loopback_create_audio_patch(dev,
+                                        num_sources,
+                                        sources,
+                                        num_sinks,
+                                        sinks,
+                                        handle);
+    ret |= audio_extn_auto_hal_create_audio_patch(dev,
+                                        num_sources,
+                                        sources,
+                                        num_sinks,
+                                        sinks,
+                                        handle);
+    return ret;
 }
 
 int adev_release_audio_patch(struct audio_hw_device *dev,
                            audio_patch_handle_t handle)
 {
-    return audio_extn_hw_loopback_release_audio_patch(dev, handle);
+    int ret;
+
+    ret = audio_extn_hw_loopback_release_audio_patch(dev, handle);
+    ret |= audio_extn_auto_hal_release_audio_patch(dev, handle);
+    return ret;
 }
 
 int adev_get_audio_port(struct audio_hw_device *dev, struct audio_port *config)
@@ -8573,6 +8660,7 @@
                      hw_device_t **device)
 {
     int ret;
+    char value[PROPERTY_VALUE_MAX] = {0};
 
     ALOGD("%s: enter", __func__);
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
@@ -8602,8 +8690,13 @@
     register_for_dynamic_logging("hal");
 #endif
 
+    /* default audio HAL major version */
+    uint32_t maj_version = 2;
+    if(property_get("vendor.audio.hal.maj.version", value, NULL))
+        maj_version = atoi(value);
+
     adev->device.common.tag = HARDWARE_DEVICE_TAG;
-    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->device.common.version = HARDWARE_DEVICE_API_VERSION(maj_version, 0);
     adev->device.common.module = (struct hw_module_t *)module;
     adev->device.common.close = adev_close;
 
@@ -8645,6 +8738,8 @@
     audio_feature_manager_init();
     voice_init(adev);
     list_init(&adev->usecase_list);
+    list_init(&adev->active_inputs_list);
+    list_init(&adev->active_outputs_list);
     adev->cur_wfd_channels = 2;
     adev->offload_usecases_state = 0;
     adev->pcm_record_uc_state = 0;
@@ -8788,7 +8883,6 @@
 
     audio_device_ref_count++;
 
-    char value[PROPERTY_VALUE_MAX];
     int trial;
     if ((property_get("vendor.audio_hal.period_size", value, NULL) > 0) ||
         (property_get("audio_hal.period_size", value, NULL) > 0)) {
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 147fdaf..db3474f 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -473,6 +473,16 @@
     struct stream_app_type_cfg app_type_cfg;
 };
 
+typedef struct streams_input_ctxt {
+    struct listnode list;
+    struct stream_in *input;
+} streams_input_ctxt_t;
+
+typedef struct streams_output_ctxt {
+    struct listnode list;
+    struct stream_out *output;
+} streams_output_ctxt_t;
+
 typedef void* (*adm_init_t)();
 typedef void (*adm_deinit_t)(void *);
 typedef void (*adm_register_output_stream_t)(void *, audio_io_handle_t, audio_output_flags_t);
@@ -589,6 +599,9 @@
      * or other capabilities are present for the device corresponding to that usecase.
      */
     struct pcm_params *use_case_table[AUDIO_USECASE_MAX];
+
+    struct listnode active_inputs_list;
+    struct listnode active_outputs_list;
 };
 
 int select_devices(struct audio_device *adev,
@@ -635,6 +648,11 @@
 
 bool is_interactive_usecase(audio_usecase_t uc_id);
 
+streams_input_ctxt_t *in_get_stream(struct audio_device *dev,
+                                  audio_io_handle_t input);
+streams_output_ctxt_t *out_get_stream(struct audio_device *dev,
+                                  audio_io_handle_t output);
+
 #define LITERAL_TO_STRING(x) #x
 #define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)\