Merge "Revert "Delete KM1""
diff --git a/modules/usbaudio/audio_hal.c b/modules/usbaudio/audio_hal.c
index a19a0ae..39c0fb5 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 {
@@ -75,6 +78,12 @@
     pthread_mutex_t pre_lock;           /* acquire before lock to avoid DOS by playback thread */
 };
 
+struct alsa_device_info {
+    alsa_device_profile profile;        /* The profile of the ALSA device */
+    alsa_device_proxy proxy;            /* The state */
+    struct listnode list_node;
+};
+
 struct stream_out {
     struct audio_stream_out stream;
 
@@ -84,10 +93,7 @@
 
     struct audio_device *adev;           /* hardware information - only using this for the lock */
 
-    alsa_device_profile profile;        /* The profile of the ALSA device connected to the stream.
-                                         */
-
-    alsa_device_proxy proxy;            /* state of the stream */
+    struct listnode alsa_devices;       /* The ALSA devices connected to the stream. */
 
     unsigned hal_channel_count;         /* channel count exposed to AudioFlinger.
                                          * This may differ from the device channel count when
@@ -106,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 {
@@ -117,10 +129,7 @@
 
     struct audio_device *adev;           /* hardware information - only using this for the lock */
 
-    alsa_device_profile profile;        /* The profile of the ALSA device connected to the stream.
-                                         */
-
-    alsa_device_proxy proxy;            /* state of the stream */
+    struct listnode alsa_devices;       /* The ALSA devices connected to the stream. */
 
     unsigned hal_channel_count;         /* channel count exposed to AudioFlinger.
                                          * This may differ from the device channel count when
@@ -140,8 +149,80 @@
                                          * 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[FCC_24 + 1] = {
+    [0] = AUDIO_CHANNEL_NONE,  // == 0 (so this line is optional and could be omitted)
+                               // != AUDIO_CHANNEL_INVALID == 0xC0000000u
+
+    [1] = AUDIO_CHANNEL_OUT_MONO,
+    [2] = AUDIO_CHANNEL_OUT_STEREO,
+    [3] = AUDIO_CHANNEL_OUT_2POINT1,
+    [4] = AUDIO_CHANNEL_OUT_QUAD,
+    [5] = AUDIO_CHANNEL_OUT_PENTA,
+    [6] = AUDIO_CHANNEL_OUT_5POINT1,
+    [7] = AUDIO_CHANNEL_OUT_6POINT1,
+    [8] = AUDIO_CHANNEL_OUT_7POINT1,
+
+    [9 ... 11] = AUDIO_CHANNEL_NONE,  // == 0 (so this line is optional and could be omitted).
+
+    [12] = AUDIO_CHANNEL_OUT_7POINT1POINT4,
+
+    [13 ... 23] = AUDIO_CHANNEL_NONE,  //  == 0 (so this line is optional and could be omitted).
+
+    [24] = AUDIO_CHANNEL_OUT_22POINT2,
+};
+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[FCC_24 + 1] = {
+    [0] = AUDIO_CHANNEL_NONE,  // == 0 (so this line is optional and could be omitted).
+
+    [1] = AUDIO_CHANNEL_INDEX_MASK_1,
+    [2] = AUDIO_CHANNEL_INDEX_MASK_2,
+    [3] = AUDIO_CHANNEL_INDEX_MASK_3,
+    [4] = AUDIO_CHANNEL_INDEX_MASK_4,
+    [5] = AUDIO_CHANNEL_INDEX_MASK_5,
+    [6] = AUDIO_CHANNEL_INDEX_MASK_6,
+    [7] = AUDIO_CHANNEL_INDEX_MASK_7,
+    [8] = AUDIO_CHANNEL_INDEX_MASK_8,
+
+    [9] = AUDIO_CHANNEL_INDEX_MASK_9,
+    [10] = AUDIO_CHANNEL_INDEX_MASK_10,
+    [11] = AUDIO_CHANNEL_INDEX_MASK_11,
+    [12] = AUDIO_CHANNEL_INDEX_MASK_12,
+    [13] = AUDIO_CHANNEL_INDEX_MASK_13,
+    [14] = AUDIO_CHANNEL_INDEX_MASK_14,
+    [15] = AUDIO_CHANNEL_INDEX_MASK_15,
+    [16] = AUDIO_CHANNEL_INDEX_MASK_16,
+
+    [17] = AUDIO_CHANNEL_INDEX_MASK_17,
+    [18] = AUDIO_CHANNEL_INDEX_MASK_18,
+    [19] = AUDIO_CHANNEL_INDEX_MASK_19,
+    [20] = AUDIO_CHANNEL_INDEX_MASK_20,
+    [21] = AUDIO_CHANNEL_INDEX_MASK_21,
+    [22] = AUDIO_CHANNEL_INDEX_MASK_22,
+    [23] = AUDIO_CHANNEL_INDEX_MASK_23,
+    [24] = AUDIO_CHANNEL_INDEX_MASK_24,
+};
+static const int CHANNEL_INDEX_MASKS_SIZE = AUDIO_ARRAY_SIZE(CHANNEL_INDEX_MASKS_MAP);
+
 /*
  * Locking Helpers
  */
@@ -158,6 +239,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 +275,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 +401,65 @@
     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;
+    }
+}
+
+static unsigned int populate_channel_mask_from_profile(const alsa_device_profile* profile,
+                                                       bool is_output,
+                                                       audio_channel_mask_t channel_masks[])
+{
+    unsigned int num_channel_masks = 0;
+    const audio_channel_mask_t* channel_masks_map =
+            is_output ? OUT_CHANNEL_MASKS_MAP : IN_CHANNEL_MASKS_MAP;
+    int channel_masks_size = is_output ? OUT_CHANNEL_MASKS_SIZE : IN_CHANNEL_MASKS_SIZE;
+    if (channel_masks_size > FCC_LIMIT + 1) {
+        channel_masks_size = FCC_LIMIT + 1;
+    }
+    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 &&
+            num_channel_masks < AUDIO_PORT_MAX_CHANNEL_MASKS; ++i) {
+        if (channel_count < channel_masks_size &&
+            channel_masks_map[channel_count] != AUDIO_CHANNEL_NONE) {
+            channel_masks[num_channel_masks++] = channel_masks_map[channel_count];
+            if (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) {
+            channel_masks[num_channel_masks++] = CHANNEL_INDEX_MASKS_MAP[channel_count];
+        }
+    }
+    return num_channel_masks;
+}
+
+static unsigned int populate_sample_rates_from_profile(const alsa_device_profile* profile,
+                                                       unsigned int sample_rates[])
+{
+    unsigned int num_sample_rates = 0;
+    for (;num_sample_rates < min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
+            profile->sample_rates[num_sample_rates] != 0; num_sample_rates++) {
+        sample_rates[num_sample_rates] = profile->sample_rates[num_sample_rates];
+    }
+    return num_sample_rates;
+}
+
 /*
  * HAl Functions
  */
