hal: configure playback stream rendering based on output policy

For the usecases where individual streams have to be rendered
through different post processing chain on DSP an output policy
is defined. The output configuration is read from the
output policy file based on the stream atrributes to render the
playback appropriately.

Change-Id: I095592013ffa5b04bea69254026ebb78e2934822
diff --git a/hal/Android.mk b/hal/Android.mk
index 0ec3e3d..53b4a09 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -35,7 +35,8 @@
 	platform_info.c \
 	$(AUDIO_PLATFORM)/platform.c
 
-LOCAL_SRC_FILES += audio_extn/audio_extn.c
+LOCAL_SRC_FILES += audio_extn/audio_extn.c \
+                   audio_extn/utils.c
 
 ifneq ($(strip $(AUDIO_FEATURE_DISABLED_PCM_OFFLOAD)),true)
     LOCAL_CFLAGS += -DPCM_OFFLOAD_ENABLED
@@ -91,6 +92,11 @@
     LOCAL_CFLAGS += -DCOMPRESS_VOIP_ENABLED
     LOCAL_SRC_FILES += voice_extn/compress_voip.c
 endif
+
+endif
+
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_FORMATS)),true)
+LOCAL_CFLAGS += -DFORMATS_ENABLED
 endif
 
 ifneq ($(strip, $(AUDIO_FEATURE_DISABLED_SPKR_PROTECTION)),true)
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index a17f011..771d913 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -61,6 +61,8 @@
 #endif
 
 
+#define MAX_LENGTH_MIXER_CONTROL_IN_INT                  (128)
+
 void audio_extn_set_parameters(struct audio_device *adev,
                                struct str_parms *parms);
 
@@ -182,11 +184,15 @@
 #define audio_extn_spkr_prot_start_processing(snd_device)    (-EINVAL)
 #define audio_extn_spkr_prot_stop_processing()     (0)
 #define audio_extn_spkr_prot_is_enabled() (false)
+#define audio_extn_spkr_prot_get_acdb_id(snd_device)         (-EINVAL)
+#define audio_extn_get_spkr_prot_snd_device(snd_device) (SND_DEVICE_OUT_SPEAKER)
 #else
 void audio_extn_spkr_prot_init(void *adev);
 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
 void audio_extn_spkr_prot_stop_processing();
 bool audio_extn_spkr_prot_is_enabled();
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
 #endif
 
 #ifndef COMPRESS_CAPTURE_ENABLED
@@ -246,4 +252,17 @@
 audio_usecase_t audio_extn_hfp_get_usecase();
 #endif
 
