Upgrade device version of usb audio HAL to 3.0.

Upgrade device version of usb audio HAL to 3.0. With this change, the
framework will use createAudioPatch API for audio routing.

When opening a stream, the HAL will open alsa device with the given
device information. The HAL will cache the io handle to identify the
stream. Before starting audio playback/recording, create_audio_patch
API is called to set the correct devices. All the devices will be
cached as a list in stream_in/stream_out. The HAL will return a unique
patch handle which is used when updating/releasing the patch. When
patch is released, the patch handle will also be removed.

Bug: 160352965
Test: playback/record with usb device
Change-Id: Iba69c5cc82d1063af8ab050183d6a57b27083ee1
diff --git a/modules/usbaudio/audio_hal.c b/modules/usbaudio/audio_hal.c
index 14ef993..cdcf254 100644
--- a/modules/usbaudio/audio_hal.c
+++ b/modules/usbaudio/audio_hal.c
@@ -22,6 +22,7 @@
 #include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/time.h>
 #include <unistd.h>
 
@@ -68,6 +69,8 @@
     bool mic_muted;
 
     int32_t inputs_open; /* number of input streams currently open. */
+
+    audio_patch_handle_t next_patch_handle; // Increase 1 when create audio patch
 };
 
 struct stream_lock {
@@ -109,6 +112,12 @@
                                          * they could come from here too if
                                          * there was a previous conversion */
     size_t conversion_buffer_size;      /* in bytes */
+
+    struct pcm_config config;
+
+    audio_io_handle_t handle; // Unique constant for a stream
+
+    audio_patch_handle_t patch_handle; // Patch handle for this stream
 };
 
 struct stream_in {
@@ -140,8 +149,55 @@
                                          * they could come from here too if
                                          * there was a previous conversion */
     size_t conversion_buffer_size;      /* in bytes */
+
+    struct pcm_config config;
+
+    audio_io_handle_t handle; // Unique identifier for a stream
+
+    audio_patch_handle_t patch_handle; // Patch handle for this stream
 };
 
+// Map channel count to output channel mask
+static const audio_channel_mask_t OUT_CHANNEL_MASKS_MAP[] = {
+    AUDIO_CHANNEL_NONE,        /* 0 */
+    AUDIO_CHANNEL_OUT_MONO,    /* 1 */
+    AUDIO_CHANNEL_OUT_STEREO,  /* 2 */
+    AUDIO_CHANNEL_OUT_2POINT1, /* 3 */
+    AUDIO_CHANNEL_OUT_QUAD,    /* 4 */
+    AUDIO_CHANNEL_OUT_PENTA,   /* 5 */
+    AUDIO_CHANNEL_OUT_5POINT1, /* 6 */
+    AUDIO_CHANNEL_OUT_6POINT1, /* 7 */
+    AUDIO_CHANNEL_OUT_7POINT1  /* 8 */
+    /* channel counts greater than this are not considered */
+};
+static const int OUT_CHANNEL_MASKS_SIZE = AUDIO_ARRAY_SIZE(OUT_CHANNEL_MASKS_MAP);
+
+// Map channel count to input channel mask
+static const audio_channel_mask_t IN_CHANNEL_MASKS_MAP[] = {
+    AUDIO_CHANNEL_NONE,       /* 0 */
+    AUDIO_CHANNEL_IN_MONO,    /* 1 */
+    AUDIO_CHANNEL_IN_STEREO,  /* 2 */
+    /* channel counts greater than this are not considered */
+};
+static const int IN_CHANNEL_MASKS_SIZE = AUDIO_ARRAY_SIZE(IN_CHANNEL_MASKS_MAP);
+
+// Map channel count to index mask
+static const audio_channel_mask_t CHANNEL_INDEX_MASKS_MAP[] = {
+    AUDIO_CHANNEL_NONE,         /* 0 */
+    AUDIO_CHANNEL_INDEX_MASK_1, /* 1 */
+    AUDIO_CHANNEL_INDEX_MASK_2, /* 2 */
+    AUDIO_CHANNEL_INDEX_MASK_3, /* 3 */
+    AUDIO_CHANNEL_INDEX_MASK_4, /* 4 */
+    AUDIO_CHANNEL_INDEX_MASK_5, /* 5 */
+    AUDIO_CHANNEL_INDEX_MASK_6, /* 6 */
+    AUDIO_CHANNEL_INDEX_MASK_7, /* 7 */
+    AUDIO_CHANNEL_INDEX_MASK_8  /* 8 */
+    /* channel counts greater than this are not considered */
+};
+static const int CHANNEL_INDEX_MASKS_SIZE = AUDIO_ARRAY_SIZE(CHANNEL_INDEX_MASKS_MAP);
+
+
+
 /*
  * Locking Helpers
  */