@@ -286,12 +468,106 @@
  * following order: hw device > out stream
  */
 
+static struct alsa_device_info* stream_get_first_alsa_device(const struct listnode *alsa_devices) {
+    if (list_empty(alsa_devices)) {
+        return NULL;
+    }
+    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;
+    list_for_each(node, alsa_devices) {
+        struct alsa_device_info *device_info =
+                node_to_item(node, struct alsa_device_info, list_node);
+        dprintf(fd, "Output Profile %zu:\n", i);
+        profile_dump(&device_info->profile, fd);
+
+        dprintf(fd, "Output Proxy %zu:\n", i);
+        proxy_dump(&device_info->proxy, fd);
+    }
+}
+
 /*
  * OUT functions
  */
 static uint32_t out_get_sample_rate(const struct audio_stream *stream)
 {
-    uint32_t rate = proxy_get_sample_rate(&((struct stream_out*)stream)->proxy);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(
+            &((struct stream_out*)stream)->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return 0;
+    }
+    uint32_t rate = proxy_get_sample_rate(&device_info->proxy);
     ALOGV("out_get_sample_rate() = %d", rate);
     return rate;
 }
@@ -304,9 +580,12 @@
 static size_t out_get_buffer_size(const struct audio_stream *stream)
 {
     const struct stream_out* out = (const struct stream_out*)stream;
-    size_t buffer_size =
-        proxy_get_period_size(&out->proxy) * audio_stream_out_frame_size(&(out->stream));
-    return buffer_size;
+    const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return 0;
+    }
+    return proxy_get_period_size(&device_info->proxy) * audio_stream_out_frame_size(&(out->stream));
 }
 
 static uint32_t out_get_channels(const struct audio_stream *stream)