+void audio_extn_utils_update_streams_output_cfg_list(void *platform,
+                                  struct mixer *mixer,
+                                  struct listnode *streams_output_cfg_list);
+void audio_extn_utils_dump_streams_output_cfg_list(
+                                  struct listnode *streams_output_cfg_list);
+void audio_extn_utils_release_streams_output_cfg_list(
+                                  struct listnode *streams_output_cfg_list);
+void audio_extn_utils_update_stream_app_type_cfg(void *platform,
+                                  struct listnode *streams_output_cfg_list,
+                                  audio_output_flags_t flags,
+                                  audio_format_t format,
+                                  struct stream_app_type_cfg *app_type_cfg);
+int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase);
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
index ce673ea..8e3e980 100644
--- a/hal/audio_extn/spkr_protection.c
+++ b/hal/audio_extn/spkr_protection.c
@@ -577,6 +577,25 @@
     }
 }
 
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
+{
+    int acdb_id;
+
+    acdb_id = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
+              platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED) :
+              -EINVAL;
+
+    return acdb_id;
+}
+
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
+{
+    if ((snd_device == SND_DEVICE_OUT_SPEAKER) && handle.spkr_prot_enable)
+        return SND_DEVICE_OUT_SPEAKER_PROTECTED;
+    else
+        return snd_device;
+}
+
 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
 {
     struct audio_usecase uc_info_tx;
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
new file mode 100644
index 0000000..bb34bf0
--- /dev/null
+++ b/hal/audio_extn/utils.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_utils"
+/* #define LOG_NDEBUG 0 */
+
+#include <errno.h>
+#include <cutils/properties.h>
+#include <cutils/config_utils.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+#include <cutils/misc.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "audio_extn.h"
+
+#define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_output_policy.conf"
+
+#define OUTPUTS_TAG "outputs"
+
+#define DYNAMIC_VALUE_TAG "dynamic"
+#define FLAGS_TAG "flags"
+#define FORMATS_TAG "formats"
+#define SAMPLING_RATES_TAG "sampling_rates"
+#define BIT_WIDTH_TAG "bit_width"
+#define APP_TYPE_TAG "app_type"
+
+#define STRING_TO_ENUM(string) { #string, string }
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+struct string_to_enum {
+    const char *name;
+    uint32_t value;
+};
+
+const struct string_to_enum s_flag_name_to_enum_table[] = {
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
+#ifdef INCALL_MUSIC_ENABLED
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
+#endif
+#ifdef COMPRESS_VOIP_ENABLED
+    STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
+#endif
+};
+
+const struct string_to_enum s_format_name_to_enum_table[] = {
+    STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT),
+    STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT),
+    STRING_TO_ENUM(AUDIO_FORMAT_MP3),
+    STRING_TO_ENUM(AUDIO_FORMAT_AAC),
+    STRING_TO_ENUM(AUDIO_FORMAT_VORBIS),
+#ifdef FORMATS_ENABLED
+    STRING_TO_ENUM(AUDIO_FORMAT_AC3),
+    STRING_TO_ENUM(AUDIO_FORMAT_EAC3),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS_LBR),
+    STRING_TO_ENUM(AUDIO_FORMAT_WMA),
+    STRING_TO_ENUM(AUDIO_FORMAT_WMA_PRO),
+    STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADIF),
+    STRING_TO_ENUM(AUDIO_FORMAT_AMR_NB),
+    STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB),
+    STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB_PLUS),
+    STRING_TO_ENUM(AUDIO_FORMAT_EVRC),
+    STRING_TO_ENUM(AUDIO_FORMAT_EVRCB),
+    STRING_TO_ENUM(AUDIO_FORMAT_EVRCWB),
+    STRING_TO_ENUM(AUDIO_FORMAT_QCELP),
+    STRING_TO_ENUM(AUDIO_FORMAT_MP2),
+    STRING_TO_ENUM(AUDIO_FORMAT_EVRCNW),
+    STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT_OFFLOAD),
+    STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_OFFLOAD),
+#endif
+};
+
+static uint32_t string_to_enum(const struct string_to_enum *table, size_t size,
+                               const char *name)
+{
+    size_t i;
+    for (i = 0; i < size; i++) {
+        if (strcmp(table[i].name, name) == 0) {
+            ALOGV("%s found %s", __func__, table[i].name);
+            return table[i].value;
+        }
+    }
+    return 0;
+}
+
+static audio_output_flags_t parse_flag_names(char *name)
+{
+    uint32_t flag = 0;
+    char *flag_name = strtok(name, "|");
+    while (flag_name != NULL) {
+        if (strlen(flag_name) != 0) {
+            flag |= string_to_enum(s_flag_name_to_enum_table,
+                               ARRAY_SIZE(s_flag_name_to_enum_table),
+                               flag_name);
+        }
+        flag_name = strtok(NULL, "|");
+    }
+
+    ALOGV("parse_flag_names: flag - %d", flag);
+    return (audio_output_flags_t)flag;
+}
+
+static void parse_format_names(char *name, struct streams_output_cfg *so_info)
+{
+    struct stream_format *sf_info = NULL;
+    char *str = strtok(name, "|");
+
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0)
+        return;
+
+    list_init(&so_info->format_list);
+    while (str != NULL) {
+        audio_format_t format = (audio_format_t)string_to_enum(s_format_name_to_enum_table,
+                                              ARRAY_SIZE(s_format_name_to_enum_table), str);
+        ALOGV("%s: format - %d", __func__, format);
+        if (format != 0) {
+            sf_info = (struct stream_format *)calloc(1, sizeof(struct stream_format));
+            sf_info->format = format;
+            list_add_tail(&so_info->format_list, &sf_info->list);
+        }
+        str = strtok(NULL, "|");
+    }
+}
+
+static int parse_sample_rate_names(char *name)
+{
+    int sample_rate = 48000;
+    char *str = strtok(name, "|");
+
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
+        sample_rate = (int)strtol(str, (char **)NULL, 10);
+
+    ALOGV("%s: sample_rate - %d", __func__, sample_rate);
+    return sample_rate;
+}
+
+static int parse_bit_width_names(char *name)
+{
+    int bit_width = 16;
+    char *str = strtok(name, "|");
+
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
+        bit_width = (int)strtol(str, (char **)NULL, 10);
+
+    ALOGV("%s: bit_width - %d", __func__, bit_width);
+    return bit_width;
+}
+
+static int parse_app_type_names(void *platform, char *name)
+{
+    int app_type = 0; /* TODO: default app type from acdb when exposed using "platform" */
+    char *str = strtok(name, "|");
+
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
+        app_type = (int)strtol(str, (char **)NULL, 10);
+
+    ALOGV("%s: app_type - %d", __func__, app_type);
+    return app_type;
+}
+
+static void update_streams_output_cfg_list(cnode *root, void *platform,
+                                           struct listnode *streams_output_cfg_list)
+{
+    cnode *node = root->first_child;
+    struct streams_output_cfg *so_info;
+
+    ALOGV("%s", __func__);
+    so_info = (struct streams_output_cfg *)calloc(1, sizeof(struct streams_output_cfg));
+    while (node) {
+        if (strcmp(node->name, FLAGS_TAG) == 0) {
+            so_info->flags = parse_flag_names((char *)node->value);
+        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+            parse_format_names((char *)node->value, so_info);
+        } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+            so_info->app_type_cfg.sample_rate = parse_sample_rate_names((char *)node->value);
+        } else if (strcmp(node->name, BIT_WIDTH_TAG) == 0) {
+            so_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value);
+        } else if (strcmp(node->name, APP_TYPE_TAG) == 0) {
+            so_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value);
+        }
+        node = node->next;
+    }
+    list_add_tail(streams_output_cfg_list, &so_info->list);
+}
+
+static void load_output(cnode *root, void *platform,
+                        struct listnode *streams_output_cfg_list)
+{
+    cnode *node = config_find(root, OUTPUTS_TAG);
+    if (node == NULL) {
+        ALOGE("%s: could not load output, node is NULL", __func__);
+        return;
+    }
+
+    node = node->first_child;
+    while (node) {
+        ALOGV("%s: loading output %s", __func__, node->name);
+        update_streams_output_cfg_list(node, platform, streams_output_cfg_list);
+        node = node->next;
+    }
+}
+
+static void send_app_type_cfg(void *platform, struct mixer *mixer,
+                              struct listnode *streams_output_cfg_list)
+{
+    int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
+    int length = 0, i, num_app_types = 0;
+    struct listnode *node;
+    bool update;
+    struct mixer_ctl *ctl = NULL;
+    const char *mixer_ctl_name = "App Type Config";
+    struct streams_output_cfg *so_info;
+
+    if (!mixer) {
+        ALOGE("%s: mixer is null",__func__);
+        return;
+    }
+    ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
+        return;
+    }
+    if (streams_output_cfg_list == NULL) {
+        app_type_cfg[length++] = 1;
+        app_type_cfg[length++] = 0; /* TODO: default app type from acdb when exposed from "platform" */
+        app_type_cfg[length++] = 48000;
+        app_type_cfg[length++] = 16;
+        mixer_ctl_set_array(ctl, app_type_cfg, length);
+        return;
+    }
+
+    app_type_cfg[length++] = num_app_types;
+    list_for_each(node, streams_output_cfg_list) {
+        so_info = node_to_item(node, struct streams_output_cfg, list);
+        update = true;
+        for (i=0; i<length; i=i+3) {
+            if (app_type_cfg[i+1] == -1)
+                break;
+            else if (app_type_cfg[i+1] == so_info->app_type_cfg.app_type) {
+                update = false;
+                break;
+            }
+        }
+        if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) {
+            num_app_types += 1 ;
+            app_type_cfg[length++] = so_info->app_type_cfg.app_type;
+            app_type_cfg[length++] = so_info->app_type_cfg.sample_rate;
+            app_type_cfg[length++] = so_info->app_type_cfg.bit_width;
+        }
+    }
+    ALOGV("%s: num_app_types: %d", __func__, num_app_types);
+    if (num_app_types) {
+        app_type_cfg[0] = num_app_types;
+        mixer_ctl_set_array(ctl, app_type_cfg, length);
+    }
+}
+
+void audio_extn_utils_update_streams_output_cfg_list(void *platform,
+                                       struct mixer *mixer,
+                                       struct listnode *streams_output_cfg_list)
+{
+    cnode *root;
+    char *data;
+
+    ALOGV("%s", __func__);
+    list_init(streams_output_cfg_list);
+    data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL);
+    if (data == NULL) {
+        send_app_type_cfg(platform, mixer, NULL);
+        ALOGE("%s: could not load output policy config file", __func__);
+        return;
+    }
+
+    root = config_node("", "");
+    config_load(root, data);
+    load_output(root, platform, streams_output_cfg_list);
+
+    send_app_type_cfg(platform, mixer, streams_output_cfg_list);
+}
+
+void audio_extn_utils_dump_streams_output_cfg_list(
+                                       struct listnode *streams_output_cfg_list)
+{
+    int i=0;
+    struct listnode *node_i, *node_j;
+    struct streams_output_cfg *so_info;
+    struct stream_format *sf_info;
+    ALOGV("%s", __func__);
+    list_for_each(node_i, streams_output_cfg_list) {
+        so_info = node_to_item(node_i, struct streams_output_cfg, list);
+        ALOGV("%d: flags-%d, output_sample_rate-%d, output_bit_width-%d, app_type-%d",
+               i++, so_info->flags, so_info->app_type_cfg.sample_rate,
+               so_info->app_type_cfg.bit_width, so_info->app_type_cfg.app_type);
+        list_for_each(node_j, &so_info->format_list) {
+            sf_info = node_to_item(node_j, struct stream_format, list);
+            ALOGV("format-%x", sf_info->format);
+        }
+    }
+}
+
+void audio_extn_utils_release_streams_output_cfg_list(
+                                       struct listnode *streams_output_cfg_list)
+{
+    struct listnode *node_i, *node_j;
+    struct streams_output_cfg *so_info;
+    struct stream_format *sf_info;
+
+    ALOGV("%s", __func__);
+    while (!list_empty(streams_output_cfg_list)) {
+        node_i = list_head(streams_output_cfg_list);
+        so_info = node_to_item(node_i, struct streams_output_cfg, list);
+        while (!list_empty(&so_info->format_list)) {
+            node_j = list_head(&so_info->format_list);
+            list_remove(node_j);
+            free(node_to_item(node_j, struct stream_format, list));
+        }
+        list_remove(node_i);
+        free(node_to_item(node_i, struct streams_output_cfg, list));
+    }
+}
+
+void audio_extn_utils_update_stream_app_type_cfg(void *platform,
+                                  struct listnode *streams_output_cfg_list,
+                                  audio_output_flags_t flags,
+                                  audio_format_t format,
+                                  struct stream_app_type_cfg *app_type_cfg)
+{
+    struct listnode *node_i, *node_j;
+    struct streams_output_cfg *so_info;
+    struct stream_format *sf_info;
+
+    ALOGV("%s: flags: %x, format: %x", __func__, flags, format);
+    list_for_each(node_i, streams_output_cfg_list) {
+        so_info = node_to_item(node_i, struct streams_output_cfg, list);
+        if (so_info->flags == flags) {
+            list_for_each(node_j, &so_info->format_list) {
+                sf_info = node_to_item(node_j, struct stream_format, list);
+                if (sf_info->format == format) {
+                    ALOGV("App type: %d", so_info->app_type_cfg.app_type);
+                    app_type_cfg->app_type = so_info->app_type_cfg.app_type;
+                    app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
+                    app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
+                    return;
+                }
+            }
+        }
+    }
+    list_for_each(node_i, streams_output_cfg_list) {
+        so_info = node_to_item(node_i, struct streams_output_cfg, list);
+        if (so_info->flags == AUDIO_OUTPUT_FLAG_PRIMARY) {
+            ALOGV("Compatible output profile not found.");
+            ALOGV("App type default to primary output: %d", so_info->app_type_cfg.app_type);
+            app_type_cfg->app_type = so_info->app_type_cfg.app_type;
+            app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
+            app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
+            return;
+        }
+    }
+    ALOGW("%s: App type could not be selected. Falling back to default", __func__);
+    app_type_cfg->app_type = 0; /* TODO: default app type from acdb when exposed from "platform" */
+    app_type_cfg->sample_rate = 48000;
+    app_type_cfg->bit_width = 16;
+}
+
+int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase)
+{
+    char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
+    int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
+    struct stream_out *out = usecase->stream.out;
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    int pcm_device_id, acdb_dev_id, snd_device = usecase->out_snd_device;
+
+    ALOGV("%s", __func__);
+
+    if (usecase->type != PCM_PLAYBACK) {
+        ALOGV("%s: not a playback path, no need to cfg app type", __func__);
+        rc = 0;
+        goto exit_send_app_type_cfg;
+    }
+    if ((usecase->id != USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) &&
+        (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) &&
+        (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
+        (usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD)) {
+        ALOGV("%s: a playback path where app type cfg is not required", __func__);
+        rc = 0;
+        goto exit_send_app_type_cfg;
+    }
+
+    snd_device = usecase->out_snd_device;
+
+    pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Audio Stream %d App Type Cfg", pcm_device_id);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__,
+              mixer_ctl_name);
+        rc = -EINVAL;
+        goto exit_send_app_type_cfg;
+    }
+    snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
+                 audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
+    acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
+    if (acdb_dev_id < 0) {
+        ALOGE("%s: Couldn't get the acdb dev id", __func__);
+        rc = -EINVAL;
+        goto exit_send_app_type_cfg;
+    }
+    app_type_cfg[len++] = out->app_type_cfg.app_type;
+    app_type_cfg[len++] = acdb_dev_id;
+
+    mixer_ctl_set_array(ctl, app_type_cfg, len);
+
+    rc = 0;
+exit_send_app_type_cfg:
+    return rc;
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 81ba6f1..e93abdb 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -257,6 +257,7 @@
     audio_extn_dolby_set_dmid(adev);
     audio_extn_dolby_set_endpoint(adev);
 #endif