@@ -158,6 +214,9 @@
 }
 
 static void stream_lock(struct stream_lock *lock) {
+    if (lock == NULL) {
+        return;
+    }
     pthread_mutex_lock(&lock->pre_lock);
     pthread_mutex_lock(&lock->lock);
     pthread_mutex_unlock(&lock->pre_lock);
@@ -191,13 +250,52 @@
     device_unlock(adev);
 }
 
-static void adev_remove_stream_from_list(
-    struct audio_device* adev, struct listnode* stream_node) {
-    device_lock(adev);
+static struct stream_out* adev_get_stream_out_by_io_handle_l(
+        struct audio_device* adev, audio_io_handle_t handle) {
+    struct listnode *node;
+    list_for_each (node, &adev->output_stream_list) {
+        struct stream_out *out = node_to_item(node, struct stream_out, list_node);
+        if (out->handle == handle) {
+            return out;
+        }
+    }
+    return NULL;
+}
 
-    list_remove(stream_node);
+static struct stream_in* adev_get_stream_in_by_io_handle_l(
+        struct audio_device* adev, audio_io_handle_t handle) {
+    struct listnode *node;
+    list_for_each (node, &adev->input_stream_list) {
+        struct stream_in *in = node_to_item(node, struct stream_in, list_node);
+        if (in->handle == handle) {
+            return in;
+        }
+    }
+    return NULL;
+}
 
-    device_unlock(adev);
+static struct stream_out* adev_get_stream_out_by_patch_handle_l(
+        struct audio_device* adev, audio_patch_handle_t patch_handle) {
+    struct listnode *node;
+    list_for_each (node, &adev->output_stream_list) {
+        struct stream_out *out = node_to_item(node, struct stream_out, list_node);
+        if (out->patch_handle == patch_handle) {
+            return out;
+        }
+    }
+    return NULL;
+}
+
+static struct stream_in* adev_get_stream_in_by_patch_handle_l(
+        struct audio_device* adev, audio_patch_handle_t patch_handle) {
+    struct listnode *node;
+    list_for_each (node, &adev->input_stream_list) {
+        struct stream_in *in = node_to_item(node, struct stream_in, list_node);
+        if (in->patch_handle == patch_handle) {
+            return in;
+        }
+    }
+    return NULL;
 }
 
 /*
@@ -278,6 +376,24 @@
     return result_str;
 }
 
+static audio_format_t audio_format_from(enum pcm_format format)
+{
+    switch (format) {
+    case PCM_FORMAT_S16_LE:
+        return AUDIO_FORMAT_PCM_16_BIT;
+    case PCM_FORMAT_S32_LE:
+        return AUDIO_FORMAT_PCM_32_BIT;
+    case PCM_FORMAT_S8:
+        return AUDIO_FORMAT_PCM_8_BIT;
+    case PCM_FORMAT_S24_LE:
+        return AUDIO_FORMAT_PCM_8_24_BIT;
+    case PCM_FORMAT_S24_3LE:
+        return AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    default:
+        return AUDIO_FORMAT_INVALID;
+    }
+}
+
 /*
  * HAl Functions
  */
@@ -293,6 +409,73 @@
     return node_to_item(list_head(alsa_devices), struct alsa_device_info, list_node);
 }
 
