hal: support for sound trigger LAB and concurrency

Sound trigger lookahead buffering data is read through audio hal.
Also the audio hal has to inform sound trigger hal about concurrencies
and subsytsem restart events. Audio Hal uses proprietary interface
defined by sound trigger for communication with sound trigger hal.

Change-Id: Id344077b40b481a953769518f8a6ac0adc13b709
diff --git a/hal/Android.mk b/hal/Android.mk
index 32b9275..227a309 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -171,6 +171,13 @@
     LOCAL_SRC_FILES += audio_extn/listen.c
 endif
 
+ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true)
+    LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED
+    LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger
+    LOCAL_SRC_FILES += audio_extn/soundtrigger.c
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_AUXPCM_BT)),true)
     LOCAL_CFLAGS += -DAUXPCM_BT_ENABLED
 endif
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 7d88ac9..64bb2f5 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -457,6 +457,7 @@
    audio_extn_set_fluence_parameters(adev, parms);
    audio_extn_set_afe_proxy_parameters(adev, parms);
    audio_extn_fm_set_parameters(adev, parms);
+   audio_extn_sound_trigger_set_parameters(adev, parms);
    audio_extn_listen_set_parameters(adev, parms);
    audio_extn_hfp_set_parameters(adev, parms);
    audio_extn_ddp_set_parameters(adev, parms);
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 57cb643..78eaded 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -177,6 +177,36 @@
                                       struct str_parms *parms);
 #endif /* AUDIO_LISTEN_ENABLED */
 
+#ifndef SOUND_TRIGGER_ENABLED
+#define audio_extn_sound_trigger_init(adev)                            (0)
+#define audio_extn_sound_trigger_deinit(adev)                          (0)
+#define audio_extn_sound_trigger_update_device_status(snd_dev, event)  (0)
+#define audio_extn_sound_trigger_update_stream_status(uc_info, event)  (0)
+#define audio_extn_sound_trigger_set_parameters(adev, parms)           (0)
+#define audio_extn_sound_trigger_check_and_get_session(in)             (0)
+#define audio_extn_sound_trigger_stop_lab(in)                          (0)
+#else
+
+enum st_event_type {
+    ST_EVENT_SND_DEVICE_FREE,
+    ST_EVENT_SND_DEVICE_BUSY,
+    ST_EVENT_STREAM_FREE,
+    ST_EVENT_STREAM_BUSY
+};
+typedef enum st_event_type st_event_type_t;
+
+int audio_extn_sound_trigger_init(struct audio_device *adev);
+void audio_extn_sound_trigger_deinit(struct audio_device *adev);
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+                                     st_event_type_t event);
+void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
+                                     st_event_type_t event);
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
+                                             struct str_parms *parms);
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
+#endif
+
 #ifndef AUXPCM_BT_ENABLED
 #define audio_extn_read_xml(adev, mixer_card, MIXER_XML_PATH, \
                             MIXER_XML_PATH_AUXPCM)               (-ENOSYS)