+    audio_extn_utils_send_app_type_cfg(usecase);
     strcpy(mixer_path, use_case_table[usecase->id]);
     platform_add_backend_name(mixer_path, snd_device);
     ALOGV("%s: apply mixer and update path: %s", __func__, mixer_path);
@@ -2218,6 +2219,7 @@
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_out *out;
     int i, ret = 0;
+    audio_format_t format;
 
     *stream_out = NULL;
     out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
@@ -2240,7 +2242,7 @@
     out->flags = flags;
     out->devices = devices;
     out->dev = adev;
-    out->format = config->format;
+    format = out->format = config->format;
     out->sample_rate = config->sample_rate;
     out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
     out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
@@ -2306,7 +2308,7 @@
             out->channel_mask = config->channel_mask;
             config->offload_info.channel_mask = config->channel_mask;
         }
-        out->format = config->offload_info.format;
+        format = out->format = config->offload_info.format;
         out->sample_rate = config->offload_info.sample_rate;
 
         out->stream.set_callback = out_set_callback;
@@ -2366,16 +2368,21 @@
             goto error_open;
         }
     } else if (out->flags & AUDIO_OUTPUT_FLAG_FAST) {
+        format = AUDIO_FORMAT_PCM_16_BIT;
         out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
         out->config = pcm_config_low_latency;
         out->sample_rate = out->config.rate;
     } else {
         /* primary path is the default path selected if no other outputs are available/suitable */
+        format = AUDIO_FORMAT_PCM_16_BIT;
         out->usecase = USECASE_AUDIO_PLAYBACK_PRIMARY;
         out->config = pcm_config_deep_buffer;
         out->sample_rate = out->config.rate;
     }
 