+/**
+ * Must be called with holding the stream's lock.
+ */
+static void stream_standby_l(struct listnode *alsa_devices, bool *standby)
+{
+    if (!*standby) {
+        struct listnode *node;
+        list_for_each (node, alsa_devices) {
+            struct alsa_device_info *device_info =
+                    node_to_item(node, struct alsa_device_info, list_node);
+            proxy_close(&device_info->proxy);
+        }
+        *standby = true;
+    }
+}
+
+static void stream_clear_devices(struct listnode *alsa_devices)
+{
+    struct listnode *node, *temp;
+    struct alsa_device_info *device_info = NULL;
+    list_for_each_safe (node, temp, alsa_devices) {
+        device_info = node_to_item(node, struct alsa_device_info, list_node);
+        if (device_info != NULL) {
+            list_remove(&device_info->list_node);
+            free(device_info);
+        }
+    }
+}
+
+static int stream_set_new_devices(struct pcm_config *config,
+                                  struct listnode *alsa_devices,
+                                  unsigned int num_devices,
+                                  const int cards[],
+                                  const int devices[],
+                                  int direction)
+{
+    int status = 0;
+    stream_clear_devices(alsa_devices);
+
+    for (unsigned int i = 0; i < num_devices; ++i) {
+        struct alsa_device_info *device_info =
+                (struct alsa_device_info *) calloc(1, sizeof(struct alsa_device_info));
+        profile_init(&device_info->profile, direction);
+        device_info->profile.card = cards[i];
+        device_info->profile.device = devices[i];
+        status = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
+        if (status != 0) {
+            ALOGE("%s failed to read device info card=%d;device=%d",
+                    __func__, cards[i], devices[i]);
+            goto exit;
+        }
+        status = proxy_prepare(&device_info->proxy, &device_info->profile, config);
+        if (status != 0) {
+            ALOGE("%s failed to prepare device card=%d;device=%d",
+                    __func__, cards[i], devices[i]);
+            goto exit;
+        }
+        list_add_tail(alsa_devices, &device_info->list_node);
+    }
+
+exit:
+    if (status != 0) {
+        stream_clear_devices(alsa_devices);
+    }
+    return status;
+}
+
 static void stream_dump_alsa_devices(const struct listnode *alsa_devices, int fd) {
     struct listnode *node;
     size_t i = 0;
@@ -371,15 +554,9 @@
     struct stream_out *out = (struct stream_out *)stream;
 
     stream_lock(&out->lock);
-    if (!out->standby) {
-        struct listnode *node;
-        list_for_each(node, &out->alsa_devices) {
-            struct alsa_device_info *device_info =
-                    node_to_item(node, struct alsa_device_info, list_node);
-            proxy_close(&device_info->proxy);
-        }
-        out->standby = true;
-    }
+    device_lock(out->adev);
+    stream_standby_l(&out->alsa_devices, &out->standby);
+    device_unlock(out->adev);
     stream_unlock(&out->lock);
     return 0;
 }
@@ -394,43 +571,14 @@
     return 0;
 }
 
-static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+static int out_set_parameters(struct audio_stream *stream __unused, const char *kvpairs)
 {
     ALOGV("out_set_parameters() keys:%s", kvpairs);
 
-    struct stream_out *out = (struct stream_out *)stream;
-
-    int ret_value = 0;
-    int card = -1;
-    int device = -1;
-
-    if (!parse_card_device_params(kvpairs, &card, &device)) {
-        // nothing to do
-        return ret_value;
-    }
-
-    stream_lock(&out->lock);
-    struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
-    if (device_info != NULL && !profile_is_cached_for(&device_info->profile, card, device)) {
-        /* cannot read pcm device info if playback is active */
-        if (!out->standby)
-            ret_value = -ENOSYS;
-        else {
-            int saved_card = device_info->profile.card;
-            int saved_device = device_info->profile.device;
-            device_info->profile.card = card;
-            device_info->profile.device = device;
-            ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
-            if (ret_value != 0) {
-                device_info->profile.card = saved_card;
-                device_info->profile.device = saved_device;
-            }
-        }
-    }
-
-    stream_unlock(&out->lock);
-
-    return ret_value;
+    // The set parameters here only matters when the routing devices are changed.
+    // When the device version is not less than 3.0, the framework will use create
+    // audio patch API instead of set parameters to chanage audio routing.
+    return 0;
 }
 
 static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
@@ -624,6 +772,8 @@
     out->stream.get_presentation_position = out_get_presentation_position;
     out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
 
+    out->handle = handle;
+
     stream_lock_init(&out->lock);
 
     out->adev = (struct audio_device *)hw_dev;