diff --git a/hal/audio_extn/soundtrigger.c b/hal/audio_extn/soundtrigger.c
new file mode 100644
index 0000000..c6d7077
--- /dev/null
+++ b/hal/audio_extn/soundtrigger.c
@@ -0,0 +1,352 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#define LOG_TAG "soundtrigger"
+/* #define LOG_NDEBUG 0 */
+#define LOG_NDDEBUG 0
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/log.h>
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "sound_trigger_prop_intf.h"
+
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+struct sound_trigger_info  {
+    struct sound_trigger_session_info st_ses;
+    bool lab_stopped;
+    struct listnode list;
+};
+
+struct sound_trigger_audio_device {
+    void *lib_handle;
+    struct audio_device *adev;
+    sound_trigger_hw_call_back_t st_callback;
+    struct listnode st_ses_list;
+    pthread_mutex_t lock;
+};
+
+static struct sound_trigger_audio_device *st_dev;
+
+static struct sound_trigger_info *
+get_sound_trigger_info(int capture_handle)
+{
+    struct sound_trigger_info  *st_ses_info = NULL;
+    struct listnode *node;
+    ALOGD("%s: list %d capture_handle %d", __func__,
+           list_empty(&st_dev->st_ses_list), capture_handle);
+    list_for_each(node, &st_dev->st_ses_list) {
+        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+        if (st_ses_info->st_ses.capture_handle == capture_handle)
+            return st_ses_info;
+    }
+    return NULL;
+}
+
+int audio_hw_call_back(sound_trigger_event_type_t event,
+                       sound_trigger_event_info_t* config)
+{
+    int status = 0;
+    struct sound_trigger_info  *st_ses_info;
+
+    if (!st_dev)
+       return -EINVAL;
+
+    pthread_mutex_lock(&st_dev->lock);
+    switch (event) {
+    case ST_EVENT_SESSION_REGISTER:
+        if (!config) {
+            ALOGE("%s: NULL config", __func__);
+            status = -EINVAL;
+            break;
+        }
+        st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
+        if (!st_ses_info) {
+            ALOGE("%s: st_ses_info alloc failed", __func__);
+            status = -ENOMEM;
+            break;
+        }
+        memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
+        ALOGV("%s: add capture_handle %d pcm %p", __func__,
+              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+        list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
+        break;
+
+    case ST_EVENT_SESSION_DEREGISTER:
+        if (!config) {
+            ALOGE("%s: NULL config", __func__);
+            status = -EINVAL;
+            break;
+        }
+        st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
+        if (!st_ses_info) {
+            ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
+            status = -EINVAL;
+            break;
+        }
+        ALOGV("%s: remove capture_handle %d pcm %p", __func__,
+              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+        list_remove(&st_ses_info->list);
+        free(st_ses_info);
+        break;
+    default:
+        ALOGW("%s: Unknown event %d", __func__, event);
+        break;
+    }
+    pthread_mutex_unlock(&st_dev->lock);
+    return status;
+}
+
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
+{
+    int status = 0;
+    struct sound_trigger_info  *st_ses_info = NULL;
+    audio_event_info_t event;
+
+    if (!st_dev || !in)
+       return;
+
+    pthread_mutex_lock(&st_dev->lock);
+    st_ses_info = get_sound_trigger_info(in->capture_handle);
+    if (st_ses_info) {
+        event.u.ses_info = st_ses_info->st_ses;
+        ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
+        st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
+    }
+    pthread_mutex_unlock(&st_dev->lock);
+}
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
+{
+    struct sound_trigger_info  *st_ses_info = NULL;
+    struct listnode *node;
+
+    if (!st_dev || !in)
+       return;
+
+    pthread_mutex_lock(&st_dev->lock);
+    in->is_st_session = false;
+    ALOGV("%s: list %d capture_handle %d", __func__,
+          list_empty(&st_dev->st_ses_list), in->capture_handle);
+    list_for_each(node, &st_dev->st_ses_list) {
+        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+        if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
+            in->pcm = st_ses_info->st_ses.pcm;
+            in->config = st_ses_info->st_ses.config;
+            in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
+            in->is_st_session = true;
+            ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
+            break;
+        }
+    }
+    pthread_mutex_unlock(&st_dev->lock);
+}
+
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+                                     st_event_type_t event)
+{
+    bool raise_event = false;
+    int device_type = -1;
+
+    if (!st_dev)
+       return;
+
+    if (snd_device >= SND_DEVICE_OUT_BEGIN &&
+        snd_device < SND_DEVICE_OUT_END)
+        device_type = PCM_PLAYBACK;
+    else if (snd_device >= SND_DEVICE_IN_BEGIN &&
+        snd_device < SND_DEVICE_IN_END)
+        device_type = PCM_CAPTURE;
+    else {
+        ALOGE("%s: invalid device 0x%x, for event %d",
+                           __func__, snd_device, event);
+        return;
+    }
+
+    raise_event = platform_sound_trigger_device_needs_event(snd_device);
+    ALOGI("%s: device 0x%x of type %d for Event %d, with Raise=%d",
+        __func__, snd_device, device_type, event, raise_event);
+    if (raise_event && (device_type == PCM_CAPTURE)) {
+        switch(event) {
+        case ST_EVENT_SND_DEVICE_FREE:
+            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
+            break;
+        case ST_EVENT_SND_DEVICE_BUSY:
+            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
+            break;
+        default:
+            ALOGW("%s:invalid event %d for device 0x%x",
+                                  __func__, event, snd_device);
+        }
+    }/*Events for output device, if required can be placed here in else*/
+}
+
+void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
+                                     st_event_type_t event)
+{
+    bool raise_event = false;
+    audio_usecase_t uc_id;
+    int usecase_type = -1;
+
+    if (!st_dev) {
+        return;
+    }
+
+    if (uc_info == NULL) {
+        ALOGE("%s: usecase is NULL!!!", __func__);
+        return;
+    }
+    uc_id = uc_info->id;
+    usecase_type = uc_info->type;
+
+    raise_event = platform_sound_trigger_usecase_needs_event(uc_id);
+    ALOGD("%s: uc_id %d of type %d for Event %d, with Raise=%d",
+        __func__, uc_id, usecase_type, event, raise_event);
+    if (raise_event && (usecase_type == PCM_PLAYBACK)) {
+        switch(event) {
+        case ST_EVENT_STREAM_FREE:
+            st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE, NULL);
+            break;
+        case ST_EVENT_STREAM_BUSY:
+            st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE, NULL);
+            break;
+        default:
+            ALOGW("%s:invalid event %d, for usecase %d",
+                                  __func__, event, uc_id);
+        }
+    }/*Events for capture usecase, if required can be placed here in else*/
+}
+
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
+                               struct str_parms *params)
+{
+    audio_event_info_t event;
+    char value[32];
+    int ret;
+
+    if(!st_dev || !params) {
+        ALOGE("%s: str_params NULL", __func__);
+        return;
+    }
+
+    ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
+                            sizeof(value));
+    if (ret > 0) {
+        if (strstr(value, "OFFLINE")) {
+            event.u.status = SND_CARD_STATUS_OFFLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else if (strstr(value, "ONLINE")) {
+            event.u.status = SND_CARD_STATUS_ONLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else
+            ALOGE("%s: unknown snd_card_status", __func__);
+    }
+
+    ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
+    if (ret > 0) {
+        if (strstr(value, "OFFLINE")) {
+            event.u.status = CPE_STATUS_OFFLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else if (strstr(value, "ONLINE")) {
+            event.u.status = CPE_STATUS_ONLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else
+            ALOGE("%s: unknown CPE status", __func__);
+    }
+}
+
+int audio_extn_sound_trigger_init(struct audio_device *adev)
+{
+    int status = 0;
+    char sound_trigger_lib[100];
+    void *lib_handle;
+
+    ALOGI("%s: Enter", __func__);
+
+    st_dev = (struct sound_trigger_audio_device*)
+                        calloc(1, sizeof(struct sound_trigger_audio_device));
+    if (!st_dev) {
+        ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
+        return -ENOMEM;
+    }
+
+    snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
+             "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
+              XSTR(SOUND_TRIGGER_PLATFORM_NAME));
+
+    st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
+
+    if (st_dev->lib_handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
+                dlerror());
+        status = -EINVAL;
+        goto cleanup;
+    }
+    ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
+
+    st_dev->st_callback = (sound_trigger_hw_call_back_t)
+              dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
+
+    if (st_dev->st_callback == NULL) {
+       ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
+               dlerror());
+       goto cleanup;
+    }
+
+    st_dev->adev = adev;
+    list_init(&st_dev->st_ses_list);
+
+    return 0;
+
+cleanup:
+    if (st_dev->lib_handle)
+        dlclose(st_dev->lib_handle);
+    free(st_dev);
+    st_dev = NULL;
+    return status;
+
+}
+
+void audio_extn_sound_trigger_deinit(struct audio_device *adev)
+{
+    ALOGI("%s: Enter", __func__);
+    if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
+        dlclose(st_dev->lib_handle);
+        free(st_dev);
+        st_dev = NULL;
+    }
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index c01ba77..b8f8c82 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -288,6 +288,7 @@
     audio_extn_dolby_set_dmid(adev);
     audio_extn_dolby_set_endpoint(adev);
 #endif