+    audio_extn_utils_update_stream_app_type_cfg(adev->platform,
+                                                &adev->streams_output_cfg_list,
+                                                flags, format, &out->app_type_cfg);
     if ((out->usecase == USECASE_AUDIO_PLAYBACK_PRIMARY) ||
         (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
         /* Ensure the default output is not selected twice */
@@ -2788,6 +2795,7 @@
 
     if ((--audio_device_ref_count) == 0) {
         audio_extn_listen_deinit(adev);
+        audio_extn_utils_release_streams_output_cfg_list(&adev->streams_output_cfg_list);
         audio_route_free(adev->audio_route);
         free(adev->snd_dev_ref_cnt);
         platform_deinit(adev->platform);
@@ -2907,6 +2915,9 @@
 
     *device = &adev->device.common;
 
+    audio_extn_utils_update_streams_output_cfg_list(adev->platform, adev->mixer,
+                                                    &adev->streams_output_cfg_list);
+
     audio_device_ref_count++;
     pthread_mutex_unlock(&adev_init_lock);
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 3f780d3..7d8d924 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -152,6 +152,12 @@
     int data[];
 };
 
+struct stream_app_type_cfg {
+    int sample_rate;
+    int bit_width;
+    int app_type;
+};
+
 struct stream_out {
     struct audio_stream_out stream;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -173,6 +179,7 @@
     bool muted;
     uint64_t written; /* total frames written, not cleared when entering standby */
     audio_io_handle_t handle;
+    struct stream_app_type_cfg app_type_cfg;
 
     int non_blocking;
     int playback_started;
@@ -238,6 +245,18 @@
     int state;
 };
 
+struct stream_format {
+    struct listnode list;
+    audio_format_t format;
+};
+
+struct streams_output_cfg {
+    struct listnode list;
+    audio_output_flags_t flags;
+    struct listnode format_list;
+    struct stream_app_type_cfg app_type_cfg;
+};
+
 struct audio_device {
     struct audio_hw_device device;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -250,6 +269,7 @@
     bool screen_off;
     int *snd_dev_ref_cnt;
     struct listnode usecase_list;
+    struct listnode streams_output_cfg_list;
     struct audio_route *audio_route;
     int acdb_settings;
     bool speaker_lr_swap;
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c71706c..d2a6807 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -894,6 +894,15 @@
     return ret;
 }
 
+int platform_get_snd_device_acdb_id(snd_device_t snd_device)
+{
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return -EINVAL;
+    }
+    return acdb_device_table[snd_device];
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index ecb8b50..dae686e 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -970,6 +970,15 @@
     return ret;
 }
 
+int platform_get_snd_device_acdb_id(snd_device_t snd_device)
+{
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return -EINVAL;
+    }
+    return acdb_device_table[snd_device];
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/platform_api.h b/hal/platform_api.h
index bf6bdcb..15deea5 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -31,6 +31,7 @@
 int platform_set_fluence_type(void *platform, char *value);
 int platform_get_fluence_type(void *platform, char *value, uint32_t len);
 int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id);
+int platform_get_snd_device_acdb_id(snd_device_t snd_device);
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
 int platform_switch_voice_call_device_pre(void *platform);
 int platform_switch_voice_call_enable_device_config(void *platform,