@@ -709,6 +859,7 @@
     proxy_config.channels =
             profile_get_closest_channel_count(&device_info->profile, out->hal_channel_count);
     proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config);
+    out->config = proxy_config;
 
     list_add_tail(&out->alsa_devices, &device_info->list_node);
 
@@ -735,31 +886,21 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
 
+    stream_lock(&out->lock);
     /* Close the pcm device */
-    out_standby(&stream->common);
+    stream_standby_l(&out->alsa_devices, &out->standby);
+    stream_clear_devices(&out->alsa_devices);
 
     free(out->conversion_buffer);
 
     out->conversion_buffer = NULL;
     out->conversion_buffer_size = 0;
 
-    struct listnode *node, *temp;
-    struct alsa_device_info *item = NULL;
-    stream_lock(&out->lock);
-    list_for_each_safe (node, temp, &out->alsa_devices) {
-        item = node_to_item(node, struct alsa_device_info, list_node);
-        if (item != NULL) {
-            list_remove(&item->list_node);
-            free(item);
-        }
-    }
-    stream_unlock(&out->lock);
-
-    adev_remove_stream_from_list(out->adev, &out->list_node);
-
     device_lock(out->adev);
+    list_remove(&out->list_node);
     out->adev->device_sample_rate = 0;
     device_unlock(out->adev);
+    stream_unlock(&out->lock);
 
     free(stream);
 }
@@ -835,17 +976,10 @@
     struct stream_in *in = (struct stream_in *)stream;
 
     stream_lock(&in->lock);
-    if (!in->standby) {
-        struct listnode *node;
-        list_for_each (node, &in->alsa_devices) {
-            struct alsa_device_info *device_info =
-                    node_to_item(node, struct alsa_device_info, list_node);
-            proxy_close(&device_info->proxy);
-        }
-        in->standby = true;
-    }
+    device_lock(in->adev);
+    stream_standby_l(&in->alsa_devices, &in->standby);
+    device_unlock(in->adev);
     stream_unlock(&in->lock);
-
     return 0;
 }
 
@@ -863,45 +997,10 @@
 {
     ALOGV("in_set_parameters() keys:%s", kvpairs);
 
-    struct stream_in *in = (struct stream_in *)stream;
-
-    int ret_value = 0;
-    int card = -1;
-    int device = -1;
-
-    if (!parse_card_device_params(kvpairs, &card, &device)) {
-        // nothing to do
-        return ret_value;
-    }
-
-    stream_lock(&in->lock);
-    device_lock(in->adev);
-
-    struct alsa_device_info* device_info = stream_get_first_alsa_device(&in->alsa_devices);
-
-    if (card >= 0 && device >= 0 &&
-            device_info != NULL && !profile_is_cached_for(&device_info->profile, card, device)) {
-        /* cannot read pcm device info if capture is active, or more than one open stream */
-        if (!in->standby || in->adev->inputs_open > 1)
-            ret_value = -ENOSYS;
-        else {
-            int saved_card = device_info->profile.card;
-            int saved_device = device_info->profile.device;
-            device_info->profile.card = card;
-            device_info->profile.device = device;
-            ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
-            if (ret_value != 0) {
-                ALOGE("Can't read device profile. card:%d, device:%d", card, device);
-                device_info->profile.card = saved_card;
-                device_info->profile.device = saved_device;
-            }
-        }
-    }
-
-    device_unlock(in->adev);
-    stream_unlock(&in->lock);
-
-    return ret_value;
+    // The set parameters here only matters when the routing devices are changed.
+    // When the device version higher than 3.0, the framework will use create_audio_patch
+    // API instead of set_parameters to change audio routing.
+    return 0;
 }
 
 static char * in_get_parameters(const struct audio_stream *stream, const char *keys)
@@ -1119,6 +1218,8 @@
     in->stream.set_microphone_direction = in_set_microphone_direction;
     in->stream.set_microphone_field_dimension = in_set_microphone_field_dimension;
 
+    in->handle = handle;
+
     stream_lock_init(&in->lock);
 
     in->adev = (struct audio_device *)hw_dev;