@@ -321,8 +600,13 @@
      * Relies on the framework to provide data in the specified format.
      * This could change in the future.
      */
-    alsa_device_proxy * proxy = &((struct stream_out*)stream)->proxy;
-    audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy));
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(
+            &((struct stream_out*)stream)->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return AUDIO_FORMAT_DEFAULT;
+    }
+    audio_format_t format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy));
     return format;
 }
 
@@ -336,10 +620,9 @@
     struct stream_out *out = (struct stream_out *)stream;
 
     stream_lock(&out->lock);
-    if (!out->standby) {
-        proxy_close(&out->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;
 }
@@ -348,67 +631,44 @@
     const struct stream_out* out_stream = (const struct stream_out*) stream;
 
     if (out_stream != NULL) {
-        dprintf(fd, "Output Profile:\n");
-        profile_dump(&out_stream->profile, fd);
-
-        dprintf(fd, "Output Proxy:\n");
-        proxy_dump(&out_stream->proxy, fd);
+        stream_dump_alsa_devices(&out_stream->alsa_devices, fd);
     }
 
     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);
-    if (!profile_is_cached_for(&out->profile, card, device)) {
-        /* cannot read pcm device info if playback is active */
-        if (!out->standby)
-            ret_value = -ENOSYS;
-        else {
-            int saved_card = out->profile.card;
-            int saved_device = out->profile.device;
-            out->profile.card = card;
-            out->profile.device = device;
-            ret_value = profile_read_device_info(&out->profile) ? 0 : -EINVAL;
-            if (ret_value != 0) {
-                out->profile.card = saved_card;
-                out->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)
 {
     struct stream_out *out = (struct stream_out *)stream;
     stream_lock(&out->lock);
-    char * params_str =  device_get_parameters(&out->profile, keys);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&out->alsa_devices);
+    char *params_str = NULL;
+    if (device_info != NULL) {
+        params_str =  device_get_parameters(&device_info->profile, keys);
+    }
     stream_unlock(&out->lock);
     return params_str;
 }
 
 static uint32_t out_get_latency(const struct audio_stream_out *stream)
 {
-    alsa_device_proxy * proxy = &((struct stream_out*)stream)->proxy;
-    return proxy_get_latency(proxy);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(
+            &((struct stream_out*)stream)->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return 0;
+    }
+    return proxy_get_latency(&device_info->proxy);
 }
 
 static int out_set_volume(struct audio_stream_out *stream, float left, float right)
@@ -419,9 +679,31 @@
 /* must be called with hw device and output stream mutexes locked */
 static int start_output_stream(struct stream_out *out)
 {
-    ALOGV("start_output_stream(card:%d device:%d)", out->profile.card, out->profile.device);
+    int status = 0;
+    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);
+        ALOGV("start_output_stream(card:%d device:%d)",
+                device_info->profile.card, device_info->profile.device);
+        status = proxy_open(&device_info->proxy);
+        if (status != 0) {
+            ALOGE("%s failed to open device(card: %d device: %d)",
+                    __func__, device_info->profile.card, device_info->profile.device);
+            goto exit;
+        }
+    }
 
-    return proxy_open(&out->proxy);
+exit:
+    if (status != 0) {
+        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);
+        }
+
+    }
+    return status;
 }
 
 static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
@@ -438,32 +720,37 @@
         out->standby = false;
     }
 