+    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_BUSY);
     audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_BUSY);
     audio_extn_utils_send_audio_calibration(adev, usecase);
     audio_extn_utils_send_app_type_cfg(usecase);
@@ -317,6 +318,7 @@
     platform_add_backend_name(mixer_path, snd_device);
     ALOGV("%s: reset and update mixer path: %s", __func__, mixer_path);
     audio_route_reset_and_update_path(adev->audio_route, mixer_path);
+    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_FREE);
     audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_FREE);
     ALOGV("%s: exit", __func__);
     return 0;
@@ -372,10 +374,14 @@
         snd_device, device_name);
         /* due to the possibility of calibration overwrite between listen
             and audio, notify listen hal before audio calibration is sent */
+        audio_extn_sound_trigger_update_device_status(snd_device,
+                                        ST_EVENT_SND_DEVICE_BUSY);
         audio_extn_listen_update_device_status(snd_device,
                                         LISTEN_EVENT_SND_DEVICE_BUSY);
         if (platform_get_snd_device_acdb_id(snd_device) < 0) {
             adev->snd_dev_ref_cnt[snd_device]--;
+            audio_extn_sound_trigger_update_device_status(snd_device,
+                                            ST_EVENT_SND_DEVICE_FREE);
             audio_extn_listen_update_device_status(snd_device,
                                         LISTEN_EVENT_SND_DEVICE_FREE);
             return -EINVAL;
@@ -428,6 +434,9 @@
             audio_route_reset_and_update_path(adev->audio_route, device_name);
             audio_extn_dev_arbi_release(snd_device);
         }