@@ -1128,8 +1229,7 @@
             (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info));
     profile_init(&device_info->profile, PCM_IN);
 
-    struct pcm_config proxy_config;
-    memset(&proxy_config, 0, sizeof(proxy_config));
+    memset(&in->config, 0, sizeof(in->config));
 
     int ret = 0;
     device_lock(in->adev);
@@ -1171,9 +1271,9 @@
             if (highest_rate == 0) {
                 ret = -EINVAL; /* error with device */
             } else {
-                proxy_config.rate = config->sample_rate =
+                in->config.rate = config->sample_rate =
                         min(highest_rate, in->adev->device_sample_rate);
-                if (request_config_rate != 0 && proxy_config.rate != config->sample_rate) {
+                if (request_config_rate != 0 && in->config.rate != config->sample_rate) {
                     /* Changing the requested rate */
                     ret = -EINVAL;
                 } else {
@@ -1183,24 +1283,24 @@
             }
         }
     } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
-        proxy_config.rate = config->sample_rate;
+        in->config.rate = config->sample_rate;
     } else {
-        proxy_config.rate = config->sample_rate =
+        in->config.rate = config->sample_rate =
                 profile_get_default_sample_rate(&device_info->profile);
         ret = -EINVAL;
     }
 
     /* Format */
     if (config->format == AUDIO_FORMAT_DEFAULT) {
-        proxy_config.format = profile_get_default_format(&device_info->profile);
-        config->format = audio_format_from_pcm_format(proxy_config.format);
+        in->config.format = profile_get_default_format(&device_info->profile);
+        config->format = audio_format_from_pcm_format(in->config.format);
     } else {
         enum pcm_format fmt = pcm_format_from_audio_format(config->format);
         if (profile_is_format_valid(&device_info->profile, fmt)) {
-            proxy_config.format = fmt;
+            in->config.format = fmt;
         } else {
-            proxy_config.format = profile_get_default_format(&device_info->profile);
-            config->format = audio_format_from_pcm_format(proxy_config.format);
+            in->config.format = profile_get_default_format(&device_info->profile);
+            config->format = audio_format_from_pcm_format(in->config.format);
             ret = -EINVAL;
         }
     }
@@ -1245,9 +1345,9 @@
         // Validate the "logical" channel count against support in the "actual" profile.
         // if they differ, choose the "actual" number of channels *closest* to the "logical".
         // and store THAT in proxy_config.channels
-        proxy_config.channels =
+        in->config.channels =
                 profile_get_closest_channel_count(&device_info->profile, in->hal_channel_count);