-    alsa_device_proxy* proxy = &out->proxy;
-    const void * write_buff = buffer;
-    int num_write_buff_bytes = bytes;
-    const int num_device_channels = proxy_get_channel_count(proxy); /* what we told alsa */
-    const int num_req_channels = out->hal_channel_count; /* what we told AudioFlinger */
-    if (num_device_channels != num_req_channels) {
-        /* allocate buffer */
-        const size_t required_conversion_buffer_size =
-                 bytes * num_device_channels / num_req_channels;
-        if (required_conversion_buffer_size > out->conversion_buffer_size) {
-            out->conversion_buffer_size = required_conversion_buffer_size;
-            out->conversion_buffer = realloc(out->conversion_buffer,
-                                             out->conversion_buffer_size);
+    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);
+        alsa_device_proxy* proxy = &device_info->proxy;
+        const void * write_buff = buffer;
+        int num_write_buff_bytes = bytes;
+        const int num_device_channels = proxy_get_channel_count(proxy); /* what we told alsa */
+        const int num_req_channels = out->hal_channel_count; /* what we told AudioFlinger */
+        if (num_device_channels != num_req_channels) {
+            /* allocate buffer */
+            const size_t required_conversion_buffer_size =
+                     bytes * num_device_channels / num_req_channels;
+            if (required_conversion_buffer_size > out->conversion_buffer_size) {
+                out->conversion_buffer_size = required_conversion_buffer_size;
+                out->conversion_buffer = realloc(out->conversion_buffer,
+                                                 out->conversion_buffer_size);
+            }
+            /* convert data */
+            const audio_format_t audio_format = out_get_format(&(out->stream.common));
+            const unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
+            num_write_buff_bytes =
+                    adjust_channels(write_buff, num_req_channels,
+                                    out->conversion_buffer, num_device_channels,
+                                    sample_size_in_bytes, num_write_buff_bytes);
+            write_buff = out->conversion_buffer;
         }
-        /* convert data */
-        const audio_format_t audio_format = out_get_format(&(out->stream.common));
-        const unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
-        num_write_buff_bytes =
-                adjust_channels(write_buff, num_req_channels,
-                                out->conversion_buffer, num_device_channels,
-                                sample_size_in_bytes, num_write_buff_bytes);
-        write_buff = out->conversion_buffer;
-    }
 
-    if (write_buff != NULL && num_write_buff_bytes != 0) {
-        proxy_write(&out->proxy, write_buff, num_write_buff_bytes);
+        if (write_buff != NULL && num_write_buff_bytes != 0) {
+            proxy_write(proxy, write_buff, num_write_buff_bytes);
+        }
     }
 
     stream_unlock(&out->lock);
@@ -491,9 +778,9 @@
     struct stream_out *out = (struct stream_out *)stream; // discard const qualifier
     stream_lock(&out->lock);
 
-    const alsa_device_proxy *proxy = &out->proxy;
-    const int ret = proxy_get_presentation_position(proxy, frames, timestamp);
-
+    const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
+    const int ret = device_info == NULL ? -ENODEV :
+            proxy_get_presentation_position(&device_info->proxy, frames, timestamp);
     stream_unlock(&out->lock);
     return ret;
 }
@@ -551,30 +838,35 @@
     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;
 
-    profile_init(&out->profile, PCM_OUT);
+    list_init(&out->alsa_devices);
+    struct alsa_device_info *device_info =
+            (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info));
+    profile_init(&device_info->profile, PCM_OUT);
 
     // build this to hand to the alsa_device_proxy
-    struct pcm_config proxy_config;
-    memset(&proxy_config, 0, sizeof(proxy_config));
+    struct pcm_config proxy_config = {};
 
     /* Pull out the card/device pair */
-    parse_card_device_params(address, &out->profile.card, &out->profile.device);
+    parse_card_device_params(address, &device_info->profile.card, &device_info->profile.device);
 
-    profile_read_device_info(&out->profile);
+    profile_read_device_info(&device_info->profile);
 
     int ret = 0;
 
     /* Rate */
     if (config->sample_rate == 0) {
-        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(&out->profile);
-    } else if (profile_is_sample_rate_valid(&out->profile, config->sample_rate)) {
+        proxy_config.rate = profile_get_default_sample_rate(&device_info->profile);
+    } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
         proxy_config.rate = config->sample_rate;
     } else {
-        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(&out->profile);
+        proxy_config.rate = config->sample_rate =
+                profile_get_default_sample_rate(&device_info->profile);
         ret = -EINVAL;
     }
 