+
+        audio_extn_sound_trigger_update_device_status(snd_device,
+                                        ST_EVENT_SND_DEVICE_FREE);
         audio_extn_listen_update_device_status(snd_device,
                                         LISTEN_EVENT_SND_DEVICE_FREE);
     }
@@ -2101,7 +2110,6 @@
     ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__,
           stream, in->usecase, use_case_table[in->usecase]);
 
-
     if (in->usecase == USECASE_COMPRESS_VOIP_CALL) {
         /* Ignore standby in case of voip call because the voip input
          * stream is closed in adev_close_input_stream()
@@ -2111,6 +2119,12 @@
     }
 
     pthread_mutex_lock(&in->lock);
+    if (!in->standby && in->is_st_session) {
+        ALOGD("%s: sound trigger pcm stop lab", __func__);
+        audio_extn_sound_trigger_stop_lab(in);
+        in->standby = 1;
+    }
+
     if (!in->standby) {
         pthread_mutex_lock(&adev->lock);
         in->standby = true;
@@ -2172,7 +2186,7 @@
         if ((in->device != val) && (val != 0)) {
             in->device = val;
             /* If recording is in progress, change the tx device to new device */
-            if (!in->standby)
+            if (!in->standby && !in->is_st_session)
                 ret = select_devices(adev, in->usecase);
         }
     }
@@ -2236,14 +2250,16 @@
     }
 
     if (in->standby) {
-        pthread_mutex_lock(&adev->lock);
-        if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
-            ret = voice_extn_compress_voip_start_input_stream(in);
-        else
-            ret = start_input_stream(in);
-        pthread_mutex_unlock(&adev->lock);
-        if (ret != 0) {
-            goto exit;
+        if (!in->is_st_session) {
+            pthread_mutex_lock(&adev->lock);
+            if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
+                ret = voice_extn_compress_voip_start_input_stream(in);
+            else
+                ret = start_input_stream(in);
+            pthread_mutex_unlock(&adev->lock);
+            if (ret != 0) {
+                goto exit;
+            }
         }
         in->standby = 0;
     }
@@ -2283,7 +2299,7 @@
         }
         memset(buffer, 0, bytes);
         in_standby(&in->stream.common);
-        ALOGV("%s: read failed - sleeping for buffer duration", __func__);
+        ALOGV("%s: read failed status %d- sleeping for buffer duration", __func__, ret);
         usleep(bytes * 1000000 / audio_stream_frame_size(&in->stream.common) /
                                    in_get_sample_rate(&in->stream.common));
     }
@@ -2896,8 +2912,8 @@
     }
 
     ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x)\
-        stream_handle(%p)",__func__, config->sample_rate, config->channel_mask,
-        devices, &in->stream);
+        stream_handle(%p) io_handle(%d)",__func__, config->sample_rate, config->channel_mask,
+        devices, &in->stream, handle);
 
     pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
 
@@ -2922,6 +2938,7 @@
     in->dev = adev;
     in->standby = 1;
     in->channel_mask = config->channel_mask;
+    in->capture_handle = handle;
 
     /* Update config params with the requested sample rate and channels */
     in->usecase = USECASE_AUDIO_RECORD;
@@ -2953,6 +2970,10 @@
         in->config.period_size = buffer_size / frame_size;
     }
 
+    /* This stream could be for sound trigger lab,
+       get sound trigger pcm if present */
+    audio_extn_sound_trigger_check_and_get_session(in);
+
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
     return ret;
@@ -2985,11 +3006,12 @@
     if (audio_extn_ssr_get_enabled() && (popcount(in->channel_mask) == 6)) {
         audio_extn_ssr_deinit();
     }
-    free(stream);
 
     if(audio_extn_compr_cap_enabled() &&
             audio_extn_compr_cap_format_supported(in->config.format))
         audio_extn_compr_cap_deinit();
+
+    free(stream);
     return;
 }
 
@@ -3008,6 +3030,7 @@
     pthread_mutex_lock(&adev_init_lock);
 
     if ((--audio_device_ref_count) == 0) {
+        audio_extn_sound_trigger_deinit(adev);
         audio_extn_listen_deinit(adev);
         audio_extn_utils_release_streams_output_cfg_list(&adev->streams_output_cfg_list);
         audio_route_free(adev->audio_route);
@@ -3116,6 +3139,7 @@
         }
     }
     audio_extn_listen_init(adev, adev->snd_card);