-        ret = proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config);
+        ret = proxy_prepare(&device_info->proxy, &device_info->profile, &in->config);
         if (ret == 0) {
             in->standby = true;
 
@@ -1291,9 +1391,9 @@
 {
     struct stream_in *in = (struct stream_in *)stream;
 
-    adev_remove_stream_from_list(in->adev, &in->list_node);
-
+    stream_lock(&in->lock);
     device_lock(in->adev);
+    list_remove(&in->list_node);
     --in->adev->inputs_open;
     struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
     if (device_info != NULL) {
@@ -1302,10 +1402,13 @@
     }
     LOG_ALWAYS_FATAL_IF(in->adev->inputs_open < 0,
             "invalid inputs_open: %d", in->adev->inputs_open);
+
+    stream_standby_l(&in->alsa_devices, &in->standby);
+
     device_unlock(in->adev);
 
-    /* Close the pcm device */
-    in_standby(&stream->common);
+    stream_clear_devices(&in->alsa_devices);
+    stream_unlock(&in->lock);
 
     free(in->conversion_buffer);
 
@@ -1359,6 +1462,240 @@
     return -ENOSYS;
 }
 
+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) {
+        // Only accept mix->device and device->mix cases. In that case, the number of sources
+        // must be 1. The number of sinks must be in the range of (0, 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 audio_device* adev = (struct audio_device*) dev;
+    bool generatedPatchHandle = false;
+    device_lock(adev);
+    if (*handle == AUDIO_PATCH_HANDLE_NONE) {
+        *handle = ++adev->next_patch_handle;
+        generatedPatchHandle = true;
+    }
+
+    int cards[AUDIO_PATCH_PORTS_MAX];
+    int devices[AUDIO_PATCH_PORTS_MAX];
+    const struct audio_port_config *port_configs =
+            sources[0].type == AUDIO_PORT_TYPE_DEVICE ? sources : sinks;
+    int num_configs = 0;
+    audio_io_handle_t io_handle = 0;
+    bool wasStandby = true;
+    int direction = PCM_OUT;
+    audio_patch_handle_t *patch_handle = NULL;
+    struct listnode *alsa_devices = NULL;
+    struct stream_lock *lock = NULL;
+    struct pcm_config *config = NULL;
+    struct stream_in *in = NULL;
+    struct stream_out *out = NULL;
+
+    unsigned int num_saved_devices = 0;
+    int saved_cards[AUDIO_PATCH_PORTS_MAX];
+    int saved_devices[AUDIO_PATCH_PORTS_MAX];
+
+    struct listnode *node;
+
+    // Only handle patches for mix->devices and device->mix case.
+    if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        in = adev_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__, sinks[0].ext.mix.handle);
+            device_unlock(adev);
+            return -EINVAL;
+        }
+
+        direction = PCM_IN;
+        wasStandby = in->standby;
+        io_handle = in->handle;
+        num_configs = num_sources;
+        patch_handle = &in->patch_handle;
+        alsa_devices = &in->alsa_devices;
+        lock = &in->lock;
+        config = &in->config;
+    } else {
+        out = adev_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);
+            device_unlock(adev);
+            return -EINVAL;
+        }
+
+        direction = PCM_OUT;
+        wasStandby = out->standby;
+        io_handle = out->handle;
+        num_configs = num_sinks;
+        patch_handle = &out->patch_handle;
+        alsa_devices = &out->alsa_devices;
+        lock = &out->lock;
+        config = &out->config;
+    }
+
+    // Check if the patch handle match the recorded one if a valid patch handle is passed.
+    if (!generatedPatchHandle && *patch_handle != *handle) {
+        ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
+              "with handle(%d) when creating audio patch",
+              __func__, *handle, *patch_handle, io_handle);
+        device_unlock(adev);
+        return -EINVAL;
+    }
+    device_unlock(adev);
+
+    for (unsigned int i = 0; i < num_configs; ++i) {
+        if (!parse_card_device_params(port_configs[i].ext.device.address, &cards[i], &devices[i])) {
+            ALOGE("%s, failed to parse card and device %s",
+                    __func__, port_configs[i].ext.device.address);
+            return -EINVAL;
+        }
+    }
+
+    stream_lock(lock);
+    list_for_each (node, alsa_devices) {
+        struct alsa_device_info *device_info =
+                node_to_item(node, struct alsa_device_info, list_node);
+        saved_cards[num_saved_devices] = device_info->profile.card;
+        saved_devices[num_saved_devices++] = device_info->profile.device;
+    }
+
+    device_lock(adev);
+    stream_standby_l(alsa_devices, out == NULL ? &in->standby : &out->standby);
+    device_unlock(adev);
+
+    int ret = stream_set_new_devices(config, alsa_devices, num_configs, cards, devices, direction);
+
+    if (ret != 0) {
+        *handle = generatedPatchHandle ? AUDIO_PATCH_HANDLE_NONE : *handle;
+        stream_set_new_devices(
+                config, alsa_devices, num_saved_devices, saved_cards, saved_devices, direction);
+    } else {
+        *patch_handle = *handle;
+    }
+    if (!wasStandby) {
+        device_lock(adev);
+        if (in != NULL) {
+            start_input_stream(in);
+        }
+        if (out != NULL) {
+            start_output_stream(out);
+        }
+        device_unlock(adev);
+    }
+    stream_unlock(lock);
+    return ret;
+}
+
+static int adev_release_audio_patch(struct audio_hw_device *dev,
+                                    audio_patch_handle_t patch_handle)
+{
+    struct audio_device* adev = (struct audio_device*) dev;
+
+    device_lock(adev);
+    struct stream_out *out = adev_get_stream_out_by_patch_handle_l(adev, patch_handle);
+    device_unlock(adev);
+    if (out != NULL) {
+        stream_lock(&out->lock);
+        device_lock(adev);
+        stream_standby_l(&out->alsa_devices, &out->standby);
+        device_unlock(adev);
+        out->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+        stream_unlock(&out->lock);
+        return 0;
+    }
+
+    device_lock(adev);
+    struct stream_in *in = adev_get_stream_in_by_patch_handle_l(adev, patch_handle);
+    device_unlock(adev);
+    if (in != NULL) {
+        stream_lock(&in->lock);
+        device_lock(adev);
+        stream_standby_l(&in->alsa_devices, &in->standby);
+        device_unlock(adev);
+        in->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+        stream_unlock(&in->lock);
+        return 0;
+    }
+
+    ALOGE("%s cannot find stream with patch handle as %d", __func__, patch_handle);
+    return -EINVAL;
+}
+
+static int adev_get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+    if (port->type != AUDIO_PORT_TYPE_DEVICE) {
+        return -EINVAL;
+    }
+
+    alsa_device_profile profile;
+    const bool is_output = audio_is_output_device(port->ext.device.type);
+    profile_init(&profile, is_output ? PCM_OUT : PCM_IN);
+    if (!parse_card_device_params(port->ext.device.address, &profile.card, &profile.device)) {
+        return -EINVAL;
+    }
+
+    if (!profile_read_device_info(&profile)) {
+        return -ENOENT;
+    }
+
+    port->num_formats = 0;;
+    for (size_t i = 0; i < min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_FORMATS) &&
+            profile.formats[i] != 0; ++i) {
+        audio_format_t format = audio_format_from(profile.formats[i]);
+        if (format != AUDIO_FORMAT_INVALID) {
+            port->formats[port->num_formats++] = format;
+        }
+    }
+
+    port->num_sample_rates = min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES);
+    memcpy(port->sample_rates, profile.sample_rates, port->num_sample_rates * sizeof(unsigned int));
+
+    port->num_channel_masks = 0;
+    const audio_channel_mask_t* channel_masks_map =
+            is_output ? OUT_CHANNEL_MASKS_MAP : IN_CHANNEL_MASKS_MAP;
+    const int channel_masks_size = is_output ? OUT_CHANNEL_MASKS_SIZE : IN_CHANNEL_MASKS_SIZE;
+    unsigned int channel_count = 0;
+    for (size_t i = 0; i < min(channel_masks_size, AUDIO_PORT_MAX_CHANNEL_MASKS) &&
+            (channel_count = profile.channel_counts[i]) != 0 &&
+            port->num_channel_masks < AUDIO_PORT_MAX_CHANNEL_MASKS; ++i) {
+        if (channel_count < channel_masks_size &&
+            channel_masks_map[channel_count] != AUDIO_CHANNEL_NONE) {
+            port->channel_masks[port->num_channel_masks++] = channel_masks_map[channel_count];
+            if (port->num_channel_masks >= AUDIO_PORT_MAX_CHANNEL_MASKS) {
+                break;
+            }
+        }
+        if (channel_count < CHANNEL_INDEX_MASKS_SIZE &&
+            CHANNEL_INDEX_MASKS_MAP[channel_count] != AUDIO_CHANNEL_NONE) {
+            port->channel_masks[port->num_channel_masks++] = CHANNEL_INDEX_MASKS_MAP[channel_count];
+        }
+    }
+    return 0;
+}
+
 static int adev_dump(const struct audio_hw_device *device, int fd)
 {
     dprintf(fd, "\nUSB audio module:\n");
@@ -1428,7 +1765,7 @@
     list_init(&adev->input_stream_list);
 
     adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
-    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_3_0;
     adev->hw_device.common.module = (struct hw_module_t *)module;
     adev->hw_device.common.close = adev_close;
 
@@ -1445,6 +1782,9 @@
     adev->hw_device.close_output_stream = adev_close_output_stream;
     adev->hw_device.open_input_stream = adev_open_input_stream;
     adev->hw_device.close_input_stream = adev_close_input_stream;
+    adev->hw_device.create_audio_patch = adev_create_audio_patch;
+    adev->hw_device.release_audio_patch = adev_release_audio_patch;
+    adev->hw_device.get_audio_port = adev_get_audio_port;
     adev->hw_device.dump = adev_dump;
 
     *device = &adev->hw_device.common;