@@ -585,14 +877,14 @@
 
     /* Format */
     if (config->format == AUDIO_FORMAT_DEFAULT) {
-        proxy_config.format = profile_get_default_format(&out->profile);
+        proxy_config.format = profile_get_default_format(&device_info->profile);
         config->format = audio_format_from_pcm_format(proxy_config.format);
     } else {
         enum pcm_format fmt = pcm_format_from_audio_format(config->format);
-        if (profile_is_format_valid(&out->profile, fmt)) {
+        if (profile_is_format_valid(&device_info->profile, fmt)) {
             proxy_config.format = fmt;
         } else {
-            proxy_config.format = profile_get_default_format(&out->profile);
+            proxy_config.format = profile_get_default_format(&device_info->profile);
             config->format = audio_format_from_pcm_format(proxy_config.format);
             ret = -EINVAL;
         }
@@ -602,7 +894,7 @@
     bool calc_mask = false;
     if (config->channel_mask == AUDIO_CHANNEL_NONE) {
         /* query case */
-        out->hal_channel_count = profile_get_default_channel_count(&out->profile);
+        out->hal_channel_count = profile_get_default_channel_count(&device_info->profile);
         calc_mask = true;
     } else {
         /* explicit case */
@@ -610,19 +902,19 @@
     }
 
     /* The Framework is currently limited to no more than this number of channels */
-    if (out->hal_channel_count > FCC_8) {
-        out->hal_channel_count = FCC_8;
+    if (out->hal_channel_count > FCC_LIMIT) {
+        out->hal_channel_count = FCC_LIMIT;
         calc_mask = true;
     }
 
     if (calc_mask) {
         /* need to calculate the mask from channel count either because this is the query case
-         * or the specified mask isn't valid for this device, or is more then the FW can handle */
+         * or the specified mask isn't valid for this device, or is more than the FW can handle */
         config->channel_mask = out->hal_channel_count <= FCC_2
-            /* position mask for mono and stereo*/
-            ? audio_channel_out_mask_from_count(out->hal_channel_count)
-            /* otherwise indexed */
-            : audio_channel_mask_for_index_assignment_from_count(out->hal_channel_count);
+                /* position mask for mono and stereo*/
+                ? audio_channel_out_mask_from_count(out->hal_channel_count)
+                /* otherwise indexed */
+                : audio_channel_mask_for_index_assignment_from_count(out->hal_channel_count);
     }
 
     out->hal_channel_mask = config->channel_mask;
@@ -631,8 +923,11 @@
     // if they differ, choose the "actual" number of channels *closest* to the "logical".
     // and store THAT in proxy_config.channels
     proxy_config.channels =
-            profile_get_closest_channel_count(&out->profile, out->hal_channel_count);
-    proxy_prepare(&out->proxy, &out->profile, &proxy_config);
+            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);
 
     /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger
      * So clear any errors that may have occurred above.
@@ -656,21 +951,22 @@
                                      struct audio_stream_out *stream)
 {
     struct stream_out *out = (struct stream_out *)stream;
-    ALOGV("adev_close_output_stream(c:%d d:%d)", out->profile.card, out->profile.device);
 
+    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;
 
-    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);
 }
@@ -687,7 +983,13 @@
  */
 static uint32_t in_get_sample_rate(const struct audio_stream *stream)
 {
-    uint32_t rate = proxy_get_sample_rate(&((const struct stream_in *)stream)->proxy);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(
+            &((const struct stream_in *)stream)->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return 0;
+    }
+    uint32_t rate = proxy_get_sample_rate(&device_info->proxy);
     ALOGV("in_get_sample_rate() = %d", rate);
     return rate;
 }
@@ -701,7 +1003,12 @@
 static size_t in_get_buffer_size(const struct audio_stream *stream)
 {
     const struct stream_in * in = ((const struct stream_in*)stream);
-    return proxy_get_period_size(&in->proxy) * audio_stream_in_frame_size(&(in->stream));
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return 0;
+    }
+    return proxy_get_period_size(&device_info->proxy) * audio_stream_in_frame_size(&(in->stream));
 }
 
 static uint32_t in_get_channels(const struct audio_stream *stream)
@@ -712,7 +1019,13 @@
 
 static audio_format_t in_get_format(const struct audio_stream *stream)
 {
-     alsa_device_proxy *proxy = &((struct stream_in*)stream)->proxy;
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(
+            &((const struct stream_in *)stream)->alsa_devices);
+    if (device_info == NULL) {
+        ALOGW("%s device info is null", __func__);
+        return AUDIO_FORMAT_DEFAULT;
+    }
+     alsa_device_proxy *proxy = &device_info->proxy;
      audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy));
      return format;
 }