+    audio_extn_sound_trigger_init(adev);
 
     if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
         adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 5c67286..44caed4 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -209,6 +209,8 @@
     bool enable_aec;
     bool enable_ns;
     audio_format_t format;
+    audio_io_handle_t capture_handle;
+    bool is_st_session;
 
     struct audio_device *dev;
 };
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index a079da6..fb8d2ef 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2037,6 +2037,67 @@
     return needs_event;
 }
 
+bool platform_sound_trigger_device_needs_event(snd_device_t snd_device)
+{
+    bool needs_event = false;
+
+    if ((snd_device >= SND_DEVICE_IN_BEGIN) &&
+        (snd_device < SND_DEVICE_IN_END) &&
+        (snd_device != SND_DEVICE_IN_CAPTURE_FM) &&
+        (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK))
+        needs_event = true;
+
+    return needs_event;
+}
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id)
+{
+    bool needs_event = false;
+
+    switch(uc_id){
+    /* concurrent playback usecases needs event */
+    case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+    case USECASE_AUDIO_PLAYBACK_MULTI_CH:
+    case USECASE_AUDIO_PLAYBACK_OFFLOAD:
+        needs_event = true;
+        break;
+    /* concurrent playback in low latency allowed */
+    case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
+        break;
+    /* concurrent playback FM needs event */
+    case USECASE_AUDIO_PLAYBACK_FM:
+        needs_event = true;
+        break;
+
+    /* concurrent capture usecases, no event, capture handled by device
+    *  USECASE_AUDIO_RECORD:
+    *  USECASE_AUDIO_RECORD_COMPRESS:
+    *  USECASE_AUDIO_RECORD_LOW_LATENCY:
+
+    *  USECASE_VOICE_CALL:
+    *  USECASE_VOICE2_CALL:
+    *  USECASE_VOLTE_CALL:
+    *  USECASE_QCHAT_CALL:
+    *  USECASE_VOWLAN_CALL:
+    *  USECASE_COMPRESS_VOIP_CALL:
+    *  USECASE_AUDIO_RECORD_FM_VIRTUAL:
+    *  USECASE_INCALL_REC_UPLINK:
+    *  USECASE_INCALL_REC_DOWNLINK:
+    *  USECASE_INCALL_REC_UPLINK_AND_DOWNLINK:
+    *  USECASE_INCALL_REC_UPLINK_COMPRESS:
+    *  USECASE_INCALL_REC_DOWNLINK_COMPRESS:
+    *  USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS:
+    *  USECASE_INCALL_MUSIC_UPLINK:
+    *  USECASE_INCALL_MUSIC_UPLINK2:
+    *  USECASE_AUDIO_SPKR_CALIB_RX:
+    *  USECASE_AUDIO_SPKR_CALIB_TX:
+    */
+    default:
+        ALOGV("%s:usecase_id[%d] no need to raise event.", __func__, uc_id);
+    }
+    return needs_event;
+}
+
 /* Read  offload buffer size from a property.
  * If value is not power of 2  round it to
  * power of 2.
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index d83a93f..a2d6e52 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -975,3 +975,13 @@
 {
     return -ENOSYS;
 }
+
+bool platform_sound_trigger_device_needs_event(snd_device_t snd_device __unused)
+{
+    return false;
+}
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id  __unused)
+{
+    return false;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 1e6f681..c1dbfd1 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2259,7 +2259,25 @@
     return needs_event;
 }
 
-bool platform_listen_usecase_needs_event(audio_usecase_t uc_id)
+bool platform_listen_usecase_needs_event(audio_usecase_t uc_id __unused)
+{
+    return false;
+}
+
+bool platform_sound_trigger_device_needs_event(snd_device_t snd_device)
+{
+    bool needs_event = false;
+
+    if ((snd_device >= SND_DEVICE_IN_BEGIN) &&
+        (snd_device < SND_DEVICE_IN_END) &&
+        (snd_device != SND_DEVICE_IN_CAPTURE_FM) &&
+        (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK))
+        needs_event = true;
+
+    return needs_event;
+}
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id __unused)
 {
     return false;
 }
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 85b3c4f..6b75fce 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -76,6 +76,9 @@
 bool platform_listen_device_needs_event(snd_device_t snd_device);
 bool platform_listen_usecase_needs_event(audio_usecase_t uc_id);
 
+bool platform_sound_trigger_device_needs_event(snd_device_t snd_device);
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id);
+
 int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend);
 
 /* From platform_info_parser.c */