Merge "Implement create/release_audio_patch API in audio HAL for cuttlefish." am: 3788a5ef4e am: a21d4b48f3 am: d5aadbcd40

Change-Id: Ib4ca5bfd4cc18385cebbd621c76ab412d63868d2
diff --git a/guest/hals/audio/audio_hw.c b/guest/hals/audio/audio_hw.c
index 40396d7..41a0543 100644
--- a/guest/hals/audio/audio_hw.c
+++ b/guest/hals/audio/audio_hw.c
@@ -34,6 +34,7 @@
 #include <unistd.h>
 
 #include <log/log.h>
+#include <cutils/list.h>
 #include <cutils/str_parms.h>
 
 #include <hardware/hardware.h>
@@ -52,10 +53,13 @@
 #define IN_PERIOD_COUNT 4
 
 struct generic_audio_device {
-    struct audio_hw_device device; // Constant after init
+    struct audio_hw_device device;          // Constant after init
     pthread_mutex_t lock;
-    bool mic_mute;                 // Proteced by this->lock
-    struct mixer* mixer;           // Proteced by this->lock
+    bool mic_mute;                          // Protected by this->lock
+    struct mixer* mixer;                    // Protected by this->lock
+    struct listnode out_streams;            // Record for output streams, protected by this->lock
+    struct listnode in_streams;             // Record for input streams, protected by this->lock
+    audio_patch_handle_t next_patch_handle; // Protected by this->lock
 };
 
 /* If not NULL, this is a pointer to the fallback module.
@@ -172,13 +176,14 @@
 }
 
 struct generic_stream_out {
-    struct audio_stream_out stream;   // Constant after init
+    struct audio_stream_out stream;                 // Constant after init
     pthread_mutex_t lock;
-    struct generic_audio_device *dev; // Constant after init
-    audio_devices_t device;           // Protected by this->lock
-    struct audio_config req_config;   // Constant after init
-    struct pcm_config pcm_config;     // Constant after init
-    audio_vbuffer_t buffer;           // Constant after init
+    struct generic_audio_device *dev;               // Constant after init
+    uint32_t num_devices;                           // Protected by this->lock
+    audio_devices_t devices[AUDIO_PATCH_PORTS_MAX]; // Protected by this->lock
+    struct audio_config req_config;                 // Constant after init
+    struct pcm_config pcm_config;                   // Constant after init
+    audio_vbuffer_t buffer;                         // Constant after init
 
     // Time & Position Keeping
     bool standby;                      // Protected by this->lock
@@ -194,6 +199,11 @@
     pthread_cond_t worker_wake;       // Protected by this->lock
     bool worker_standby;              // Protected by this->lock
     bool worker_exit;                 // Protected by this->lock
+
+    audio_io_handle_t handle;          // Constant after init
+    audio_patch_handle_t patch_handle; // Protected by this->dev->lock
+
+    struct listnode stream_node;       // Protected by this->dev->lock
 };
 
 struct generic_stream_in {
@@ -219,6 +229,11 @@
     pthread_cond_t worker_wake;       // Protected by this->lock
     bool worker_standby;              // Protected by this->lock
     bool worker_exit;                 // Protected by this->lock
+
+    audio_io_handle_t handle;          // Constant after init
+    audio_patch_handle_t patch_handle; // Protected by this->dev->lock
+
+    struct listnode stream_node;       // Protected by this->dev->lock
 };
 
 static struct pcm_config pcm_config_out = {
@@ -290,29 +305,32 @@
                 "\t\tbuffer size: %zu\n"
                 "\t\tchannel mask: %08x\n"
                 "\t\tformat: %d\n"
-                "\t\tdevice: %08x\n"
-                "\t\taudio dev: %p\n\n",
+                "\t\tdevice(s): ",
                 out_get_sample_rate(stream),
                 out_get_buffer_size(stream),
                 out_get_channels(stream),
-                out_get_format(stream),
-                out->device,
-                out->dev);
+                out_get_format(stream));
+    if (out->num_devices == 0) {
+        dprintf(fd, "%08x\n", AUDIO_DEVICE_NONE);
+    } else {
+        for (uint32_t i = 0; i < out->num_devices; i++) {
+            if (i != 0) {
+                dprintf(fd, ", ");
+            }
+            dprintf(fd, "%08x", out->devices[i]);
+        }
+        dprintf(fd, "\n");
+    }
+    dprintf(fd, "\t\taudio dev: %p\n\n", out->dev);
     pthread_mutex_unlock(&out->lock);
     return 0;
 }
 
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
     struct str_parms *parms;
     char value[32];
-    int ret = -EINVAL;
     int success;
-    long val;
-    char *end;
-    bool new_device_req = false;
-    int new_device;
 
     if (kvpairs == NULL || kvpairs[0] == 0) {
         return 0;
@@ -320,28 +338,16 @@
     parms = str_parms_create_str(kvpairs);
     success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
             value, sizeof(value));
-    if (success >= 0) {
-        errno = 0;
-        val = strtol(value, &end, 10);
-        if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
-            new_device_req = true;
-            new_device = (int)val;
-            ret = 0;
-        }
-    }
-    str_parms_destroy(parms);
-    if (ret != 0) {
-        ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs);
-        return ret;
-    }
+    // As the hal version is 3.0, it must not use set parameters API to set audio devices.
+    // Instead, it should use create_audio_patch API.
+    assert(("Must not use set parameters API to set audio devices", success < 0));
 
-    // Try applying change requests
-    pthread_mutex_lock(&out->lock);
-    if (new_device_req) {
-        out->device = new_device;
-    }
-    pthread_mutex_unlock(&out->lock);
-    return ret;
+    str_parms_destroy(parms);
+
+    ALOGW("%s(), unsupported parameter %s", __func__, kvpairs);
+    // There is not any key supported for set_parameters API.
+    // Return error when there is non-null value passed in.
+    return -EINVAL;
 }
 
 static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
@@ -357,7 +363,11 @@
     ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
     if (ret >= 0) {
         pthread_mutex_lock(&out->lock);
-        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device);
+        audio_devices_t device = AUDIO_DEVICE_NONE;
+        for (uint32_t i = 0; i < out->num_devices; i++) {
+            device |= out->devices[i];
+        }
+        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, device);
         pthread_mutex_unlock(&out->lock);
         get = true;
     }
@@ -834,15 +844,9 @@
 
 static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
     struct str_parms *parms;
     char value[32];
-    int ret = -EINVAL;
     int success;
-    long val;
-    char *end;
-    bool new_device_req = false;
-    int new_device;
 
     if (kvpairs == NULL || kvpairs[0] == 0) {
         return 0;
@@ -850,28 +854,16 @@
     parms = str_parms_create_str(kvpairs);
     success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
             value, sizeof(value));
-    if (success >= 0) {
-        errno = 0;
-        val = strtol(value, &end, 10);
-        if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
-            new_device_req = true;
-            new_device = (int)val;
-            ret = 0;
-        }
-    }
-    str_parms_destroy(parms);
-    if (ret != 0) {
-        ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs);
-        return ret;
-    }
+    // As the hal version is 3.0, it must not use set parameters API to set audio device.
+    // Instead, it should use create_audio_patch API.
+    assert(("Must not use set parameters API to set audio devices", success < 0));
 
-    // Try applying change requests
-    pthread_mutex_lock(&in->lock);
-    if (new_device_req) {
-        in->device = new_device;
-    }
-    pthread_mutex_unlock(&in->lock);
-    return ret;
+    str_parms_destroy(parms);
+
+    ALOGW("%s(), unsupported parameter %s", __func__, kvpairs);
+    // There is not any key supported for set_parameters API.
+    // Return error when there is non-null value passed in.
+    return -EINVAL;
 }
 
 static char * in_get_parameters(const struct audio_stream *stream,
@@ -1222,9 +1214,13 @@
     out->stream.get_presentation_position = out_get_presentation_position;
     out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
 
+    out->handle = handle;
+
     pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
     out->dev = adev;
-    out->device = devices;
+    // Only 1 device is expected despite the argument being named 'devices'
+    out->num_devices = 1;
+    out->devices[0] = devices;
     memcpy(&out->req_config, config, sizeof(struct audio_config));
     memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config));
     out->pcm_config.rate = config->sample_rate;
@@ -1250,14 +1246,33 @@
         pthread_create(&out->worker_thread, NULL, out_write_worker, out);
 
     }
-    *stream_out = &out->stream;
 
+    pthread_mutex_lock(&adev->lock);
+    list_add_tail(&adev->out_streams, &out->stream_node);
+    pthread_mutex_unlock(&adev->lock);
+
+    *stream_out = &out->stream;
 
 error:
 
     return ret;
 }
 
+// This must be called with adev->lock held.
+struct generic_stream_out *get_stream_out_by_io_handle_l(
+        struct generic_audio_device *adev, audio_io_handle_t handle) {
+    struct listnode *node;
+
+    list_for_each(node, &adev->out_streams) {
+        struct generic_stream_out *out = node_to_item(
+                node, struct generic_stream_out, stream_node);
+        if (out->handle == handle) {
+            return out;
+        }
+    }
+    return NULL;
+}
+
 static void adev_close_output_stream(struct audio_hw_device *dev,
                                      struct audio_stream_out *stream)
 {
@@ -1272,6 +1287,11 @@
     pthread_join(out->worker_thread, NULL);
     pthread_mutex_destroy(&out->lock);
     audio_vbuffer_destroy(&out->buffer);
+
+    struct generic_audio_device *adev = (struct generic_audio_device *) dev;
+    pthread_mutex_lock(&adev->lock);
+    list_remove(&out->stream_node);
+    pthread_mutex_unlock(&adev->lock);
     free(stream);
 }
 
@@ -1348,6 +1368,20 @@
     return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask);
 }
 
+// This must be called with adev->lock held.
+struct generic_stream_in *get_stream_in_by_io_handle_l(
+        struct generic_audio_device *adev, audio_io_handle_t handle) {
+    struct listnode *node;
+
+    list_for_each(node, &adev->in_streams) {
+        struct generic_stream_in *in = node_to_item(
+                node, struct generic_stream_in, stream_node);
+        if (in->handle == handle) {
+            return in;
+        }
+    }
+    return NULL;
+}
 
 static void adev_close_input_stream(struct audio_hw_device *dev,
                                    struct audio_stream_in *stream)
@@ -1368,6 +1402,11 @@
 
     pthread_mutex_destroy(&in->lock);
     audio_vbuffer_destroy(&in->buffer);
+
+    struct generic_audio_device *adev = (struct generic_audio_device *) dev;
+    pthread_mutex_lock(&adev->lock);
+    list_remove(&in->stream_node);
+    pthread_mutex_unlock(&adev->lock);
     free(stream);
 }
 
@@ -1442,6 +1481,11 @@
         in->worker_exit = false;
         pthread_create(&in->worker_thread, NULL, in_read_worker, in);
     }
+    in->handle = handle;
+
+    pthread_mutex_lock(&adev->lock);
+    list_add_tail(&adev->in_streams, &in->stream_node);
+    pthread_mutex_unlock(&adev->lock);
 
     *stream_in = &in->stream;
 
@@ -1497,6 +1541,159 @@
     return 0;
 }
 
+static int adev_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) {
+    if (num_sources != 1 || num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        return -EINVAL;
+    }
+
+    if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        // If source is a device, the number of sinks should be 1.
+        if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) {
+            return -EINVAL;
+        }
+    } else if (sources[0].type == AUDIO_PORT_TYPE_MIX) {
+        // If source is a mix, all sinks should be device.
+        for (unsigned int i = 0; i < num_sinks; i++) {
+            if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+                ALOGE("%s() invalid sink type %#x for mix source", __func__, sinks[i].type);
+                return -EINVAL;
+            }
+        }
+    } else {
+        // All other cases are invalid.
+        return -EINVAL;
+    }
+
+    struct generic_audio_device* adev = (struct generic_audio_device*) dev;
+    int ret = 0;
+    bool generatedPatchHandle = false;
+    pthread_mutex_lock(&adev->lock);
+    if (*handle == AUDIO_PATCH_HANDLE_NONE) {
+        *handle = ++adev->next_patch_handle;
+        generatedPatchHandle = true;
+    }
+
+    // Only handle patches for mix->devices and device->mix case.
+    if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        struct generic_stream_in *in =
+                get_stream_in_by_io_handle_l(adev, sinks[0].ext.mix.handle);
+        if (in == NULL) {
+            ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        // Check if the patch handle match the recorded one if a valid patch handle is passed.
+        if (!generatedPatchHandle && in->patch_handle != *handle) {
+            ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
+                  "with handle(%d) when creating audio patch for device->mix",
+                  __func__, *handle, in->patch_handle, in->handle);
+            ret = -EINVAL;
+            goto error;
+        }
+        pthread_mutex_lock(&in->lock);
+        in->device = sources[0].ext.device.type;
+        pthread_mutex_unlock(&in->lock);
+        in->patch_handle = *handle;
+    } else {
+        struct generic_stream_out *out =
+                get_stream_out_by_io_handle_l(adev, sources[0].ext.mix.handle);
+        if (out == NULL) {
+            ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        // Check if the patch handle match the recorded one if a valid patch handle is passed.
+        if (!generatedPatchHandle && out->patch_handle != *handle) {
+            ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
+                  "with handle(%d) when creating audio patch for mix->device",
+                  __func__, *handle, out->patch_handle, out->handle);
+            ret = -EINVAL;
+            pthread_mutex_unlock(&out->lock);
+            goto error;
+        }
+        pthread_mutex_lock(&out->lock);
+        for (out->num_devices = 0; out->num_devices < num_sinks; out->num_devices++) {
+            out->devices[out->num_devices] = sinks[out->num_devices].ext.device.type;
+        }
+        pthread_mutex_unlock(&out->lock);
+        out->patch_handle = *handle;
+    }
+
+error:
+    if (ret != 0 && generatedPatchHandle) {
+        *handle = AUDIO_PATCH_HANDLE_NONE;
+    }
+    pthread_mutex_unlock(&adev->lock);
+    return 0;
+}
+
+// This must be called with adev->lock held.
+struct generic_stream_out *get_stream_out_by_patch_handle_l(
+        struct generic_audio_device *adev, audio_patch_handle_t patch_handle) {
+    struct listnode *node;
+
+    list_for_each(node, &adev->out_streams) {
+        struct generic_stream_out *out = node_to_item(
+                node, struct generic_stream_out, stream_node);
+        if (out->patch_handle == patch_handle) {
+            return out;
+        }
+    }
+    return NULL;
+}
+
+// This must be called with adev->lock held.
+struct generic_stream_in *get_stream_in_by_patch_handle_l(
+        struct generic_audio_device *adev, audio_patch_handle_t patch_handle) {
+    struct listnode *node;
+
+    list_for_each(node, &adev->in_streams) {
+        struct generic_stream_in *in = node_to_item(
+                node, struct generic_stream_in, stream_node);
+        if (in->patch_handle == patch_handle) {
+            return in;
+        }
+    }
+    return NULL;
+}
+
+static int adev_release_audio_patch(struct audio_hw_device *dev,
+                                    audio_patch_handle_t patch_handle) {
+    struct generic_audio_device *adev = (struct generic_audio_device *) dev;
+
+    pthread_mutex_lock(&adev->lock);
+    struct generic_stream_out *out = get_stream_out_by_patch_handle_l(adev, patch_handle);
+    if (out != NULL) {
+        pthread_mutex_lock(&out->lock);
+        out->num_devices = 0;
+        memset(out->devices, 0, sizeof(out->devices));
+        pthread_mutex_unlock(&out->lock);
+        out->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+        pthread_mutex_unlock(&adev->lock);
+        return 0;
+    }
+    struct generic_stream_in *in = get_stream_in_by_patch_handle_l(adev, patch_handle);
+    if (in != NULL) {
+        pthread_mutex_lock(&in->lock);
+        in->device = AUDIO_DEVICE_NONE;
+        pthread_mutex_unlock(&in->lock);
+        in->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+        pthread_mutex_unlock(&adev->lock);
+        return 0;
+    }
+
+    pthread_mutex_unlock(&adev->lock);
+    ALOGW("%s() cannot find stream for patch handle: %d", __func__, patch_handle);
+    return -EINVAL;
+}
+
 static int adev_close(hw_device_t *dev)
 {
     struct generic_audio_device *adev = (struct generic_audio_device *)dev;
@@ -1545,7 +1742,7 @@
     pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
 
     adev->device.common.tag = HARDWARE_DEVICE_TAG;
-    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->device.common.version = AUDIO_DEVICE_API_VERSION_3_0;
     adev->device.common.module = (struct hw_module_t *) module;
     adev->device.common.close = adev_close;
 
@@ -1567,9 +1764,15 @@
     adev->device.close_input_stream = adev_close_input_stream;
     adev->device.dump = adev_dump;
     adev->device.get_microphones = adev_get_microphones;
+    adev->device.create_audio_patch = adev_create_audio_patch;
+    adev->device.release_audio_patch = adev_release_audio_patch;
 
     *device = &adev->device.common;
 
+    adev->next_patch_handle = AUDIO_PATCH_HANDLE_NONE;
+    list_init(&adev->out_streams);
+    list_init(&adev->in_streams);
+
     adev->mixer = mixer_open(PCM_CARD);
     struct mixer_ctl *ctl;