@@ -729,12 +1042,10 @@
     struct stream_in *in = (struct stream_in *)stream;
 
     stream_lock(&in->lock);
-    if (!in->standby) {
-        proxy_close(&in->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;
 }
 
@@ -742,11 +1053,7 @@
 {
   const struct stream_in* in_stream = (const struct stream_in*)stream;
   if (in_stream != NULL) {
-      dprintf(fd, "Input Profile:\n");
-      profile_dump(&in_stream->profile, fd);
-
-      dprintf(fd, "Input Proxy:\n");
-      proxy_dump(&in_stream->proxy, fd);
+      stream_dump_alsa_devices(&in_stream->alsa_devices, fd);
   }
 
   return 0;
@@ -756,42 +1063,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);
-
-    if (card >= 0 && device >= 0 && !profile_is_cached_for(&in->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 = in->profile.card;
-            int saved_device = in->profile.device;
-            in->profile.card = card;
-            in->profile.device = device;
-            ret_value = profile_read_device_info(&in->profile) ? 0 : -EINVAL;
-            if (ret_value != 0) {
-                ALOGE("Can't read device profile. card:%d, device:%d", card, device);
-                in->profile.card = saved_card;
-                in->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)
@@ -799,7 +1074,11 @@
     struct stream_in *in = (struct stream_in *)stream;
 
     stream_lock(&in->lock);
-    char * params_str =  device_get_parameters(&in->profile, keys);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+    char *params_str = NULL;
+    if (device_info != NULL) {
+        params_str =  device_get_parameters(&device_info->profile, keys);
+    }
     stream_unlock(&in->lock);
 
     return params_str;
@@ -823,9 +1102,15 @@
 /* must be called with hw device and output stream mutexes locked */
 static int start_input_stream(struct stream_in *in)
 {
-    ALOGV("start_input_stream(card:%d device:%d)", in->profile.card, in->profile.device);
+    // Only care about the first device as only one input device is allowed.
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+    if (device_info == NULL) {
+        return -ENODEV;
+    }
 
-    return proxy_open(&in->proxy);
+    ALOGV("start_input_stream(card:%d device:%d)",
+            device_info->profile.card, device_info->profile.device);
+    return proxy_open(&device_info->proxy);
 }
 
 /* TODO mutex stuff here (see out_write) */
@@ -847,12 +1132,18 @@
         in->standby = false;
     }
 
+    // Only care about the first device as only one input device is allowed.
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+    if (device_info == NULL) {
+        return 0;
+    }
+
     /*
      * OK, we need to figure out how much data to read to be able to output the requested
      * number of bytes in the HAL format (16-bit, stereo).
      */
     num_read_buff_bytes = bytes;
-    int num_device_channels = proxy_get_channel_count(&in->proxy); /* what we told Alsa */
+    int num_device_channels = proxy_get_channel_count(&device_info->proxy); /* what we told Alsa */
     int num_req_channels = in->hal_channel_count; /* what we told AudioFlinger */
 
     if (num_device_channels != num_req_channels) {
@@ -870,7 +1161,7 @@
         read_buff = in->conversion_buffer;
     }
 
-    ret = proxy_read(&in->proxy, read_buff, num_read_buff_bytes);
+    ret = proxy_read(&device_info->proxy, read_buff, num_read_buff_bytes);
     if (ret == 0) {
         if (num_device_channels != num_req_channels) {
             // ALOGV("chans dev:%d req:%d", num_device_channels, num_req_channels);
@@ -911,8 +1202,10 @@
     struct stream_in *in = (struct stream_in *)stream; // discard const qualifier
     stream_lock(&in->lock);
 
-    const alsa_device_proxy *proxy = &in->proxy;
-    const int ret = proxy_get_capture_position(proxy, frames, time);
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+
+    const int ret = device_info == NULL ? -ENODEV
+            : proxy_get_capture_position(&device_info->proxy, frames, time);
 
     stream_unlock(&in->lock);
     return ret;
@@ -991,14 +1284,18 @@
     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;
 
-    profile_init(&in->profile, PCM_IN);
+    list_init(&in->alsa_devices);
+    struct alsa_device_info *device_info =
+            (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);
@@ -1007,16 +1304,16 @@
 
     /* Check if an input stream is already open */
     if (num_open_inputs > 0) {
-        if (!profile_is_cached_for(&in->profile, card, device)) {
+        if (!profile_is_cached_for(&device_info->profile, card, device)) {
             ALOGW("%s fail - address card:%d device:%d doesn't match existing profile",
                     __func__, card, device);
             ret = -EINVAL;
         }
     } else {
         /* Read input profile only if necessary */
-        in->profile.card = card;
-        in->profile.device = device;
-        if (!profile_read_device_info(&in->profile)) {
+        device_info->profile.card = card;
+        device_info->profile.device = device;
+        if (!profile_read_device_info(&device_info->profile)) {
             ALOGW("%s fail - cannot read profile", __func__);
             ret = -EINVAL;
         }
@@ -1030,19 +1327,19 @@
     /* Rate */
     int request_config_rate = config->sample_rate;
     if (config->sample_rate == 0) {
-        config->sample_rate = profile_get_default_sample_rate(&in->profile);
+        config->sample_rate = profile_get_default_sample_rate(&device_info->profile);
     }
 
     if (in->adev->device_sample_rate != 0 &&   /* we are playing, so lock the rate if possible */
         in->adev->device_sample_rate >= RATELOCK_THRESHOLD) {/* but only for high sample rates */
         if (config->sample_rate != in->adev->device_sample_rate) {
-            unsigned highest_rate = profile_get_highest_sample_rate(&in->profile);
+            unsigned highest_rate = profile_get_highest_sample_rate(&device_info->profile);
             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 {
@@ -1051,24 +1348,25 @@
                 }
             }
         }
-    } else if (profile_is_sample_rate_valid(&in->profile, config->sample_rate)) {
-        proxy_config.rate = config->sample_rate;
+    } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
+        in->config.rate = config->sample_rate;
     } else {
-        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(&in->profile);
+        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(&in->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(&in->profile, fmt)) {
-            proxy_config.format = fmt;
+        if (profile_is_format_valid(&device_info->profile, fmt)) {
+            in->config.format = fmt;
         } else {
-            proxy_config.format = profile_get_default_format(&in->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;
         }
     }
@@ -1077,7 +1375,7 @@
     bool calc_mask = false;
     if (config->channel_mask == AUDIO_CHANNEL_NONE) {
         /* query case */
-        in->hal_channel_count = profile_get_default_channel_count(&in->profile);
+        in->hal_channel_count = profile_get_default_channel_count(&device_info->profile);
         calc_mask = true;
     } else {
         /* explicit case */
@@ -1085,14 +1383,14 @@
     }
 
     /* The Framework is currently limited to no more than this number of channels */
-    if (in->hal_channel_count > FCC_8) {
-        in->hal_channel_count = FCC_8;
+    if (in->hal_channel_count > FCC_LIMIT) {
+        in->hal_channel_count = FCC_LIMIT;
         calc_mask = true;
     }
 
     if (calc_mask) {
         /* need to calculate the mask from channel count either because this is the query case
-         * or the specified mask isn't valid for this device, or is more then the FW can handle */
+         * or the specified mask isn't valid for this device, or is more than the FW can handle */
         in->hal_channel_mask = in->hal_channel_count <= FCC_2
             /* position mask for mono & stereo */
             ? audio_channel_in_mask_from_count(in->hal_channel_count)
@@ -1113,9 +1411,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 =
-                profile_get_closest_channel_count(&in->profile, in->hal_channel_count);
-        ret = proxy_prepare(&in->proxy, &in->profile, &proxy_config);
+        in->config.channels =
+                profile_get_closest_channel_count(&device_info->profile, in->hal_channel_count);
+        ret = proxy_prepare(&device_info->proxy, &device_info->profile, &in->config);
         if (ret == 0) {
             in->standby = true;
 
@@ -1128,12 +1426,12 @@
             adev_add_stream_to_list(in->adev, &in->adev->input_stream_list, &in->list_node);
         } else {
             ALOGW("proxy_prepare error %d", ret);
-            unsigned channel_count = proxy_get_channel_count(&in->proxy);
+            unsigned channel_count = proxy_get_channel_count(&device_info->proxy);
             config->channel_mask = channel_count <= FCC_2
                 ? audio_channel_in_mask_from_count(channel_count)
                 : audio_channel_mask_for_index_assignment_from_count(channel_count);
-            config->format = audio_format_from_pcm_format(proxy_get_format(&in->proxy));
-            config->sample_rate = proxy_get_sample_rate(&in->proxy);
+            config->format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy));
+            config->sample_rate = proxy_get_sample_rate(&device_info->proxy);
         }
     }
 
@@ -1145,6 +1443,8 @@
         return ret;
     }
 
+    list_add_tail(&in->alsa_devices, &device_info->list_node);
+
     device_lock(in->adev);
     ++in->adev->inputs_open;
     device_unlock(in->adev);
@@ -1156,18 +1456,25 @@
                                     struct audio_stream_in *stream)
 {
     struct stream_in *in = (struct stream_in *)stream;
-    ALOGV("adev_close_input_stream(c:%d d:%d)", in->profile.card, in->profile.device);
 
-    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) {
+        ALOGV("adev_close_input_stream(c:%d d:%d)",
+                device_info->profile.card, device_info->profile.device);
+    }
     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);
 
@@ -1221,6 +1528,282 @@
     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);
+
+    // Timestamps:
+    // Audio timestamps assume continuous PCM frame counts which are maintained
+    // with the device proxy.transferred variable.  Technically it would be better
+    // associated with in or out stream, not the device; here we save and restore
+    // using the first alsa device as a simplification.
+    uint64_t saved_transferred_frames = 0;
+    struct alsa_device_info *device_info = stream_get_first_alsa_device(alsa_devices);
+    if (device_info != NULL) saved_transferred_frames = device_info->proxy.transferred;
+
+    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;
+    }
+
+    // Timestamps: Restore transferred frames.
+    if (saved_transferred_frames != 0) {
+        device_info = stream_get_first_alsa_device(alsa_devices);
+        if (device_info != NULL) device_info->proxy.transferred = saved_transferred_frames;
+    }
+
+    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 = populate_sample_rates_from_profile(&profile, port->sample_rates);
+    port->num_channel_masks = populate_channel_mask_from_profile(
+            &profile, is_output, port->channel_masks);
+
+    return 0;
+}
+
+static int adev_get_audio_port_v7(struct audio_hw_device *dev, struct audio_port_v7 *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;
+    }
+
+    audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
+    unsigned int num_channel_masks = populate_channel_mask_from_profile(
+            &profile, is_output, channel_masks);
+    unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
+    const unsigned int num_sample_rates =
+            populate_sample_rates_from_profile(&profile, sample_rates);
+    port->num_audio_profiles = 0;;
+    for (size_t i = 0; i < min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
+            profile.formats[i] != 0; ++i) {
+        audio_format_t format = audio_format_from(profile.formats[i]);
+        if (format == AUDIO_FORMAT_INVALID) {
+            continue;
+        }
+        const unsigned int j = port->num_audio_profiles++;
+        port->audio_profiles[j].format = format;
+        port->audio_profiles[j].num_sample_rates = num_sample_rates;
+        memcpy(port->audio_profiles[j].sample_rates,
+               sample_rates,
+               num_sample_rates * sizeof(unsigned int));
+        port->audio_profiles[j].num_channel_masks = num_channel_masks;
+        memcpy(port->audio_profiles[j].channel_masks,
+               channel_masks,
+               num_channel_masks* sizeof(audio_channel_mask_t));
+    }
+
+    return 0;
+}
+
 static int adev_dump(const struct audio_hw_device *device, int fd)
 {
     dprintf(fd, "\nUSB audio module:\n");
@@ -1290,7 +1873,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_2;
     adev->hw_device.common.module = (struct hw_module_t *)module;
     adev->hw_device.common.close = adev_close;
 
@@ -1307,6 +1890,10 @@
     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.get_audio_port_v7 = adev_get_audio_port_v7;
     adev->hw_device.dump = adev_dump;
 
     *device = &adev->hw_device.common;