Merge "st-hal: Add support for SVA sound models merging"
diff --git a/ListenSoundModelLib.h b/ListenSoundModelLib.h
new file mode 100644
index 0000000..888f79a
--- /dev/null
+++ b/ListenSoundModelLib.h
@@ -0,0 +1,919 @@
+/*
+ * ListenSoundModelLib.h
+ *
+ * This is the interface file of sound model library which provides
+ * the APIs to get sound model information and merge, delete functionality
+ * which will be used by STHAL.
+ *
+ * Copyright (c) 2019, 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.
+ */
+
+/*======================================================================
+DESCRIPTION : ListenSoundModelLibrary Version 3
+====================================================================*/
+
+#ifndef __LISTEN_SOUND_MODEL_LIB_V3_H__
+#define __LISTEN_SOUND_MODEL_LIB_V3_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#if defined(_SML_NO_DLL)
+#define DllFunc
+#define CDECLARE
+#elif defined(_MSC_VER) && defined(_EXPORT)
+#define DllFunc   __declspec( dllexport )
+#define CDECLARE    __cdecl
+#elif defined(_MSC_VER)
+#define DllFunc   __declspec( dllimport )
+#define CDECLARE    __cdecl
+#else   // For other compiler like gcc
+#define DllFunc
+#define CDECLARE
+#endif
+
+#define LSMLIB_VERSION 2 // %%% some unique number that changes when API changes
+                         // %%% could be == SoundModel version supported for SVA 2.0
+
+#define MAX_STRING_LEN (100 * 2)  // maximum byte size of string representing Unicode character string
+// %%% NOTE: "MAX_STRING_LEN" replaces old constant "MAX_KEYWORD"
+
+
+// SVA 2.0
+// Keyword & User Identifier as zero terminated strings
+typedef char * keywordId_t;
+typedef char * userId_t;
+
+
+typedef struct {
+    //state machine parameters
+    float minSnrOnset;                  // The minimum snr of frame that speech segment starts
+    float minSnrLeave;                  // The minimum snr of frame that speech segment ends
+    float snrFloor;                     // The minimum snr value assumed in the end point detector
+    float snrThresholds;                // The minimum snr value of speech to verify
+    float forgettingFactorNoise;        // The forgetting factor used for noise estimation
+    int numFrameTransientFrame;         // the number of frames in the beginning that are used for noise estimate(valid only for online mode)
+    float minEnergyFrameRatio;          // the number of frame are used for noise estimation = minenergyFrameRatio * #frames of input(valid only for batch mode)
+
+    //post processing parameters
+    //Note:
+    int numMinFramesInPhrase;           // the minimum nubmer of samples for a speech phrase (targetted speech)
+    int numMinFramesInSpeech;           //the minimum number of samples for a speech intereval
+    int numMaxFrameInSpeechGap;         //the maximum allowable number of samples for a speech gap
+    int numFramesInHead;                //the speech head
+    int numFramesInTail;                //the speech tail
+} listen_epd_params;
+
+typedef struct {
+    int16_t *data;              /* Audio samples ( in Raw PCM format: 16kHz, 16bit, mono ) */
+    uint32_t n_samples;         /* number of audio samples */
+} listen_user_recording;
+
+typedef struct {
+    uint8_t *data;              /* block of memory containing Model data */
+    uint32_t size;              /* size of memory allocated for Model data */
+} listen_language_model_type;
+
+typedef struct {
+    uint8_t *data;              /* block of memory containing Model data */
+    uint32_t size;              /* size of memory allocated for Model data */
+} listen_model_type;
+
+// kwihyuk - comments will update
+// %%% the numbering - names are up to you, as long as type + version is unique between SVA 1.0 and 2.0 SMs
+typedef enum {
+    kKeywordModel = 1,           /* Keyword model */
+    kUserKeywordModel = 2,    /* Userkeyword model */
+    kTargetSoundModel = 3,
+    kMultiUserKeywordModel = 4, /* Multiple Keyword models */
+    kKeywordModelWithVop = 5,   //kwihyuk
+    kSecondStageKeywordModel = 6,   //kwihyuk
+    kSecondStageKeywordModelWithVop = 7,    //kwihyuk
+} listen_model_enum;
+
+typedef enum {
+    kSuccess = 0,
+    kFailure = 1
+} listen_detection_status_enum;
+
+typedef struct {
+    listen_model_enum type;     /* model type: Keyword, User, TargetSound */
+    uint32_t          version;  /* model version */
+    uint32_t          size;     /* total size of the model: header + payload size */
+} listen_sound_model_info;
+
+
+typedef struct {
+    listen_detection_status_enum status; // SUCCESS or FAILURE
+    uint32_t size;               // size in bytes of payload data
+                                                 // just contains result confidence level values
+    uint8_t *data;             // block of memory containing data payload
+} listen_event_payload;
+
+// SVA 2.0
+typedef struct {
+    uint16_t                numKeywords;  /* total number of keywords  */
+    uint16_t                numUsers;    /* total number of users  */
+    uint16_t                numActiveUserKeywordPairs;    /* total number of active user+keyword pairs in SM */
+    bool                    isStripped; /* if corresponding keyword is stripped or not */
+    uint16_t                *langPerKw; /* Language code of each keyword */
+    /* number active Users per keyword - included as convenience */
+    uint16_t                *numUsersSetPerKw;
+    bool                    *isUserDefinedKeyword;
+    /* Ordered 'truth' table of all possible pairs of users for each keyword.
+    * Active entries marked with 1, inactive 0.keywordPhrase
+    * 16-bit short (rather than boolean) is used to match SM model data size */
+    uint16_t                **userKeywordPairFlags;
+    uint16_t                model_indicator; /* for SM 3.0, indicate which models were combined */
+} listen_sound_model_header;
+
+
+
+
+// SVA 2.0
+// %%% this should match the 'sensitivity' data structure input in VoiceWakeupParamType
+typedef struct {
+    uint8_t   size     ;  // number of keyword plus activePair confidence levels set d
+    uint8_t   *pConfLevels;  // level of each keyword and each active user+keyword pair
+} listen_confidence_levels ;
+
+// SVA 2.0
+typedef enum {
+    kSingleKWDetectionEvent = 1,         /* SVA 1.0 model */
+    kMultiKWDetectionEvent = 2,       /* SVA 2.0 model */
+} listen_detection_type_enum;
+
+
+// duplicates existing SVA1.0 typedef
+// Do not include listen_detection_entry_v1 in SVA 1.0 header if both headers included
+typedef struct {
+    char keyword[MAX_STRING_LEN];
+    uint16_t keywordConfidenceLevel;
+    uint16_t userConfidenceLevel;
+} listen_detection_event_v1;
+
+// denotes that a particular entry in confidence level array is not active
+static const uint8_t  NO_CONF_LEVEL = 0;
+
+typedef struct {
+
+    char     keywordPhrase[MAX_STRING_LEN]; /* string containing phrase string of keyword with highest confidence score */
+    char     userName[MAX_STRING_LEN];  /* string containing name of user with highest confidence score */
+
+    uint8_t  highestKeywordConfidenceLevel;  // set to zero if detection status is Failed
+    uint8_t  highestUserConfidenceLevel;     // set to zero if detection status is Failed
+
+    listen_confidence_levels  pairConfidenceLevels; // confidence levels of ALL pair (active or not)
+} listen_detection_event_v2;
+
+// modified for SVA 2.0 - this should override SVA 1.0 typedef
+typedef struct {
+    // %%% uint16_t version;
+    listen_detection_type_enum   detection_data_type;
+    // data structure filled is based on detection_data_type
+    union {
+        listen_detection_event_v1 event_v1;  // for SVA 1.0
+        listen_detection_event_v2 event_v2;  // for SVA 2.0
+    } event;
+} listen_detection_event_type;
+
+typedef enum {
+    kSucess = 0,
+    kFailed = 1,
+    kBadParam,
+    kKeywordNotFound,
+    kUserNotFound,
+    kUserKwPairNotActive,
+    kSMVersionUnsupported,
+    kUserDataForKwAlreadyPresent,
+    kDuplicateKeyword,
+    kDuplicateUserKeywordPair,
+    kMaxKeywordsExceeded,
+    kMaxUsersExceeded,
+    kEventStructUnsupported,    // payload contains event data that can not be processed, or mismatches SM version
+    kLastKeyword,
+    kNoSignal,
+    kLowSnr,
+    kRecordingTooShort,
+    kRecordingTooLong,
+    kNeedRetrain,
+    kUserUDKPairNotRemoved,
+    kCannotCreateUserUDK,
+    kOutputArrayTooSmall,
+    kTooManyAbnormalUserScores,
+    kWrongModel,
+    kWrongModelAndIndicator,
+    kDuplicateModel,
+} listen_status_enum;
+
+/*
+*   Notes:
+*       1. The client code that calls getKeywordPhrases(), getUserNames() must allocate DstStr as [MAX_STRING_LEN]
+*       2. The client code that calls getUserKeywordModelSize(), createUserDefinedKeywordModel(),
+*           createUserKeywordModel() should assign meaningful string for keywordId or userId, empty string is not recommended.
+*       3. verifyUserRecording() should be called before calling createUserKeywordModel(). If pConfidenceLevel returned
+*           in verifyUserRecording() is below a CONFIDENCE_THRESHOLD value, the recording should be rejected and not used
+*           to a create user model. The client code should decide the CONFIDENCE_THRESHOLD value, a recommended value range for
+*           CONFIDENCE_THRESHOLD is 60~70.
+*
+*/
+
+    /*
+    * findKeywordEndPosition
+    *
+    * Returns the keyword end position of user recordings from the keyword model
+    * the keyword model finds the keyword end position by using keyword end finding algorithm inside SVA
+    *
+    * Param [in]  pKeywordModel - pointer to keyword model data which will be used to find keyword end
+    * Param [in]  keywordId - null terminated string contains keyword phrase string
+    * Param [in]  pUserRecording - a single recording of user speaking keyword
+                                   other speech such as following command may follows
+    * Param [out] pKendPosition - returns keyword end position from the start of user recording in number of samples
+    *
+    * Return - status
+    *       kBadParam - When any input pointer (except pEpdParameter) is NULL
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kSMVersionUnsupported - When pKeywordModel is not 2.0 model
+    */
+DllFunc listen_status_enum CDECLARE findKeywordEndPosition(
+                listen_model_type           *pKeywordModel,
+                keywordId_t                 keywordId,
+                listen_user_recording       *pUserRecording,
+                uint32_t                    *pKendPosition);
+
+    /*
+    * verifyUserRecording
+    *
+    * Returns the confidence level ( 0 ~ 100 ) that user recording matches keyword
+    * User data is to be appended for a specific keyword in the model
+    * // will be updated or removed
+    * if input is SM 3.0 which combiend with GMM and other sound models,
+    * then parsing to GMM model and run same procedure.
+    *
+    * Param [in]  pKeywordModel - pointer to user-independent keyword model data
+    * Param [in]  keywordId - null terminated string contains keyword phrase string
+    * Param [in]  listen_epd_params - epd parameter
+                                      if null is passing, default epd parameter will be used internally
+    * Param [in]  pUserRecording - a single recording of user speaking keyword
+    * Param [out] pConfidenceLevel - returns confidence level returned by keyword detection
+    *
+    * Return - status
+    *       kBadParam - When any input pointer (except pEpdParameter) is NULL
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kSMVersionUnsupported - When pKeywordModel is not 2.0 (have to contain GMM) model
+    *       kLowSnr - When user recording is too noisy
+    *       kNoSignal - When user recording is non-speech
+    */
+DllFunc listen_status_enum CDECLARE verifyUserRecording(
+        listen_model_type           *pKeywordModel,
+        keywordId_t                 keywordId, // add for SVA 2.0
+        listen_epd_params           *pEpdParameter,
+        listen_user_recording       *pUserRecording,
+        int16_t                     *pConfidenceLevel);
+
+    /*
+    * checkUserRecording
+    *
+    * Returns the status of user recordings that if user recording has problem with SNR(Signal Noise Ratio) and length
+    *
+    * Param [in]  pLanguageModel - pointer to language model
+    * Param [in]  pEpdParameter - pointer to EPD parameters
+    *                            Default parameter will be used if eEpdParameter is NULL
+    * Param [in]  pUserRecording - User recording that is going to be tested
+    * Param [out]  pOutSnr - SNR of user recording
+    * Param [in]  maxPhonemeLength (optional parameter) - maximum phoneme length allowed for each user recording
+    *                                                   - It is optional parameter, whose default value is 0.
+    *
+    * Return - status
+    *       kBadParam - When any input pointer (except pEpdParameter) is NULL
+    *         kLowSnr   - When user recording is too noisy
+    *         kNoSignal - When user recording is non-speech
+    *       kRecordingTooShort - When user recording is too short
+    */
+DllFunc listen_status_enum CDECLARE checkUserRecording(
+        listen_language_model_type  *pLanguageModel,
+        listen_epd_params           *pEpdParameter,
+        listen_user_recording       *pUserRecording,
+        float                       *pOutSnr,
+        uint32_t                    maxPhonemeLength);
+
+    /*
+    * checkRecordingsQuality
+    *
+    * Returns the status of the last user recording in recording array that
+    * if user recording has problem with SNR(Signal Noise Ratio) and length
+    * Check the consistency of the input recordings if numUserRecording > 1
+    *
+    * Param [in]  pLanguageModel - pointer to language model
+    * Param [in]  pEpdParameter - pointer to EPD parameters
+    *                            Default parameter will be used if eEpdParameter is NULL
+    * Param [in]  numUserRecording - number of input recordings
+    * Param [in]  pUserRecordings - User recordings those are going to be tested
+    * Param [out]  pOutSnr - SNR of user recording
+    *
+    * Return - status
+    *       kBadParam - When any input pointer (except pEpdParameter) is NULL
+    *         kLowSnr   - When user recording is too noisy
+    *         kNoSignal - When user recording is non-speech
+    *       kRecordingTooShort - When user recording is too short
+    */
+
+DllFunc listen_status_enum CDECLARE checkRecordingsQuality(
+        listen_language_model_type  *pLanguageModel,
+        listen_epd_params           *pEpdParameter,
+        uint32_t                     numUserRecording,
+        listen_user_recording       *pUserRecordings[],
+        float                       *pOutSnr);
+
+    /*
+    * tuneUserDefinedKeywordModelThreshold
+    *
+    * This function tunes threshold of user defined keyword.
+    *
+    * This function can be used when programmer want to make testing stage after training stage of user defined keyword
+    * even though threshold of user defined keyword is automatically tunned when create user defined keyword,
+    * this function can be useful when tune more threshold of user defined keyword
+    *
+    * Param [in]  pUserDefinedKeyword - pointer to user defined keyword
+    * Param [in]  keywordId - keyword spell
+    * Param [in]  pUserRecording - user recording from testing stage
+    * Param [out]  pOutputUserDefinedKeyword - tunned user defined keyword
+    *
+    * Return - listen_status_enum
+    * Return - status
+    *       kBadParam - When any input pointer is NULL, or pUserDefinedKeyword is not UDK
+    *       kKeywordNotFound - When keywordId not exist in the model
+    */
+DllFunc listen_status_enum CDECLARE tuneUserDefinedKeywordModelThreshold(
+        listen_model_type           *pUserDefinedKeyword,
+        keywordId_t                 keywordId,
+        listen_user_recording       *pUserRecording,
+        listen_model_type           *pOutputUserDefinedKeyword);
+
+
+    /*
+    * getUserDefinedKeywordSize
+    *
+    * Get the size required to hold user defined keyword model that extends given keyword model
+    * with give user data
+    *
+    * Param [in]  pUserDefinedKeyword - pointer to previous user defined keyword
+                                        if pUserDefinedKeyword is NULL, this will create new user defined keyword model
+                                        if pUserDefinedKeyword is not NULL, this will train incrementally ( not supported now )
+
+    * Param [in]  keywordId - keyword spell of user defined keyword
+    * Param [in]  userId - user spell of user defined keyword
+    * Param [in]  pEpdParameter - epd parameter which is used for chopping user recording.
+                                   if eEpdParameter is NULL, default parameter will be used
+    * Param [in]  numUserRecording - number of user recording
+    * Param [in]  pUserRecordings[] -  multiple recording of user speaking keyword
+    * Param [in]  pLanguageModel - language model
+    * Param [out]  pOutputSize - pointer to where output model size will be written
+    *
+    * Return - listen_status_enum
+    * Return - status
+    *       kBadParam - When any input pointer (except pUserDefinedKeyword, pEpdParameter) is NULL, or pLanguageModel is fake
+    *       kNoSignal - When user recording is non-speech
+    */
+DllFunc listen_status_enum CDECLARE getUserDefinedKeywordSize(
+        listen_model_type           *pUserDefinedKeyword,
+        keywordId_t                 keywordId,
+        userId_t                    userId,
+        listen_epd_params           *pEpdParameter,
+        uint32_t                    numUserRecording,
+        listen_user_recording       *pUserRecordings[],
+        listen_language_model_type  *pLanguageModel,
+        uint32_t                    *pOutputSize);
+
+    /*
+    * getUserDefinedKeywordApproxSize
+    *
+    * Get the size required to hold user-keyword model that extends given keyword model
+    * with give user data
+    *
+    * Param [in]  keywordId - null terminated string containing keyword phrase string
+    * Param [in]  userId - null terminated string containing user name string
+    * Param [in]  pLanguageModel - pointer to language model data
+    * Param [out] pOutputSize - size of approximated output model
+    * Param [in]  maxPhonemeLength (optional parameter) - maximum phoneme length allowed for each user recording
+    *                                                   - It is optional parameter, whose default value is 0.
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    */
+
+DllFunc listen_status_enum CDECLARE getUserDefinedKeywordApproxSize(
+    keywordId_t keywordId,
+    userId_t userId,
+    listen_language_model_type *pLanguageModel,
+    uint32_t                        *pOutputSize,
+    uint32_t                        maxPhonemeLength);
+
+    /*
+    * createUserDefinedKeywordModel
+    *
+    * Description : Create User Defined Keyword Model
+    *
+    * Param [in]  pUserDefinedKeyword - pointer to previous user defined keyword
+                                        if pUserDefinedKeyword is NULL, this will create new user defined keyword model
+                                        if pUserDefinedKeyword is not NULL, this will train incrementally ( not supported now )
+
+    * Param [in]  keywordId - keyword spell of user defined keyword
+    * Param [in]  userId - user spell of user defined keyword
+    * Param [in]  pEpdParameter - epd parameter which is used for chopping user recording.
+                                   if eEpdParameter is NULL, default parameter will be used
+    * Param [in]  numUserRecording - number of user recording
+    * Param [in]  pUserRecordings[] - multiple recording of user speaking keyword
+    * Param [in]  pLanguageModel - language model
+    * Param [out] pOutputUserDefinedKeyword - pointer to where output model will be written
+    * Param [out] pMatchingScore - pointer to matching score
+    *
+    * Return - listen_status_enum
+    * Return - status
+    *       kBadParam - When any input pointer (except pUserDefinedKeyword, pEpdParameter) is NULL, or pLanguageModel is fake
+    *       kNoSignal - When user recording is non-speech
+    *       kCannotCreateUserUDK - When creation process fails somewhere
+    *       kOutputArrayTooSmall - When output size is smaller than actual udk model size
+    */
+DllFunc listen_status_enum CDECLARE createUserDefinedKeywordModel(
+        listen_model_type          *pUserDefinedKeyword,
+        keywordId_t                 keywordId,
+        userId_t                    userId,
+        listen_epd_params           *pEpdParameter,
+        uint32_t                     numUserRecording,
+        listen_user_recording      *pUserRecordings[],
+        listen_language_model_type  *pLanguageModel,
+        listen_model_type           *pOutputUserDefinedKeyword,
+        int16_t                     *pMatchingScore);
+
+    /*
+    * getStrippedUserKeywordModelSize
+    *
+    * Return stripped model size
+    *
+    * Param[in] pModel - pointer to (user)keyword model data
+    * Param[out] nStrippedModelSize - return model size of stripped model
+    *
+    * Return - status
+    *           kBadParam - When any input pointer is NULL
+    *           kSMVersionUnsupported - When pModel is not 2.0 model
+    *
+    */
+DllFunc listen_status_enum CDECLARE getStrippedUserKeywordModelSize(
+        listen_model_type           *pModel,
+        uint32_t                    *nStrippedModelSize);
+
+
+    /*
+    * stripUserKeywordModel
+    *
+    * Return stripped model
+    *
+    * Param[in] pModel - pointer to (user)keyword model data
+    * Param[out] pStrippedModel - pointer to stripped model data
+    *
+    * Return - status
+    *           kBadParam - When any input pointer is NULL
+    *           kSMVersionUnsupported - When pModel is not 2.0 model
+    *
+    */
+DllFunc listen_status_enum CDECLARE stripUserKeywordModel(
+        listen_model_type           *pModel,
+        listen_model_type           *pStrippedModel);
+
+    /*
+    * getUserKeywordModelSize
+    *
+    * Get the size required to hold user-keyword model that extends given keyword model
+    * with give user data
+    *
+    * Param [in]  pKeywordModel - pointer to keyword model data
+    * Param [in]  keywordId - null terminated string containing keyword phrase string
+    * Param [in]  userId - null terminated string containing user name string
+    * Param [out] nUserKeywordModelSize - size of user keyword model
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kSMVersionUnsupported - When pKeywordModel is not 2.0/3.0 model
+    */
+DllFunc listen_status_enum CDECLARE getUserKeywordModelSize(
+        listen_model_type           *pKeywordModel,
+        keywordId_t                 keywordId, // add for SVA 2.0
+        userId_t                    userId, // add for SVA 2.0
+        uint32_t                    *nUserKeywordModelSize);
+
+    /*
+    * createUserKeywordModel
+    *
+    * Create a user keyword model
+    * Writes the user keyword model into given memory location
+    *
+    * Param [in]  pKeywordModel - pointer to Keyword model  or User keyword model data
+                                if it is keyword model, create user-keyword model
+                                if it is user keyword model, incrementally train user keyword model
+    * Param [in]  keywordId - user data is to be appended for keyword in model with given identifier
+    * Param [in]  userId - identifier of user data is created
+    *            If identifier is already used, will replace existing user data with newly created data.
+    *            The User Name is passed to this function so that if this is the first time user data is
+    *            being added for a new user, the User's Name can be stored in the SM
+    * Param [in]  pEpdParameter - end point detection parameter
+    *                             if eEpdParameter is NULL, default parameter will be used
+    * Param [in]  numUserRecording - number of user recordings
+    * Param [in]  pUserRecordings - multiple recording of user speaking keyword
+    * Param [out] pUserKeywordModel - pointer to where user keyword model data is to be written
+    * Param [out] pUserMatchingScore - pointer to user matching score
+    * Return - status
+    *       kBadParam - When any input pointer (except pEpdParameter) is NULL
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kSMVersionUnsupported - When pKeywordModel is not 2.0 or 3.0 model
+    *        kLowSnr    - When user recording is too noisy
+    *        kNoSignal - When user recording is non-speech
+    *       kCannotCreateUserUDK - When pKeywordModel is UDK model
+    */
+DllFunc listen_status_enum CDECLARE createUserKeywordModel(
+        listen_model_type           *pKeywordModel,
+        keywordId_t                 keywordId, // add for SVA 2.0
+        userId_t                    userId, // add for SVA 2.0
+        listen_epd_params           *pEpdParameter,
+        uint32_t                    numUserRecording,
+        listen_user_recording       *pUserRecordings[],
+        listen_model_type           *pUserKeywordModel,
+        int16_t                     *pUserMatchingScore);
+
+// Since size of new SM after removing data will be less than or equal to size of
+// input SM, this function could be optional and size of pInputModel could be used
+// to allocate memory for pResultModel when deleteFromModel() called.
+    /*
+    * getSizeAfterDeleting
+    *
+    * Return the size of sound model after removing data from a given SM for either
+    * a keyword, a user, or a specific user+keyword pair.
+    *
+    * Param [in]  pInputModel - pointer to sound model
+    * Param [in]  keywordId - data for this keyword in model with given identifier is removed
+    *           If userId is 'null', then all keyword-only data and all user data associated
+    *           with the given non-null keywordId is removed.
+    *           If userId is also non-null, then only data associated with the userId+keywordId
+    *           pair is removed.
+    * Param [in]  userId - all data for this user in model with given identifier is removed
+    *           If keywordId is 'null', then all all user data for the given non-null userId
+    *           is removed.
+    *           If keywordId is also non-null, then only data associated with the userId+keywordId
+    *           pair is removed.
+    * Param [out]  nOutputModelSize - outputs size of resulting soundmodel after removing data.
+    * Return - status
+    *       kBadParam - When any input pointer (except keywordId, userId) is NULL
+    *       kLastKeyword - When pInputModel has only one keyword
+    *       kSMVersionUnsupported - When pInputModel is not 2.0 model
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kUserNotFound - When userId not exist in the model
+    *       kUserKWPairNotActive - When <keywordId, userId> pair not exist in the model
+    *       kUserUDKPairNotRemoved - When <keywordId, userId> pair to delete is UDK
+    */
+DllFunc listen_status_enum CDECLARE getSizeAfterDeleting(
+        listen_model_type           *pInputModel,
+        keywordId_t                 keywordId, // add for SVA 2.0
+        userId_t                    userId, // add for SVA 2.0
+        uint32_t                    *nOutputModelSize);
+
+// If getSizeAfterDeleting() supported, call it get size of new sound model after
+// removing desired data from given input sound model, and
+// allocate ResultModel with this size
+// Otherwise, use size of input SoundModel since size of ResultModel will be
+// less than or equal to size of input SoundModel.
+    /*
+    * deleteFromModel
+    *
+    * Return a new sound model after removing data from a given SM for a keyword, a user,
+    * or a user+keyword pair.
+    *
+    * Param [in]  pInputModel - pointer to sound model
+    * Param [in]  keywordId - data for this keyword in model with given identifier is removed
+    *           If userId is 'null', then all keyword-only data and all user data associated
+    *           with the given non-null keywordId is removed.
+    *           If userId is also non-null, then only data associated with the userId+keywordId
+    *           pair is removed.
+    * Param [in]  userId - all data for this user in model with given identifier is removed
+    *           If keywordId is 'null', then all all user data for the given non-null userId
+    *           is removed.
+    *           If keywordId is also non-null, then only data associated with the userId+keywordId
+    *           pair is removed.
+    * Param [out]  pResultModel - pointer to where user keyword model data is to be written
+    * Return - status
+    *       kBadParam - When any input pointer (except keywordId, userId) is NULL
+    *       kLastKeyword - When pInputModel has only one keyword
+    *       kSMVersionUnsupported - When pInputModel is not 2.0 or 3.0 model
+    *       kKeywordNotFound - When keywordId not exist in the model
+    *       kUserNotFound - When userId not exist in the model
+    *       kUserKWPairNotActive - When <keywordId, userId> pair not exist in the model
+    *       kUserUDKPairNotRemoved - When <keywordId, userId> pair to delete is UDK
+    */
+DllFunc listen_status_enum CDECLARE deleteFromModel(
+        listen_model_type           *pInputModel,
+        keywordId_t                 keywordId, // add for SVA 2.0
+        userId_t                    userId, // add for SVA 2.0
+        listen_model_type           *pResultModel);
+
+
+    /*
+    * getMergedModelSize
+    *
+    * Return the size of sound model after merging required models
+    *
+    * Param [in]  numModels - number of model files to be merged
+    * Param [in]  pModels - array of pointers to Keyword or User keyword model data
+    * Param [out]  nOutputModelSize - outputs size of resulting soundmodel after merging models
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kSMVersionUnsupported - When pInputModel is not 2.0 model
+    *       kDuplicateKeyword - When same keywordId exists in more than 1 model
+    *       kDuplicateUserKeywordPair
+    *       kMaxKeywordsExceeded
+    *       kMaxUsersExceeded,
+    */
+DllFunc listen_status_enum CDECLARE getMergedModelSize(
+        uint16_t                    numModels,
+        listen_model_type           *pModels[],
+        uint32_t                    *nOutputModelSize);
+
+
+
+    /*
+    * mergeModels
+    *
+    * merges two or more Sound Models
+    *
+    * Writes the new merged model into given memory location
+    *
+    * Param [in]  numModels - number of model files to be merged
+    * Param [in]  pModels - array of pointers to Keyword or User keyword model data
+    * Param [out]  pMergedModel - pointer to where merged model data is to be written
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kSMVersionUnsupported - When pInputModel is not 2.0 model
+    *       kDuplicateKeyword - When same keywordId exists in more than 1 model
+    *       kDuplicateUserKeywordPair - N/A to current version
+    *       kMaxKeywordsExceeded - N/A to current version
+    *       kMaxUsersExceeded - N/A to current version
+    */
+DllFunc listen_status_enum CDECLARE mergeModels(
+        uint16_t                    numModels,
+        listen_model_type       *pModels[],
+        listen_model_type       *pMergedModel);
+
+
+
+/* ParseFromBigSoundModel
+*
+* Parse the big 3.0 sound model to individual sub models
+*
+* Param [in]   *pSM3p0Model – The pointer to the big 3.0 sound model
+* Param [out]  p1stStageModel - pointer to parsed 1stStageModel
+* Param [out]  p2ndStageKWModel - pointer to parsed 2nd stage KW Model
+* Param [out]  p2stStageVoPModel - pointer to parsed 2nd stage VoP Model
+* Param [out]  indicator – returned indicator, indicating what types of models parsed/returned
+* Return - status
+*       kBadParam - When any input pointer is NULL
+*       kSMVersionUnsupported - When pInputModel is not 3.0 model
+*       kDuplicateKeyword - When same keywordId exists in more than 1 model
+*       kWrongModel - when input is broken
+*/
+
+DllFunc listen_status_enum CDECLARE parseFromBigSoundModel(
+        listen_model_type       *pSM3p0Model,
+        listen_model_type       *p1stStageModel,
+        listen_model_type       *p2ndStageKWModel,
+        listen_model_type       *p2stStageVoPModel,
+        uint16_t                *indicator );
+
+
+
+
+    /*
+    * parseDetectionEventData
+    *
+    * parse event payload into detection event.
+    *
+    * Version of input SM will detemine DetectionType created/returned
+    *
+    * Param [in]  pUserKeywordModel - pointer to keyword or user keyword model data
+    * Param [in]  pEventPayload - pointer to received event payload data
+    * Param [out] pDetectEvent - pointer to where detection event data is to be written
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kSMVersionUnsupported - When pUserKeywordModel is not 2.0 model
+    *       kEventStructUnsupported - When pEventPayload->size != numKeywords + numActiveUsers
+    */
+
+DllFunc listen_status_enum CDECLARE parseDetectionEventData(
+        listen_model_type           *pUserKeywordModel,
+        listen_event_payload        *pEventPayload,
+        listen_detection_event_type *pDetectionEvent);
+
+
+// Declared in both SVA 1.0 and SVA 2.0 versions and SML 3.0 of ListenSoundModelMLib
+//
+    /*
+    * querySoundModel
+    *
+    * Returns the information about a sound model
+    * Sound model could be of any type: Keyword, UserKeyword, TargetSound,...
+    * Sound model could be any versions
+    *
+    * Param [in] pSoundModel - pointer to model data
+    * Param [out] pListenSoundModelInfo - returns information about the give sound model
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kFailed - When input model failed to be decoded
+    *        kSMVersionUnsupported - When pSoundModel is fake model (invalid model other than 1.0 model, 2.0 model and 3.0 model)
+    */
+DllFunc listen_status_enum CDECLARE querySoundModel(
+        listen_model_type           *pSoundModel,
+        listen_sound_model_info     *pListenSoundModelInfo);
+
+
+    /*
+    * getSoundModelHeader
+    *
+    * Returns additional information about the sound model
+    * Sound model could be of any type: Keyword, UserKeyword, TargetSound,...
+    * Keyword
+    *
+    * Param [in] pSoundModel - pointer to model data
+    * Param [out] pListenSoundModelHeader - returns header field from sound model
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    *       kSMVersionUnsupported - When pSoundModel is not 2.0 or 3.0 model
+    */
+
+DllFunc listen_status_enum CDECLARE getSoundModelHeader(
+        listen_model_type           *pSoundModel,
+        listen_sound_model_header   *pListenSoundModelHeader);
+
+
+    /*
+    * release sound model header
+    *
+    * deallocate sound model header
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    */
+DllFunc listen_status_enum CDECLARE releaseSoundModelHeader(
+        listen_sound_model_header   *pListenSoundModelHeader);
+
+
+    /*
+    * getKeywordPhrases
+    *
+    * Get keyword phrase string for all Keywords defined in given SM 2.0 / 3.0
+    *
+    * App calling this function must allocate memory for all phrases
+    * by getting the number of keywords from querySoundModel() and allocating
+    * totalNumKeywords*MAX_STRING_LEN
+    * This function copies phrases into this allocated keywords array
+    *
+    * Param [in]  pSoundModel - pointer to model data
+    * Param [in/out] numKeywords - [in]  number of string entries allocated in keywords array
+    *                             [out] number of keyword phrase strings copied keyword array
+    * Param [out] keywords - array of keyword phrase null-terminated strings
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL, or numKeywords < real keywords number
+    *       kSMVersionUnsupported - When pSoundModel is not 2.0 or 3.0 model
+    */
+DllFunc listen_status_enum CDECLARE getKeywordPhrases(
+        listen_model_type           *pSoundModel,
+        uint16_t                    *numKeywords,
+        keywordId_t                 *keywords);
+
+
+    /*
+    * getUserNames
+    *
+    * Get user names for user data associated with a given SM 2.0 / 3.0
+    *
+    * App calling this function must allocate memory for all names
+    * by getting the number of users  from querySoundModel() and allocating
+    * totalNumUsers*MAX_STRING_LEN
+    * This function copies names into this allocated keywords array
+    *
+    * Param [in]  pSoundModel - pointer to model data
+    * Param [in/out] numUsers - [in]  number of string entries allocated in users array
+    *                          [out] number of user name strings copied users array
+    * Param [out] users - array of user name null-terminated strings
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL, or numUsers < real users number, or pSoundModel is keyword-only model
+    *       kSMVersionUnsupported - When pSoundModel is not 2.0 or 3.0 model
+    */
+DllFunc listen_status_enum CDECLARE getUserNames(
+        listen_model_type           *pSoundModel,
+        uint16_t                        *numUsers,
+        userId_t                        *users);
+
+    /*
+    * loadConfParams
+    *
+    * Load configurable parameters to the sound model library
+    *
+    * Param [in] pConfData - pointer to param data
+    * Param [in] confDataSize - size of memory allocated for param data
+    *
+    * Return - status
+    *       kBadParam - When any input pointer is NULL
+    */
+DllFunc listen_status_enum CDECLARE loadConfParams(
+        uint8_t                     *pConfData,
+        uint32_t                    confDataSize);
+    /*
+    * getBinaryModelSize
+    *
+    * Return binary model size
+    *
+    * Param[in] pListenModel - pointer to (user)keyword model data
+    * Param[out] nBinaryModelSize - return model size of binary model
+    *
+    * Return - status
+    *           kBadParam - When any input pointer is NULL
+    *           kSMVersionUnsupported - When pModel is not 2.0 or 3.0 model
+    *
+    */
+
+DllFunc listen_status_enum CDECLARE getBinaryModelSize(
+        listen_model_type           *pListenModel,
+        uint32_t                    *nBinaryModelSize);
+
+    /*
+    * getSortedKeywordStatesUserKeywordModelSize
+    *
+    * Return sorted model size
+    *
+    * Param[in] pModel - pointer to (user)keyword model data
+    * Param[out] nSortedModelSize - return model size of sorted keyword states model
+    *
+    * Return - status
+    *           kBadParam - When any input pointer is NULL
+    *
+    */
+
+DllFunc listen_status_enum CDECLARE getSortedKeywordStatesUserKeywordModelSize(
+    listen_model_type           *pModel,
+    uint32_t                    *nSortedModelSize);
+
+    /*
+    * sortKeywordStatesOfUserKeywordModel
+    *
+    * Return sorted model
+    *
+    * Param[in] pInputModel - pointer to (user)keyword model data
+    * Param[out] pSortedModel - pointer to sorted keyword states model data
+    *
+    * Return - status
+    *           kBadParam - When any input pointer is NULL
+    *           kSMVersionUnsupported - when pModel is not 2.0 model
+    *
+    */
+
+DllFunc listen_status_enum CDECLARE sortKeywordStatesOfUserKeywordModel(
+        listen_model_type           *pInputModel,
+        listen_model_type           *pSortedModel);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __LISTEN_SOUND_MODEL_LIB_V3_H__ */
+
diff --git a/sound_trigger_hw.c b/sound_trigger_hw.c
index ce879fc..2506887 100644
--- a/sound_trigger_hw.c
+++ b/sound_trigger_hw.c
@@ -121,6 +121,11 @@
         free(st_session->rc_config);
         st_session->rc_config = NULL;
     }
+    if (st_session->st_conf_levels) {
+        free(st_session->st_conf_levels);
+        st_session->st_conf_levels = NULL;
+    }
+
     pthread_mutex_unlock(&st_session->lock);
     stdev_reconfig_backend_on_stop(st_session);
     return status;
@@ -339,7 +344,7 @@
         p_ses = node_to_item(node, st_session_t, list_node);
         if (p_ses->exec_mode == ST_EXEC_MODE_CPE) {
             /* check for sessions to keep on WDSP mode if requested by APP */
-            if (ST_HW_SESS_DET_LOW_POWER_MODE == p_ses->hw_ses_current->client_req_det_mode) {
+            if (ST_DET_LOW_POWER_MODE == p_ses->client_req_det_mode) {
                 ALOGV("%s:[%d] session is requested on WDSP mode, skip",
                        __func__, p_ses->sm_handle);
                 continue;
@@ -348,9 +353,9 @@
             if (cur_ses && (cur_ses != p_ses) &&
                 !platform_stdev_check_backends_match(stdev->platform,
                                                   cur_ses->exec_mode,
-                                  cur_ses->hw_ses_current->st_device,
+                    cur_ses->hw_proxy_ses->hw_ses_current->st_device,
                                                     p_ses->exec_mode,
-                                   p_ses->hw_ses_current->st_device)) {
+                    p_ses->hw_proxy_ses->hw_ses_current->st_device)) {
                 ALOGV("%s:[%d] session not sharing backend",
                        __func__, p_ses->sm_handle);
                 continue;
@@ -400,7 +405,7 @@
         p_ses = node_to_item(node, st_session_t, transit_list_node);
 
         if (p_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE)
-            update_available_phrase_info(p_ses, p_ses->sm_data, true);
+            update_available_phrase_info(p_ses, p_ses->phrase_sm, true);
 
         ALOGD("%s:[%d] switch session to NONE", __func__, p_ses->sm_handle);
         ret = st_session_set_exec_mode(p_ses, ST_EXEC_MODE_NONE);
@@ -432,7 +437,7 @@
         }
 
         if (p_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE)
-            update_available_phrase_info(p_ses, p_ses->sm_data, false);
+            update_available_phrase_info(p_ses, p_ses->phrase_sm, false);
     }
 
 ssr_exit:
@@ -502,7 +507,7 @@
         p_ses = node_to_item(node, st_session_t, list_node);
         if (p_ses->exec_mode == ST_EXEC_MODE_ADSP) {
             /* check for sessions to keep on ADSP mode if requested by APP */
-            if (ST_HW_SESS_DET_HIGH_PERF_MODE == p_ses->hw_ses_current->client_req_det_mode) {
+            if (ST_DET_HIGH_PERF_MODE == p_ses->client_req_det_mode) {
                 ALOGV("%s:[%d] session is requested on ADSP mode, skip",
                        __func__, p_ses->sm_handle);
                 continue;
@@ -511,9 +516,9 @@
             if (cur_ses && (cur_ses != p_ses) &&
                 !platform_stdev_check_backends_match(stdev->platform,
                                                   cur_ses->exec_mode,
-                                  cur_ses->hw_ses_current->st_device,
+                    cur_ses->hw_proxy_ses->hw_ses_current->st_device,
                                                     p_ses->exec_mode,
-                                   p_ses->hw_ses_current->st_device)) {
+                    p_ses->hw_proxy_ses->hw_ses_current->st_device)) {
                 ALOGV("%s:[%d] session not sharing backend",
                        __func__, p_ses->sm_handle);
                 continue;
@@ -563,7 +568,7 @@
         p_ses = node_to_item(node, st_session_t, transit_list_node);
 
         if (p_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE)
-            update_available_phrase_info(p_ses, p_ses->sm_data, true);
+            update_available_phrase_info(p_ses, p_ses->phrase_sm, true);
 
         ALOGD("%s:[%d] switch session to NONE", __func__, p_ses->sm_handle);
         ret = st_session_set_exec_mode(p_ses, ST_EXEC_MODE_NONE);
@@ -595,7 +600,7 @@
         }
 
         if (p_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE)
-            update_available_phrase_info(p_ses, p_ses->sm_data, false);
+            update_available_phrase_info(p_ses, p_ses->phrase_sm, false);
     }
 
 ssr_exit:
@@ -736,15 +741,16 @@
     ALOGD("%s: Exit audio ec ref=%d", __func__, stdev->audio_ec_enabled);
 }
 
-static void handle_audio_concurrency(audio_event_type_t event_type, audio_event_info_t* config)
+static void handle_audio_concurrency(audio_event_type_t event_type,
+    audio_event_info_t* config)
 {
     struct listnode *p_ses_node = NULL;
     st_session_t *p_ses = NULL;
     bool conc_allowed = false;
     unsigned int num_sessions = 0;
 
-    ALOGV_IF(config != NULL, "%s: Enter, event type = %d, audio device = %d", __func__,
-                        event_type, config->device_info.device);
+    ALOGV_IF(config != NULL, "%s: Enter, event type = %d, audio device = %d",
+             __func__, event_type, config->device_info.device);
 
     /*
     UC1:
@@ -859,6 +865,7 @@
      */
     stdev->lpi_enable = st_hw_check_lpi_support(stdev, p_ses);
     stdev->vad_enable = st_hw_check_vad_support(stdev, p_ses, stdev->lpi_enable);
+
     if (stdev->lpi_enable != platform_get_lpi_mode(stdev->platform) &&
         !is_any_session_buffering()) {
         list_for_each(p_ses_node, &stdev->sound_model_list) {
@@ -1432,7 +1439,7 @@
    struct sound_trigger_recognition_config *new_config
 )
 {
-    unsigned int i, j;
+    unsigned int i = 0, j = 0;
 
     /*
      * Sometimes if the number of user confidence levels is 0, the
@@ -1581,7 +1588,8 @@
     return status;
 }
 
-static void deallocate_arm_second_stage_session(struct st_arm_second_stage *st_sec_stage)
+static void deallocate_arm_second_stage_session(
+   struct st_arm_second_stage *st_sec_stage)
 {
     if (st_sec_stage) {
         if (st_sec_stage->ss_info) {
@@ -1650,8 +1658,6 @@
     uint32_t i;
     uint8_t *sound_model;
 
-    list_init(&st_ses->second_stage_list);
-
     for (i = 0; i < num_models; i++) {
         big_sm = (SML_BigSoundModelTypeV3 *)(sm_payload + sizeof(SML_GlobalHeaderType) +
             sizeof(SML_HeaderTypeV3) + (i * sizeof(SML_BigSoundModelTypeV3)));
@@ -1677,7 +1683,7 @@
                 memcpy(st_sec_stage->ss_session->sound_model, sound_model, big_sm->size);
                 memcpy((char *)st_sec_stage->ss_info, (char *)&ss_usecase.arm->common_params,
                     sizeof(struct st_second_stage_info));
-
+                st_sec_stage->stdev = st_ses->stdev;
                 list_add_tail(&st_ses->second_stage_list, &st_sec_stage->list_node);
                 ALOGD("%s: Added second stage session of type %d", __func__,
                     st_sec_stage->ss_info->sm_detection_type);
@@ -1704,7 +1710,8 @@
 
                 ss_cfg->params = ss_usecase.lsm;
                 ss_cfg->ss_info = &ss_usecase.lsm->common_params;
-                list_add_tail(&st_ses->hw_ses_adsp->lsm_ss_cfg_list, &ss_cfg->list_node);
+                list_add_tail(&st_ses->hw_proxy_ses->hw_ses_adsp->lsm_ss_cfg_list,
+                    &ss_cfg->list_node);
                 ALOGD("%s: Added second stage lsm usecase with sm id %d", __func__, big_sm->type);
             } else if (ss_usecase.type == ST_SS_USECASE_TYPE_NONE) {
                 ALOGE("%s: No matching usecase in sound trigger platform for sm_id %d",
@@ -1714,8 +1721,6 @@
             }
         }
     }
-    if (!list_empty(&st_ses->second_stage_list))
-        st_ses->enable_second_stage = true;
 
     return 0;
 
@@ -1731,8 +1736,8 @@
 static int get_gmm_model(struct sound_trigger_sound_model **common_sm,
                                        uint8_t *sm_payload, uint32_t num_models)
 {
-    SML_BigSoundModelTypeV3 *big_sm;
-    uint32_t i;
+    SML_BigSoundModelTypeV3 *big_sm = NULL;
+    uint32_t i = 0;
     int status = 0;
 
     for (i = 0; i < num_models; i++) {
@@ -1924,45 +1929,52 @@
 
     *handle = android_atomic_inc(&stdev->session_id);
 
-    ALOGD("%s: calling st_session_init with st_session ptr %p", __func__, st_session);
+    ALOGD("%s:[%d] calling st_session_init", __func__, *handle);
     status = st_session_init(st_session, stdev, exec_mode, *handle);
     if (status) {
-        ALOGE("%s: failed to initialize st_session with error %d", __func__, status);
+        ALOGE("%s: failed to initialize st_session with error %d", __func__,
+              status);
         goto exit_1;
     }
 
-    /* Parse second stage sound models and populate the second stage list for this session. */
-    st_session->enable_second_stage = false;
+    /*
+     * Parse second stage sound models and populate the second stage list for
+     * this session.
+     */
+    list_init(&st_session->second_stage_list);
     if (sm_version == SML_MODEL_V3) {
-        status = check_and_configure_second_stage_models(st_session, sm_payload, num_models,
-            phrase_sm->phrases[0].recognition_mode);
+        status = check_and_configure_second_stage_models(st_session, sm_payload,
+            num_models, phrase_sm->phrases[0].recognition_mode);
         if (status) {
             ALOGE("%s: Failed to set the second stage list", __func__);
             goto exit_2;
         }
     }
 
-    if (st_session->enable_second_stage) {
-        ALOGD("%s: calling st_session_ss_init with st_session ptr %p", __func__, st_session);
+    if (!list_empty(&st_session->second_stage_list)) {
+        ALOGD("%s: calling st_session_ss_init ", __func__);
         status = st_session_ss_init(st_session);
         if (status) {
-            ALOGE("%s: failed to initialize st_session second stage, error %d", __func__, status);
+            ALOGE("%s: failed to initialize st_session second stage,"
+                  "error %d", __func__, status);
             goto exit_2;
         }
     }
 
-    /* Store the sound model information for handling SSR
-       and interaction with smlib */
-    st_session->sm_data = calloc(1, sm_size);
-    if (!st_session->sm_data) {
+    /*
+     * Store the sound model information for handling SSR
+     * and interaction with smlib
+     */
+    st_session->phrase_sm = calloc(1, sm_size);
+    if (!st_session->phrase_sm) {
         status = -ENOMEM;
         goto exit_3;
     }
 
     if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        memcpy(st_session->sm_data, (char *)phrase_sm, sizeof(*phrase_sm));
-        st_session->sm_data->common.data_offset = sizeof(*phrase_sm);
-        memcpy((char *)st_session->sm_data + sizeof(*phrase_sm),
+        memcpy(st_session->phrase_sm, (char *)phrase_sm, sizeof(*phrase_sm));
+        st_session->phrase_sm->common.data_offset = sizeof(*phrase_sm);
+        memcpy((char *)st_session->phrase_sm + sizeof(*phrase_sm),
                (char *)phrase_sm + phrase_sm->common.data_offset,
                phrase_sm->common.data_size);
         /* TODO: SVA doesn't support per keyword recognition mode.
@@ -1971,16 +1983,18 @@
          */
         st_session->recognition_mode = phrase_sm->phrases[0].recognition_mode;
         ALOGD("%s: sm magic number 0x%x rm %d", __func__,
-              ((int *)((char *)st_session->sm_data + phrase_sm->common.data_offset))[0],
+              ((int *)((char *)st_session->phrase_sm +
+                       phrase_sm->common.data_offset))[0],
               phrase_sm->phrases[0].recognition_mode);
     } else {
         st_session->recognition_mode = RECOGNITION_MODE_VOICE_TRIGGER;
-        memcpy(st_session->sm_data, (char *)common_sm, sizeof(*common_sm));
-        memcpy((char *)st_session->sm_data + common_sm->data_offset,
+        memcpy(st_session->phrase_sm, (char *)common_sm, sizeof(*common_sm));
+        memcpy((char *)st_session->phrase_sm + common_sm->data_offset,
                (char *)common_sm + common_sm->data_offset,
                common_sm->data_size);
         ALOGD("%s: sm magic number 0x%x", __func__,
-              ((int *)((char *)st_session->sm_data + common_sm->data_offset))[0]);
+              ((int *)((char *)st_session->phrase_sm +
+                       common_sm->data_offset))[0]);
     }
 
     st_session->sm_type = sound_model->type;
@@ -2027,9 +2041,9 @@
     return 0;
 
 exit_3:
-    if (st_session->sm_data != NULL)
-        free(st_session->sm_data);
-    if (st_session->enable_second_stage)
+    if (st_session->phrase_sm != NULL)
+        free(st_session->phrase_sm);
+    if (!list_empty(&st_session->second_stage_list))
         st_session_ss_deinit(st_session);
 
 exit_2:
@@ -2040,16 +2054,17 @@
 
 exit:
     if (st_session != NULL) {
-        if (st_session->enable_second_stage) {
-            list_for_each_safe(node, tmp_node, &st_session->second_stage_list) {
-                st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                list_remove(&st_sec_stage->list_node);
-                deallocate_arm_second_stage_session(st_sec_stage);
-            }
+        list_for_each_safe(node, tmp_node, &st_session->second_stage_list) {
+            st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+            list_remove(&st_sec_stage->list_node);
+            deallocate_arm_second_stage_session(st_sec_stage);
         }
-        if (st_session->hw_ses_adsp) {
-            list_for_each_safe(node, tmp_node, &st_session->hw_ses_adsp->lsm_ss_cfg_list) {
-                st_lsm_ss_config_t *cfg = node_to_item(node, st_lsm_ss_config_t, list_node);
+        if (st_session->hw_proxy_ses &&
+            st_session->hw_proxy_ses->hw_ses_adsp) {
+            list_for_each_safe(node, tmp_node,
+                &st_session->hw_proxy_ses->hw_ses_adsp->lsm_ss_cfg_list) {
+                st_lsm_ss_config_t *cfg =
+                node_to_item(node, st_lsm_ss_config_t, list_node);
                 list_remove(&cfg->list_node);
                 deallocate_lsm_ss_config(cfg);
             }
@@ -2081,12 +2096,12 @@
      */
     st_session_t *best_ses = NULL;
     st_session_t *ses = NULL;
-    struct listnode *ses_node;
+    struct listnode *ses_node = NULL;
     int ses_channel_count = 0;
     int stopped_ses_channel_count = 0;
     int best_channel_count = 0;
     unsigned int stopped_ses_vad_preroll = 0;
-    unsigned int best_vad_preroll = 0;
+    unsigned int best_vad_preroll = 0, preroll = 0;
     bool stopped_ses_lpi_mode = false, is_stopped = false;
 
     if (stopped_ses->exec_mode != ST_EXEC_MODE_ADSP &&
@@ -2110,17 +2125,13 @@
                                                  stopped_v_info);
 
     if ((stopped_ses->exec_mode == ST_EXEC_MODE_ADSP) &&
-        stopped_ses->hw_ses_adsp) {
-        stopped_ses_lpi_mode = stopped_ses->hw_ses_adsp->lpi_enable;
-        stopped_ses_vad_preroll = stopped_ses->hw_ses_current->client_req_preroll;
+        stopped_ses->hw_proxy_ses->hw_ses_adsp) {
+        stopped_ses_lpi_mode = stopped_ses->hw_proxy_ses->hw_ses_adsp->lpi_enable;
+        stopped_ses_vad_preroll = st_session_get_preroll(stopped_ses);
     }
 
     list_for_each(ses_node, &stdev->sound_model_list) {
         ses = node_to_item(ses_node, st_session_t, list_node);
-        ses_channel_count =
-            platform_stdev_get_backend_channel_count(stdev->platform,
-                                                     ses->vendor_uuid_info);
-        ALOGV("%s:[%d] check ses_v_info %p", __func__, ses->sm_handle, ses->vendor_uuid_info);
         if (ses->exec_mode != ST_EXEC_MODE_ADSP &&
             ses->exec_mode != ST_EXEC_MODE_ARM)
             continue;
@@ -2137,9 +2148,17 @@
             continue;
         }
 
-        if ((ses->exec_mode == ST_EXEC_MODE_ADSP) &&
-            (ses->hw_ses_current->client_req_preroll > best_vad_preroll))
-            best_vad_preroll = ses->hw_ses_current->client_req_preroll;
+        ses_channel_count =
+            platform_stdev_get_backend_channel_count(stdev->platform,
+              ses->vendor_uuid_info);
+        ALOGV("%s:[%d] check ses_v_info %p", __func__, ses->sm_handle,
+            ses->vendor_uuid_info);
+
+        if (ses->exec_mode == ST_EXEC_MODE_ADSP) {
+            preroll = st_session_get_preroll(ses);
+            if (preroll > best_vad_preroll)
+                best_vad_preroll = preroll;
+        }
 
         if ((best_ses == NULL) || (ses_channel_count > best_channel_count)) {
             best_ses = ses;
@@ -2184,18 +2203,16 @@
                                    st_session_event_id_t event)
 {
     pthread_mutex_lock(&stdev->lock);
-    st_session_t* st_sess = get_sound_trigger_session(stdev, handle);
+    st_session_t* st_ses = get_sound_trigger_session(stdev, handle);
 
-    if (!st_sess)
+    if (!st_ses)
         goto exit;
 
     ALOGV("%s:[%d] event %d", __func__, handle, event);
     switch (event) {
     case ST_SES_EV_DEFERRED_STOP:
-        if (st_sess->pending_stop) {
-            stop_recognition_l(st_sess);
-            st_sess->pending_stop = false;
-        }
+        if (st_ses->pending_stop)
+            stop_recognition_l(st_ses);
         break;
     default:
         break;
@@ -2252,17 +2269,17 @@
     list_remove(&st_session->list_node);
 
     if (st_session->sm_type == SOUND_MODEL_TYPE_KEYPHRASE)
-        update_available_phrase_info(st_session, st_session->sm_data, true);
+        update_available_phrase_info(st_session, st_session->phrase_sm, true);
 
     if (!get_num_sessions())
         stdev->exec_mode = ST_EXEC_MODE_NONE;
 
     pthread_mutex_lock(&st_session->lock);
-    free(st_session->sm_data);
+    free(st_session->phrase_sm);
     pthread_mutex_unlock(&st_session->lock);
 
     run_keep_alive_session(stdev, st_session, ST_EVENT_STOP_KEEP_ALIVE);
-    if (st_session->enable_second_stage) {
+    if (!list_empty(&st_session->second_stage_list)) {
         st_session_ss_deinit(st_session);
         list_for_each_safe(node, tmp_node, &st_session->second_stage_list) {
             st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
@@ -2270,9 +2287,11 @@
             deallocate_arm_second_stage_session(st_sec_stage);
         }
     }
-    if (st_session && st_session->hw_ses_adsp) {
-        list_for_each_safe(node, tmp_node, &st_session->hw_ses_adsp->lsm_ss_cfg_list) {
-            st_lsm_ss_config_t *cfg = node_to_item(node, st_lsm_ss_config_t, list_node);
+    if (st_session && st_session->hw_proxy_ses->hw_ses_adsp) {
+        list_for_each_safe(node, tmp_node,
+            &st_session->hw_proxy_ses->hw_ses_adsp->lsm_ss_cfg_list) {
+            st_lsm_ss_config_t *cfg =
+                node_to_item(node, st_lsm_ss_config_t, list_node);
             list_remove(&cfg->list_node);
             deallocate_lsm_ss_config(cfg);
         }
@@ -2323,25 +2342,12 @@
     }
 
     ALOGV("%s:[%d] About to take session lock", __func__, sound_model_handle);
-    /* lock the session as we are about to change its
-        stored parameters */
     pthread_mutex_lock(&st_session->lock);
-
-    /*
-     *  cancel pending stop notifications to avoid unnecessary
-     *  backend teardown and restart
-     */
-    ALOGV("%s cancel req with handle %d and ev ST_SES_EV_DEFERRED_STOP to cancel",
-          __func__, st_session->sm_handle);
-    hw_session_notifier_cancel(st_session->sm_handle, ST_SES_EV_DEFERRED_STOP);
-    st_session->pending_stop = false;
-
     if (!st_session->rc_config ||
         !compare_recognition_config(config, st_session->rc_config)) {
         config_updated = true;
 
-        ALOGV("%s: received new params for session %d", __func__,
-            st_session->sm_handle);
+        ALOGV("%s:[%d] received new params ", __func__, st_session->sm_handle);
 
         /*
          * Store the recogntion configuration for sending opaque data
@@ -2356,8 +2362,9 @@
          */
         if ((config->data_size <= CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
             st_session->vendor_uuid_info->is_qcva_uuid &&
-            st_session->enable_second_stage) {
-            ALOGE("%s: SVA 3.0 rc_config opaque data required, exiting", __func__);
+            !list_empty(&st_session->second_stage_list)) {
+            ALOGE("%s: SVA 3.0 rc_config opaque data required, exiting",
+                  __func__);
             status = -EINVAL;
             goto cleanup;
         }
@@ -2373,7 +2380,8 @@
         memcpy((char *)st_session->rc_config + st_session->rc_config->data_offset,
            (char *)config + config->data_offset, config->data_size);
 
-        ALOGVV("%s: num_phrases=%d, id=%d", __func__, st_session->rc_config->num_phrases,
+        ALOGVV("%s: num_phrases=%d, id=%d", __func__,
+               st_session->rc_config->num_phrases,
                st_session->rc_config->phrases[0].id);
         st_session->callback = callback;
         st_session->cookie = cookie;
@@ -2381,8 +2389,11 @@
         st_session->capture_handle = config->capture_handle;
         st_session->capture_requested = config->capture_requested;
 
-        st_session->rc_config_update_counter++;
-        status = st_hw_ses_update_config(st_session, st_session->hw_ses_current);
+       /*
+        * Must be called before lpi decision with updated config:
+        * preroll, client requested mode etc..
+        */
+        status = st_session_update_recongition_config(st_session);
         if (status) {
             ALOGE("%s: ERROR. updating rc_config, returned status %d",
                   __func__, status);
@@ -2393,8 +2404,9 @@
     if (ST_EXEC_MODE_ADSP == st_session->exec_mode ||
         ST_EXEC_MODE_ARM == st_session->exec_mode) {
         stdev->lpi_enable = st_hw_check_lpi_support(stdev, st_session);
-        stdev->vad_enable = st_hw_check_vad_support(stdev, st_session, stdev->lpi_enable);
-        int vad_preroll = st_session->hw_ses_current->client_req_preroll;
+        stdev->vad_enable = st_hw_check_vad_support(stdev, st_session,
+                                                    stdev->lpi_enable);
+        int vad_preroll = st_session_get_preroll(st_session);
 
         status = platform_stdev_check_and_set_codec_backend_cfg(stdev->platform,
                              st_session->vendor_uuid_info, &backend_cfg_change,
@@ -2437,11 +2449,11 @@
     }
 
     /* Switch session to high performance/low power mode if requested by APP */
-    if ((ST_EXEC_MODE_CPE == st_session->hw_ses_current->exec_mode) &&
-        (ST_HW_SESS_DET_HIGH_PERF_MODE == st_session->hw_ses_current->client_req_det_mode))
+    if ((ST_EXEC_MODE_CPE == st_session->exec_mode) &&
+        (ST_DET_HIGH_PERF_MODE == st_session->client_req_det_mode))
         check_and_transit_cpe_ses_to_ape(st_session);
-    else if ((ST_EXEC_MODE_ADSP == st_session->hw_ses_current->exec_mode) &&
-        (ST_HW_SESS_DET_LOW_POWER_MODE == st_session->hw_ses_current->client_req_det_mode))
+    else if ((ST_EXEC_MODE_ADSP == st_session->exec_mode) &&
+        (ST_DET_LOW_POWER_MODE == st_session->client_req_det_mode))
         check_and_transit_ape_ses_to_cpe(st_session);
 
 cleanup:
@@ -2513,10 +2525,10 @@
         list_remove(node);
         st_session_stop_lab(st_session);
         st_session_stop(st_session);
-        if (st_session->enable_second_stage)
+        if (!list_empty(&st_session->second_stage_list))
             st_session_ss_deinit(st_session);
         st_session_deinit(st_session);
-        free(st_session->sm_data);
+        free(st_session->phrase_sm);
         free(st_session->rc_config);
         free(st_session);
     }
@@ -2766,8 +2778,10 @@
     return ret;
 }
 
-/* Audio hal calls this callback for notifying Subsystem restart,
-   lab stop and concurrency events */
+/*
+ * Audio hal calls this callback for notifying Subsystem restart,
+ * lab stop and concurrency events
+ */
 int sound_trigger_hw_call_back(audio_event_type_t event,
                                audio_event_info_t* config)
 {
diff --git a/sound_trigger_hw.h b/sound_trigger_hw.h
index c72f900..f241111 100644
--- a/sound_trigger_hw.h
+++ b/sound_trigger_hw.h
@@ -99,13 +99,71 @@
 typedef int (*g722_init_decoder_t)(void *decoder_inp);
 typedef int (*g722_dec_get_total_byte_size_t)(int *total_byte_size);
 typedef int (*g722_dec_process_t)(short *in_buf, short *out_buf,
-                                   int in_bytes, int *out_samples,
-                                   void *decoder_inp);
+                                  int in_bytes, int *out_samples,
+                                  void *decoder_inp);
 
 /* MULAW decoder APIs */
 typedef int (*mulaw_dec_process_t)(short *out_buf, char *in_buf,
                                    unsigned int in_bytes);
 
+/* Listen Sound Model Library APIs */
+#include "ListenSoundModelLib.h"
+
+typedef listen_status_enum (*smlib_getSoundModelHeader_t)
+(
+    listen_model_type         *pSoundModel,
+    listen_sound_model_header *pListenSoundModelHeader
+);
+
+typedef listen_status_enum (*smlib_releaseSoundModelHeader_t)
+(
+    listen_sound_model_header *pListenSoundModelHeader
+);
+
+typedef listen_status_enum (*smlib_getKeywordPhrases_t)
+(
+    listen_model_type *pSoundModel,
+    uint16_t          *numKeywords,
+    keywordId_t       *keywords
+);
+
+typedef listen_status_enum (*smlib_getUserNames_t)
+(
+    listen_model_type *pSoundModel,
+    uint16_t          *numUsers,
+    userId_t          *users
+);
+
+typedef listen_status_enum (*smlib_getMergedModelSize_t)
+(
+     uint16_t          numModels,
+     listen_model_type *pModels[],
+     uint32_t          *nOutputModelSize
+);
+
+typedef listen_status_enum (*smlib_mergeModels_t)
+(
+     uint16_t          numModels,
+     listen_model_type *pModels[],
+     listen_model_type *pMergedModel
+);
+
+typedef listen_status_enum (*getSizeAfterDeleting_t)
+(
+    listen_model_type *pInputModel,
+    keywordId_t       keywordId,
+    userId_t          userId,
+    uint32_t          *nOutputModelSize
+);
+
+typedef listen_status_enum (*deleteFromModel_t)
+(
+    listen_model_type *pInputModel,
+    keywordId_t       keywordId,
+    userId_t          userId,
+    listen_model_type *pResultModel
+);
+
 struct sound_trigger_device {
     struct sound_trigger_hw_device device;
     struct sound_trigger_properties *hw_properties;
@@ -176,14 +234,16 @@
     bool is_gcs;
 
     struct listnode vendor_uuid_list;
-    void *smlib_handle;
-    smlib_generate_sound_trigger_recognition_config_payload_t
-                                    generate_st_recognition_config_payload;
-    smlib_generate_sound_trigger_phrase_recognition_event_t
-                                    generate_st_phrase_recognition_event;
-    smlib_generate_sound_trigger_phrase_recognition_event_t
-                                    generate_st_phrase_recognition_event_v2;
 
+    void *smlib_handle;
+    smlib_getSoundModelHeader_t smlib_getSoundModelHeader;
+    smlib_releaseSoundModelHeader_t smlib_releaseSoundModelHeader;
+    smlib_getKeywordPhrases_t smlib_getKeywordPhrases;
+    smlib_getUserNames_t smlib_getUserNames;
+    smlib_getMergedModelSize_t smlib_getMergedModelSize;
+    smlib_mergeModels_t smlib_mergeModels;
+    getSizeAfterDeleting_t smlib_getSizeAfterDeleting;
+    deleteFromModel_t smlib_deleteFromModel;
 
     void *adpcm_dec_lib_handle;
     g722_init_decoder_t adpcm_dec_init;
diff --git a/sound_trigger_platform.c b/sound_trigger_platform.c
index c7308cf..c048569 100644
--- a/sound_trigger_platform.c
+++ b/sound_trigger_platform.c
@@ -90,8 +90,8 @@
 
 #define ST_PARAM_KEY_FIRMWARE_IMAGE "firmware_image"
 #define ST_PARAM_KEY_SM_VENDOR_UUID "vendor_uuid"
+#define ST_PARAM_KEY_MERGE_FIRST_STAGE_SOUNDMODELS "merge_first_stage_sound_models"
 #define ST_PARAM_KEY_APP_TYPE "app_type"
-#define ST_PARAM_KEY_LIBRARY "library"
 #define ST_PARAM_KEY_MAX_CPE_PHRASES "max_cpe_phrases"
 #define ST_PARAM_KEY_MAX_APE_USERS "max_ape_users"
 #define ST_PARAM_KEY_MAX_APE_PHRASES "max_ape_phrases"
@@ -411,45 +411,62 @@
     struct listnode acdb_meta_key_list;
 };
 
-static int load_smlib(struct st_vendor_info *sm_info, const char *name)
+static int load_soundmodel_lib(sound_trigger_device_t *stdev)
 {
     int status = 0;
 
-    sm_info->smlib_handle = dlopen(name, RTLD_NOW);
-    if (!sm_info->smlib_handle) {
+    stdev->smlib_handle = dlopen(LIB_SVA_SOUNDMODEL, RTLD_NOW);
+    if (!stdev->smlib_handle) {
         ALOGE("%s: ERROR. %s", __func__, dlerror());
         return -ENODEV;
     }
 
-    DLSYM(sm_info->smlib_handle, sm_info->generate_st_phrase_recognition_event,
-          generate_sound_trigger_phrase_recognition_event, status);
+    DLSYM(stdev->smlib_handle, stdev->smlib_getSoundModelHeader,
+          getSoundModelHeader, status);
     if (status)
         goto cleanup;
-    DLSYM(sm_info->smlib_handle, sm_info->generate_st_phrase_recognition_event_v2,
-          generate_sound_trigger_phrase_recognition_event_v2, status);
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_releaseSoundModelHeader,
+          releaseSoundModelHeader, status);
     if (status)
         goto cleanup;
-    DLSYM(sm_info->smlib_handle,
-          sm_info->generate_st_recognition_config_payload,
-          generate_sound_trigger_recognition_config_payload, status);
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_getKeywordPhrases,
+          getKeywordPhrases, status);
     if (status)
         goto cleanup;
-    DLSYM(sm_info->smlib_handle,
-          sm_info->generate_st_recognition_config_payload_v2,
-          generate_sound_trigger_recognition_config_payload_v2, status);
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_getUserNames,
+          getUserNames, status);
     if (status)
         goto cleanup;
-    DLSYM(sm_info->smlib_handle,
-          sm_info->generate_st_phrase_recognition_event_v3,
-          generate_sound_trigger_phrase_recognition_event_v3, status);
-    /* Ignore error for this symbol */
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_getMergedModelSize,
+          getMergedModelSize, status);
+    if (status)
+        goto cleanup;
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_mergeModels,
+          mergeModels, status);
+    if (status)
+        goto cleanup;
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_getSizeAfterDeleting,
+          getSizeAfterDeleting, status);
+    if (status)
+        goto cleanup;
+
+    DLSYM(stdev->smlib_handle, stdev->smlib_deleteFromModel,
+          deleteFromModel, status);
+    if (status)
+        goto cleanup;
 
     return 0;
 
 cleanup:
-    if (sm_info->smlib_handle) {
-        dlclose(sm_info->smlib_handle);
-        sm_info->smlib_handle = NULL;
+    if (stdev->smlib_handle) {
+        dlclose(stdev->smlib_handle);
+        stdev->smlib_handle = NULL;
     }
     return status;
 }
@@ -2326,16 +2343,6 @@
         sm_info->app_type = strtoul(str_value, NULL, 16);
     }
 
-    err = str_parms_get_str(parms, ST_PARAM_KEY_LIBRARY,
-                            str_value, sizeof(str_value));
-    if (err >= 0) {
-        str_parms_del(parms, ST_PARAM_KEY_LIBRARY);
-        /* if soundmodel library for ISV vendor uuid is mentioned, use it. If not
-           ignore and continue sending the opaque data from HAL to DSP */
-        if (strcmp(str_value, "none"))
-            load_smlib(sm_info, str_value);
-    }
-
     err = str_parms_get_int(parms, ST_PARAM_KEY_MAX_CPE_PHRASES, &value);
     if (err >= 0) {
         str_parms_del(parms, ST_PARAM_KEY_MAX_CPE_PHRASES);
@@ -2512,6 +2519,14 @@
         sm_info->lab_dam_cfg_payload.token_id = value;
     }
 
+    err = str_parms_get_str(parms, ST_PARAM_KEY_MERGE_FIRST_STAGE_SOUNDMODELS,
+                            str_value, sizeof(str_value));
+    if (err >= 0) {
+        str_parms_del(parms, ST_PARAM_KEY_MERGE_FIRST_STAGE_SOUNDMODELS);
+        sm_info->merge_fs_soundmodels =
+            !strncasecmp(str_value, "true", 4) ? true : false;
+    }
+
     sm_info->avail_transit_ape_phrases = sm_info->avail_ape_phrases;
     sm_info->avail_transit_ape_users = sm_info->avail_ape_users;
     sm_info->avail_transit_cpe_phrases = sm_info->avail_cpe_phrases;
@@ -3612,9 +3627,18 @@
         v_info = node_to_item(v_node, struct st_vendor_info, list_node);
         if (!memcmp(&v_info->uuid, &qcva_uuid, sizeof(sound_trigger_uuid_t))) {
             v_info->is_qcva_uuid = true;
+            if (!stdev->smlib_handle && v_info->merge_fs_soundmodels) {
+                ret = load_soundmodel_lib(stdev);
+                if (ret) {
+                    v_info->merge_fs_soundmodels = false;
+                    ret = 0;
+                }
+            }
         } else if (!memcmp(&v_info->uuid, &qcmd_uuid, sizeof(sound_trigger_uuid_t))) {
             v_info->is_qcmd_uuid = true;
+            v_info->merge_fs_soundmodels = false;
         }  else {
+            v_info->merge_fs_soundmodels = false;
             ALOGV("%s: ISV uuid present", __func__);
         }
         if (!stdev->adpcm_dec_lib_handle &&
@@ -3684,8 +3708,6 @@
     }
     list_for_each_safe(v_node, temp_node, &stdev->vendor_uuid_list) {
         v_info = node_to_item(v_node, struct st_vendor_info, list_node);
-        if (v_info->smlib_handle)
-            dlclose(v_info->smlib_handle);
         list_remove(v_node);
         free(v_info);
     }
@@ -3741,8 +3763,6 @@
                 free(lsm_info);
             }
 
-            if (v_info->smlib_handle)
-                dlclose(v_info->smlib_handle);
             list_remove(v_node);
             free(v_info);
         }
diff --git a/sound_trigger_platform.h b/sound_trigger_platform.h
index 1520042..c6543d3 100644
--- a/sound_trigger_platform.h
+++ b/sound_trigger_platform.h
@@ -66,6 +66,8 @@
 #define LIB_ACDB_LOADER "libacdbloader.so"
 #define LIB_ADPCM_DECODER "libadpcmdec.so"
 #define LIB_MULAW_DECODER "libmulawdec.so"
+#define LIB_SVA_SOUNDMODEL "liblistensoundmodel2.so"
+
 #define BUF_SIZE 1024
 
 #define MAX_SND_CARD 8
@@ -274,34 +276,6 @@
     ST_SHARED_BUF_RAW,
 } st_shared_buf_fmt_t;
 
-/* soundmodel library wrapper functions */
-typedef int (*smlib_generate_sound_trigger_phrase_recognition_event_t)
-(
-    const struct sound_trigger_phrase_sound_model *sm,
-    const struct sound_trigger_recognition_config *config,
-    const void *payload,
-    unsigned int payload_size,
-    struct sound_trigger_phrase_recognition_event **r_event
-);
-
-typedef int (*smlib_generate_sound_trigger_recognition_config_payload_t)
-(
-   const struct sound_trigger_phrase_sound_model *sm,
-   const struct sound_trigger_recognition_config *config,
-   unsigned char **out_payload,
-   unsigned int *out_payload_size
-);
-
-typedef int (*smlib_generate_sound_trigger_phrase_recognition_event_v3_t)
-(
-    const struct sound_trigger_phrase_sound_model *sm,
-    const struct sound_trigger_recognition_config *config,
-    const void *payload,
-    unsigned int payload_size,
-    qsthw_recognition_event_type_t event_type,
-    void **r_event
-);
-
 struct st_lsm_params {
     struct listnode list_node;
     st_exec_mode_t exec_mode;
@@ -368,6 +342,7 @@
     int app_type;
     bool is_qcva_uuid;
     bool is_qcmd_uuid;
+    bool merge_fs_soundmodels;
     unsigned int fwk_mode;
     int sample_rate;
     int format;
@@ -404,19 +379,6 @@
     struct listnode lsm_usecase_list;
     struct listnode arm_ss_usecase_list;
     struct listnode lsm_ss_usecase_list;
-
-    void *smlib_handle;
-    smlib_generate_sound_trigger_recognition_config_payload_t
-                                    generate_st_recognition_config_payload;
-    smlib_generate_sound_trigger_recognition_config_payload_t
-                                    generate_st_recognition_config_payload_v2;
-
-    smlib_generate_sound_trigger_phrase_recognition_event_t
-                                    generate_st_phrase_recognition_event;
-    smlib_generate_sound_trigger_phrase_recognition_event_t
-                                    generate_st_phrase_recognition_event_v2;
-    smlib_generate_sound_trigger_phrase_recognition_event_v3_t
-                                        generate_st_phrase_recognition_event_v3;
 };
 
 typedef struct st_codec_backend_cfg {
diff --git a/st_extn/st_hw_extn.c b/st_extn/st_hw_extn.c
index 2fb2496..d6351ca 100644
--- a/st_extn/st_hw_extn.c
+++ b/st_extn/st_hw_extn.c
@@ -49,6 +49,78 @@
 #include "st_session.h"
 #include "st_hw_defs.h"
 
+static int generate_sound_trigger_phrase_recognition_event_v3
+(
+    const struct sound_trigger_phrase_sound_model *phrase_sm,
+    const struct sound_trigger_recognition_config *rc_config,
+    const void *payload,
+    unsigned int payload_size,
+    qsthw_recognition_event_type_t event_type,
+    void **out_rc_event
+)
+{
+    unsigned int i = 0, j = 0, user_id = 0;
+    int rc = 0;
+
+    ALOGD("%s: Enter payload_size %d", __func__, payload_size);
+
+    if (!payload || !phrase_sm || !rc_config || !out_rc_event) {
+        ALOGE("%s: Null params", __func__);
+        return -EINVAL;
+    }
+
+    *out_rc_event = NULL;
+
+    switch (event_type) {
+    case QSTHW_RC_EVENT_TYPE_TIMESTAMP: {
+        struct qsthw_phrase_recognition_event *event;
+        struct sound_trigger_phrase_recognition_event *phrase_event;
+        event = calloc(1, sizeof(*event) + payload_size);
+        if (!event) {
+            ALOGE("%s: event allocation failed size %d",
+                   __func__, payload_size);
+            rc = -ENODEV;
+            break;
+        }
+
+        phrase_event = &event->phrase_event;
+
+        phrase_event->num_phrases = rc_config->num_phrases;
+        phrase_event->common.data_offset = sizeof(*event);
+        phrase_event->common.data_size = payload_size;
+
+        /* fill confidence levels */
+        for (i = 0; i < rc_config->num_phrases; i++) {
+             phrase_event->phrase_extras[i].id = rc_config->phrases[i].id;
+             phrase_event->phrase_extras[i].recognition_modes =
+                                 phrase_sm->phrases[0].recognition_mode;
+             phrase_event->phrase_extras[i].confidence_level =
+                                 ((char *)payload)[i];
+             phrase_event->phrase_extras[i].num_levels =
+                                 rc_config->phrases[i].num_levels;
+             for (j = 0; j < rc_config->phrases[i].num_levels; j++) {
+                  user_id = rc_config->phrases[i].levels[j].user_id;
+                  phrase_event->phrase_extras[i].levels[j].user_id = user_id;
+                  phrase_event->phrase_extras[i].levels[j].level =
+                                             ((char *)payload)[user_id];
+             }
+        }
+
+        /* Copy payload to event using offset generated above */
+        memcpy((char *)event + phrase_event->common.data_offset,
+               payload, payload_size);
+
+        *out_rc_event = (struct qsthw_phrase_recognition_event *)event;
+        break;
+    }
+    default:
+        ALOGE("%s: Invalid event type passed %d", __func__, event_type);
+        rc = -EINVAL;
+        break;
+    }
+    return rc;
+}
+
 bool sthw_extn_check_process_det_ev_support()
 {
     return true;
@@ -56,21 +128,20 @@
 
 /* recognition event with timestamp */
 int sthw_extn_process_detection_event_keyphrase(
-    st_session_t *st_ses, uint64_t timestamp, int detect_status,
+    st_proxy_session_t *st_ses, uint64_t timestamp, int detect_status,
     void *payload, size_t payload_size,
     struct sound_trigger_phrase_recognition_event **event)
 {
-    st_hw_session_t *st_hw_ses = NULL;
+    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
     struct qsthw_phrase_recognition_event *local_event = NULL;
     struct st_vendor_info *v_info = st_ses->vendor_uuid_info;
-    struct sound_trigger_phrase_sound_model *phrase_sm= st_ses->sm_data;
+    struct sound_trigger_phrase_sound_model *phrase_sm = stc_ses->phrase_sm;
 
     int status = 0;
     unsigned int i, j;
     qsthw_recognition_event_type_t event_type = QSTHW_RC_EVENT_TYPE_TIMESTAMP;
 
-    st_hw_ses = st_ses->hw_ses_current;
-
     if (st_hw_ses->is_generic_event) {
         /*
          * For generic event, reusing the st_session v2 event generation.
@@ -104,19 +175,15 @@
         free(generic_phrase_event);
         *event = &local_event->phrase_event;
         goto exit;
-    } else if (v_info && v_info->smlib_handle) {
+    } else if (v_info->is_qcva_uuid) {
         /* if smlib is present, get the event from it, else send the
          * DSP received payload as it is to App
          */
-        status = v_info->generate_st_phrase_recognition_event_v3(
-                                        phrase_sm,
-                                        st_ses->rc_config,
-                                        payload,
-                                        payload_size,
-                                        event_type,
-                                        (void *)&local_event);
+        status = generate_sound_trigger_phrase_recognition_event_v3(
+            phrase_sm, stc_ses->rc_config, payload, payload_size,
+            event_type, (void *)&local_event);
 
-        if (status) {
+        if (status || !local_event) {
             ALOGW("%s: smlib fill recognition event failed, status %d",
                   __func__, status);
             goto exit;
@@ -130,11 +197,12 @@
             goto exit;
         }
 
-        memcpy(local_event->phrase_event.phrase_extras, st_ses->rc_config->phrases,
-               st_ses->rc_config->num_phrases *
-               sizeof(struct sound_trigger_phrase_recognition_extra));
+        memcpy(local_event->phrase_event.phrase_extras,
+            stc_ses->rc_config->phrases,
+            stc_ses->rc_config->num_phrases *
+            sizeof(struct sound_trigger_phrase_recognition_extra));
 
-        local_event->phrase_event.num_phrases = st_ses->rc_config->num_phrases;
+        local_event->phrase_event.num_phrases = stc_ses->rc_config->num_phrases;
         local_event->phrase_event.common.data_offset = sizeof(*local_event);
         local_event->phrase_event.common.data_size = payload_size;
         memcpy((char *)local_event + local_event->phrase_event.common.data_offset,
@@ -147,9 +215,9 @@
     local_event->timestamp = timestamp;
 
     local_event->phrase_event.common.status = detect_status;
-    local_event->phrase_event.common.type = st_ses->sm_data->common.type;;
-    local_event->phrase_event.common.model = st_ses->sm_handle;
-    local_event->phrase_event.common.capture_available = st_ses->capture_requested;
+    local_event->phrase_event.common.type = phrase_sm->common.type;;
+    local_event->phrase_event.common.model = stc_ses->sm_handle;
+    local_event->phrase_event.common.capture_available = stc_ses->capture_requested;
     local_event->phrase_event.common.capture_delay_ms = 0;
     local_event->phrase_event.common.capture_preamble_ms = 0;
     local_event->phrase_event.common.audio_config.sample_rate =
@@ -170,11 +238,11 @@
         }
     }
 
-    ALOGI("%s:[%d] send keyphrase recognition event %d", __func__,
-                     st_ses->sm_handle, detect_status);
-    ALOGV("%s:[%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
+    ALOGI("%s:[c%d] send keyphrase recognition event %d", __func__,
+                     stc_ses->sm_handle, detect_status);
+    ALOGV("%s:[c%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
            "num_phrases=%d, id=%d, timestamp = %" PRIu64,
-           __func__, st_ses->sm_handle, local_event->phrase_event.common.status,
+           __func__, stc_ses->sm_handle, local_event->phrase_event.common.status,
            local_event->phrase_event.common.type, local_event->phrase_event.common.model,
            local_event->phrase_event.common.capture_available, local_event->phrase_event.num_phrases,
            local_event->phrase_event.phrase_extras[0].id, local_event->timestamp);
@@ -182,4 +250,4 @@
     *event = &local_event->phrase_event;
 exit:
     return status;
-}
+}
\ No newline at end of file
diff --git a/st_extn/st_hw_extn.h b/st_extn/st_hw_extn.h
index 82cc324..c5b3efb 100644
--- a/st_extn/st_hw_extn.h
+++ b/st_extn/st_hw_extn.h
@@ -37,7 +37,7 @@
 #define sthw_extn_check_process_det_ev_support() (false)
 #else
 int sthw_extn_process_detection_event_keyphrase(
-    st_session_t *st_ses, uint64_t timestamp, int detect_status,
+    st_proxy_session_t *st_ses, uint64_t timestamp, int detect_status,
     void *payload, size_t payload_size,
     struct sound_trigger_phrase_recognition_event **event);
 bool sthw_extn_check_process_det_ev_support();
diff --git a/st_hw_common.c b/st_hw_common.c
index b550fd7..4e55daa 100644
--- a/st_hw_common.c
+++ b/st_hw_common.c
@@ -80,21 +80,29 @@
      * Second stage is only supported on an adsp session,
      * and when multi-stage support is available in lsm drivers.
      */
-    if (!st_ses || !st_ses->hw_ses_adsp || !st_hw_check_multi_stage_lsm_support())
+    if (!st_ses || st_ses->hw_proxy_ses ||
+        !st_ses->hw_proxy_ses->hw_ses_adsp ||
+        !st_hw_check_multi_stage_lsm_support()) {
         return false;
+    }
 
     stdev = st_ses->stdev;
     list_for_each(node, &stdev->sound_model_list) {
         p_ses = node_to_item(node, st_session_t, list_node);
-        if (p_ses == NULL || p_ses == st_ses || p_ses->exec_mode != ST_EXEC_MODE_ADSP)
+        if (p_ses == NULL || p_ses == st_ses ||
+            p_ses->exec_mode != ST_EXEC_MODE_ADSP)
             continue;
-        if (p_ses->hw_ses_adsp && !list_empty(&p_ses->hw_ses_adsp->lsm_ss_cfg_list))
+        pthread_mutex_lock(&p_ses->hw_proxy_ses->lock);
+        if (p_ses->hw_proxy_ses->hw_ses_adsp &&
+            !list_empty(&p_ses->hw_proxy_ses->hw_ses_adsp->lsm_ss_cfg_list))
             lsm_ss_uc_count++;
         if (lsm_ss_uc_count >= max_lsm_ss_uc_count) {
-            ALOGD("%s: max supported ss usecase count(%d) already active, not allowing further",
-                  __func__, max_lsm_ss_uc_count);
+            ALOGD("%s: max supported ss usecase count(%d) already active,"
+                  "not allowing further", __func__, max_lsm_ss_uc_count);
+            pthread_mutex_unlock(&p_ses->hw_proxy_ses->lock);
             return false;
         }
+        pthread_mutex_unlock(&p_ses->hw_proxy_ses->lock);
     }
     ALOGD("%s: ss usecase allowed", __func__);
     return true;
@@ -209,8 +217,7 @@
     list_for_each(ses_node, &stdev->sound_model_list) {
         ses = node_to_item(ses_node, st_session_t, list_node);
 
-        if (ses->hw_ses_current->client_req_det_mode ==
-            ST_HW_SESS_DET_HIGH_PERF_MODE) {
+        if (ses->client_req_det_mode == ST_DET_HIGH_PERF_MODE) {
             ALOGD("%s:[%d] lpi NOT supported due to high perf mode", __func__,
                 ses->sm_handle);
             return false;
@@ -271,332 +278,30 @@
     return vad_enable;
 }
 
-void st_hw_check_and_set_lpi_mode(st_session_t *st_ses)
+void st_hw_check_and_set_lpi_mode(st_session_t *stc_ses)
 {
-    if (st_ses && st_ses->hw_ses_adsp) {
+    st_proxy_session_t *st_ses = NULL;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return;
+
+    st_ses = stc_ses->hw_proxy_ses;
+
+    pthread_mutex_lock(&st_ses->lock);
+    if (st_ses->hw_ses_adsp) {
         if (st_ses->stdev->platform_lpi_enable == ST_PLATFORM_LPI_NONE) {
             st_ses->hw_ses_adsp->lpi_enable =
                 (st_ses->vendor_uuid_info->lpi_enable &&
-                is_projected_lpi_budget_available(st_ses->stdev, st_ses));
+                is_projected_lpi_budget_available(st_ses->stdev, stc_ses));
         } else {
             st_ses->hw_ses_adsp->lpi_enable =
                 (st_ses->stdev->platform_lpi_enable ==
                  ST_PLATFORM_LPI_ENABLE) ? true: false;
         }
     }
+    pthread_mutex_unlock(&st_ses->lock);
 }
 
-static int parse_config_key_conf_levels
-(
-    st_session_t *st_ses,
-    st_hw_session_t* st_hw_ses,
-    void *opaque_conf_levels
-)
-{
-    struct st_confidence_levels_info *conf_levels = NULL;
-    struct st_confidence_levels_info_v2 *conf_levels_v2 = NULL;
-    struct st_sound_model_conf_levels *sm_levels = NULL;
-    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    st_lsm_ss_config_t *ss_cfg = NULL;
-    st_arm_second_stage_t *st_sec_stage = NULL;
-    int status = 0;
-    uint32_t i = 0;
-    bool gmm_conf_found = false;
-    uint8_t confidence_level = 0;
-    int32_t confidence_level_v2 = 0;
-    bool arm_second_stage = st_hw_ses->enable_second_stage;
-    bool adsp_second_stage = (st_hw_ses == st_ses->hw_ses_adsp &&
-                              !list_empty(&st_hw_ses->lsm_ss_cfg_list));
-
-    if (arm_second_stage || adsp_second_stage) {
-        if (st_ses->rc_config->num_phrases > 1) {
-            ALOGE("%s: Multi keyword is unsupported with 2nd stage detection",
-                  __func__);
-            return -EINVAL;
-        }
-
-        if (st_ses->rc_config->phrases[0].num_levels > 1) {
-            ALOGE("%s: Multi user is unsupported with 2nd stage detection",
-                  __func__);
-            return -EINVAL;
-        }
-    }
-
-    if (st_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002) {
-        conf_levels = (struct st_confidence_levels_info *)
-            ((char *)opaque_conf_levels + sizeof(struct st_param_header));
-        st_hw_ses->conf_levels_info =
-            calloc(1, sizeof(struct st_confidence_levels_info));
-        if (!st_hw_ses->conf_levels_info) {
-            ALOGE("%s: failed to alloc conf_levels_info", __func__);
-            return -ENOMEM;
-        }
-        memcpy(st_hw_ses->conf_levels_info, (char *)conf_levels,
-            sizeof(struct st_confidence_levels_info));
-
-        for (i = 0; i < conf_levels->num_sound_models; i++) {
-            sm_levels = (struct st_sound_model_conf_levels *)
-                &conf_levels->conf_levels[i];
-            if (sm_levels->sm_id == ST_SM_ID_SVA_GMM) {
-                if ((st_ses->stdev->is_gcs) && (st_hw_ses == st_ses->hw_ses_cpe))
-                    status =
-                        generate_sound_trigger_recognition_config_payload_v2(
-                        (void *)sm_levels, &st_hw_ses->conf_levels,
-                        &st_hw_ses->num_conf_levels,
-                        st_ses->conf_levels_intf_version);
-                else
-                    status =
-                        generate_sound_trigger_recognition_config_payload(
-                        (void *)sm_levels, &st_hw_ses->conf_levels,
-                        &st_hw_ses->num_conf_levels,
-                        st_ses->conf_levels_intf_version);
-                gmm_conf_found = true;
-            } else if ((sm_levels->sm_id == ST_SM_ID_SVA_CNN) ||
-                       (sm_levels->sm_id == ST_SM_ID_SVA_VOP)) {
-                confidence_level = (sm_levels->sm_id == ST_SM_ID_SVA_CNN) ?
-                    sm_levels->kw_levels[0].kw_level:
-                    sm_levels->kw_levels[0].user_levels[0].level;
-                if (arm_second_stage) {
-                    list_for_each_safe(node, tmp_node,
-                        st_hw_ses->second_stage_list) {
-                        st_sec_stage = node_to_item(node, st_arm_second_stage_t,
-                            list_node);
-                        if (st_sec_stage->ss_info->sm_id == sm_levels->sm_id)
-                            st_sec_stage->ss_session->confidence_threshold =
-                                confidence_level;
-                    }
-                } else if (adsp_second_stage) {
-                    list_for_each_safe(node, tmp_node,
-                        &st_hw_ses->lsm_ss_cfg_list) {
-                        ss_cfg = node_to_item(node, st_lsm_ss_config_t,
-                            list_node);
-                        if (ss_cfg->ss_info->sm_id == sm_levels->sm_id)
-                            ss_cfg->confidence_threshold = confidence_level;
-                    }
-                }
-            } else {
-                ALOGE("%s: Unsupported sm id (%d), exiting", __func__,
-                    sm_levels->sm_id);
-                status = -EINVAL;
-                break;
-            }
-        }
-    } else {
-        conf_levels_v2 = (struct st_confidence_levels_info_v2 *)
-            ((char *)opaque_conf_levels + sizeof(struct st_param_header));
-        st_hw_ses->conf_levels_info =
-            calloc(1, sizeof(struct st_confidence_levels_info_v2));
-        if (!st_hw_ses->conf_levels_info) {
-            ALOGE("%s: failed to alloc conf_levels_info", __func__);
-            return -ENOMEM;
-        }
-        memcpy(st_hw_ses->conf_levels_info, (char *)conf_levels_v2,
-            sizeof(struct st_confidence_levels_info_v2));
-
-        for (i = 0; i < conf_levels_v2->num_sound_models; i++) {
-            sm_levels_v2 = (struct st_sound_model_conf_levels_v2 *)
-                &conf_levels_v2->conf_levels[i];
-            if (sm_levels_v2->sm_id == ST_SM_ID_SVA_GMM) {
-                if ((st_ses->stdev->is_gcs) &&
-                    (st_hw_ses == st_ses->hw_ses_cpe))
-                    status =
-                        generate_sound_trigger_recognition_config_payload_v2(
-                        (void *)sm_levels_v2, &st_hw_ses->conf_levels,
-                        &st_hw_ses->num_conf_levels,
-                        st_ses->conf_levels_intf_version);
-                else
-                    status =
-                        generate_sound_trigger_recognition_config_payload(
-                        (void *)sm_levels_v2, &st_hw_ses->conf_levels,
-                        &st_hw_ses->num_conf_levels,
-                        st_ses->conf_levels_intf_version);
-                gmm_conf_found = true;
-            } else if ((sm_levels_v2->sm_id == ST_SM_ID_SVA_CNN) ||
-                       (sm_levels_v2->sm_id == ST_SM_ID_SVA_VOP)) {
-                confidence_level_v2 =
-                    (sm_levels_v2->sm_id == ST_SM_ID_SVA_CNN) ?
-                    sm_levels_v2->kw_levels[0].kw_level:
-                    sm_levels_v2->kw_levels[0].user_levels[0].level;
-                if (arm_second_stage) {
-                    list_for_each_safe(node, tmp_node,
-                        st_hw_ses->second_stage_list) {
-                        st_sec_stage = node_to_item(node, st_arm_second_stage_t,
-                            list_node);
-                        if (st_sec_stage->ss_info->sm_id ==
-                            sm_levels_v2->sm_id)
-                            st_sec_stage->ss_session->confidence_threshold =
-                                confidence_level_v2;
-                    }
-                } else if (adsp_second_stage) {
-                    list_for_each_safe(node, tmp_node,
-                        &st_hw_ses->lsm_ss_cfg_list) {
-                        ss_cfg = node_to_item(node, st_lsm_ss_config_t,
-                            list_node);
-                        if (ss_cfg->ss_info->sm_id == sm_levels_v2->sm_id)
-                            ss_cfg->confidence_threshold = confidence_level_v2;
-                    }
-                }
-            } else {
-                ALOGE("%s: Unsupported sm id (%d), exiting", __func__,
-                    sm_levels_v2->sm_id);
-                status = -EINVAL;
-                break;
-            }
-        }
-    }
-
-    if (!gmm_conf_found) {
-        ALOGE("%s: Did not receive GMM confidence threshold, error!", __func__);
-        status  = -EINVAL;
-    }
-
-    if (status && st_hw_ses->conf_levels_info) {
-        free(st_hw_ses->conf_levels_info);
-        st_hw_ses->conf_levels_info = NULL;
-    }
-
-    return status;
-}
-
-int st_hw_ses_update_config(st_session_t *st_ses, st_hw_session_t* st_hw_ses)
-{
-    int status = 0;
-    uint8_t *opaque_ptr = NULL;
-    unsigned int opaque_size = 0, conf_levels_payload_size = 0;
-    struct st_param_header *param_hdr = NULL;
-    struct st_hist_buffer_info *hist_buf = NULL;
-    struct st_det_perf_mode_info *det_perf_mode = NULL;
-    struct sound_trigger_recognition_config *rc_config = st_ses->rc_config;
-
-    ST_DBG_DECLARE(FILE *rc_opaque_fd = NULL; static int rc_opaque_cnt = 0);
-    ST_DBG_FILE_OPEN_WR(rc_opaque_fd, ST_DEBUG_DUMP_LOCATION,
-                        "rc_config_opaque_data", "bin", rc_opaque_cnt++);
-    ST_DBG_FILE_WRITE(rc_opaque_fd, (uint8_t *)rc_config + rc_config->data_offset,
-                      rc_config->data_size);
-    ST_DBG_FILE_CLOSE(rc_opaque_fd);
-
-    /* First release memory and reset allocation from previous rc_config update */
-    if (st_hw_ses->conf_levels_info) {
-        free(st_hw_ses->conf_levels_info);
-        st_hw_ses->conf_levels_info = NULL;
-    }
-    if (st_hw_ses->conf_levels) {
-        free(st_hw_ses->conf_levels);
-        st_hw_ses->conf_levels = NULL;
-    }
-    st_hw_ses->client_req_hist_buf = 0;
-    st_hw_ses->client_req_preroll = 0;
-    st_hw_ses->client_req_det_mode = ST_HW_SESS_DET_UNKNOWN_MODE;
-
-    if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-        st_hw_ses->vendor_uuid_info->is_qcva_uuid) {
-        opaque_ptr = (uint8_t *)rc_config + rc_config->data_offset;
-        while (opaque_size < rc_config->data_size) {
-            param_hdr = (struct st_param_header *)opaque_ptr;
-            ALOGV("%s: key %d, payload size %d", __func__,
-                  param_hdr->key_id, param_hdr->payload_size);
-
-            switch(param_hdr->key_id) {
-            case ST_PARAM_KEY_CONFIDENCE_LEVELS:
-                memcpy((char *)&st_ses->conf_levels_intf_version,
-                    (char *)(opaque_ptr + sizeof(struct st_param_header)),
-                    sizeof(uint32_t));
-                if (st_ses->conf_levels_intf_version !=
-                    CONF_LEVELS_INTF_VERSION_0002) {
-                    conf_levels_payload_size =
-                        sizeof(struct st_confidence_levels_info);
-                } else {
-                    conf_levels_payload_size =
-                        sizeof(struct st_confidence_levels_info_v2);
-                }
-                if (param_hdr->payload_size != conf_levels_payload_size) {
-                    ALOGE("%s: Conf level format error, exiting", __func__);
-                    return -EINVAL;
-                }
-                status = parse_config_key_conf_levels(st_ses, st_hw_ses,
-                    opaque_ptr);
-                opaque_size += sizeof(struct st_param_header) +
-                    conf_levels_payload_size;
-                opaque_ptr += sizeof(struct st_param_header) +
-                    conf_levels_payload_size;
-                if (status) {
-                    ALOGE("%s: parsing conf levels failed(status=%d)",
-                        __func__, status);
-                    return -EINVAL;
-                }
-                break;
-            case ST_PARAM_KEY_HISTORY_BUFFER_CONFIG:
-                if (param_hdr->payload_size != sizeof(struct st_hist_buffer_info)) {
-                    ALOGE("%s: History buffer config format error, exiting", __func__);
-                    return -EINVAL;
-                }
-                hist_buf = (struct st_hist_buffer_info *)(opaque_ptr +
-                    sizeof(struct st_param_header));
-                st_hw_ses->client_req_hist_buf = hist_buf->hist_buffer_duration_msec;
-                st_hw_ses->client_req_preroll = hist_buf->pre_roll_duration_msec;
-                ALOGV("%s: recognition config history buf len = %d, preroll len = %d, minor version = %d",
-                      __func__, hist_buf->hist_buffer_duration_msec,
-                      hist_buf->pre_roll_duration_msec, hist_buf->version);
-                opaque_size += sizeof(struct st_param_header) + sizeof(struct st_hist_buffer_info);
-                opaque_ptr += sizeof(struct st_param_header) + sizeof(struct st_hist_buffer_info);
-                break;
-            case ST_PARAM_KEY_DETECTION_PERF_MODE:
-                if (param_hdr->payload_size != sizeof(struct st_det_perf_mode_info)) {
-                    ALOGE("%s: Opaque data format error, exiting", __func__);
-                    return -EINVAL;
-                }
-                det_perf_mode = (struct st_det_perf_mode_info *)(opaque_ptr +
-                    sizeof(struct st_param_header));
-                ALOGV("set perf mode to %d", det_perf_mode->mode);
-                st_hw_ses->client_req_det_mode = det_perf_mode->mode;
-                opaque_size += sizeof(struct st_param_header) +
-                    sizeof(struct st_det_perf_mode_info);
-                opaque_ptr += sizeof(struct st_param_header) +
-                    sizeof(struct st_det_perf_mode_info);
-                break;
-            default:
-                ALOGE("%s: Unsupported opaque data key id, exiting", __func__);
-                return -EINVAL;
-            }
-        }
-    } else if (st_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        struct sound_trigger_phrase_sound_model *phrase_sm = st_ses->sm_data;
-        struct st_vendor_info *v_info = st_hw_ses->vendor_uuid_info;
-
-        ALOGV("%s: num_phrases=%d, id=%d", __func__,
-               rc_config->num_phrases, rc_config->phrases[0].id);
-
-        /*
-         * Can be QC SVA or ISV vendor. Get from corresponding smlib defined
-         * in platform file. if soundmodel library for ISV vendor uuid is
-         * mentioned, use it. If not ignore, in this case opaque data would be
-         * send from HAL to DSP.
-         */
-        if (v_info && v_info->smlib_handle) {
-            if (st_ses->stdev->is_gcs && st_hw_ses == st_ses->hw_ses_cpe)
-                status = v_info->generate_st_recognition_config_payload_v2(
-                                                 phrase_sm, rc_config,
-                                                 &st_hw_ses->conf_levels,
-                                                 &st_hw_ses->num_conf_levels);
-            else
-                status = v_info->generate_st_recognition_config_payload(
-                                                 phrase_sm, rc_config,
-                                                 &st_hw_ses->conf_levels,
-                                                 &st_hw_ses->num_conf_levels);
-            if (status || !st_hw_ses->conf_levels)
-                ALOGE("%s: failed to get conf levels from lib handle", __func__);
-        } else {
-            ALOGD("%s: No smlib, opaque data would be sent as is", __func__);
-        }
-    }
-
-    st_hw_ses->rc_config = st_ses->rc_config;
-    st_hw_ses->rc_config_update_counter = st_ses->rc_config_update_counter;
-    return status;
-}
-
-
 int stop_other_sessions(struct sound_trigger_device *stdev,
                          st_session_t *cur_ses)
 {
@@ -671,6 +376,58 @@
     return NULL;
 }
 
+/*
+ * This function is used to prepare the detection engine custom config payload for
+ * sound trigger sessions that have second stage sessions. If history buffering is not
+ * requested by the client, add it into the payload here. Second stage sessions require
+ * the keyword to be buffered.
+ */
+int st_hw_ses_get_hist_buff_payload
+(
+    st_hw_session_t *p_ses,
+    uint8_t *payload_buf,
+    size_t buff_size
+)
+{
+    struct st_hist_buffer_info *hist_buf = NULL;
+
+    if (!payload_buf || buff_size < sizeof(*hist_buf)) {
+        ALOGE("%s: buffer size(%zd) too small to fill payload(%zd)",
+              __func__, buff_size, sizeof(*hist_buf));
+        return -EINVAL;
+    }
+
+    hist_buf = (struct st_hist_buffer_info *) payload_buf;
+    hist_buf->version = DEFAULT_CUSTOM_CONFIG_MINOR_VERSION;
+
+    if (p_ses->sthw_cfg.client_req_hist_buf > 0) {
+        hist_buf->hist_buffer_duration_msec =
+            p_ses->sthw_cfg.client_req_hist_buf;
+        hist_buf->pre_roll_duration_msec = p_ses->sthw_cfg.client_req_preroll;
+
+        if (p_ses->is_generic_event) {
+            if (p_ses->sthw_cfg.client_req_hist_buf <=
+                (p_ses->sthw_cfg.client_req_preroll + KW_LEN_WARNING))
+                ALOGW("%s: Client hist buf and preroll lens leave only"
+                      "%dms for keyword", __func__,
+                      (p_ses->sthw_cfg.client_req_hist_buf -
+                       p_ses->sthw_cfg.client_req_preroll));
+
+            if (p_ses->sthw_cfg.client_req_preroll < PREROLL_LEN_WARNING)
+                ALOGW("%s: Client requested small preroll length %dms",
+                    __func__, p_ses->sthw_cfg.client_req_preroll);
+        }
+    } else {
+        hist_buf->hist_buffer_duration_msec =
+            p_ses->vendor_uuid_info->kw_duration;
+        hist_buf->pre_roll_duration_msec = 0;
+    }
+    ALOGD("%s: history buf duration %d, preroll %d", __func__,
+          hist_buf->hist_buffer_duration_msec,
+          hist_buf->pre_roll_duration_msec);
+    return 0;
+}
+
 /* ---------------- hw session notify thread --------------- */
 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
 
@@ -909,379 +666,3 @@
     }
     enable_gcov();
 }
-
-/*
- * This function is used to prepare the detection engine custom config payload for
- * sound trigger sessions that have second stage sessions. If history buffering is not
- * requested by the client, add it into the payload here. Second stage sessions require
- * the keyword to be buffered.
- */
-int st_hw_ses_get_hist_buff_payload
-(
-    st_hw_session_t *p_ses,
-    uint8_t *payload_buff,
-    size_t buff_size
-)
-{
-    struct st_hist_buffer_info *hist_buf = NULL;
-
-    if (!payload_buff || buff_size < sizeof(*hist_buf)) {
-        ALOGE("%s: buffer size(%zd) too small to fill payload(%zd)",
-              __func__, buff_size, sizeof(*hist_buf));
-        return -EINVAL;
-    }
-
-    hist_buf = (struct st_hist_buffer_info *) payload_buff;
-    hist_buf->version = DEFAULT_CUSTOM_CONFIG_MINOR_VERSION;
-
-    if (p_ses->client_req_hist_buf > 0) {
-        hist_buf->hist_buffer_duration_msec = p_ses->client_req_hist_buf;
-        hist_buf->pre_roll_duration_msec = p_ses->client_req_preroll;
-
-        if (p_ses->is_generic_event) {
-            if (p_ses->client_req_hist_buf <= p_ses->client_req_preroll + KW_LEN_WARNING)
-                ALOGW("%s: Warning: Client hist buf and preroll lens leave only %dms for keyword",
-                    __func__, (p_ses->client_req_hist_buf - p_ses->client_req_preroll));
-
-            if (p_ses->client_req_preroll < PREROLL_LEN_WARNING)
-                ALOGW("%s: Warning: Client requested small preroll length %dms",
-                    __func__, p_ses->client_req_preroll);
-        }
-
-    } else {
-        hist_buf->hist_buffer_duration_msec = p_ses->vendor_uuid_info->kw_duration;
-        hist_buf->pre_roll_duration_msec = 0;
-    }
-
-    return 0;
-}
-
-static int fill_sound_trigger_recognition_config_payload
-(
-   const void *sm_levels_generic,
-   unsigned char *conf_levels,
-   unsigned int num_conf_levels,
-   unsigned int total_num_users,
-   uint32_t version
-)
-{
-    int status = 0;
-    unsigned int user_level, user_id;
-    unsigned int i, j;
-    unsigned char *user_id_tracker;
-    struct st_sound_model_conf_levels *sm_levels = NULL;
-    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
-
-    /*  Example: Say the recognition structure has 3 keywords with users
-     *  |kid|
-     *  [0] k1 |uid|
-     *         [0] u1 - 1st trainer
-     *         [1] u2 - 4th trainer
-     *         [3] u3 - 3rd trainer
-     *  [1] k2
-     *         [2] u2 - 2nd trainer
-     *         [4] u3 - 5th trainer
-     *  [2] k3
-     *         [5] u4 - 6th trainer
-     *
-     *  Output confidence level array will be
-     *  [k1, k2, k3, u1k1, u2k1, u2k2, u3k1, u3k2, u4k3]
-     */
-
-    if (version != CONF_LEVELS_INTF_VERSION_0002) {
-        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
-        if (!sm_levels || !conf_levels || !num_conf_levels) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            return -EINVAL;
-        }
-        user_id_tracker = calloc(1, num_conf_levels);
-        if (!user_id_tracker) {
-            ALOGE("%s: failed to allocate user_id_tracker", __func__);
-            return -ENOMEM;
-        }
-
-        for (i = 0; i < sm_levels->num_kw_levels; i++) {
-            ALOGV("%s: [%d] kw level %d", __func__, i,
-                            sm_levels->kw_levels[i].kw_level);
-            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++) {
-                ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
-                      sm_levels->kw_levels[i].user_levels[j].user_id,
-                      sm_levels->kw_levels[i].user_levels[j].level);
-            }
-        }
-
-        for (i = 0; i < sm_levels->num_kw_levels; i++) {
-            conf_levels[i] = sm_levels->kw_levels[i].kw_level;
-            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++) {
-                user_level = sm_levels->kw_levels[i].user_levels[j].level;
-                user_id = sm_levels->kw_levels[i].user_levels[j].user_id;
-                if ((user_id < sm_levels->num_kw_levels) ||
-                    (user_id >= num_conf_levels)) {
-                    ALOGE("%s: ERROR. Invalid params user id %d>%d",
-                          __func__, user_id, total_num_users);
-                    status = -EINVAL;
-                    goto exit;
-                } else {
-                    if (user_id_tracker[user_id] == 1) {
-                        ALOGE("%s: ERROR. Duplicate user id %d",
-                              __func__, user_id);
-                        status = -EINVAL;
-                        goto exit;
-                    }
-                    conf_levels[user_id] = (user_level < 100) ?
-                        user_level: 100;
-                    user_id_tracker[user_id] = 1;
-                    ALOGV("%s: user_conf_levels[%d] = %d", __func__,
-                        user_id, conf_levels[user_id]);
-                }
-            }
-        }
-    } else {
-        sm_levels_v2 =
-            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
-        if (!sm_levels_v2 || !conf_levels || !num_conf_levels) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            return -EINVAL;
-        }
-        user_id_tracker = calloc(1, num_conf_levels);
-        if (!user_id_tracker) {
-            ALOGE("%s: failed to allocate user_id_tracker", __func__);
-            return -ENOMEM;
-        }
-
-        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
-            ALOGV("%s: [%d] kw level %d", __func__, i,
-                            sm_levels_v2->kw_levels[i].kw_level);
-            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++) {
-                ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
-                      sm_levels_v2->kw_levels[i].user_levels[j].user_id,
-                      sm_levels_v2->kw_levels[i].user_levels[j].level);
-            }
-        }
-
-        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
-            conf_levels[i] = sm_levels_v2->kw_levels[i].kw_level;
-            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++) {
-                user_level = sm_levels_v2->kw_levels[i].user_levels[j].level;
-                user_id = sm_levels_v2->kw_levels[i].user_levels[j].user_id;
-                if ((user_id < sm_levels_v2->num_kw_levels) ||
-                    (user_id >= num_conf_levels)) {
-                    ALOGE("%s: ERROR. Invalid params user id %d>%d",
-                          __func__, user_id, total_num_users);
-                    status = -EINVAL;
-                    goto exit;
-                } else {
-                    if (user_id_tracker[user_id] == 1) {
-                        ALOGE("%s: ERROR. Duplicate user id %d",
-                              __func__, user_id);
-                        status = -EINVAL;
-                        goto exit;
-                    }
-                    conf_levels[user_id] = (user_level < 100) ?
-                        user_level: 100;
-                    user_id_tracker[user_id] = 1;
-                    ALOGV("%s: user_conf_levels[%d] = %d", __func__,
-                        user_id, conf_levels[user_id]);
-                }
-            }
-        }
-    }
-
-exit:
-    free(user_id_tracker);
-    return status;
-}
-
-int generate_sound_trigger_recognition_config_payload
-(
-   const void *sm_levels_generic,
-   unsigned char **out_payload,
-   unsigned int *out_payload_size,
-   uint32_t version
-)
-{
-    int status = 0;
-    unsigned int total_num_users = 0, num_conf_levels = 0;
-    unsigned char *conf_levels = NULL;
-    unsigned int i = 0, j = 0;
-    struct st_sound_model_conf_levels *sm_levels = NULL;
-    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
-
-    ALOGV("%s: Enter...", __func__);
-
-    if (version != CONF_LEVELS_INTF_VERSION_0002) {
-        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
-        if (!sm_levels || !out_payload || !out_payload_size) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        *out_payload = NULL;
-        *out_payload_size = 0;
-
-        if (sm_levels->num_kw_levels == 0) {
-            ALOGE("%s: ERROR. No confidence levels present", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        for (i = 0; i < sm_levels->num_kw_levels; i++) {
-            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++)
-                total_num_users++;
-        }
-
-        num_conf_levels = total_num_users + sm_levels->num_kw_levels;
-        conf_levels = calloc(1, num_conf_levels);
-        if (!conf_levels) {
-            ALOGE("%s: ERROR. conf levels alloc failed", __func__);
-            status = -ENOMEM;
-            goto exit;
-        }
-    } else {
-        sm_levels_v2 =
-            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
-        if (!sm_levels_v2 || !out_payload || !out_payload_size) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        *out_payload = NULL;
-        *out_payload_size = 0;
-
-        if (sm_levels_v2->num_kw_levels == 0) {
-            ALOGE("%s: ERROR. No confidence levels present", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
-            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++)
-                total_num_users++;
-        }
-
-        num_conf_levels = total_num_users + sm_levels_v2->num_kw_levels;
-        conf_levels = calloc(1, num_conf_levels);
-        if (!conf_levels) {
-            ALOGE("%s: ERROR. conf levels alloc failed", __func__);
-            status = -ENOMEM;
-            goto exit;
-        }
-    }
-
-    status = fill_sound_trigger_recognition_config_payload(sm_levels_generic,
-        conf_levels, num_conf_levels, total_num_users, version);
-    if (status) {
-        ALOGE("%s: fill config payload failed, error %d", __func__, status);
-        goto exit;
-    }
-
-    *out_payload = conf_levels;
-    *out_payload_size = num_conf_levels;
-
-    return status;
-
-exit:
-    if (conf_levels)
-        free(conf_levels);
-
-    return status;
-}
-
-int generate_sound_trigger_recognition_config_payload_v2
-(
-   const void *sm_levels_generic,
-   unsigned char **out_payload,
-   unsigned int *out_payload_size,
-   uint32_t version
-)
-{
-    int status = 0;
-    unsigned int total_num_users = 0, num_conf_levels = 0;
-    unsigned char *conf_levels = NULL;
-    unsigned int i = 0, j = 0;
-    struct st_sound_model_conf_levels *sm_levels = NULL;
-    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
-
-    ALOGV("%s: Enter...", __func__);
-
-    if (version != CONF_LEVELS_INTF_VERSION_0002) {
-        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
-        if (!sm_levels || !out_payload || !out_payload_size) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        *out_payload = NULL;
-        *out_payload_size = 0;
-
-        if (sm_levels->num_kw_levels == 0) {
-            ALOGE("%s: ERROR. No confidence levels present", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        for (i = 0; i < sm_levels->num_kw_levels; i++) {
-            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++)
-                total_num_users++;
-        }
-
-        num_conf_levels = total_num_users + sm_levels->num_kw_levels;
-    } else {
-        sm_levels_v2 =
-            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
-        if (!sm_levels_v2 || !out_payload || !out_payload_size) {
-            ALOGE("%s: ERROR. Invalid inputs", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        *out_payload = NULL;
-        *out_payload_size = 0;
-
-        if (sm_levels_v2->num_kw_levels == 0) {
-            ALOGE("%s: ERROR. No confidence levels present", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
-            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++)
-                total_num_users++;
-        }
-
-        num_conf_levels = total_num_users + sm_levels_v2->num_kw_levels;
-    }
-
-    /*
-     * allocate dsp payload w/additional 2 bytes for minor_version and
-     * num_active_models and additional num_conf_levels for KW enable
-     * fields
-     */
-    conf_levels = calloc(1, 2 + 2 * num_conf_levels);
-    if (!conf_levels) {
-        ALOGE("%s: ERROR. conf levels alloc failed", __func__);
-        status = -ENOMEM;
-        goto exit;
-    }
-
-    conf_levels[0] = 1; /* minor version */
-    conf_levels[1] = num_conf_levels; /* num_active_models */
-    status = fill_sound_trigger_recognition_config_payload(sm_levels_generic,
-        conf_levels + 2, num_conf_levels, total_num_users, version);
-    if (status) {
-        ALOGE("%s: fill config payload failed, error %d", __func__, status);
-        goto exit;
-    }
-
-    /* set KW enable fields to 1 for now
-     * TODO set appropriately based on what client is passing in rc_config
-     */
-    memset(&conf_levels[num_conf_levels + 2], 0x1, num_conf_levels);
-    ALOGV("%s: here", __func__);
-    *out_payload = conf_levels;
-    /* add size of minor version and num_active_models */
-    *out_payload_size = 2 + 2 * num_conf_levels;
-
-    return status;
-
-exit:
-    if (conf_levels)
-        free(conf_levels);
-
-    return status;
-}
diff --git a/st_hw_common.h b/st_hw_common.h
index 5b2529a..6fb9548 100644
--- a/st_hw_common.h
+++ b/st_hw_common.h
@@ -61,12 +61,6 @@
 /* 2nd stage detection */
 int st_hw_ses_get_hist_buff_payload(st_hw_session_t *p_ses,
     uint8_t *payload_buff, size_t buff_size);
-int generate_sound_trigger_recognition_config_payload(
-    const void *sm_levels_generic, unsigned char **out_payload,
-    unsigned int *out_payload_size, uint32_t version);
-int generate_sound_trigger_recognition_config_payload_v2(
-    const void *sm_levels_generic, unsigned char **out_payload,
-    unsigned int *out_payload_size, uint32_t version);
 
 static inline uint64_t get_current_time_ns()
 {
diff --git a/st_hw_session.h b/st_hw_session.h
index 1ea0fd2..6c61bee 100644
--- a/st_hw_session.h
+++ b/st_hw_session.h
@@ -53,12 +53,6 @@
     ST_HW_SESS_EVENT_MAX
 }  st_hw_sess_event_id_t;
 
-typedef enum {
-    ST_HW_SESS_DET_LOW_POWER_MODE,
-    ST_HW_SESS_DET_HIGH_PERF_MODE,
-    ST_HW_SESS_DET_UNKNOWN_MODE = 0xFF,
-}  st_hw_sess_det_mode_t;
-
 typedef struct st_hw_sess_detected_ev {
     uint64_t timestamp;
     int detect_status;
@@ -75,6 +69,15 @@
 
 typedef void (*hw_ses_event_callback_t)(st_hw_sess_event_t *event, void *cookie);
 
+struct st_hw_ses_config {
+    unsigned int client_req_hist_buf;
+    unsigned int client_req_preroll;
+    unsigned char *conf_levels;
+    unsigned int num_conf_levels;
+    char *custom_data;
+    unsigned int custom_data_size;
+};
+
 struct st_hw_session {
 
     struct st_session_fptrs *fptrs;
@@ -84,37 +87,34 @@
     struct pcm_config  config;
     struct st_vendor_info *vendor_uuid_info;
 
-    unsigned int num_conf_levels;
-    unsigned char *conf_levels;
-
     hw_ses_event_callback_t callback_to_st_session;
     void *cookie;
 
     st_exec_mode_t exec_mode;
 
     enum sound_trigger_states state;
-    sound_model_handle_t sm_handle; /* used when logging debug info */
+    sound_model_handle_t sm_handle; /* used when logging */
     struct sound_trigger_device *stdev;
 
     st_device_t st_device;
     char *st_device_name;
-    unsigned int client_req_hist_buf;
-    unsigned int client_req_preroll;
     struct listnode *second_stage_list;
     uint32_t kw_start_idx;
     uint32_t kw_end_idx;
     int32_t user_level;
     bool enable_second_stage;
-    void *conf_levels_info;
     bool is_generic_event;
     struct listnode lsm_ss_cfg_list;
     bool lpi_enable;
-    st_hw_sess_det_mode_t client_req_det_mode;
-    struct sound_trigger_recognition_config *rc_config;
+    bool lab_enabled;
+
     int rc_config_update_counter;
     uint64_t first_stage_det_event_time;
     uint64_t second_stage_det_event_time;
     st_buffer_t *buffer;
+
+    struct st_hw_ses_config sthw_cfg;
+    bool sthw_cfg_updated;
 };
 
 typedef struct st_hw_session st_hw_session_t;
@@ -122,22 +122,20 @@
 /* Function pointers to routing layers */
 typedef void (*sound_trigger_init_session_t)(st_hw_session_t *);
 typedef int (*sound_trigger_reg_sm_t)(st_hw_session_t *,
-    void*,  sound_trigger_sound_model_type_t sm_type);
+    void*,  unsigned int, sound_trigger_sound_model_type_t sm_type);
 typedef int (*sound_trigger_reg_sm_params_t)(st_hw_session_t *,
     unsigned int recognition_mode, bool capture_requested,
     struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void * sm_data);
 
-typedef int (*sound_trigger_dereg_sm_t)(st_hw_session_t *,
-    bool capture_requested);
+typedef int (*sound_trigger_dereg_sm_t)(st_hw_session_t *);
 typedef int (*sound_trigger_dereg_sm_params_t)(st_hw_session_t *);
 typedef int (*sound_trigger_start_t)(st_hw_session_t *);
-typedef int (*sound_trigger_restart_t)(st_hw_session_t *, unsigned int, bool,
+typedef int (*sound_trigger_restart_t)(st_hw_session_t *, unsigned int,
    struct sound_trigger_recognition_config *,
    sound_trigger_sound_model_type_t, void *);
 typedef int (*sound_trigger_stop_t)(st_hw_session_t *);
-typedef int (*sound_trigger_stop_buffering_t)(st_hw_session_t *,
-    bool capture_requested);
+typedef int (*sound_trigger_stop_buffering_t)(st_hw_session_t *);
 typedef int (*sound_trigger_set_device_t)(st_hw_session_t *, bool);
 typedef int (*sound_trigger_read_pcm_t)(st_hw_session_t *,
                               unsigned char *, unsigned int );
diff --git a/st_hw_session_gcs.c b/st_hw_session_gcs.c
index e5a2980..83c84ff 100644
--- a/st_hw_session_gcs.c
+++ b/st_hw_session_gcs.c
@@ -56,7 +56,7 @@
 #define WDSP_SYSFS_NAME "/dev/wcd_dsp0_control"
 
 static int reg_sm(st_hw_session_t *p_ses,
-    void *sm_data,
+    void *sm_data, unsigned int sm_size,
     sound_trigger_sound_model_type_t sm_type);
 static int reg_sm_params(st_hw_session_t *p_ses,
     unsigned int recognition_mode,
@@ -68,15 +68,15 @@
     unsigned char *buf,
     unsigned int bytes);
 static void process_lab_capture(st_hw_session_t *p_ses);
-static int dereg_sm(st_hw_session_t *p_ses, bool capture_requested);
+static int dereg_sm(st_hw_session_t *p_ses);
 static int dereg_sm_params(st_hw_session_t *p_ses);
 static int start(st_hw_session_t *p_ses);
 static int restart(st_hw_session_t *p_ses,
-    unsigned int recognition_mode, bool capture_requested,
+    unsigned int recognition_mode,
     struct sound_trigger_recognition_config *rc_config __unused,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
 static int stop(st_hw_session_t *p_ses);
-static int stop_buffering(st_hw_session_t *p_ses, bool capture_requested);
+static int stop_buffering(st_hw_session_t *p_ses);
 static int set_device(st_hw_session_t *p_ses,
     bool enable);
 static int disable_device(st_hw_session_t *p_ses, bool setting_device);
@@ -504,22 +504,15 @@
     return NULL;
 }
 
-static int reg_sm(st_hw_session_t *p_ses,
-    void *sm_data,
-    sound_trigger_sound_model_type_t sm_type)
+static int reg_sm(st_hw_session_t *p_ses, void *sm_data,
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type __unused)
 {
     int status = 0;
     uint8_t *load_sm_msg = NULL;
     struct graphite_cal_header *sm_msg_hdr = NULL;
-    size_t load_sm_msg_sz = 0;
+    unsigned int load_sm_msg_sz = 0;
     struct st_vendor_info *v_info = p_ses->vendor_uuid_info;
     struct gcs_module_param_info gcs_module_info; /* used to register for ev*/
-    struct sound_trigger_phrase_sound_model *phrase_sm =
-        (struct sound_trigger_phrase_sound_model *)sm_data;
-    struct sound_trigger_sound_model *common_sm =
-        (struct sound_trigger_sound_model *)sm_data;
-    size_t sm_data_size = 0;
-    uint32_t sm_data_offset = 0;
     st_hw_session_gcs_t *p_gcs_ses = (st_hw_session_gcs_t *)p_ses;
     int st_device = 0, device_acdb_id = 0;
     int capture_device;
@@ -575,22 +568,14 @@
         goto cleanup1;
     }
 
-    if (sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        sm_data_size = phrase_sm->common.data_size;
-        sm_data_offset = phrase_sm->common.data_offset;
-    } else {
-        sm_data_size = common_sm->data_size;
-        sm_data_offset = common_sm->data_offset;
-    }
-
     /* calculate size of load sm msg */
     load_sm_msg_sz = sizeof(struct graphite_cal_header); /* param header for sound model param */
-    load_sm_msg_sz += sm_data_size; /* SM opaque data size from upper layers */
+    load_sm_msg_sz += sm_size; /* SM opaque data size from upper layers */
     load_sm_msg_sz = ALIGN(load_sm_msg_sz, 4);
 
     load_sm_msg = calloc(load_sm_msg_sz, sizeof(uint8_t));
     if (!load_sm_msg) {
-        ALOGE("%s: failed to allocate memory for sm msg, size = %zu", __func__,
+        ALOGE("%s: failed to allocate memory for sm msg, size = %u", __func__,
             load_sm_msg_sz);
         status = -ENOMEM;
         goto cleanup1;
@@ -602,15 +587,15 @@
         params[LOAD_SOUND_MODEL].instance_id;
     sm_msg_hdr->param_id = p_gcs_ses->gcs_usecase->
         params[LOAD_SOUND_MODEL].param_id;
-    sm_msg_hdr->size = sm_data_size;
+    sm_msg_hdr->size = sm_size;
 
-    ALOGV("%s: sm cal header MID %x, IID %x, PID %x \n sm data %p, sm size %zu,"
-        " alloc size %zu", __func__, sm_msg_hdr->module_id,
+    ALOGV("%s: sm cal header MID %x, IID %x, PID %x \n sm data %p, sm size %u,"
+        " alloc size %u", __func__, sm_msg_hdr->module_id,
         sm_msg_hdr->instance_id, sm_msg_hdr->param_id, sm_data,
-        sm_data_size, load_sm_msg_sz);
+        sm_size, load_sm_msg_sz);
 
     memcpy(load_sm_msg + sizeof(struct graphite_cal_header),
-        (uint8_t *)sm_data + sm_data_offset, sm_data_size);
+        (uint8_t *)sm_data, sm_size);
 
 
     ST_DBG_DECLARE(FILE *load_fd = NULL; static int load_fd_cnt = 0);
@@ -619,11 +604,11 @@
     ST_DBG_FILE_CLOSE(load_fd);
 
     ALOGD("%s:[%d] calling gcs_load_data with graph_handle %d, load_sm_msg %p, "
-        "load_sm_msg_sz %zu", __func__, p_ses->sm_handle, p_gcs_ses->graph_handle,
+        "load_sm_msg_sz %d", __func__, p_ses->sm_handle, p_gcs_ses->graph_handle,
         load_sm_msg, load_sm_msg_sz);
     ATRACE_BEGIN("sthal:gcs: gcs_load_data");
     status = gcs_load_data_fn(p_gcs_ses->graph_handle, load_sm_msg,
-        (uint32_t)load_sm_msg_sz, &p_gcs_ses->loaded_sm_handle);
+        load_sm_msg_sz, &p_gcs_ses->loaded_sm_handle);
     ATRACE_END();
     if (status) {
         ALOGE("%s: gcs_load_data failed with status %d", __func__, status);
@@ -729,12 +714,12 @@
      */
 
     if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-        p_ses->vendor_uuid_info->is_qcva_uuid) {
+        v_info->is_qcva_uuid) {
         if (!capture_requested)
             disable_custom_config = true;
 
         det_config_size = sizeof(struct gcs_det_engine_config_param) +
-                          p_ses->num_conf_levels;
+                          p_ses->sthw_cfg.num_conf_levels;
         det_config_size = ALIGN(det_config_size, 4);
         p_hw_ses->nonpersistent_cal_size += det_config_size;
 
@@ -758,9 +743,9 @@
             p_hw_ses->nonpersistent_cal_size += custom_config_size;
         }
 
-        if (v_info->smlib_handle) {
+        if (v_info->is_qcva_uuid || v_info->is_qcmd_uuid) {
             det_config_size = sizeof(struct gcs_det_engine_config_param) +
-                              p_ses->num_conf_levels;
+                              p_ses->sthw_cfg.num_conf_levels;
             /* If not using custom config param, send opaque data as part of
              * DETECTION_ENGINE_CONFIG. Opaque data will be put after confidence
              * level payload data.
@@ -780,7 +765,7 @@
          * Just set this flag to handle generic detection event coming from DSP
          */
         p_ses->is_generic_event = true;
-        if (p_ses->vendor_uuid_info->is_qcva_uuid) {
+        if (v_info->is_qcva_uuid) {
             det_event_type_size +=
                 sizeof(struct gcs_det_event_type_custom_config);
             det_event_type_size = ALIGN(det_event_type_size, 4);
@@ -841,19 +826,20 @@
             }
 
             msg_offset += sizeof(struct gcs_det_engine_config_param);
-            if (p_ses->conf_levels) {
-                memcpy(msg_offset, (uint8_t *)p_ses->conf_levels, p_ses->num_conf_levels);
-                msg_offset += p_ses->num_conf_levels;
-                p_msg->custom_payload_sz = p_ses->num_conf_levels;
+            if (p_ses->sthw_cfg.conf_levels) {
+                memcpy(msg_offset, (uint8_t *)p_ses->sthw_cfg.conf_levels,
+                    p_ses->sthw_cfg.num_conf_levels);
+                msg_offset += p_ses->sthw_cfg.num_conf_levels;
+                p_msg->custom_payload_sz = p_ses->sthw_cfg.num_conf_levels;
                 /*
                  * The detection_engine_config struct has 2 bytes for minor
                  * version and num_active_models before the confidence levels.
                  * There is also 1 byte added for each confidence level as an
                  * enable/disable flag.
                  */
-                for (i = 0; i < (p_ses->num_conf_levels - 2) / 2; i++) {
+                for (i = 0; i < (p_ses->sthw_cfg.num_conf_levels - 2) / 2; i++) {
                     ALOGD("%s: First stage conf_levels[%d] = %d",
-                        __func__, i, *(p_ses->conf_levels + 2 + i));
+                        __func__, i, *(p_ses->sthw_cfg.conf_levels + 2 + i));
                 }
             }
         }
@@ -878,7 +864,7 @@
                     cc_msg->cal_hdr.param_id);
 
                 if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-                    p_ses->vendor_uuid_info->is_qcva_uuid) {
+                    v_info->is_qcva_uuid) {
                     /* Custom config for updated opaque data structure for SVA 3.0. */
                     cc_msg->cal_hdr.size = sizeof(struct st_hist_buffer_info);
                     msg_offset += sizeof(struct gcs_det_engine_custom_config_param);
@@ -1002,7 +988,7 @@
     return status;
 }
 
-static int dereg_sm(st_hw_session_t *p_ses, bool capture_requested __unused)
+static int dereg_sm(st_hw_session_t *p_ses)
 {
     int status = 0, rc = 0;
     st_hw_session_gcs_t *p_gcs_ses = (st_hw_session_gcs_t *)p_ses;
@@ -1039,19 +1025,6 @@
         if (status)
             rc = status;
     }
-
-    if (p_ses->conf_levels_info) {
-        free(p_ses->conf_levels_info);
-        p_ses->conf_levels_info = NULL;
-    }
-
-    if (p_ses->conf_levels) {
-        free(p_ses->conf_levels);
-        p_ses->conf_levels = NULL;
-    }
-
-    p_ses->rc_config = NULL;
-
     return rc;
 }
 
@@ -1116,7 +1089,6 @@
 
 static int restart(st_hw_session_t *p_ses,
     unsigned int recognition_mode __unused,
-    bool capture_requested __unused,
     struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data)
 {
@@ -1159,7 +1131,7 @@
             goto exit;
         }
 
-        status = reg_sm_params(p_ses, recognition_mode, capture_requested,
+        status = reg_sm_params(p_ses, recognition_mode, p_ses->lab_enabled,
             rc_config, sm_type, sm_data);
         if (status) {
             ALOGE("%s: failed to reg_sm_params err %d", __func__, status);
@@ -1220,8 +1192,7 @@
     return status;
 }
 
-static int stop_buffering(st_hw_session_t *p_ses,
-    bool capture_requested)
+static int stop_buffering(st_hw_session_t *p_ses)
 {
     int status = 0;
     st_hw_session_gcs_t *p_hw_ses = (st_hw_session_gcs_t *)p_ses;
@@ -1240,7 +1211,7 @@
         }
     }
 
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         /* signal stop of bufferinng */
         ALOGV("%s: acquirung lock", __func__);
         pthread_mutex_lock(&p_hw_ses->lock);
@@ -1539,9 +1510,9 @@
         prepend_bytes =
             convert_ms_to_bytes(p_ses->vendor_uuid_info->kw_start_tolerance,
                 &p_ses->config);
-        if (p_ses->client_req_hist_buf) {
+        if (p_ses->sthw_cfg.client_req_hist_buf) {
             kw_duration_bytes =
-                convert_ms_to_bytes(p_ses->client_req_hist_buf, &p_ses->config);
+                convert_ms_to_bytes(p_ses->sthw_cfg.client_req_hist_buf, &p_ses->config);
         } else {
             kw_duration_bytes =
                 convert_ms_to_bytes(p_ses->vendor_uuid_info->kw_duration,
@@ -1610,7 +1581,7 @@
             pthread_mutex_unlock(&st_sec_stage->ss_session->lock);
         }
 
-        if (p_ses->enable_second_stage && !p_ses->client_req_hist_buf)
+        if (p_ses->enable_second_stage && !p_ses->sthw_cfg.client_req_hist_buf)
             p_hw_ses->move_client_ptr = true;
         else
             p_hw_ses->move_client_ptr = false;
@@ -1855,7 +1826,6 @@
     p_ses->fptrs = &fptrs_gcs;
     p_ses->stdev = stdev;
     p_ses->is_generic_event = false;
-    p_ses->client_req_det_mode = ST_HW_SESS_DET_UNKNOWN_MODE;
     p_hw_ses->nonpersistent_cal = NULL;
     p_hw_ses->detect_payload_size = 0;
     p_hw_ses->detection_signaled = false;
diff --git a/st_hw_session_lsm.c b/st_hw_session_lsm.c
index 1e9867a..a2c41c4 100644
--- a/st_hw_session_lsm.c
+++ b/st_hw_session_lsm.c
@@ -63,46 +63,45 @@
 #define DOA_POLAR_ACTIVITY_INDICATORS 360
 
 static int ape_reg_sm(st_hw_session_t* p_ses, void *sm_data,
-           sound_trigger_sound_model_type_t sm_type);
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type);
 static int ape_reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
     bool capture_requested, struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
 
-static int ape_dereg_sm(st_hw_session_t* p_ses, bool capture_requested);
+static int ape_dereg_sm(st_hw_session_t* p_ses);
 static int ape_dereg_sm_params(st_hw_session_t* p_ses);
 static int ape_start(st_hw_session_t* p_ses);
 static int ape_stop(st_hw_session_t* p_ses);
 static int ape_stop_buffering(st_hw_session_t* p_ses);
 
-static int cpe_reg_sm(st_hw_session_t* p_ses,void *sm_data, sound_trigger_sound_model_type_t sm_type);
+static int cpe_reg_sm(st_hw_session_t* p_ses,void *sm_data, unsigned int sm_size, sound_trigger_sound_model_type_t sm_type);
 static int cpe_reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
     bool capture_requested, struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
 
-static int cpe_dereg_sm(st_hw_session_t* p_ses, bool capture_requested);
+static int cpe_dereg_sm(st_hw_session_t* p_ses);
 static int cpe_dereg_sm_params(st_hw_session_t* p_ses);
 static int cpe_start(st_hw_session_t* p_ses);
 static int cpe_stop(st_hw_session_t* p_ses);
-static int cpe_stop_buffering(st_hw_session_t *p_ses, bool capture_requested);
+static int cpe_stop_buffering(st_hw_session_t *p_ses);
 
 /* Routing layer functions to route to either ADSP or CPE */
 static int route_reg_sm_ape(st_hw_session_t *p_ses,
-    void *sm_data, sound_trigger_sound_model_type_t sm_type);
+    void *sm_data, unsigned int sm_size, sound_trigger_sound_model_type_t sm_type);
 static int route_reg_sm_params_ape(st_hw_session_t* p_ses,
     unsigned int recognition_mode, bool capture_requested,
     struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
-static int route_dereg_sm_ape(st_hw_session_t* p_ses, bool capture_requested);
+static int route_dereg_sm_ape(st_hw_session_t* p_ses);
 static int route_dereg_sm_params_ape(st_hw_session_t* p_ses);
 static int route_restart_ape(st_hw_session_t* p_ses,
                              unsigned int recognition_mode,
-                             bool capture_requested,
                              struct sound_trigger_recognition_config *rc_config,
                              sound_trigger_sound_model_type_t sm_type,
                              void *sm_data);
 static int route_start_ape(st_hw_session_t* p_ses);
 static int route_stop_ape(st_hw_session_t* p_ses);
-static int route_stop_buffering_ape(st_hw_session_t* p_ses, bool capture_requested);
+static int route_stop_buffering_ape(st_hw_session_t* p_ses);
 static int route_set_device_ape(st_hw_session_t* p_ses,
                                 bool enable);
 static int route_read_pcm_ape(st_hw_session_t *p_ses,
@@ -112,19 +111,19 @@
 static int route_send_custom_chmix_coeff_ape(st_hw_session_t *p_ses, char *str);
 
 static int route_reg_sm_cpe(st_hw_session_t *p_ses,
-    void *sm_data, sound_trigger_sound_model_type_t sm_type);
+    void *sm_data, unsigned int sm_size, sound_trigger_sound_model_type_t sm_type);
 static int route_reg_sm_params_cpe(st_hw_session_t* p_ses,
     unsigned int recognition_mode, bool capture_requested,
     struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
-static int route_dereg_sm_cpe(st_hw_session_t* p_ses, bool capture_requested);
+static int route_dereg_sm_cpe(st_hw_session_t* p_ses);
 static int route_dereg_sm_params_cpe(st_hw_session_t* p_ses);
 static int route_start_cpe(st_hw_session_t* p_ses);
 static int route_restart(st_hw_session_t* p_ses, unsigned int recognition_mode,
-   bool capture_requested, struct sound_trigger_recognition_config *rc_config __unused,
+   struct sound_trigger_recognition_config *rc_config __unused,
    sound_trigger_sound_model_type_t sm_type, void *sm_data);
 static int route_stop_cpe(st_hw_session_t* p_ses);
-static int route_stop_buffering_cpe(st_hw_session_t* p_ses, bool capture_requested);
+static int route_stop_buffering_cpe(st_hw_session_t* p_ses);
 static int route_set_device_cpe(st_hw_session_t* p_ses,
                                 bool enable);
 static int route_read_pcm_cpe(st_hw_session_t *p_ses,
@@ -1013,13 +1012,15 @@
             convert_ms_to_bytes(
                 p_lsm_ses->common.vendor_uuid_info->kw_start_tolerance,
                 &p_lsm_ses->common.config);
-        if (p_lsm_ses->common.client_req_hist_buf) {
+        if (p_lsm_ses->common.sthw_cfg.client_req_hist_buf) {
             kw_duration_bytes =
-                convert_ms_to_bytes(p_lsm_ses->common.client_req_hist_buf,
+                convert_ms_to_bytes(
+                    p_lsm_ses->common.sthw_cfg.client_req_hist_buf,
                     &p_lsm_ses->common.config);
         } else {
             kw_duration_bytes =
-                convert_ms_to_bytes(p_lsm_ses->common.vendor_uuid_info->kw_duration,
+                convert_ms_to_bytes(
+                    p_lsm_ses->common.vendor_uuid_info->kw_duration,
                     &p_lsm_ses->common.config);
         }
 
@@ -1086,7 +1087,7 @@
         }
 
         if (p_lsm_ses->common.enable_second_stage &&
-            !p_lsm_ses->common.client_req_hist_buf)
+            !p_lsm_ses->common.sthw_cfg.client_req_hist_buf)
             p_lsm_ses->move_client_ptr = true;
         else
             p_lsm_ses->move_client_ptr = false;
@@ -1115,7 +1116,7 @@
             ALOGE("%s: pcm read failed status %d - %s", __func__, status,
                   pcm_get_error(p_lsm_ses->pcm));
             p_lsm_ses->exit_lab_processing = true;
-            p_lsm_ses->common.fptrs->stop_buffering(&p_lsm_ses->common, true);
+            p_lsm_ses->common.fptrs->stop_buffering(&p_lsm_ses->common);
             pcm_stop(p_lsm_ses->pcm);
             pcm_start(p_lsm_ses->pcm);
             break;
@@ -1359,7 +1360,7 @@
 exit:
     if (!p_ses->exit_lab_processing) {
         p_ses->exit_lab_processing = true;
-        p_ses->common.fptrs->stop_buffering(&p_ses->common, true);
+        p_ses->common.fptrs->stop_buffering(&p_ses->common);
     }
 
     ST_DBG_FILE_CLOSE(fptr_drv);
@@ -1396,13 +1397,15 @@
             convert_ms_to_bytes(
                 p_lsm_ses->common.vendor_uuid_info->kw_start_tolerance,
                 &p_lsm_ses->common.config);
-        if (p_lsm_ses->common.client_req_hist_buf) {
+        if (p_lsm_ses->common.sthw_cfg.client_req_hist_buf) {
             kw_duration_bytes =
-                convert_ms_to_bytes(p_lsm_ses->common.client_req_hist_buf,
+                convert_ms_to_bytes(
+                    p_lsm_ses->common.sthw_cfg.client_req_hist_buf,
                     &p_lsm_ses->common.config);
         } else {
             kw_duration_bytes =
-                convert_ms_to_bytes(p_lsm_ses->common.vendor_uuid_info->kw_duration,
+                convert_ms_to_bytes(
+                    p_lsm_ses->common.vendor_uuid_info->kw_duration,
                     &p_lsm_ses->common.config);
         }
 
@@ -1472,7 +1475,7 @@
         }
 
         if (p_lsm_ses->common.enable_second_stage &&
-            !p_lsm_ses->common.client_req_hist_buf)
+            !p_lsm_ses->common.sthw_cfg.client_req_hist_buf)
             p_lsm_ses->move_client_ptr = true;
         else
             p_lsm_ses->move_client_ptr = false;
@@ -1524,6 +1527,7 @@
     }
 
     ape_stop_buffering(&p_lsm_ses->common);
+    p_lsm_ses->lab_on_detection = false;
     p_lsm_ses->lab_processing_active = false;
     pthread_cond_broadcast(&p_lsm_ses->cond);
     pthread_mutex_unlock(&p_lsm_ses->lock);
@@ -1648,6 +1652,9 @@
             hw_sess_event.payload.detected.payload_size = params->payload_size;
         }
 
+        if (p_lsm_ses->common.lab_enabled)
+            p_lsm_ses->lab_on_detection = true;
+
         ST_DBG_DECLARE(FILE *detect_fd = NULL; static int detect_fd_cnt = 0);
         ST_DBG_FILE_OPEN_WR(detect_fd, ST_DEBUG_DUMP_LOCATION,
                             "lsm_detection_event", "bin", detect_fd_cnt++);
@@ -1854,8 +1861,9 @@
     unsigned int rt_bytes_one_sec;
 
     p_lsm_ses->lab_drv_buf_size = pcm_frames_to_bytes(p_lsm_ses->pcm,
-                                                      p_lsm_ses->common.config.period_size);
-    p_lsm_ses->lab_drv_buf = (unsigned char *)calloc(1, p_lsm_ses->lab_drv_buf_size);
+        p_lsm_ses->common.config.period_size);
+    p_lsm_ses->lab_drv_buf = (unsigned char *)calloc(1,
+        p_lsm_ses->lab_drv_buf_size);
     if (!p_lsm_ses->lab_drv_buf) {
         ALOGE("%s: ERROR. Can not allocate lab buffer size %d", __func__,
             p_lsm_ses->lab_drv_buf_size);
@@ -1866,13 +1874,13 @@
         __func__, p_lsm_ses->lab_drv_buf_size);
 
     rt_bytes_one_sec = (p_lsm_ses->common.config.rate *
-                        p_lsm_ses->common.config.channels *
-                        (pcm_format_to_bits(p_lsm_ses->common.config.format) >> 3));
+        p_lsm_ses->common.config.channels *
+        (pcm_format_to_bits(p_lsm_ses->common.config.format) >> 3));
 
-    if ((p_lsm_ses->common.client_req_hist_buf +
-         p_lsm_ses->common.client_req_preroll) > v_info->kw_duration) {
-        circ_buff_sz = ((p_lsm_ses->common.client_req_hist_buf +
-            p_lsm_ses->common.client_req_preroll +
+    if ((p_lsm_ses->common.sthw_cfg.client_req_hist_buf +
+         p_lsm_ses->common.sthw_cfg.client_req_preroll) > v_info->kw_duration) {
+        circ_buff_sz = ((p_lsm_ses->common.sthw_cfg.client_req_hist_buf +
+            p_lsm_ses->common.sthw_cfg.client_req_preroll +
             v_info->client_capture_read_delay) * rt_bytes_one_sec) / 1000;
     } else {
         circ_buff_sz = ((v_info->kw_duration +
@@ -1918,12 +1926,10 @@
     profile_type = get_profile_type(p_ses);
     if (enable) {
         status = platform_stdev_check_and_set_codec_backend_cfg(
-                                                 p_ses->stdev->platform,
-                                                 v_info,
-                                                 &backend_cfg_change,
-                                                 p_ses->stdev->lpi_enable,
-                                                 p_ses->stdev->vad_enable,
-                                                 p_ses->client_req_preroll);
+            p_ses->stdev->platform, v_info, &backend_cfg_change,
+            p_ses->stdev->lpi_enable, p_ses->stdev->vad_enable,
+            p_ses->sthw_cfg.client_req_preroll);
+
         if (status) {
             ALOGE("%s: ERROR. codec backend config update failed, status=%d",
                 __func__, status);
@@ -2032,15 +2038,11 @@
 }
 
 static int ape_reg_sm(st_hw_session_t *p_ses, void *sm_data,
-    sound_trigger_sound_model_type_t sm_type)
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type __unused)
 {
     int status = 0, param_count = 0, stage_idx = 0;
     st_hw_session_lsm_t *p_lsm_ses = (st_hw_session_lsm_t*)p_ses;
     struct st_vendor_info *v_info = p_ses->vendor_uuid_info;
-    struct sound_trigger_phrase_sound_model *phrase_sm =
-       (struct sound_trigger_phrase_sound_model*)sm_data;
-    struct sound_trigger_sound_model *common_sm=
-       (struct sound_trigger_sound_model*)sm_data;
     struct snd_lsm_session_data ses_data;
     struct snd_lsm_module_params lsm_params;
     lsm_param_info_t param_info[LSM_MAX_STAGES_PER_SESSION];
@@ -2051,7 +2053,7 @@
     struct st_module_param_info *mparams = NULL;
     audio_devices_t capture_device = 0;
 
-    ALOGD("%s:[%d] Enter vinfo %p", __func__, p_ses->sm_handle, v_info);
+    ALOGD("%s:[%d] Enter", __func__, p_ses->sm_handle);
 
     p_lsm_ses->pcm_id = platform_ape_get_pcm_device_id(p_ses->stdev->platform,
         &p_ses->use_case_idx);
@@ -2172,16 +2174,9 @@
     stage_idx = LSM_STAGE_INDEX_FIRST;
     lsm_params.params = (unsigned char*)&param_info[0];
     mparams = p_lsm_ses->lsm_usecase->params;
+    param_info[param_count].param_data = sm_data;
+    param_info[param_count].param_size = sm_size;
 
-    if (sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        param_info[param_count].param_size = phrase_sm->common.data_size;
-        param_info[param_count].param_data =
-            (unsigned char*)phrase_sm + phrase_sm->common.data_offset;
-    } else {
-        param_info[param_count].param_size = common_sm->data_size;
-        param_info[param_count].param_data =
-            (unsigned char*)common_sm + common_sm->data_offset;
-    }
     lsm_fill_param_info(LSM_REG_SND_MODEL, &param_info[param_count++],
                         &mparams[LOAD_SOUND_MODEL], stage_idx);
 
@@ -2258,7 +2253,7 @@
     return status;
 }
 
-static int ape_dereg_sm(st_hw_session_t *p_ses, bool capture_requested)
+static int ape_dereg_sm(st_hw_session_t *p_ses)
 {
     int status = 0, buf_en = 0;
     struct snd_lsm_module_params lsm_params;
@@ -2271,18 +2266,6 @@
 
     ALOGD("%s:[%d] Enter", __func__, p_lsm_ses->common.sm_handle);
 
-    if (p_ses->conf_levels_info) {
-        free(p_ses->conf_levels_info);
-        p_ses->conf_levels_info = NULL;
-    }
-
-    if (p_ses->conf_levels) {
-        free(p_ses->conf_levels);
-        p_ses->conf_levels = NULL;
-    }
-
-    p_ses->rc_config = NULL;
-
     if (!p_lsm_ses->pcm) {
         ALOGV("%s: pcm NULL", __func__);
         return status;
@@ -2303,7 +2286,7 @@
     mparams = p_lsm_ses->lsm_usecase->params;
 
     /* reset last stage only if lab capture was set */
-    if ((stage_idx == p_lsm_ses->num_stages - 1) && capture_requested &&
+    if ((stage_idx == p_lsm_ses->num_stages - 1) && p_ses->lab_enabled &&
         (p_lsm_ses->lsm_usecase->param_tag_tracker & PARAM_LAB_CONTROL_BIT)) {
         param_info[param_count].param_size = sizeof(buf_en);
         param_info[param_count].param_data = (unsigned char *)&buf_en;
@@ -2316,7 +2299,7 @@
         ss_cfg = node_to_item(node, st_lsm_ss_config_t, list_node);
         mparams = ss_cfg->params->params;
         /* reset last stage only if lab capture was set */
-        if ((stage_idx == p_lsm_ses->num_stages) && (!capture_requested))
+        if ((stage_idx == p_lsm_ses->num_stages) && (!p_ses->lab_enabled))
             break;
 
             param_info[param_count].param_size = sizeof(buf_en);
@@ -2335,7 +2318,7 @@
                     __func__, status);
     }
 
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         /*
          * Check and reset lab if ses was non-multi-stage,
          * and lab control param bit was not set.
@@ -2395,8 +2378,9 @@
     return status;
 }
 
-static int ape_reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
-    bool capture_requested, struct sound_trigger_recognition_config *rc_config,
+static int ape_reg_sm_params(st_hw_session_t* p_ses,
+    unsigned int recognition_mode, bool capture_requested,
+    struct sound_trigger_recognition_config *rc_config __unused,
     sound_trigger_sound_model_type_t sm_type __unused, void *sm_data __unused)
 {
     int status = 0, buf_en = 1, retry_num = 0, offset = 0;
@@ -2460,8 +2444,8 @@
     if (status)
         goto error_exit;
 
-    if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-        p_ses->vendor_uuid_info->is_qcva_uuid && !capture_requested)
+    if ((p_ses->sthw_cfg.custom_data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
+        v_info->is_qcva_uuid && !capture_requested)
         disable_custom_config = true;
 
     ATRACE_BEGIN("sthal:lsm: pcm_start");
@@ -2490,6 +2474,8 @@
         ALOGE("%s: Unknown recognition mode %d", __func__, recognition_mode);
         goto error_exit_1;
     }
+    ALOGV("%s: st recogntion_mode %d, dsp det_mode %d", __func__,
+          recognition_mode, det_mode.mode);
 
     stage_idx = LSM_STAGE_INDEX_FIRST;
     param_count = 0;
@@ -2498,18 +2484,18 @@
     mparams = p_lsm_ses->lsm_usecase->params;
 
     /*
-     * If smlib is not present, pass only the opaque data as custom params and
+     * For other than QTI VA, pass only the opaque data as custom params and
      * ignore sending all other params
      */
-    if (v_info->smlib_handle) {
+    if (v_info->is_qcva_uuid || v_info->is_qcmd_uuid) {
         det_mode.detect_failure = p_ses->stdev->detect_failure;
         ALOGV("%s: dm %d df %d lab %d", __func__,
                det_mode.mode, det_mode.detect_failure, capture_requested);
         if (param_tag_tracker & PARAM_CONFIDENCE_LEVELS_BIT) {
             /* fill confidence level params */
             cfl_params = &param_info[param_count++];
-            cfl_params->param_size = p_ses->num_conf_levels;
-            cfl_params->param_data = p_ses->conf_levels;
+            cfl_params->param_size = p_ses->sthw_cfg.num_conf_levels;
+            cfl_params->param_data = p_ses->sthw_cfg.conf_levels;
             lsm_fill_param_info(LSM_MIN_CONFIDENCE_LEVELS, cfl_params,
                                 &mparams[CONFIDENCE_LEVELS], stage_idx);
             {
@@ -2541,7 +2527,7 @@
      * Custom config is mandatory for adsp multi-stage session,
      * Default config would be sent if not explicitly set from client applicaiton.
      */
-    if ((rc_config->data_size && !disable_custom_config) ||
+    if ((p_ses->sthw_cfg.custom_data_size && !disable_custom_config) ||
         !list_empty(&p_ses->lsm_ss_cfg_list)) {
         /* fill opaque data as custom params */
         cus_params = &param_info[param_count++];
@@ -2600,29 +2586,34 @@
             }
 
             /* copy opaque data from recognition config to payload */
-            if (((rc_config->data_size == 0) ||
-                 (rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE)) &&
-                p_ses->vendor_uuid_info->is_qcva_uuid) {
-                st_hw_ses_get_hist_buff_payload(p_ses, (uint8_t *)custom_payload + offset,
-                                                custom_payload_size - offset);
+            if (v_info->is_qcva_uuid &&
+                ((p_ses->sthw_cfg.custom_data_size == 0) ||
+                 (p_ses->sthw_cfg.custom_data_size >
+                  CUSTOM_CONFIG_OPAQUE_DATA_SIZE))) {
+                st_hw_ses_get_hist_buff_payload(p_ses,
+                    (uint8_t *)custom_payload + offset,
+                    custom_payload_size - offset);
             } else {
                 /* copy opaque data from recognition config to payload */
                 memcpy((char *)custom_payload + offset,
-                    (char *)rc_config + rc_config->data_offset, rc_config->data_size);
+                    p_ses->sthw_cfg.custom_data,
+                    p_ses->sthw_cfg.custom_data_size);
             }
         } else {
             /*
              * Send opaque data as it is,
-             * Using legacy custom param where app needs to form appropriate payload.
+             * Using legacy custom param where app needs to form appropriate
+             * payload.
              */
-            custom_payload_size = rc_config->data_size;
-            custom_payload = (unsigned char *)calloc(1, custom_payload_size);
+            custom_payload_size = p_ses->sthw_cfg.custom_data_size;
+            custom_payload = calloc(1, custom_payload_size);
             if (!custom_payload) {
-                ALOGE("%s: ERROR. Cannot allocate memory for custom_payload", __func__);
+                ALOGE("%s: ERROR. Cannot allocate memory for custom_payload",
+                      __func__);
                 goto error_exit_1;
             }
-            memcpy(custom_payload, (char *)rc_config + rc_config->data_offset,
-                rc_config->data_size);
+            memcpy(custom_payload, p_ses->sthw_cfg.custom_data,
+                p_ses->sthw_cfg.custom_data_size);
         }
         cus_params->param_size = custom_payload_size;
         cus_params->param_data = (unsigned char *)custom_payload;
@@ -2844,7 +2835,7 @@
                 goto error_exit_1;
         }
     }
-
+    p_ses->lab_enabled = capture_requested;
     return status;
 
 error_exit_1:
@@ -2971,12 +2962,11 @@
     return status;
 }
 
-static int cpe_reg_sm(st_hw_session_t *p_ses, void* sm_data,
-    sound_trigger_sound_model_type_t sm_type)
+static int cpe_reg_sm(st_hw_session_t *p_ses,
+    void* sm_data, unsigned int sm_size,
+    sound_trigger_sound_model_type_t sm_type __unused)
 {
     int status = 0, fd;
-    struct sound_trigger_phrase_sound_model *phrase_sm = sm_data;
-    struct sound_trigger_sound_model *common_sm= sm_data;
     struct snd_lsm_module_params lsm_params;
     struct lsm_params_info param_info;
     struct st_vendor_info *v_info = p_ses->vendor_uuid_info;
@@ -3080,16 +3070,8 @@
     param_info.param_type = LSM_REG_SND_MODEL;
     param_info.module_id = mparams[LOAD_SOUND_MODEL].module_id;
     param_info.param_id = mparams[LOAD_SOUND_MODEL].param_id;
-
-    if (sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        param_info.param_size = phrase_sm->common.data_size;
-        param_info.param_data =
-            (unsigned char*)phrase_sm + phrase_sm->common.data_offset;
-    } else {
-        param_info.param_size = common_sm->data_size;
-        param_info.param_data =
-            (unsigned char*)common_sm + common_sm->data_offset;
-    }
+    param_info.param_data = sm_data;
+    param_info.param_size = sm_size;
 
     lsm_params.num_params = 1;
     lsm_params.params = (unsigned char*)&param_info;
@@ -3131,7 +3113,7 @@
     return status;
 }
 
-static int cpe_dereg_sm(st_hw_session_t *p_ses, bool capture_requested)
+static int cpe_dereg_sm(st_hw_session_t *p_ses)
 {
     int status = 0, buf_en;
     struct snd_lsm_module_params lsm_params;
@@ -3141,18 +3123,6 @@
 
     ALOGD("%s:[%d] Enter", __func__, p_lsm_ses->common.sm_handle);
 
-    if (p_ses->conf_levels_info) {
-        free(p_ses->conf_levels_info);
-        p_ses->conf_levels_info = NULL;
-    }
-
-    if (p_ses->conf_levels) {
-        free(p_ses->conf_levels);
-        p_ses->conf_levels = NULL;
-    }
-
-    p_ses->rc_config = NULL;
-
     if (!p_lsm_ses->pcm) {
         ALOGV("%s: pcm NULL", __func__);
         return status;
@@ -3164,7 +3134,7 @@
     /* note see note in ape_dereg_sm */
     pthread_join(p_lsm_ses->callback_thread, (void **) NULL);
 
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         buf_en = 0;
         ATRACE_BEGIN("sthal:lsm: pcm_ioctl sndrv_lsm_lab_control");
         status = pcm_ioctl(p_lsm_ses->pcm, SNDRV_LSM_LAB_CONTROL, &buf_en);
@@ -3209,8 +3179,9 @@
     return status;
 }
 
-static int cpe_reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
-    bool capture_requested, struct sound_trigger_recognition_config *rc_config,
+static int cpe_reg_sm_params(st_hw_session_t* p_ses,
+    unsigned int recognition_mode, bool capture_requested,
+    struct sound_trigger_recognition_config *rc_config __unused,
     sound_trigger_sound_model_type_t sm_type, void *sm_data __unused)
 {
     int status = 0, idx = 0;
@@ -3248,8 +3219,8 @@
         return status;
     }
 
-    if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-        p_ses->vendor_uuid_info->is_qcva_uuid && !capture_requested)
+    if (!capture_requested && v_info->is_qcva_uuid &&
+        (p_ses->sthw_cfg.custom_data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE))
         disable_custom_config = true;
 
     ATRACE_BEGIN("sthal:lsm: pcm_start");
@@ -3287,24 +3258,24 @@
     param_tag_tracker = p_lsm_ses->lsm_usecase->param_tag_tracker;
 
     /*
-     * If this is generic sound model or smlib is not present, pass only the
+     * If this is generic sound model or other than QTI VA, pass only the
      * opaque data as custom params and ignore sending all other params.
      */
-    if (v_info->smlib_handle &&
+    if ((v_info->is_qcva_uuid || v_info->is_qcmd_uuid) &&
         (sm_type == SOUND_MODEL_TYPE_KEYPHRASE)) {
         det_mode.detect_failure = p_ses->stdev->detect_failure;
         ALOGV("%s: dm %d df %d lab %d", __func__,
                det_mode.mode, det_mode.detect_failure, capture_requested);
 
         if ((param_tag_tracker & PARAM_CONFIDENCE_LEVELS_BIT) &&
-             p_ses->conf_levels) {
+             p_ses->sthw_cfg.conf_levels) {
             /* fill confidence level params if present */
             cfl_params = &param_info[idx++];
             cfl_params->param_type = LSM_MIN_CONFIDENCE_LEVELS;
             cfl_params->module_id = mparams[CONFIDENCE_LEVELS].module_id;
             cfl_params->param_id = mparams[CONFIDENCE_LEVELS].param_id;
-            cfl_params->param_size = p_ses->num_conf_levels;
-            cfl_params->param_data = p_ses->conf_levels;
+            cfl_params->param_size = p_ses->sthw_cfg.num_conf_levels;
+            cfl_params->param_data = p_ses->sthw_cfg.conf_levels;
             lsm_params.num_params++;
             {
                 unsigned int i;
@@ -3327,7 +3298,7 @@
         }
     }
 
-    if (rc_config->data_size && !disable_custom_config) {
+    if (p_ses->sthw_cfg.custom_data_size && !disable_custom_config) {
         /* fill opaque data as custom params */
         cus_params = &param_info[idx++];
         if (param_tag_tracker & PARAM_CUSTOM_CONFIG_BIT) {
@@ -3348,29 +3319,31 @@
             memcpy(custom_payload, &custom_conf_params, sizeof(struct lsm_param_payload));
             offset += sizeof(struct lsm_param_payload);
             /* copy opaque data from recognition config to payload */
-            if (((rc_config->data_size == 0) ||
-               (rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE)) &&
-               p_ses->vendor_uuid_info->is_qcva_uuid) {
-               st_hw_ses_get_hist_buff_payload(p_ses, (uint8_t *)custom_payload + offset,
-                                               custom_payload_size - offset);
+            if (v_info->is_qcva_uuid &&
+                ((p_ses->sthw_cfg.custom_data_size == 0) ||
+                 (p_ses->sthw_cfg.custom_data_size >
+                  CUSTOM_CONFIG_OPAQUE_DATA_SIZE))) {
+               st_hw_ses_get_hist_buff_payload(p_ses,
+                   (uint8_t *)custom_payload + offset,
+                   custom_payload_size - offset);
             } else {
               /* copy opaque data from recognition config to payload */
               memcpy((char *)custom_payload + offset,
-                   (char *)rc_config + rc_config->data_offset, rc_config->data_size);
+                    p_ses->sthw_cfg.custom_data, p_ses->sthw_cfg.custom_data_size);
             }
         } else {
-             /*
+            /*
              * Send opaque data as it is,
              * Using legacy custom param where app needs to form appropriate payload.
              */
-             custom_payload_size = rc_config->data_size;
-             custom_payload = (unsigned char *)calloc(1, custom_payload_size);
-             if (!custom_payload) {
-                 ALOGE("%s: ERROR. Cannot allocate memory for custom_payload", __func__);
-                 goto error_exit;
-             }
-             memcpy(custom_payload, (char *)rc_config + rc_config->data_offset,
-             rc_config->data_size);
+            custom_payload_size = p_ses->sthw_cfg.custom_data_size;
+            custom_payload = calloc(1, custom_payload_size);
+            if (!custom_payload) {
+                ALOGE("%s: ERROR. Cannot allocate memory for custom_payload", __func__);
+                goto error_exit;
+            }
+            memcpy(custom_payload, p_ses->sthw_cfg.custom_data,
+                p_ses->sthw_cfg.custom_data_size);
         }
         cus_params->param_type = LSM_CUSTOM_PARAMS;
         cus_params->param_size = custom_payload_size;
@@ -3406,7 +3379,7 @@
         if (status)
             goto err_exit_1;
     }
-
+    p_ses->lab_enabled = capture_requested;
     ALOGD("%s:[%d] Exit, status=%d", __func__, p_lsm_ses->common.sm_handle, status);
     return 0;
 error_exit:
@@ -3489,7 +3462,7 @@
     return status;
 }
 
-static int cpe_stop_buffering(st_hw_session_t* p_ses, bool capture_requested)
+static int cpe_stop_buffering(st_hw_session_t* p_ses)
 {
     int status = 0;
     st_hw_session_lsm_t *p_lsm_ses =
@@ -3516,7 +3489,7 @@
             ALOGE("%s: ERROR. SNDRV_PCM_IOCTL_RESET failed status %d",
                __func__, status);
     }
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         pthread_mutex_lock(&p_lsm_ses->lock);
         pthread_cond_broadcast(&p_lsm_ses->cond);
         pthread_mutex_unlock(&p_lsm_ses->lock);
@@ -3527,9 +3500,9 @@
 }
 
 static int route_reg_sm_ape(st_hw_session_t *p_ses,void *sm_data,
-        sound_trigger_sound_model_type_t sm_type)
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type)
 {
-    return ape_reg_sm(p_ses, sm_data, sm_type);
+    return ape_reg_sm(p_ses, sm_data, sm_size, sm_type);
 }
 
 static int route_reg_sm_params_ape(st_hw_session_t* p_ses,
@@ -3546,12 +3519,11 @@
     return ape_dereg_sm_params(p_ses);
 }
 
-static int route_dereg_sm_ape(st_hw_session_t* p_ses,
-    bool capture_requested)
+static int route_dereg_sm_ape(st_hw_session_t* p_ses)
 {
     int status = 0;
 
-    status = ape_dereg_sm(p_ses, capture_requested);
+    status = ape_dereg_sm(p_ses);
 
     return status;
 }
@@ -3564,7 +3536,6 @@
 
 static int route_restart_ape(st_hw_session_t* p_ses,
                              unsigned int recognition_mode __unused,
-                             bool capture_requested __unused,
                              struct sound_trigger_recognition_config *rc_config __unused,
                              sound_trigger_sound_model_type_t sm_type __unused,
                              void *sm_data __unused)
@@ -3582,7 +3553,7 @@
     return ape_stop(p_ses);
 }
 
-static int route_stop_buffering_ape(st_hw_session_t* p_ses, bool capture_requested __unused)
+static int route_stop_buffering_ape(st_hw_session_t* p_ses)
 {
     int status = 0;
     st_hw_session_lsm_t *p_lsm_ses = (st_hw_session_lsm_t *)p_ses;
@@ -3630,6 +3601,11 @@
             break;
         }
     }
+    if (p_lsm_ses->lab_on_detection) {
+        ape_stop_buffering(&p_lsm_ses->common);
+        p_lsm_ses->lab_on_detection = false;
+    }
+
     pthread_mutex_unlock(&p_lsm_ses->lock);
 
     return status;
@@ -3675,9 +3651,9 @@
 }
 
 static int route_reg_sm_cpe(st_hw_session_t *p_ses, void* sm_data,
-    sound_trigger_sound_model_type_t sm_type)
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type)
 {
-    return cpe_reg_sm(p_ses, sm_data, sm_type);
+    return cpe_reg_sm(p_ses, sm_data, sm_size, sm_type);
 }
 
 static int route_reg_sm_params_cpe(st_hw_session_t* p_ses,
@@ -3694,9 +3670,9 @@
     return cpe_dereg_sm_params(p_ses);
 }
 
-static int route_dereg_sm_cpe(st_hw_session_t* p_ses, bool capture_requested)
+static int route_dereg_sm_cpe(st_hw_session_t* p_ses)
 {
-    return cpe_dereg_sm(p_ses, capture_requested);
+    return cpe_dereg_sm(p_ses);
 }
 
 static int route_start_cpe(st_hw_session_t* p_ses)
@@ -3705,7 +3681,7 @@
 }
 
 static int route_restart(st_hw_session_t* p_ses __unused, unsigned int recognition_mode __unused,
-   bool capture_requested __unused, struct sound_trigger_recognition_config *rc_config __unused,
+   struct sound_trigger_recognition_config *rc_config __unused,
    sound_trigger_sound_model_type_t sm_type __unused, void *sm_data __unused)
 {
     /*
@@ -3720,8 +3696,7 @@
     return cpe_stop(p_ses);
 }
 
-static int route_stop_buffering_cpe(st_hw_session_t* p_ses,
-    bool capture_requested)
+static int route_stop_buffering_cpe(st_hw_session_t* p_ses)
 {
     int status = 0;
     st_arm_second_stage_t *st_sec_stage;
@@ -3742,7 +3717,7 @@
     }
 
     check_and_exit_lab(p_ses);
-    status = cpe_stop_buffering(p_ses, capture_requested);
+    status = cpe_stop_buffering(p_ses);
 
     CLEAR_STATE(p_ses->state, SES_BUFFERING);
     return status;
@@ -4147,7 +4122,6 @@
     p_ses->sm_handle = sm_handle;
     p_ses->stdev = stdev;
     p_ses->is_generic_event = false;
-    p_ses->client_req_det_mode = ST_HW_SESS_DET_UNKNOWN_MODE;
 
     p_lsm_ses->exit_lab_processing = false;
     p_lsm_ses->lab_processing_active = false;
diff --git a/st_hw_session_lsm.h b/st_hw_session_lsm.h
index 69fab9a..bbb6b63 100644
--- a/st_hw_session_lsm.h
+++ b/st_hw_session_lsm.h
@@ -185,6 +185,7 @@
     pthread_mutex_t callback_thread_lock;
 
     bool lab_buffers_allocated;
+    bool lab_on_detection;
 
     void *adpcm_dec_state;
 
diff --git a/st_hw_session_pcm.c b/st_hw_session_pcm.c
index 8ec3e3c..a8592e8 100644
--- a/st_hw_session_pcm.c
+++ b/st_hw_session_pcm.c
@@ -73,23 +73,23 @@
 } st_get_param_payload_t;
 
 static int reg_sm(st_hw_session_t* p_ses, void *sm_data,
-           sound_trigger_sound_model_type_t sm_type);
+    unsigned int sm_size,
+    sound_trigger_sound_model_type_t sm_type);
 static int reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
     bool capture_requested, struct sound_trigger_recognition_config *rc_config,
     sound_trigger_sound_model_type_t sm_type, void *sm_data);
 
-static int dereg_sm(st_hw_session_t* p_ses, bool capture_requested);
+static int dereg_sm(st_hw_session_t* p_ses);
 static int dereg_sm_params(st_hw_session_t* p_ses);
 static int start(st_hw_session_t* p_ses);
 static int stop(st_hw_session_t* p_ses);
-static int stop_buffering(st_hw_session_t* p_ses, bool capture_requested);
+static int stop_buffering(st_hw_session_t* p_ses);
 static int set_device(st_hw_session_t *p_ses,
     bool enable);
 static int disable_device(st_hw_session_t *p_ses, bool setting_device);
 static int enable_device(st_hw_session_t *p_ses, bool setting_device);
 static void process_lab_capture(st_hw_session_t *p_ses);
 static int restart(st_hw_session_t* p_ses, unsigned int recognition_mode,
-    bool capture_requested,
     struct sound_trigger_recognition_config *rc_config __unused,
     sound_trigger_sound_model_type_t sm_type, void *sm_data __unused);
 static int read_pcm(st_hw_session_t *p_ses,
@@ -1214,7 +1214,7 @@
 }
 
 static int reg_sm(st_hw_session_t *p_ses, void *sm_data,
-    sound_trigger_sound_model_type_t sm_type __unused)
+    unsigned int sm_size, sound_trigger_sound_model_type_t sm_type __unused)
 {
     int status = 0;
     st_hw_session_pcm_t *p_pcm_ses =
@@ -1225,10 +1225,6 @@
     int frame_len;
     int sample_rate;
     const char *config_file_path = ST_FFV_CONFIG_FILE_PATH;
-    struct sound_trigger_phrase_sound_model *phrase_sm =
-       (struct sound_trigger_phrase_sound_model*)sm_data;
-    char* sm_buffer = (char *)((unsigned char*)phrase_sm + phrase_sm->common.data_offset);
-    uint32_t sm_size = phrase_sm->common.data_size;
     FfvStatusType status_type;
     EspStatusType esp_status_type;
     int total_mem_size;
@@ -1334,7 +1330,7 @@
     ALOGD("%s: LICENSE[%s] key[%d]", __func__, product_license, product_id);
 
     status_type = ffv_init_fn(&p_pcm_ses->handle, num_tx_in_ch, num_out_ch, num_ec_ref_ch,
-                      frame_len, sample_rate, config_file_path, sm_buffer, sm_size,
+                      frame_len, sample_rate, config_file_path, (char *)sm_data, sm_size,
                       &total_mem_size, product_id, product_license);
     if (status_type) {
         ALOGE("%s: ERROR. ffv_init returned %d", __func__, status_type);
@@ -1400,7 +1396,7 @@
     return status;
 }
 
-static int dereg_sm(st_hw_session_t *p_ses, bool capture_requested)
+static int dereg_sm(st_hw_session_t *p_ses)
 {
     int status = 0;
     st_hw_session_pcm_t *p_pcm_ses =
@@ -1444,7 +1440,7 @@
                                     false);
 
     /* Deallocate buffers allocated during start_recognition */
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         if (p_pcm_ses->lab_buffers_allocated) {
             deallocate_lab_buffers(p_pcm_ses);
         }
@@ -1478,6 +1474,7 @@
         if (!p_pcm_ses->lab_buffers_allocated)
             status = allocate_lab_buffers(p_pcm_ses);
     }
+    p_ses->lab_enabled = capture_requested;
 
 error:
     ALOGD("%s:[%d] Exit, status=%d", __func__,
@@ -1645,7 +1642,7 @@
     return status;
 }
 
-static int pcm_stop_buffering(st_hw_session_t* p_ses, bool capture_requested)
+static int pcm_stop_buffering(st_hw_session_t* p_ses)
 {
     int status = 0;
     st_hw_session_pcm_t *p_pcm_ses =
@@ -1654,7 +1651,7 @@
     ALOGD("%s:[%d] Enter pcm %p", __func__, p_pcm_ses->common.sm_handle,
         p_pcm_ses->pcm);
 
-    if (capture_requested) {
+    if (p_ses->lab_enabled) {
         pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
         pthread_cond_broadcast(&p_pcm_ses->lab_out_buf_cond);
         pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
@@ -1679,7 +1676,7 @@
     }
 }
 
-static int stop_buffering(st_hw_session_t* p_ses, bool capture_requested)
+static int stop_buffering(st_hw_session_t* p_ses)
 {
     st_hw_session_pcm_t *p_pcm_ses =
        (st_hw_session_pcm_t *)p_ses;
@@ -1693,14 +1690,13 @@
     }
 
     check_and_exit_lab(p_ses);
-    status = pcm_stop_buffering(p_ses, capture_requested);
+    status = pcm_stop_buffering(p_ses);
     CLEAR_STATE(p_ses->state, SES_BUFFERING);
 
     return status;
 }
 
 static int restart(st_hw_session_t* p_ses, unsigned int recognition_mode __unused,
-   bool capture_requested __unused,
    struct sound_trigger_recognition_config *rc_config __unused,
    sound_trigger_sound_model_type_t sm_type __unused, void *sm_data __unused)
 {
diff --git a/st_second_stage.c b/st_second_stage.c
index 1090513..e153e39 100644
--- a/st_second_stage.c
+++ b/st_second_stage.c
@@ -33,7 +33,7 @@
  * 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 "sound_trigger_hw"
+#define LOG_TAG "sound_trigger_hw:ss"
 #define ATRACE_TAG (ATRACE_TAG_HAL)
 /* #define LOG_NDEBUG 0 */
 #define LOG_NDDEBUG 0
@@ -227,7 +227,8 @@
             kw_start_ms, kw_end_ms);
     } else {
         ss_session->det_status = KEYWORD_DETECTION_REJECT;
-        ALOGD("%s: Detection reject", __func__);
+        ALOGD("%s: Detection reject, confidence level = %d", __func__,
+            ss_session->confidence_score);
     }
     /* Signal aggregator thread with detection result */
     pthread_cond_signal(&ss_session->st_ses->ss_detections_cond);
@@ -362,8 +363,8 @@
     capi_uv_ptr.actual_data_len = sizeof(voiceprint2_sva_uv_score_t);
     capi_uv_ptr.max_data_len = sizeof(voiceprint2_sva_uv_score_t);
 
-    ALOGV("%s: Issuing capi_set_param for param %d", __func__,
-        VOICEPRINT2_ID_SVA_UV_SCORE);
+    ALOGV("%s: Issuing capi_set_param for param %d, uv_conf_score %f", __func__,
+        VOICEPRINT2_ID_SVA_UV_SCORE, uv_cfg_ptr->sva_uv_confidence_score);
     rc = ss_session->capi_handle->vtbl_ptr->set_param(ss_session->capi_handle,
         VOICEPRINT2_ID_SVA_UV_SCORE, NULL, &capi_uv_ptr);
     if (CAPI_V2_EOK != rc) {
@@ -429,7 +430,8 @@
             ss_session->confidence_score);
     } else {
         ss_session->det_status = USER_VERIFICATION_REJECT;
-        ALOGD("%s: Detection reject", __func__);
+        ALOGD("%s: Detection reject, confidence level = %d", __func__,
+            ss_session->confidence_score);
     }
     /* Signal aggregator thread with detection result */
     pthread_cond_signal(&ss_session->st_ses->ss_detections_cond);
@@ -721,8 +723,10 @@
 {
     if (st_sec_stage) {
         if (st_sec_stage->ss_session) {
-            dlclose(st_sec_stage->ss_session->capi_lib_handle);
-            st_sec_stage->ss_session->capi_lib_handle = NULL;
+            if (st_sec_stage->ss_session->capi_lib_handle) {
+                dlclose(st_sec_stage->ss_session->capi_lib_handle);
+                st_sec_stage->ss_session->capi_lib_handle = NULL;
+            }
             if (st_sec_stage->ss_session->capi_handle) {
                 st_sec_stage->ss_session->capi_handle->vtbl_ptr = NULL;
                 free(st_sec_stage->ss_session->capi_handle);
@@ -762,7 +766,7 @@
 
     if ((st_sec_stage->ss_info->sm_detection_type ==
          ST_SM_TYPE_USER_VERIFICATION) &&
-        !st_sec_stage->ss_session->st_ses->stdev->ssr_offline_received) {
+        !st_sec_stage->stdev->ssr_offline_received) {
         ALOGV("%s: Issuing capi_end", __func__);
         rc = st_sec_stage->ss_session->capi_handle->vtbl_ptr->end(
             st_sec_stage->ss_session->capi_handle);
diff --git a/st_second_stage.h b/st_second_stage.h
index 6107775..64662d8 100644
--- a/st_second_stage.h
+++ b/st_second_stage.h
@@ -88,6 +88,7 @@
     struct st_second_stage_info *ss_info;
     struct listnode list_node;
     struct st_arm_ss_session *ss_session;
+    struct sound_trigger_device *stdev;
     FILE *dump_fp;
 }st_arm_second_stage_t;
 
@@ -97,7 +98,7 @@
     pthread_mutex_t lock;
     pthread_cond_t cond;
     bool exit_thread;
-    struct st_session *st_ses;
+    struct st_proxy_session *st_ses;
 
     /* For CNN to overwrite 1st stage indices */
     uint32_t kw_start_idx;
diff --git a/st_session.c b/st_session.c
index f5b362a..aaac3df 100644
--- a/st_session.c
+++ b/st_session.c
@@ -54,8 +54,10 @@
 
 #ifdef LINUX_ENABLED
 #define ST_SES_DEFERRED_STOP_DELAY_MS 0
+#define ST_SES_DEFERRED_STOP_SS_DELAY_MS 0
 #else
 #define ST_SES_DEFERRED_STOP_DELAY_MS 1000
+#define ST_SES_DEFERRED_STOP_SS_DELAY_MS 250
 #endif
 
 #define IS_SS_DETECTION_PENDING(det)\
@@ -63,6 +65,26 @@
 #define IS_SS_DETECTION_SUCCESS(det)\
     !(det & (KEYWORD_DETECTION_REJECT | USER_VERIFICATION_REJECT))
 
+#define STATE_TRANSITION(st_session, new_state_fn)\
+do {\
+    if (st_session->current_state != new_state_fn) {\
+        st_session->current_state = new_state_fn;\
+        ALOGD("session[%d]: %s ---> %s", st_session->sm_handle, __func__, \
+            #new_state_fn);\
+    }\
+} while(0)
+
+#define DISPATCH_EVENT(st_session, event, status)\
+do {\
+    status = st_session->current_state(st_session, &event);\
+} while (0)
+
+#define REG_SM_RETRY_CNT 5
+#define REG_SM_WAIT_TIME_MS 100
+
+#define MAX_CONF_LEVEL_VALUE (100)
+#define MAX_KW_USERS_NAME_LEN (2 * MAX_STRING_LEN)
+
 /* below enum used in cleanup in error scenarios */
 enum hw_session_err_mask {
     HW_SES_ERR_MASK_DEVICE_SET = 0x1,
@@ -72,58 +94,8 @@
     HW_SES_ERR_MASK_BUFFERING = 0x10,
 };
 
-#define STATE_TRANSITION(st_session, new_state_fn)\
-do {\
-        st_session->current_state = new_state_fn;\
-        ALOGD("session[%d]: %s ---> %s %s", st_session->sm_handle, __func__, \
-            #new_state_fn, st_session->paused ? "(paused)" : "");\
-} while(0)
-
-#define DISPATCH_EVENT(ST_SESSION, EVENT, STATUS)\
-do {\
-    STATUS = ST_SESSION->current_state(ST_SESSION, &EVENT);\
-} while (0)
-
-#define REG_SM_RETRY_CNT 5
-#define REG_SM_WAIT_TIME_MS 100
-
-static inline int process_detection_event
-(
-    st_session_t *st_ses, uint64_t timestamp, int detect_status,
-    void *payload, size_t payload_size,
-    struct sound_trigger_recognition_event **event
-);
-
-static inline void enable_second_stage_processing
-(
-    st_session_t *st_ses,
-    st_hw_session_t *hw_ses
-)
-{
-    hw_ses->enable_second_stage = st_ses->enable_second_stage;
-    st_ses->lab_enabled =
-        (st_ses->capture_requested || st_ses->enable_second_stage);
-}
-
-static inline void disable_second_stage_processing
-(
-    st_session_t *st_ses,
-    st_hw_session_t *hw_ses
-)
-{
-    hw_ses->enable_second_stage = false;
-    st_ses->lab_enabled = st_ses->capture_requested;
-}
-
-static int idle_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-static int loaded_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-static int active_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-static int detected_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-static int buffering_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-static int ssr_state_fn(st_session_t *st_ses, st_session_ev_t *ev);
-
 typedef struct st_session_loadsm_payload {
-    struct sound_trigger_phrase_sound_model *sm_data;
+    struct sound_trigger_phrase_sound_model *phrase_sm;
 } st_session_loadsm_payload_t;
 
 typedef struct st_session_start_payload {
@@ -159,13 +131,28 @@
         bool enable;
         st_session_getparam_payload_t getparam;
     } payload;
+    st_session_t *stc_ses;
 };
 
+static int idle_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+static int loaded_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+static int active_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+static int detected_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+static int buffering_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+static int ssr_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev);
+
+static inline int process_detection_event
+(
+    st_proxy_session_t *st_ses, uint64_t timestamp, int detect_status,
+    void *payload, size_t payload_size,
+    struct sound_trigger_recognition_event **event
+);
+
 ST_DBG_DECLARE(static int file_cnt = 0);
 
 void hw_sess_cb(st_hw_sess_event_t *hw_event, void *cookie)
 {
-    st_session_t *st_ses = (st_session_t *)cookie;
+    st_proxy_session_t *st_ses = (st_proxy_session_t *)cookie;
     int status = 0;
     int lock_status = 0;
 
@@ -176,17 +163,27 @@
 
     switch (hw_event->event_id) {
     case ST_HW_SESS_EVENT_DETECTED:
-        {
-            st_session_ev_t ev;
-            ev.ev_id = ST_SES_EV_DETECTED;
-            ev.payload.detected = hw_event->payload.detected;
+    {
+        st_session_ev_t ev;
+        ev.ev_id = ST_SES_EV_DETECTED;
+        ev.payload.detected = hw_event->payload.detected;
 
-            do {
-                lock_status = pthread_mutex_trylock(&st_ses->lock);
-            } while (lock_status && !st_ses->device_disabled &&
-                     (st_ses->exec_mode != ST_EXEC_MODE_NONE) &&
-                     (st_ses->current_state != ssr_state_fn));
+        do {
+            lock_status = pthread_mutex_trylock(&st_ses->lock);
+        } while (lock_status && !st_ses->device_disabled &&
+                 (st_ses->exec_mode != ST_EXEC_MODE_NONE) &&
+                 (st_ses->current_state != ssr_state_fn));
 
+        if (st_ses->device_disabled) {
+            ALOGV("%s:[%d] device switch in progress, ignore event",
+                  __func__, st_ses->sm_handle);
+        } else if (st_ses->exec_mode == ST_EXEC_MODE_NONE) {
+            ALOGV("%s:[%d] transition in progress, ignore event",
+                  __func__, st_ses->sm_handle);
+        } else if (st_ses->current_state == ssr_state_fn) {
+            ALOGV("%s:[%d] SSR handling in progress, ignore event",
+                  __func__, st_ses->sm_handle);
+        } else if (!lock_status) {
             /*
              * TODO: Add RECOGNITION_STATUS_GET_STATE_RESPONSE to
              * the SoundTrigger API header.
@@ -194,24 +191,14 @@
             if (st_ses->detection_requested)
                 ev.payload.detected.detect_status = 3;
 
-            if (st_ses->device_disabled) {
-                ALOGV("%s:[%d] device switch in progress, ignore event",
-                      __func__, st_ses->sm_handle);
-            } else if (st_ses->exec_mode == ST_EXEC_MODE_NONE) {
-                ALOGV("%s:[%d] transition in progress, ignore event",
-                      __func__, st_ses->sm_handle);
-            } else if (st_ses->current_state == ssr_state_fn) {
-                ALOGV("%s:[%d] SSR handling in progress, ignore event",
-                      __func__, st_ses->sm_handle);
-            } else if (!lock_status) {
-                DISPATCH_EVENT(st_ses, ev, status);
-            }
-
-            if (!lock_status)
-                pthread_mutex_unlock(&st_ses->lock);
-            break;
+            DISPATCH_EVENT(st_ses, ev, status);
         }
 
+        if (!lock_status)
+            pthread_mutex_unlock(&st_ses->lock);
+        break;
+    }
+
     default:
         ALOGD("%s:[%d] unhandled event", __func__, st_ses->sm_handle);
         break;
@@ -219,11 +206,2133 @@
 
 }
 
-static void do_hw_sess_cleanup(st_session_t *st_ses, st_hw_session_t *hw_ses,
-    enum hw_session_err_mask err)
+static inline void free_array_ptrs(char **arr, unsigned int arr_len)
+{
+    int i = 0;
+
+    if (!arr)
+        return;
+
+    for (i = 0; i < arr_len; i++) {
+        if (arr[i]) {
+            free(arr[i]);
+            arr[i] = NULL;
+        }
+    }
+    free(arr);
+    arr = NULL;
+}
+
+static inline void alloc_array_ptrs(char ***arr, unsigned int arr_len,
+    unsigned int elem_len)
+{
+    char **str_arr = NULL;
+    int i = 0;
+
+    str_arr = (char **) calloc(arr_len, sizeof(char *));
+
+    if (!str_arr) {
+        *arr = NULL;
+        return;
+    }
+
+    for (i = 0; i < arr_len; i++) {
+         str_arr[i] = (char *) calloc(elem_len, sizeof(char));
+         if (str_arr[i] == NULL) {
+             free_array_ptrs(str_arr, i);
+             *arr = NULL;
+             return;
+         }
+    }
+    *arr = str_arr;
+    ALOGV("%s: string array %p", __func__, *arr);
+    for (i = 0; i < arr_len; i++)
+        ALOGV("%s: string array[%d] %p", __func__, i, (*arr)[i]);
+}
+
+
+static int merge_sound_models(struct sound_trigger_device *stdev,
+    unsigned int num_models, listen_model_type *in_models[],
+    listen_model_type *out_model)
+{
+    listen_status_enum sm_ret = 0;
+    int status = 0;
+
+    ALOGV("%s: num_models to merge %d", __func__, num_models);
+
+    if (!stdev->smlib_handle) {
+        ALOGE("%s: NULL sound model lib handle", __func__);
+        return -ENOSYS;
+    }
+
+    sm_ret = stdev->smlib_getMergedModelSize(num_models, in_models,
+        &out_model->size);
+    if ((sm_ret != kSucess) || !out_model->size) {
+        ALOGE("%s: smlib_getMergedModelSize failed, err %d, size %d", __func__,
+            sm_ret, out_model->size);
+        return -EINVAL;
+    }
+    ALOGD("%s: merge sound model size %d", __func__, out_model->size);
+
+    out_model->data = calloc(1, out_model->size * sizeof(char));
+    if (!out_model->data) {
+        ALOGE("%s: Merged sound model allocation failed", __func__);
+        return -ENOMEM;
+    }
+
+    sm_ret = stdev->smlib_mergeModels(num_models, in_models, out_model);
+    if (sm_ret != kSucess) {
+        ALOGE("%s: smlib_mergeModels failed, err %d", __func__, sm_ret);
+        status = -EINVAL;
+        goto cleanup;
+    }
+    if (!out_model->data || !out_model->size) {
+        ALOGE("%s: MergeModels returned NULL data or size %d", __func__,
+              out_model->size);
+        status = -EINVAL;
+        goto cleanup;
+    }
+    ALOGV("%s: Exit", __func__);
+    return 0;
+
+cleanup:
+    if (out_model->data) {
+        free(out_model->data);
+        out_model->data = NULL;
+        out_model->size = 0;
+    }
+    return status;
+}
+
+static int delete_from_merged_sound_model(struct sound_trigger_device *stdev,
+    char **keyphrases, unsigned int num_keyphrases,
+    listen_model_type *in_model, listen_model_type *out_model)
+{
+    listen_model_type merge_model = {0};
+    listen_status_enum sm_ret = 0;
+    unsigned int out_model_sz = 0;
+    int status = 0, i = 0;
+
+    out_model->data = NULL;
+    out_model->size = 0;
+    merge_model.data = in_model->data;
+    merge_model.size = in_model->size;
+
+    for (i = 0; i < num_keyphrases; i++) {
+        sm_ret = stdev->smlib_getSizeAfterDeleting(&merge_model, keyphrases[i],
+                                                   NULL, &out_model_sz);
+        if (sm_ret != kSucess) {
+            ALOGE("%s: smlib_getSizeAfterDeleting failed %d", __func__, sm_ret);
+            status = -EINVAL;
+            goto cleanup;
+        }
+        if (out_model_sz >= in_model->size) {
+            ALOGE("%s: unexpected, smlib_getSizeAfterDeleting returned size %d"
+                  "not less than merged model size %d", __func__,
+                  out_model_sz, in_model->size);
+            status = -EINVAL;
+            goto cleanup;
+        }
+        ALOGV("%s: Size after deleting kw[%d] = %d", __func__, i, out_model_sz);
+        if (!out_model->data) {
+            /* Valid if deleting multiple keyphrases one after other */
+            free (out_model->data);
+            out_model->size = 0;
+        }
+        out_model->data = calloc(1, out_model_sz * sizeof(char));
+        if (!out_model->data) {
+            ALOGE("%s: Merge sound model allocation failed, size %d ", __func__,
+                  out_model_sz);
+            status = -ENOMEM;
+            goto cleanup;
+        }
+        out_model->size = out_model_sz;
+
+        sm_ret = stdev->smlib_deleteFromModel(&merge_model, keyphrases[i],
+                                              NULL, out_model);
+        if (sm_ret != kSucess) {
+            ALOGE("%s: smlib_getSizeAfterDeleting failed %d", __func__, sm_ret);
+            status = -EINVAL;
+            goto cleanup;
+        }
+        if (out_model->size != out_model_sz) {
+            ALOGE("%s: unexpected, out_model size %d != expected size %d",
+                  __func__, out_model->size, out_model_sz);
+            status = -EINVAL;
+            goto cleanup;
+        }
+        /* Used if deleting multiple keyphrases one after other */
+        merge_model.data = out_model->data;
+        merge_model.size = out_model->size;
+    }
+    return 0;
+
+cleanup:
+    if (out_model->data) {
+        free(out_model->data);
+        out_model->data = NULL;
+    }
+    return status;
+}
+
+static void release_sound_model_info(struct sound_model_info *sm_info)
+{
+    ALOGV("%s", __func__);
+
+    if (sm_info->cf_levels) {
+        free(sm_info->cf_levels);
+        sm_info->cf_levels = NULL;
+        sm_info->det_cf_levels = NULL;
+    }
+    free_array_ptrs(sm_info->keyphrases, sm_info->num_keyphrases);
+    sm_info->keyphrases = NULL;
+
+    free_array_ptrs(sm_info->users, sm_info->num_users);
+    sm_info->users = NULL;
+
+    free_array_ptrs(sm_info->cf_levels_kw_users, sm_info->cf_levels_size);
+    sm_info->cf_levels_kw_users = NULL;
+}
+
+static inline void stdbg_print_sound_model_header(
+   listen_sound_model_header *smh)
+{
+    int i = 0, j = 0;
+
+    ALOGV("%s", __func__);
+    ALOGV("numKeywords = %d", smh->numKeywords);
+    ALOGV("numUsers = %d", smh->numUsers);
+    ALOGV("numActiveUserKeywordPairs = %d", smh->numActiveUserKeywordPairs);
+    ALOGV("isStripped = %d", smh->isStripped);
+    ALOGV("model_indicator = %d", smh->model_indicator);
+
+    for (i = 0; i < smh->numKeywords; i++) {
+        ALOGV("kw-%d langPerKw = %d", i, smh->langPerKw[i]);
+        ALOGV("kw-%d numUsersSetPerKw = %d", i, smh->numUsersSetPerKw[i]);
+        ALOGV("kw-%d isUserDefinedKeyword = %d", i,
+              smh->isUserDefinedKeyword[i]);
+    }
+    if (smh->userKeywordPairFlags) {
+        for (i = 0; i < smh->numUsers; i++) {
+            for (j = 0; j < smh->numKeywords; j++)
+                ALOGV("userKeywordPairFlags[%d][%d] = %d", i, j,
+                    smh->userKeywordPairFlags[i][j]);
+        }
+    }
+}
+
+static int query_sound_model(struct sound_trigger_device *stdev,
+    struct sound_model_info *sm_info, unsigned char *sm_data,
+    unsigned int sm_size)
+{
+    listen_sound_model_header sm_header = {0};
+    listen_model_type model = {0};
+    listen_status_enum sm_ret = 0;
+    int status = 0, i = 0, j = 0, k = 0;
+    uint16_t tmp = 0;
+
+    ALOGV("%s: enter sm_size %d", __func__, sm_size);
+
+    if (!stdev->smlib_handle) {
+        ALOGE("%s: NULL sound model lib handle", __func__);
+        return -ENOSYS;
+    }
+
+    model.data = sm_data;
+    model.size = sm_size;
+
+    sm_ret = stdev->smlib_getSoundModelHeader(&model, &sm_header);
+    if (sm_ret != kSucess) {
+        ALOGE("%s: smlib_getSoundModelHeader failed, err %d ", __func__, sm_ret);
+        return -EINVAL;
+    }
+    if (sm_header.numKeywords == 0) {
+        ALOGE("%s: num keywords zero!!", __func__);
+        return -EINVAL;
+    }
+    if (sm_header.numActiveUserKeywordPairs < sm_header.numUsers) {
+        ALOGE("%s: smlib activeUserKwPairs(%d) < total users (%d)", __func__,
+                sm_header.numActiveUserKeywordPairs, sm_header.numUsers);
+        goto cleanup;
+    }
+    if (sm_header.numUsers && !sm_header.userKeywordPairFlags) {
+        ALOGE("%s: userKeywordPairFlags is NULL, numUsers (%d)", __func__,
+                sm_header.numUsers);
+        goto cleanup;
+    }
+    stdbg_print_sound_model_header(&sm_header);
+
+    /* MAX_STRING_LEN is part of listen sound model header file */
+    alloc_array_ptrs(&sm_info->keyphrases, sm_header.numKeywords,
+        MAX_STRING_LEN);
+    if (!sm_info->keyphrases) {
+        ALOGE("%s: keyphrases allocation failed", __func__);
+        status = -ENOMEM;
+        goto cleanup;
+    }
+    sm_info->num_keyphrases = sm_header.numKeywords;
+    sm_info->num_users = sm_header.numUsers;
+
+    tmp = sm_header.numKeywords;
+    ALOGV("%s: stdb: model.data %p, model.size %d", __func__, model.data,
+            model.size);
+    sm_ret = stdev->smlib_getKeywordPhrases(&model, &tmp, sm_info->keyphrases);
+    if (sm_ret) {
+        ALOGE("%s: smlib_getKeywordPhrases failed, err %d ", __func__, sm_ret);
+        goto cleanup;
+    }
+    if (tmp != sm_header.numKeywords) {
+        ALOGE("%s: smlib_getkeywordPhrases(%d) != sml header (%d)", __func__,
+              tmp, sm_header.numKeywords);
+        goto cleanup;
+    }
+    for (i = 0; i < sm_header.numKeywords; i++)
+        ALOGV("%s: keyphrases names = %s", __func__, sm_info->keyphrases[i]);
+
+    if (sm_header.numUsers) {
+        alloc_array_ptrs(&sm_info->users, sm_header.numUsers, MAX_STRING_LEN);
+        if (!sm_info->users) {
+            ALOGE("%s: users allocation failed", __func__);
+            status = -ENOMEM;
+            goto cleanup;
+        }
+
+        tmp = sm_header.numUsers;
+        sm_ret = stdev->smlib_getUserNames(&model, &tmp, sm_info->users);
+        if (sm_ret) {
+            ALOGE("%s: smlib_getUserNames failed, err %d ", __func__, sm_ret);
+            goto cleanup;
+        }
+        if (tmp != sm_header.numUsers) {
+            ALOGE("%s: smlib_getUserNames(%d) != sml header (%d)", __func__,
+                tmp, sm_header.numUsers);
+            status = -EINVAL;
+            goto cleanup;
+        }
+        for (i = 0; i < sm_header.numUsers; i++)
+            ALOGV("%s: users names = %s", __func__, sm_info->users[i]);
+    }
+
+    sm_info->cf_levels_size = sm_header.numKeywords +
+        sm_header.numActiveUserKeywordPairs;
+    alloc_array_ptrs(&sm_info->cf_levels_kw_users, sm_info->cf_levels_size,
+                     MAX_KW_USERS_NAME_LEN);
+    if (!sm_info->cf_levels_kw_users) {
+        ALOGE("%s: cf_levels_kw_users allocation failed", __func__);
+        status = -ENOMEM;
+        goto cleanup;
+    }
+
+    /* Used later for mapping client to/from merged DSP confidence levels */
+    sm_info->cf_levels = calloc(1, 2 * sm_info->cf_levels_size);
+    if (!sm_info->cf_levels) {
+        ALOGE("%s: cf_levels allocation failed", __func__);
+        status = -ENOMEM;
+        goto cleanup;
+    }
+    /*
+     * Used for updating detection confidence level values from DSP merged
+     * detection conf levels
+     */
+    sm_info->det_cf_levels = sm_info->cf_levels + sm_info->cf_levels_size;
+
+    /*
+     * Used for conf level setting to DSP. Reset the conf value to max value,
+     * so that the keyword of a loaded and in-active model in a merged model
+     * doesn't detect.
+     */
+    memset(sm_info->cf_levels, MAX_CONF_LEVEL_VALUE, sm_info->cf_levels_size);
+
+    /*
+     * Derive the confidence level payload for keyword and user pairs.
+     * Store the user-keyword pair names in an array that will be used for
+     * mapping the DSP detection and confidence levels to the client.
+     */
+    char **kw_names = sm_info->cf_levels_kw_users;
+    char **ukw_names = &sm_info->cf_levels_kw_users[sm_header.numKeywords];
+    int ukw_idx = 0;
+
+    for (i = 0; i < sm_header.numKeywords; i++) {
+        strlcpy(kw_names[i], sm_info->keyphrases[i], MAX_KW_USERS_NAME_LEN);
+        if (!sm_header.numUsersSetPerKw)
+            continue;
+        for (j = 0, k = 0; j < sm_header.numUsers; j++) {
+            if (k >= sm_header.numUsersSetPerKw[i])
+                break;
+            if (sm_header.userKeywordPairFlags[j][i]) {
+                strlcpy(ukw_names[ukw_idx], sm_info->users[j],
+                    MAX_KW_USERS_NAME_LEN);
+                strlcat(ukw_names[ukw_idx], sm_info->keyphrases[i],
+                    MAX_KW_USERS_NAME_LEN);
+                ukw_idx++;
+                k++;
+            }
+        }
+    }
+    for (i = 0; i < sm_info->cf_levels_size; i++)
+        ALOGV("%s: cf_levels_kw_users = %s", __func__,
+                sm_info->cf_levels_kw_users[i]);
+
+    sm_ret = stdev->smlib_releaseSoundModelHeader(&sm_header);
+    if (sm_ret != kSucess) {
+        ALOGE("%s: smlib_releaseSoundModelHeader failed, err %d ", __func__,
+                sm_ret);
+        status = -EINVAL;
+        goto cleanup_1;
+    }
+    ALOGV("%s: exit", __func__);
+    return 0;
+
+cleanup:
+    sm_ret = stdev->smlib_releaseSoundModelHeader(&sm_header);
+    if (sm_ret != kSucess)
+        ALOGE("%s: smlib_releaseSoundModelHeader failed, err %d ", __func__,
+                sm_ret);
+
+cleanup_1:
+    release_sound_model_info(sm_info);
+
+    return status;
+}
+
+static int add_sound_model(st_session_t *stc_ses, unsigned char *sm_data,
+    unsigned int sm_size)
+{
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+    listen_model_type **in_models = NULL;
+    listen_model_type out_model = {0};
+    struct sound_model_info sm_info = {0};
+    int status = 0, num_models = 0;
+
+    ALOGV("%s:[c%d]", __func__, stc_ses->sm_handle);
+    if (stc_ses->sm_info.sm_data) {
+        ALOGD("%s:[c%d] Already added", __func__, stc_ses->sm_handle);
+        return 0;
+    }
+    if (!st_ses->vendor_uuid_info->merge_fs_soundmodels) {
+        stc_ses->sm_info.sm_data = sm_data;
+        stc_ses->sm_info.sm_size = sm_size;
+        st_ses->sm_info.sm_data = sm_data;
+        st_ses->sm_info.sm_size = sm_size;
+        st_ses->sm_info.sm_type = stc_ses->sm_type;
+        ALOGD("%s:[c%d] no merge", __func__, stc_ses->sm_handle);
+        return 0;
+    }
+    /* get sound model header information for client model */
+    status = query_sound_model(st_ses->stdev, &stc_ses->sm_info,
+                               sm_data, sm_size);
+    if (status)
+        return status;
+
+    stc_ses->sm_info.sm_data = sm_data;
+    stc_ses->sm_info.sm_size = sm_size;
+    ALOGV("%s: stc_ses %p - sm_data %p, sm_size %d", __func__,
+          stc_ses, stc_ses->sm_info.sm_data,
+          stc_ses->sm_info.sm_size);
+
+    /* Check for remaining client sound models to merge */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses != stc_ses) && c_ses->sm_info.sm_data)
+            num_models++;
+    }
+    if (!num_models) {
+        if (st_ses->sm_info.sm_merged && st_ses->sm_info.sm_data) {
+            free(st_ses->sm_info.sm_data);
+        }
+        /* Only one current client model, just re-use it */
+        st_ses->recognition_mode = stc_ses->recognition_mode;
+        stc_ses->sm_info.sm_type = stc_ses->sm_type;
+        st_ses->sm_info = stc_ses->sm_info;
+        st_ses->sm_info.sm_merged = false;
+        ALOGD("%s: re-use single client c%d model, size %d", __func__,
+              stc_ses->sm_handle, stc_ses->sm_info.sm_size);
+        return 0;
+    }
+    ALOGV("%s: num existing models %d", __func__, num_models);
+    /*
+     * Merge this client model with already existing merged model due to other
+     * clients models.
+     */
+    if (!st_ses->sm_info.sm_data) {
+        if (num_models == 1) {
+            /*
+             * Its not a merged model yet, but proxy ses sm_data is valid and
+             * must be pointing to client sm_data
+             */
+            ALOGE("%s: Unexpected, sm_info.sm_data NULL, num current"
+                  "models %d", __func__, num_models);
+            status = -EINVAL;
+            goto cleanup;
+        } else if (!st_ses->sm_info.sm_merged) {
+            ALOGE("%s: Unexpected, no pre-existing merged model, num current"
+                  "models %d", __func__, num_models);
+            status = -EINVAL;
+            goto cleanup;
+        }
+    }
+
+    /* Merge this client model with remaining clients models */
+    num_models = 2;/* re-use */
+    alloc_array_ptrs((char***)&in_models, num_models, sizeof(listen_model_type));
+    if (!in_models) {
+        ALOGE("%s: in_models allocation failed", __func__);
+        status = -ENOMEM;
+        goto cleanup;
+    }
+    /* Add existing model */
+    in_models[0]->data = st_ses->sm_info.sm_data;
+    in_models[0]->size = st_ses->sm_info.sm_size;
+    /* Add this client model */
+    in_models[1]->data = sm_data;
+    in_models[1]->size = sm_size;
+
+    status = merge_sound_models(st_ses->stdev, 2, in_models, &out_model);
+    if (status)
+        goto cleanup;
+
+    sm_info.sm_data = out_model.data;
+    sm_info.sm_size = out_model.size;
+    sm_info.sm_merged = true;
+
+    status = query_sound_model(st_ses->stdev, &sm_info,
+                               out_model.data, out_model.size);
+    if (status)
+        goto cleanup;
+
+    if (out_model.size < st_ses->sm_info.sm_size) {
+        ALOGE("%s: Unexpected, merged model sz %d < current sz %d",
+            __func__, out_model.size, st_ses->sm_info.sm_size);
+        release_sound_model_info(&sm_info);
+        status = -EINVAL;
+        goto cleanup;
+    }
+    free_array_ptrs((char **)in_models, num_models);
+    in_models = NULL;
+
+    /* Update the new merged model */
+    if (st_ses->sm_info.sm_merged && st_ses->sm_info.sm_data) {
+        release_sound_model_info(&st_ses->sm_info);
+        free(st_ses->sm_info.sm_data);
+    }
+    ALOGD("%s: Updated sound model: current size %d, new size %d", __func__,
+        st_ses->sm_info.sm_size, out_model.size);
+    st_ses->sm_info = sm_info;
+
+    /*
+     * If any of the clients has user identificaiton enabled, underlying
+     * hw session has to operate with user identification enabled.
+     */
+    if (stc_ses->recognition_mode & RECOGNITION_MODE_USER_IDENTIFICATION)
+        st_ses->recognition_mode |= stc_ses->recognition_mode;
+
+    return 0;
+
+cleanup:
+    release_sound_model_info(&stc_ses->sm_info);
+    stc_ses->sm_info.sm_data = NULL;
+
+    if (out_model.data)
+        free(out_model.data);
+
+    if (in_models)
+        free_array_ptrs((char **)in_models, num_models);
+
+    return status;
+}
+
+static int delete_sound_model(st_session_t *stc_ses)
+{
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL, *c_ses_rem = NULL;
+    listen_model_type in_model = {0};
+    listen_model_type out_model = {0};
+    struct sound_model_info sm_info = {0};
+    int status = 0, num_models = 0;
+    unsigned int rec_mode = RECOGNITION_MODE_VOICE_TRIGGER;
+
+    ALOGV("%s:[c%d]", __func__, stc_ses->sm_handle);
+    if (!stc_ses->sm_info.sm_data) {
+        ALOGD("%s:[c%d] Already deleted", __func__, stc_ses->sm_handle);
+        return 0;
+    }
+    if (!st_ses->vendor_uuid_info->merge_fs_soundmodels) {
+        /*
+         * As it directly points to client model, just set as NULL
+         * without freeing
+         */
+        st_ses->sm_info.sm_data = NULL;
+        stc_ses->sm_info.sm_data = NULL;
+        ALOGD("%s:[c%d] no merge", __func__, stc_ses->sm_handle);
+        return 0;
+    }
+
+    ALOGV("%s: stc_ses %p - sm_data %p, sm_size %d", __func__,
+          stc_ses, stc_ses->sm_info.sm_data,
+          stc_ses->sm_info.sm_size);
+
+    /* Check for remaining clients sound models to merge */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses != stc_ses) && c_ses->sm_info.sm_data) {
+            c_ses_rem = c_ses;
+            num_models++;
+        }
+    }
+    if (num_models == 0) {
+        ALOGD("%s: No remaining models", __func__);
+        /* Delete current client model */
+        release_sound_model_info(&stc_ses->sm_info);
+        stc_ses->sm_info.sm_data = NULL;
+        return 0;
+    }
+
+    if (num_models == 1) {
+        ALOGD("%s: reuse only remaining client model, size %d", __func__,
+            c_ses_rem->sm_info.sm_size);
+        /* If only one remaining client model exists, re-use it */
+        if (st_ses->sm_info.sm_merged) {
+            release_sound_model_info(&st_ses->sm_info);
+            if (st_ses->sm_info.sm_data)
+                free(st_ses->sm_info.sm_data);
+        }
+        st_ses->sm_info = c_ses_rem->sm_info;
+        st_ses->sm_info.sm_merged = false;
+        st_ses->hw_ses_current->sthw_cfg.conf_levels =
+            st_ses->sm_info.cf_levels;
+        st_ses->hw_ses_current->sthw_cfg.num_conf_levels =
+            st_ses->sm_info.cf_levels_size;
+        /* Delete current client model */
+        release_sound_model_info(&stc_ses->sm_info);
+        stc_ses->sm_info.sm_data = NULL;
+        return 0;
+    }
+    /* Update overall recogniton mode from remaining clients */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses != stc_ses) && c_ses->sm_info.sm_data) {
+            if (c_ses->recognition_mode == RECOGNITION_MODE_USER_IDENTIFICATION)
+                rec_mode |=  RECOGNITION_MODE_USER_IDENTIFICATION;
+        }
+    }
+
+    /*
+     * Delete this client model with already existing merged model due to other
+     * clients models.
+     */
+    if (!st_ses->sm_info.sm_merged || !st_ses->sm_info.sm_data) {
+        ALOGE("%s: Unexpected, no pre-existing merged model to delete from,"
+              "num current models %d", __func__, num_models);
+        goto cleanup;
+    }
+
+    /* Existing merged model from which the current client model to be deleted */
+    in_model.data = st_ses->sm_info.sm_data;
+    in_model.size = st_ses->sm_info.sm_size;
+
+    status = delete_from_merged_sound_model(st_ses->stdev,
+        stc_ses->sm_info.keyphrases, stc_ses->sm_info.num_keyphrases,
+        &in_model, &out_model);
+
+    if (status)
+        goto cleanup;
+
+    sm_info.sm_data = out_model.data;
+    sm_info.sm_size = out_model.size;
+    sm_info.sm_merged = true;
+
+    /* Update existing merged model info with new merged model */
+    status = query_sound_model(st_ses->stdev, &sm_info, out_model.data,
+                               out_model.size);
+    if (status)
+        goto cleanup;
+
+    if (out_model.size > st_ses->sm_info.sm_size) {
+        ALOGE("%s: Unexpected, merged model sz %d > current sz %d",
+            __func__, out_model.size, st_ses->sm_info.sm_size);
+        release_sound_model_info(&sm_info);
+        status = -EINVAL;
+        goto cleanup;
+    }
+
+    if (st_ses->sm_info.sm_merged && st_ses->sm_info.sm_data) {
+        release_sound_model_info(&st_ses->sm_info);
+        free(st_ses->sm_info.sm_data);
+    }
+
+    ALOGD("%s: Updated sound model: current size %d, new size %d", __func__,
+        st_ses->sm_info.sm_size, out_model.size);
+    st_ses->sm_info = sm_info;
+    /*
+     * If any of the remaining clients has user identificaiton enabled,
+     * underlying hw session has to operate with user identificaiton enabled.
+     */
+    st_ses->recognition_mode = rec_mode;
+
+    /* Release current client model */
+    release_sound_model_info(&stc_ses->sm_info);
+    stc_ses->sm_info.sm_data = NULL;
+
+    return 0;
+
+cleanup:
+    release_sound_model_info(&stc_ses->sm_info);
+    stc_ses->sm_info.sm_data = NULL;
+
+    if (out_model.data)
+        free(out_model.data);
+
+    return status;
+}
+
+static int update_sound_model(st_session_t *stc_ses, bool add)
+{
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    struct sound_trigger_phrase_sound_model *phrase_sm =  stc_ses->phrase_sm;
+    struct sound_trigger_sound_model *common_sm =
+        (struct sound_trigger_sound_model*)stc_ses->phrase_sm;
+    unsigned char *sm_data = NULL;
+    unsigned int sm_size = 0;
+    int status = 0;
+
+    ALOGV("%s: Enter", __func__);
+
+    if (stc_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        sm_data = (unsigned char*)phrase_sm + phrase_sm->common.data_offset;
+        sm_size = phrase_sm->common.data_size;
+    } else {
+        sm_data = (unsigned char*)common_sm + common_sm->data_offset;
+        sm_size = common_sm->data_size;
+    }
+
+    pthread_mutex_lock(&st_ses->lock);
+    if (add)
+        status = add_sound_model(stc_ses, sm_data, sm_size);
+    else
+        status = delete_sound_model(stc_ses);
+    pthread_mutex_unlock(&st_ses->lock);
+
+    ALOGV("%s: Exit", __func__);
+    return status;
+}
+
+static int update_merge_conf_levels_payload(st_proxy_session_t *st_ses,
+    struct sound_model_info *src_sm_info, unsigned char *src,
+    unsigned int src_size, bool set)
+{
+    int i = 0, j = 0;
+
+    if (!st_ses || !src) {
+        ALOGE("%s: NULL pointer", __func__);
+        return -EINVAL;
+    }
+
+    if (!st_ses->sm_info.sm_merged)
+        return 0;
+
+    if (src_size > st_ses->sm_info.cf_levels_size) {
+        ALOGE("%s:[%d] Unexpected, client conf levels %d > "
+            "merged conf levels %d", __func__, st_ses->sm_handle,
+            src_size, st_ses->sm_info.cf_levels_size);
+        return -EINVAL;
+    }
+
+    for (i = 0; i < src_size; i++)
+        ALOGV("%s: src cf_levels[%d]=%d", __func__, i, src[i]);
+
+    /* Populate DSP merged sound model conf levels */
+    for (i = 0; i < src_size; i++) {
+        for (j = 0; j < st_ses->sm_info.cf_levels_size; j++) {
+            if (!strcmp(st_ses->sm_info.cf_levels_kw_users[j],
+                        src_sm_info->cf_levels_kw_users[i])) {
+                if (set) {
+                    st_ses->sm_info.cf_levels[j] = src[i];
+                    ALOGV("%s: set: sm_info.cf_levels[%d]=%d", __func__,
+                          j, st_ses->sm_info.cf_levels[j]);
+                } else {
+                    st_ses->sm_info.cf_levels[j] = MAX_CONF_LEVEL_VALUE;
+                    ALOGV("%s: reset: sm_info.cf_levels[%d]=%d", __func__,
+                          j, st_ses->sm_info.cf_levels[j]);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+static int update_merge_conf_levels_payload_with_active_clients(
+   st_proxy_session_t *st_ses)
+{
+    int status = 0;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses->state == ST_STATE_ACTIVE) {
+            ALOGV("%s: update merge conf levels with other active"
+                  "client %d ", __func__, c_ses->sm_handle);
+            status = update_merge_conf_levels_payload(st_ses,
+                &c_ses->sm_info, c_ses->sm_info.cf_levels,
+                c_ses->sm_info.cf_levels_size, true);
+            if (status)
+                return status;
+        }
+    }
+    return status;
+}
+
+static void check_and_extract_det_conf_levels_payload(
+    st_proxy_session_t *st_ses,
+    unsigned char *src, unsigned int src_size,
+    unsigned char **dst, unsigned int *dst_size)
+{
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    int i = 0, j = 0;
+
+    *dst = src;
+    *dst_size = src_size;
+
+    if (!st_ses->vendor_uuid_info->merge_fs_soundmodels ||
+        !st_ses->sm_info.sm_merged) {
+        ALOGV("%s:[%d] not merged", __func__, st_ses->sm_handle);
+        return;
+    }
+
+    if (src_size < st_ses->sm_info.cf_levels_size) {
+        ALOGE("%s:[%d] Unexpected, detection conf payload size %d < %d",
+              __func__, st_ses->sm_handle, src_size,
+              st_ses->sm_info.cf_levels_size);
+        return;
+    }
+
+    /* Reset any cached previous detection values */
+    memset(stc_ses->sm_info.det_cf_levels, 0, stc_ses->sm_info.cf_levels_size);
+
+    /* Extract the client conf level values from DSP payload */
+    for(i = 0; i < st_ses->sm_info.cf_levels_size; i++) {
+        if (!src[i])
+            continue;
+        for(j = 0; j < stc_ses->sm_info.cf_levels_size; j++) {
+            if (!strcmp(stc_ses->sm_info.cf_levels_kw_users[j],
+                        st_ses->sm_info.cf_levels_kw_users[i])) {
+                stc_ses->sm_info.det_cf_levels[j] = src[i];
+            }
+        }
+    }
+    for (i = 0; i < stc_ses->sm_info.cf_levels_size; i++)
+        ALOGD("%s: c%d det_cf_levels[%d]=%d", __func__, stc_ses->sm_handle, i,
+              stc_ses->sm_info.det_cf_levels[i]);
+
+    *dst = stc_ses->sm_info.det_cf_levels;
+    *dst_size = stc_ses->sm_info.cf_levels_size;
+}
+
+static inline bool check_for_multi_clients(st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    int cnt = 0;
+
+    list_for_each(node, &st_ses->clients_list) {
+        if (++cnt > 1)
+            return true;
+    }
+    return false;
+}
+
+static inline bool is_other_client_attached(st_proxy_session_t *st_ses,
+    st_session_t *stc_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses != stc_ses)
+            return true;
+    }
+    return false;
+}
+
+static inline void reset_clients_pending_load(st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        c_ses->pending_load = false;
+    }
+}
+
+static inline int is_any_client_not_pending_load(st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (!c_ses->pending_load)
+            return true;
+    }
+    return false;
+}
+
+static inline int is_any_client_not_pending_set_device(
+   st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (!c_ses->pending_set_device)
+            return true;
+    }
+    return false;
+}
+
+static inline void reset_clients_pending_set_device(st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        c_ses->pending_set_device = false;
+    }
+}
+
+static bool check_and_get_other_active_client(st_proxy_session_t *st_ses,
+    st_session_t *stc_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses != stc_ses) && (c_ses->state == ST_STATE_ACTIVE))
+            return c_ses;
+    }
+    return NULL;
+}
+
+static inline bool is_any_client_paused(st_proxy_session_t *st_ses)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses->paused)
+            return true;
+    }
+    return false;
+}
+
+static inline bool is_any_client_in_state(st_proxy_session_t *st_ses,
+    enum client_states_t state)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses->state == state)
+            return true;
+    }
+    return false;
+}
+
+static void update_hw_config_on_restart(st_proxy_session_t *st_ses,
+    st_session_t *stc_ses)
+{
+    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
+    struct st_hw_ses_config *sthw_cfg = &hw_ses->sthw_cfg;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+    int hb_sz = 0, pr_sz = 0;
+    bool enable_lab = false;
+
+    if (!st_ses->vendor_uuid_info->merge_fs_soundmodels ||
+        !st_ses->sm_info.sm_merged)
+        return;
+
+    /*
+     * Adjust history buffer and preroll durations to highest of
+     * all clients, including current restarting client.
+     * If any of the clients requested capture or enabled the
+     * second stage, the underlying hw session buffering needs to be
+     * enabled.
+     */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses == stc_ses) || (c_ses->state == ST_STATE_ACTIVE)) {
+            if (hb_sz < c_ses->hist_buf_duration)
+                hb_sz = c_ses->hist_buf_duration;
+            if (pr_sz < c_ses->preroll_duration)
+                pr_sz = c_ses->preroll_duration;
+            if (!enable_lab)
+                enable_lab = (c_ses->rc_config &&
+                              c_ses->rc_config->capture_requested) ||
+                             !list_empty(&c_ses->second_stage_list);
+        }
+    }
+
+    sthw_cfg->client_req_hist_buf = hb_sz;
+    sthw_cfg->client_req_preroll = pr_sz;
+    st_ses->lab_enabled = enable_lab;
+
+    update_merge_conf_levels_payload(st_ses, &stc_ses->sm_info,
+                                     stc_ses->sm_info.cf_levels,
+                                     stc_ses->sm_info.cf_levels_size,
+                                     true);
+    hw_ses->sthw_cfg_updated = true;
+
+    ALOGV("%s:[%d] lab_enabled %d, hb_sz %d, pr_sz %d", __func__,
+        st_ses->sm_handle, st_ses->lab_enabled,
+        sthw_cfg->client_req_hist_buf, sthw_cfg->client_req_preroll);
+}
+
+static bool update_hw_config_on_stop(st_proxy_session_t *st_ses,
+    st_session_t *stc_ses)
+{
+    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
+    struct st_hw_ses_config *sthw_cfg = &hw_ses->sthw_cfg;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+    int hb_sz = 0, pr_sz = 0;
+    bool active = false, enable_lab = false;
+
+    if (!st_ses->vendor_uuid_info->merge_fs_soundmodels ||
+        !st_ses->sm_info.sm_merged) {
+        if (sthw_cfg->conf_levels) {
+            ALOGV("%s: free hw conf_levels", __func__);
+            free(sthw_cfg->conf_levels);
+            sthw_cfg->conf_levels = NULL;
+        }
+        return false;
+    }
+    /*
+     * Adjust history buffer and preroll durations to highest of
+     * remaining clients.
+     * If any of the remaining clients requested capture or enabled the
+     * second stage, the underlying hw session buffering needs to be
+     * enabled.
+     */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if ((c_ses != stc_ses) && (c_ses->state == ST_STATE_ACTIVE)) {
+            active = true;
+            if (hb_sz < c_ses->hist_buf_duration)
+                hb_sz = c_ses->hist_buf_duration;
+            if (pr_sz < c_ses->preroll_duration)
+                pr_sz = c_ses->preroll_duration;
+            if (!enable_lab)
+                enable_lab = c_ses->rc_config->capture_requested ||
+                             !list_empty(&c_ses->second_stage_list);
+        }
+    }
+    if (!active) {
+        sthw_cfg->client_req_hist_buf = 0;
+        sthw_cfg->client_req_preroll = 0;
+        st_ses->lab_enabled = 0;
+        sthw_cfg->custom_data = NULL;
+        sthw_cfg->custom_data_size = 0;
+        hw_ses->sthw_cfg_updated = true;
+        ALOGV("%s:[%d] no active client hw cfg is reset", __func__,
+              st_ses->sm_handle);
+        return false;
+    }
+
+    sthw_cfg->client_req_hist_buf = hb_sz;
+    sthw_cfg->client_req_preroll = pr_sz;
+    st_ses->lab_enabled = enable_lab;
+
+    update_merge_conf_levels_payload(st_ses, &stc_ses->sm_info,
+                                     stc_ses->sm_info.cf_levels,
+                                     stc_ses->sm_info.cf_levels_size,
+                                     false);
+    hw_ses->sthw_cfg_updated = true;
+
+    ALOGV("%s:[%d] lab_enabled %d, hb_sz %d, pr_sz %d", __func__,
+        st_ses->sm_handle, st_ses->lab_enabled,
+        sthw_cfg->client_req_hist_buf, sthw_cfg->client_req_preroll);
+
+    return active;
+}
+
+static void get_conf_levels_from_dsp_payload(st_proxy_session_t *st_ses,
+    unsigned char *payload, unsigned int payload_size,
+    unsigned char **conf_levels, unsigned int *conf_levels_size)
+{
+    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
+    uint32_t key_id = 0, key_payload_size = 0;
+    unsigned int i = 0;
+
+    if (hw_ses->is_generic_event) {
+        while (i < payload_size) {
+            key_id = *(uint32_t *)payload;
+            key_payload_size = *((uint32_t *)payload + 1);
+
+            if (key_id == KEY_ID_CONFIDENCE_LEVELS) {
+                *conf_levels = payload + (4 * sizeof(uint32_t));
+                *conf_levels_size = *((uint32_t *)payload + 3);;
+                ALOGV("%s: generic_event: DSP num conf levels %d", __func__,
+                      *conf_levels_size);
+                break;
+            }
+            i += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
+            payload += i;
+        }
+    } else {
+        if ((st_ses->exec_mode == ST_EXEC_MODE_CPE) && st_ses->stdev->is_gcs) {
+            *conf_levels = payload + 2;
+            *conf_levels_size = payload_size - 2;
+        } else {
+            *conf_levels = payload;
+            *conf_levels_size = payload_size;
+        }
+    }
+}
+
+st_session_t* get_detected_client(st_proxy_session_t *st_ses,
+    unsigned char *payload, unsigned int payload_size)
+{
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
+    unsigned char *conf_levels = NULL;
+    unsigned int conf_levels_size = 0;
+    int i = 0, j = 0;
+
+    if (list_empty(&st_ses->clients_list)) {
+        ALOGE("%s:[%d] no clients attached!!", __func__, st_ses->sm_handle);
+        return NULL;
+    }
+    /*
+     * If only single client exist, this detection is not for merged
+     * sound model, hence return this as only available client
+     */
+    if (!check_for_multi_clients(st_ses)) {
+        ALOGV("%s:[%d] single client detection", __func__, st_ses->sm_handle);
+        node = list_head(&st_ses->clients_list);
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses->state == ST_STATE_ACTIVE) {
+            ALOGD("%s: detected c%d", __func__, c_ses->sm_handle);
+            return c_ses;
+        } else {
+            ALOGE("%s: detected c%d is not active", __func__, c_ses->sm_handle);
+            return NULL;
+        }
+    }
+
+    get_conf_levels_from_dsp_payload(st_ses, payload, payload_size,
+                                     &conf_levels, &conf_levels_size);
+    if (!conf_levels) {
+        ALOGE("%s:[%d] no conf levels payload found!!", __func__,
+            st_ses->sm_handle);
+        return NULL;
+    }
+    if (conf_levels_size < st_ses->sm_info.num_keyphrases) {
+        ALOGE("%s:[%d] detection conf levels size %d < num of keywords %d",
+            __func__, st_ses->sm_handle, conf_levels_size,
+            st_ses->sm_info.num_keyphrases);
+        return NULL;
+    }
+
+    /*
+     * The DSP payload contains the keyword conf levels from the beginning.
+     * Only one keyword conf level is expected to be non-zero from keyword
+     * detection. Find non-zero conf level up to number of keyphrases and if
+     * one is found, match it to the corresponding keyphrase from list of
+     * clients to obtain the detected client.
+     */
+    for (i = 0; i < st_ses->sm_info.num_keyphrases; i++) {
+        if (!conf_levels[i])
+            continue;
+        list_for_each(node, &st_ses->clients_list) {
+            c_ses = node_to_item(node, st_session_t, hw_list_node);
+            for (j = 0; j < c_ses->sm_info.num_keyphrases; j++) {
+                if (!strcmp(st_ses->sm_info.keyphrases[i],
+                            c_ses->sm_info.keyphrases[j])) {
+                    if (c_ses->state == ST_STATE_ACTIVE) {
+                        ALOGV("%s: detected c%d", __func__, c_ses->sm_handle);
+                        return c_ses;
+                    } else {
+                        ALOGE("%s: detected c%d is not active", __func__,
+                            c_ses->sm_handle);
+                        return NULL;
+                    }
+                }
+            }
+        }
+    }
+    return c_ses;
+}
+
+static int fill_conf_levels_payload_from_rc_config
+(
+   const struct sound_trigger_phrase_sound_model *phrase_sm,
+   const struct sound_trigger_recognition_config *rc_config,
+   unsigned char *conf_levels,
+   unsigned int num_conf_levels,
+   unsigned int total_num_users
+)
+{
+    int status = 0;
+    unsigned int user_level, user_id;
+    unsigned int i = 0, j = 0;
+    unsigned char *user_id_tracker;
+
+    if (!phrase_sm || !rc_config || !conf_levels || !num_conf_levels) {
+        ALOGE("%s: ERROR. Invalid inputs",__func__);
+        return -EINVAL;
+    }
+
+    /*  Example: Say the recognition structure has 3 keywords with users
+     *  |kid|
+     *  [0] k1 |uid|
+     *         [3] u1 - 1st trainer
+     *         [4] u2 - 4th trainer
+     *         [6] u3 - 3rd trainer
+     *  [1] k2
+     *         [5] u2 - 2nd trainer
+     *         [7] u3 - 5th trainer
+     *  [2] k3
+     *         [8] u4 - 6th trainer
+     *
+     *  Output confidence level array will be
+     *  [k1, k2, k3, u1k1, u2k1, u2k2, u3k1, u3k2, u4k3]
+     */
+
+    user_id_tracker = calloc(1, num_conf_levels);
+    if (!user_id_tracker) {
+        ALOGE("%s: failed to allocate user_id_tracker", __func__);
+        return -ENOMEM;
+    }
+
+    for (i = 0; i < rc_config->num_phrases; i++) {
+        ALOGV("%s: [%d] kw level %d", __func__, i,
+              rc_config->phrases[i].confidence_level);
+        for (j = 0; j < rc_config->phrases[i].num_levels; j++) {
+            ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
+                  rc_config->phrases[i].levels[j].user_id,
+                  rc_config->phrases[i].levels[j].level);
+        }
+    }
+
+    for (i = 0; i < rc_config->num_phrases; i++) {
+        conf_levels[i] = rc_config->phrases[i].confidence_level;
+        for (j = 0; j < rc_config->phrases[i].num_levels; j++) {
+            user_level = rc_config->phrases[i].levels[j].level;
+            user_id = rc_config->phrases[i].levels[j].user_id;
+            if ((user_id < rc_config->num_phrases) ||
+                (user_id >= num_conf_levels)) {
+                ALOGE("%s: ERROR. Invalid params user id %d>%d",
+                      __func__, user_id, total_num_users);
+                status = -EINVAL;
+                goto exit;
+            } else {
+                if (user_id_tracker[user_id] == 1) {
+                    ALOGE("%s: ERROR. Duplicate user id %d",
+                          __func__, user_id);
+                    status = -EINVAL;
+                    goto exit;
+                }
+                conf_levels[user_id] = (user_level < 100) ? user_level: 100;
+                user_id_tracker[user_id] = 1;
+                ALOGV("%s: user_conf_levels[%d] = %d", __func__,
+                                    user_id, conf_levels[user_id]);
+            }
+        }
+    }
+
+exit:
+    free(user_id_tracker);
+    return status;
+}
+
+int generate_conf_levels_payload_from_rc_config
+(
+   const struct sound_trigger_phrase_sound_model *phrase_sm,
+   const struct sound_trigger_recognition_config *rc_config,
+   unsigned char **out_payload,
+   unsigned int *out_payload_size
+)
+{
+    int status = 0;
+    unsigned int total_num_users = 0, num_conf_levels = 0;
+    unsigned int i = 0, j = 0;
+    unsigned char *conf_levels = NULL;
+
+    if (!phrase_sm || !rc_config || !out_payload || !out_payload_size) {
+        ALOGE("%s: ERROR. Invalid inputs",__func__);
+        status = -EINVAL;
+        goto exit;
+    }
+    *out_payload = NULL;
+    *out_payload_size = 0;
+
+    if((rc_config->num_phrases == 0) ||
+       (rc_config->num_phrases > phrase_sm->num_phrases)) {
+        ALOGE("%s: ERROR. Invalid phrases %d!=%d",__func__,
+              rc_config->num_phrases, phrase_sm->num_phrases);
+        status = -EINVAL;
+        goto exit;
+    }
+    for (i = 0; i < rc_config->num_phrases; i++) {
+        for (j = 0; j < rc_config->phrases[i].num_levels; j++)
+            total_num_users++;
+    }
+
+    num_conf_levels = total_num_users + rc_config->num_phrases;
+    conf_levels = calloc(1, num_conf_levels);
+    if (!conf_levels) {
+        ALOGE("%s: ERROR. conf levels alloc failed",__func__);
+        status = -ENOMEM;
+        goto exit;
+    }
+
+    status = fill_conf_levels_payload_from_rc_config(phrase_sm, rc_config,
+       conf_levels, num_conf_levels, total_num_users);
+    if (status) {
+        ALOGE("%s: fill config payload failed, error %d", __func__, status);
+        goto exit;
+    }
+
+    *out_payload = conf_levels;
+    *out_payload_size = num_conf_levels;
+
+    return status;
+
+exit:
+    if (conf_levels)
+        free(conf_levels);
+    return status;
+}
+
+int generate_conf_levels_payload_from_rc_config_v2
+(
+   const struct sound_trigger_phrase_sound_model *phrase_sm,
+   const struct sound_trigger_recognition_config *rc_config,
+   unsigned char **out_payload,
+   unsigned int *out_payload_size
+)
+{
+    int status = 0;
+    unsigned int total_num_users = 0, num_conf_levels = 0;
+    unsigned char *conf_levels = NULL;
+    unsigned int i = 0, j = 0;
+
+    ALOGV("%s: Enter...", __func__);
+
+    if (!phrase_sm || !rc_config || !out_payload || !out_payload_size) {
+        ALOGE("%s: ERROR. Invalid inputs",__func__);
+        status = -EINVAL;
+        goto exit;
+    }
+    *out_payload = NULL;
+    *out_payload_size = 0;
+
+    if((rc_config->num_phrases == 0) ||
+       (rc_config->num_phrases > phrase_sm->num_phrases)) {
+        ALOGE("%s: ERROR. Invalid phrases %d!=%d",__func__,
+              rc_config->num_phrases, phrase_sm->num_phrases);
+        status = -EINVAL;
+        goto exit;
+    }
+    for (i = 0; i < rc_config->num_phrases; i++) {
+        for (j = 0; j < rc_config->phrases[i].num_levels; j++)
+            total_num_users++;
+    }
+
+    num_conf_levels = total_num_users + rc_config->num_phrases;
+    /*
+     * allocate dsp payload w/additional 2 bytes for minor_version and
+     * num_active_models and additional num_conf_levels for KW enable
+     * fields
+     */
+    conf_levels = calloc(1, 2 + 2 * num_conf_levels);
+    if (!conf_levels) {
+        ALOGE("%s: ERROR. conf levels alloc failed",__func__);
+        status = -ENOMEM;
+        goto exit;
+    }
+
+    conf_levels[0] = 1; /* minor version */
+    conf_levels[1] = num_conf_levels; /* num_active_models */
+    status = fill_conf_levels_payload_from_rc_config(phrase_sm, rc_config,
+        conf_levels + 2, num_conf_levels, total_num_users);
+    if (status) {
+        ALOGE("%s: fill config payload failed, error %d", __func__, status);
+        goto exit;
+    }
+
+    /*
+     * set KW enable fields to 1 for now
+     * TODO: set appropriately based on what client is passing in rc_config
+     */
+    memset(&conf_levels[num_conf_levels + 2], 0x1, num_conf_levels);
+    ALOGV("%s: here", __func__);
+    *out_payload = conf_levels;
+    /* add size of minor version and num_active_models */
+    *out_payload_size = 2 + 2 * num_conf_levels;
+    return status;
+
+exit:
+    if (conf_levels)
+        free(conf_levels);
+    return status;
+}
+
+static int fill_sound_trigger_recognition_config_payload
+(
+   const void *sm_levels_generic,
+   unsigned char *conf_levels,
+   unsigned int num_conf_levels,
+   unsigned int total_num_users,
+   uint32_t version
+)
+{
+    int status = 0;
+    unsigned int user_level = 0, user_id = 0;
+    unsigned int i = 0, j = 0;
+    unsigned char *user_id_tracker = NULL;
+    struct st_sound_model_conf_levels *sm_levels = NULL;
+    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
+
+    /*  Example: Say the recognition structure has 3 keywords with users
+     *  |kid|
+     *  [0] k1 |uid|
+     *         [3] u1 - 1st trainer
+     *         [4] u2 - 4th trainer
+     *         [6] u3 - 3rd trainer
+     *  [1] k2
+     *         [5] u2 - 2nd trainer
+     *         [7] u3 - 5th trainer
+     *  [2] k3
+     *         [8] u4 - 6th trainer
+     *
+     *  Output confidence level array will be
+     *  [k1, k2, k3, u1k1, u2k1, u2k2, u3k1, u3k2, u4k3]
+     */
+
+    if (version != CONF_LEVELS_INTF_VERSION_0002) {
+        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
+        if (!sm_levels || !conf_levels || !num_conf_levels) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            return -EINVAL;
+        }
+        user_id_tracker = calloc(1, num_conf_levels);
+        if (!user_id_tracker) {
+            ALOGE("%s: failed to allocate user_id_tracker", __func__);
+            return -ENOMEM;
+        }
+
+        for (i = 0; i < sm_levels->num_kw_levels; i++) {
+            ALOGV("%s: [%d] kw level %d", __func__, i,
+                sm_levels->kw_levels[i].kw_level);
+            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++) {
+                ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
+                    sm_levels->kw_levels[i].user_levels[j].user_id,
+                    sm_levels->kw_levels[i].user_levels[j].level);
+            }
+        }
+
+        for (i = 0; i < sm_levels->num_kw_levels; i++) {
+            conf_levels[i] = sm_levels->kw_levels[i].kw_level;
+            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++) {
+                user_level = sm_levels->kw_levels[i].user_levels[j].level;
+                user_id = sm_levels->kw_levels[i].user_levels[j].user_id;
+                if ((user_id < sm_levels->num_kw_levels) ||
+                    (user_id >= num_conf_levels)) {
+                    ALOGE("%s: ERROR. Invalid params user id %d>%d",
+                        __func__, user_id, total_num_users);
+                    status = -EINVAL;
+                    goto exit;
+                } else {
+                    if (user_id_tracker[user_id] == 1) {
+                        ALOGE("%s: ERROR. Duplicate user id %d",
+                            __func__, user_id);
+                        status = -EINVAL;
+                        goto exit;
+                    }
+                    conf_levels[user_id] = (user_level < 100) ?
+                                           user_level: 100;
+                    user_id_tracker[user_id] = 1;
+                    ALOGV("%s: user_conf_levels[%d] = %d", __func__,
+                        user_id, conf_levels[user_id]);
+                }
+            }
+        }
+    } else {
+        sm_levels_v2 =
+            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
+        if (!sm_levels_v2 || !conf_levels || !num_conf_levels) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            return -EINVAL;
+        }
+        user_id_tracker = calloc(1, num_conf_levels);
+        if (!user_id_tracker) {
+            ALOGE("%s: failed to allocate user_id_tracker", __func__);
+            return -ENOMEM;
+        }
+
+        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
+            ALOGV("%s: [%d] kw level %d", __func__, i,
+                sm_levels_v2->kw_levels[i].kw_level);
+            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++) {
+                ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
+                     sm_levels_v2->kw_levels[i].user_levels[j].user_id,
+                     sm_levels_v2->kw_levels[i].user_levels[j].level);
+            }
+        }
+
+        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
+            conf_levels[i] = sm_levels_v2->kw_levels[i].kw_level;
+            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++) {
+                user_level = sm_levels_v2->kw_levels[i].user_levels[j].level;
+                user_id = sm_levels_v2->kw_levels[i].user_levels[j].user_id;
+                if ((user_id < sm_levels_v2->num_kw_levels) ||
+                    (user_id >= num_conf_levels)) {
+                    ALOGE("%s: ERROR. Invalid params user id %d>%d",
+                         __func__, user_id, total_num_users);
+                    status = -EINVAL;
+                    goto exit;
+                } else {
+                    if (user_id_tracker[user_id] == 1) {
+                        ALOGE("%s: ERROR. Duplicate user id %d",
+                            __func__, user_id);
+                        status = -EINVAL;
+                        goto exit;
+                    }
+                    conf_levels[user_id] = (user_level < 100) ?
+                                            user_level: 100;
+                    user_id_tracker[user_id] = 1;
+                    ALOGV("%s: user_conf_levels[%d] = %d", __func__,
+                        user_id, conf_levels[user_id]);
+                }
+            }
+        }
+    }
+
+exit:
+    free(user_id_tracker);
+    return status;
+}
+
+static int generate_sound_trigger_recognition_config_payload
+(
+   const void *sm_levels_generic,
+   unsigned char **out_payload,
+   unsigned int *out_payload_size,
+   uint32_t version
+)
+{
+    int status = 0;
+    unsigned int total_num_users = 0, num_conf_levels = 0;
+    unsigned char *conf_levels = NULL;
+    unsigned int i = 0, j = 0;
+    struct st_sound_model_conf_levels *sm_levels = NULL;
+    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
+
+    ALOGV("%s: Enter...", __func__);
+
+    if (version != CONF_LEVELS_INTF_VERSION_0002) {
+        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
+        if (!sm_levels || !out_payload || !out_payload_size) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        *out_payload = NULL;
+        *out_payload_size = 0;
+
+        if (sm_levels->num_kw_levels == 0) {
+            ALOGE("%s: ERROR. No confidence levels present", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        for (i = 0; i < sm_levels->num_kw_levels; i++) {
+            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++)
+                total_num_users++;
+        }
+
+        num_conf_levels = total_num_users + sm_levels->num_kw_levels;
+        conf_levels = calloc(1, num_conf_levels);
+        if (!conf_levels) {
+            ALOGE("%s: ERROR. conf levels alloc failed", __func__);
+            status = -ENOMEM;
+            goto exit;
+        }
+    } else {
+        sm_levels_v2 =
+            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
+        if (!sm_levels_v2 || !out_payload || !out_payload_size) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        *out_payload = NULL;
+        *out_payload_size = 0;
+
+        if (sm_levels_v2->num_kw_levels == 0) {
+            ALOGE("%s: ERROR. No confidence levels present", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
+            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++)
+                total_num_users++;
+        }
+
+        num_conf_levels = total_num_users + sm_levels_v2->num_kw_levels;
+        conf_levels = calloc(1, num_conf_levels);
+        if (!conf_levels) {
+            ALOGE("%s: ERROR. conf levels alloc failed", __func__);
+            status = -ENOMEM;
+            goto exit;
+        }
+    }
+
+    status = fill_sound_trigger_recognition_config_payload(sm_levels_generic,
+        conf_levels, num_conf_levels, total_num_users, version);
+    if (status) {
+        ALOGE("%s: fill config payload failed, error %d", __func__, status);
+        goto exit;
+    }
+
+    *out_payload = conf_levels;
+    *out_payload_size = num_conf_levels;
+
+    return status;
+
+exit:
+    if (conf_levels)
+        free(conf_levels);
+
+    return status;
+}
+
+static int generate_sound_trigger_recognition_config_payload_v2
+(
+   const void *sm_levels_generic,
+   unsigned char **out_payload,
+   unsigned int *out_payload_size,
+   uint32_t version
+)
+{
+    int status = 0;
+    unsigned int total_num_users = 0, num_conf_levels = 0;
+    unsigned char *conf_levels = NULL;
+    unsigned int i = 0, j = 0;
+    struct st_sound_model_conf_levels *sm_levels = NULL;
+    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
+
+    ALOGV("%s: Enter...", __func__);
+
+    if (version != CONF_LEVELS_INTF_VERSION_0002) {
+        sm_levels = (struct st_sound_model_conf_levels *)sm_levels_generic;
+        if (!sm_levels || !out_payload || !out_payload_size) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        *out_payload = NULL;
+        *out_payload_size = 0;
+
+        if (sm_levels->num_kw_levels == 0) {
+            ALOGE("%s: ERROR. No confidence levels present", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        for (i = 0; i < sm_levels->num_kw_levels; i++) {
+            for (j = 0; j < sm_levels->kw_levels[i].num_user_levels; j++)
+                total_num_users++;
+        }
+
+        num_conf_levels = total_num_users + sm_levels->num_kw_levels;
+    } else {
+        sm_levels_v2 =
+            (struct st_sound_model_conf_levels_v2 *)sm_levels_generic;
+        if (!sm_levels_v2 || !out_payload || !out_payload_size) {
+            ALOGE("%s: ERROR. Invalid inputs", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        *out_payload = NULL;
+        *out_payload_size = 0;
+
+        if (sm_levels_v2->num_kw_levels == 0) {
+            ALOGE("%s: ERROR. No confidence levels present", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        for (i = 0; i < sm_levels_v2->num_kw_levels; i++) {
+            for (j = 0; j < sm_levels_v2->kw_levels[i].num_user_levels; j++)
+                total_num_users++;
+        }
+        num_conf_levels = total_num_users + sm_levels_v2->num_kw_levels;
+    }
+
+    /*
+     * allocate dsp payload w/additional 2 bytes for minor_version and
+     * num_active_models and additional num_conf_levels for KW enable
+     * fields
+     */
+    conf_levels = calloc(1, 2 + 2 * num_conf_levels);
+    if (!conf_levels) {
+        ALOGE("%s: ERROR. conf levels alloc failed", __func__);
+        status = -ENOMEM;
+        goto exit;
+    }
+
+    conf_levels[0] = 1; /* minor version */
+    conf_levels[1] = num_conf_levels; /* num_active_models */
+    status = fill_sound_trigger_recognition_config_payload(sm_levels_generic,
+        conf_levels + 2, num_conf_levels, total_num_users, version);
+    if (status) {
+        ALOGE("%s: fill config payload failed, error %d", __func__, status);
+        goto exit;
+    }
+
+    /* set KW enable fields to 1 for now
+     * TODO set appropriately based on what client is passing in rc_config
+     */
+    memset(&conf_levels[num_conf_levels + 2], 0x1, num_conf_levels);
+    ALOGV("%s: here", __func__);
+    *out_payload = conf_levels;
+    /* add size of minor version and num_active_models */
+    *out_payload_size = 2 + 2 * num_conf_levels;
+
+    return status;
+
+exit:
+    if (conf_levels)
+        free(conf_levels);
+
+    return status;
+}
+
+static int parse_rc_config_key_conf_levels
+(
+    st_session_t *stc_ses,
+    st_hw_session_t *st_hw_ses,
+    void *opaque_conf_levels,
+    unsigned char **out_conf_levels,
+    unsigned int *out_num_conf_levels
+)
+{
+    struct st_confidence_levels_info *conf_levels = NULL;
+    struct st_confidence_levels_info_v2 *conf_levels_v2 = NULL;
+    struct st_sound_model_conf_levels *sm_levels = NULL;
+    struct st_sound_model_conf_levels_v2 *sm_levels_v2 = NULL;
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    struct listnode *node = NULL;
+    st_lsm_ss_config_t *ss_cfg = NULL;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    int status = 0;
+    uint32_t i = 0;
+    bool gmm_conf_found = false;
+    uint8_t confidence_level = 0;
+    int32_t confidence_level_v2 = 0;
+    bool arm_second_stage = !list_empty(&stc_ses->second_stage_list);
+    bool adsp_second_stage = (st_hw_ses == st_ses->hw_ses_adsp &&
+                              !list_empty(&st_hw_ses->lsm_ss_cfg_list));
+
+    if (arm_second_stage || adsp_second_stage) {
+        if (stc_ses->rc_config->num_phrases > 1) {
+            ALOGE("%s: Multi keyword is unsupported with 2nd stage detection",
+                  __func__);
+            return -EINVAL;
+        }
+
+        if (stc_ses->rc_config->phrases[0].num_levels > 1) {
+            ALOGE("%s: Multi user is unsupported with 2nd stage detection",
+                  __func__);
+            return -EINVAL;
+        }
+    }
+
+    if (stc_ses->st_conf_levels) {
+        free(stc_ses->st_conf_levels);
+        stc_ses->st_conf_levels = NULL;
+    }
+
+    if (stc_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002) {
+        conf_levels = (struct st_confidence_levels_info *)
+            ((char *)opaque_conf_levels + sizeof(struct st_param_header));
+
+        stc_ses->st_conf_levels =
+            calloc(1, sizeof(struct st_confidence_levels_info));
+        if (!stc_ses->st_conf_levels) {
+            ALOGE("%s: failed to alloc st_conf_levels", __func__);
+            return -ENOMEM;
+        }
+        /* Cache to use during detection event processing */
+        memcpy(stc_ses->st_conf_levels, (char *)conf_levels,
+            sizeof(struct st_confidence_levels_info));
+
+        for (i = 0; i < conf_levels->num_sound_models; i++) {
+            sm_levels = &conf_levels->conf_levels[i];
+            if (sm_levels->sm_id == ST_SM_ID_SVA_GMM) {
+                if ((st_ses->stdev->is_gcs) && (st_hw_ses == st_ses->hw_ses_cpe))
+                    status =
+                        generate_sound_trigger_recognition_config_payload_v2(
+                        (void *)sm_levels, out_conf_levels, out_num_conf_levels,
+                        stc_ses->conf_levels_intf_version);
+                else
+                    status =
+                        generate_sound_trigger_recognition_config_payload(
+                        (void *)sm_levels, out_conf_levels, out_num_conf_levels,
+                        stc_ses->conf_levels_intf_version);
+                gmm_conf_found = true;
+            } else if ((sm_levels->sm_id == ST_SM_ID_SVA_CNN) ||
+                       (sm_levels->sm_id == ST_SM_ID_SVA_VOP)) {
+                confidence_level = (sm_levels->sm_id == ST_SM_ID_SVA_CNN) ?
+                    sm_levels->kw_levels[0].kw_level:
+                    sm_levels->kw_levels[0].user_levels[0].level;
+                if (arm_second_stage) {
+                    list_for_each(node, &stc_ses->second_stage_list) {
+                        st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                            list_node);
+                        if (st_sec_stage->ss_info->sm_id == sm_levels->sm_id)
+                            st_sec_stage->ss_session->confidence_threshold =
+                                confidence_level;
+                    }
+                } else if (adsp_second_stage) {
+                    list_for_each(node, &st_hw_ses->lsm_ss_cfg_list) {
+                        ss_cfg = node_to_item(node, st_lsm_ss_config_t,
+                            list_node);
+                        if (ss_cfg->ss_info->sm_id == sm_levels->sm_id)
+                            ss_cfg->confidence_threshold = confidence_level;
+                    }
+                }
+            } else {
+                ALOGE("%s: Unsupported sm id (%d), exiting", __func__,
+                    sm_levels->sm_id);
+                status = -EINVAL;
+                break;
+            }
+        }
+    } else {
+        conf_levels_v2 = (struct st_confidence_levels_info_v2 *)
+            ((char *)opaque_conf_levels + sizeof(struct st_param_header));
+
+        stc_ses->st_conf_levels =
+            calloc(1, sizeof(struct st_confidence_levels_info_v2));
+        if (!stc_ses->st_conf_levels) {
+            ALOGE("%s: failed to alloc st_conf_levels", __func__);
+            return -ENOMEM;
+        }
+                /* Cache to use during detection event processing */
+        memcpy(stc_ses->st_conf_levels, (char *)conf_levels_v2,
+            sizeof(struct st_confidence_levels_info_v2));
+
+        for (i = 0; i < conf_levels_v2->num_sound_models; i++) {
+            sm_levels_v2 = &conf_levels_v2->conf_levels[i];
+            if (sm_levels_v2->sm_id == ST_SM_ID_SVA_GMM) {
+                if ((st_ses->stdev->is_gcs) &&
+                    (st_hw_ses == st_ses->hw_ses_cpe))
+                    status =
+                        generate_sound_trigger_recognition_config_payload_v2(
+                        (void *)sm_levels_v2, out_conf_levels, out_num_conf_levels,
+                        stc_ses->conf_levels_intf_version);
+                else
+                    status =
+                        generate_sound_trigger_recognition_config_payload(
+                        (void *)sm_levels_v2, out_conf_levels,
+                        out_num_conf_levels, stc_ses->conf_levels_intf_version);
+                gmm_conf_found = true;
+            } else if ((sm_levels_v2->sm_id == ST_SM_ID_SVA_CNN) ||
+                       (sm_levels_v2->sm_id == ST_SM_ID_SVA_VOP)) {
+                confidence_level_v2 =
+                    (sm_levels_v2->sm_id == ST_SM_ID_SVA_CNN) ?
+                    sm_levels_v2->kw_levels[0].kw_level:
+                    sm_levels_v2->kw_levels[0].user_levels[0].level;
+                if (arm_second_stage) {
+                    list_for_each(node, &stc_ses->second_stage_list) {
+                        st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                            list_node);
+                        if (st_sec_stage->ss_info->sm_id ==
+                            sm_levels_v2->sm_id)
+                            st_sec_stage->ss_session->confidence_threshold =
+                                confidence_level_v2;
+                    }
+                } else if (adsp_second_stage) {
+                    list_for_each(node, &st_hw_ses->lsm_ss_cfg_list) {
+                        ss_cfg = node_to_item(node, st_lsm_ss_config_t,
+                            list_node);
+                        if (ss_cfg->ss_info->sm_id == sm_levels_v2->sm_id)
+                            ss_cfg->confidence_threshold = confidence_level_v2;
+                    }
+                }
+            } else {
+                ALOGE("%s: Unsupported sm id (%d), exiting", __func__,
+                    sm_levels_v2->sm_id);
+                status = -EINVAL;
+                break;
+            }
+        }
+    }
+
+    if (!gmm_conf_found) {
+        ALOGE("%s: Did not receive GMM confidence threshold, error!", __func__);
+        status  = -EINVAL;
+    }
+
+    if (status && stc_ses->st_conf_levels) {
+        free(stc_ses->st_conf_levels);
+        stc_ses->st_conf_levels = NULL;
+    }
+    return status;
+}
+
+static int update_hw_config_on_start(st_session_t *stc_ses,
+    st_hw_session_t *st_hw_ses)
+{
+    struct st_param_header *param_hdr = NULL;
+    struct st_hist_buffer_info *hist_buf = NULL;
+    struct st_det_perf_mode_info *det_perf_mode = NULL;
+    struct sound_trigger_recognition_config *rc_config = stc_ses->rc_config;
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    struct st_vendor_info *v_info = NULL;
+    struct st_hw_ses_config *sthw_cfg = NULL;
+    unsigned char *conf_levels = NULL;
+    unsigned int num_conf_levels = 0;
+    uint8_t *opaque_ptr = NULL;
+    unsigned int opaque_size = 0, conf_levels_payload_size = 0;
+    int status = 0;
+    bool enable_lab = false;
+
+
+    ST_DBG_DECLARE(FILE *rc_opaque_fd = NULL; static int rc_opaque_cnt = 0);
+    ST_DBG_FILE_OPEN_WR(rc_opaque_fd, ST_DEBUG_DUMP_LOCATION,
+                        "rc_config_opaque_data", "bin", rc_opaque_cnt++);
+    ST_DBG_FILE_WRITE(rc_opaque_fd,
+                      (uint8_t *)rc_config + rc_config->data_offset,
+                      rc_config->data_size);
+    ST_DBG_FILE_CLOSE(rc_opaque_fd);
+
+    if (!st_hw_ses) {
+        ALOGE("%s: NULL hw session !!!", __func__);
+        return -EINVAL;
+    }
+
+    v_info = st_hw_ses->vendor_uuid_info;
+
+    if ((rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
+        v_info->is_qcva_uuid) {
+        stc_ses->client_req_det_mode = ST_DET_UNKNOWN_MODE;
+
+        opaque_ptr = (uint8_t *)rc_config + rc_config->data_offset;
+        while (opaque_size < rc_config->data_size) {
+            param_hdr = (struct st_param_header *)opaque_ptr;
+            ALOGV("%s: key %d, payload size %d", __func__,
+                  param_hdr->key_id, param_hdr->payload_size);
+
+            switch(param_hdr->key_id) {
+            case ST_PARAM_KEY_CONFIDENCE_LEVELS:
+                stc_ses->conf_levels_intf_version =
+                    *(uint32_t *)(opaque_ptr + sizeof(struct st_param_header));
+
+                if (stc_ses->conf_levels_intf_version !=
+                    CONF_LEVELS_INTF_VERSION_0002) {
+                    conf_levels_payload_size =
+                        sizeof(struct st_confidence_levels_info);
+                } else {
+                    conf_levels_payload_size =
+                        sizeof(struct st_confidence_levels_info_v2);
+                }
+                if (param_hdr->payload_size != conf_levels_payload_size) {
+                    ALOGE("%s: Conf level format error, exiting", __func__);
+                    return -EINVAL;
+                }
+                status = parse_rc_config_key_conf_levels(stc_ses, st_hw_ses,
+                    opaque_ptr, &conf_levels, &num_conf_levels);
+                opaque_size += sizeof(struct st_param_header) +
+                    conf_levels_payload_size;
+                opaque_ptr += sizeof(struct st_param_header) +
+                    conf_levels_payload_size;
+                if (status) {
+                    ALOGE("%s: parsing conf levels failed(status=%d)",
+                        __func__, status);
+                    return -EINVAL;
+                }
+                break;
+            case ST_PARAM_KEY_HISTORY_BUFFER_CONFIG:
+                if (param_hdr->payload_size !=
+                    sizeof(struct st_hist_buffer_info)) {
+                    ALOGE("%s: History buffer config format error, exiting",
+                          __func__);
+                    return -EINVAL;
+                }
+                hist_buf = (struct st_hist_buffer_info *)(opaque_ptr +
+                    sizeof(struct st_param_header));
+                stc_ses->hist_buf_duration =
+                    hist_buf->hist_buffer_duration_msec;
+                stc_ses->preroll_duration = hist_buf->pre_roll_duration_msec;
+                ALOGV("%s: recognition config history buf len = %d, "
+                      "preroll len = %d, minor version = %d",
+                      __func__, hist_buf->hist_buffer_duration_msec,
+                      hist_buf->pre_roll_duration_msec, hist_buf->version);
+                opaque_size += sizeof(struct st_param_header) +
+                    sizeof(struct st_hist_buffer_info);
+                opaque_ptr += sizeof(struct st_param_header) +
+                    sizeof(struct st_hist_buffer_info);
+                break;
+            case ST_PARAM_KEY_DETECTION_PERF_MODE:
+                if (param_hdr->payload_size !=
+                    sizeof(struct st_det_perf_mode_info)) {
+                    ALOGE("%s: Opaque data format error, exiting", __func__);
+                    return -EINVAL;
+                }
+                det_perf_mode = (struct st_det_perf_mode_info *)(opaque_ptr +
+                    sizeof(struct st_param_header));
+                ALOGV("set perf mode to %d", det_perf_mode->mode);
+                stc_ses->client_req_det_mode = det_perf_mode->mode;
+                opaque_size += sizeof(struct st_param_header) +
+                    sizeof(struct st_det_perf_mode_info);
+                opaque_ptr += sizeof(struct st_param_header) +
+                    sizeof(struct st_det_perf_mode_info);
+                break;
+            default:
+                ALOGE("%s: Unsupported opaque data key id, exiting", __func__);
+                return -EINVAL;
+            }
+        }
+    } else if (stc_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        struct sound_trigger_phrase_sound_model *phrase_sm = stc_ses->phrase_sm;
+
+        ALOGV("%s: num_phrases=%d, id=%d", __func__,
+               rc_config->num_phrases, rc_config->phrases[0].id);
+
+        if (st_ses->vendor_uuid_info->is_qcva_uuid ||
+            st_ses->vendor_uuid_info->is_qcmd_uuid) {
+            if (st_ses->stdev->is_gcs && st_hw_ses == st_ses->hw_ses_cpe)
+                status = generate_conf_levels_payload_from_rc_config_v2(
+                    phrase_sm, rc_config, &conf_levels, &num_conf_levels);
+            else
+                status = generate_conf_levels_payload_from_rc_config(
+                    phrase_sm, rc_config, &conf_levels, &num_conf_levels);
+            if (status || !conf_levels) {
+                ALOGE("%s: failed to get conf levels from lib handle",
+                      __func__);
+                return status;
+            }
+        } else {
+            ALOGD("%s: No smlib, opaque data would be sent as is", __func__);
+        }
+    }
+
+    sthw_cfg = &st_hw_ses->sthw_cfg;
+    enable_lab = stc_ses->rc_config->capture_requested ||
+                 !list_empty(&stc_ses->second_stage_list);
+
+    if (v_info->merge_fs_soundmodels) {
+        /* merge_fs_soundmodels is true only for QC SVA UUID */
+
+         /*
+          * Note:
+          * For ADSP case, the generated conf levles size must be equal to
+          * SML queried conf levels.
+          * For WDSP gcs case, there is additional payload for KW enable
+          * fields in generated conf_levels. If merge sound model is supported
+          * on WDSP case, update logic here accordingly.
+          */
+        if (num_conf_levels != stc_ses->sm_info.cf_levels_size) {
+            ALOGE("%s: Unexpected, client cf levels %d != sm_info cf levels %d",
+                __func__, num_conf_levels, stc_ses->sm_info.cf_levels_size);
+            return -EINVAL;
+        }
+
+        /*
+         * If any of the active clients requested capture or enabled the
+         * second stage, the underlying hw session buffering needs to
+         * be enabled. Ignore if it is already enabled.
+         */
+        if (!st_ses->lab_enabled && enable_lab)
+            st_ses->lab_enabled = true;
+
+        /* Aggregate DSP configuration for highest client configuration */
+
+        /* SVA2.0 sound models */
+        if (!stc_ses->hist_buf_duration &&
+            stc_ses->rc_config->capture_requested &&
+            (stc_ses->rc_config->data_size > 0)) {
+            stc_ses->hist_buf_duration = st_ses->vendor_uuid_info->kw_duration;
+            stc_ses->preroll_duration = 0;
+        }
+
+        if (stc_ses->hist_buf_duration > sthw_cfg->client_req_hist_buf)
+            sthw_cfg->client_req_hist_buf = stc_ses->hist_buf_duration;
+        if (stc_ses->preroll_duration > sthw_cfg->client_req_preroll)
+            sthw_cfg->client_req_preroll = stc_ses->preroll_duration;
+
+        ALOGV("%s: client hb_sz %d pr_sz %d, sthw lab %d hb_sz %d "
+              "pr_sz %d", __func__, stc_ses->hist_buf_duration,
+              stc_ses->preroll_duration, st_ses->lab_enabled,
+              sthw_cfg->client_req_hist_buf, sthw_cfg->client_req_preroll);
+
+        /* Cache it to use when client restarts without config update or
+         * during only one remaining client model as there won't be a
+         * merged model yet.
+         */
+        memcpy(stc_ses->sm_info.cf_levels, conf_levels,
+               stc_ses->sm_info.cf_levels_size);
+
+        status = update_merge_conf_levels_payload(st_ses, &stc_ses->sm_info,
+            conf_levels, num_conf_levels, true);
+        free(conf_levels); /* Merged model conf levels will be used further */
+        if (status)
+            return status;
+
+        sthw_cfg->conf_levels = st_ses->sm_info.cf_levels;
+        sthw_cfg->num_conf_levels = st_ses->sm_info.cf_levels_size;
+        st_hw_ses->sthw_cfg_updated = true;
+
+        /*
+         * Merging further unknown custom data is not needed, as
+         * SVA doesn't support unkown custom data. if required in future,
+         * handle here.
+         * For now just copy the the current client data which is same
+         * across SVA engines.
+         */
+        if (!sthw_cfg->custom_data) {
+            sthw_cfg->custom_data = (char *)rc_config + rc_config->data_offset;
+            if (rc_config->data_size)
+                sthw_cfg->custom_data_size =  rc_config->data_size;
+        }
+
+    } else {
+        st_ses->recognition_mode = stc_ses->recognition_mode;
+        st_ses->lab_enabled = enable_lab;
+
+        sthw_cfg->client_req_hist_buf = stc_ses->hist_buf_duration;
+        sthw_cfg->client_req_preroll = stc_ses->preroll_duration;
+
+        if (sthw_cfg->conf_levels)
+            free(sthw_cfg->conf_levels);
+        sthw_cfg->conf_levels = conf_levels;
+        sthw_cfg->num_conf_levels = num_conf_levels;
+
+        sthw_cfg->custom_data = (char *)rc_config + rc_config->data_offset;
+        sthw_cfg->custom_data_size =  rc_config->data_size;
+    }
+    ALOGD("%s:[%d] lab enabled %d", __func__, st_ses->sm_handle,
+          st_ses->lab_enabled);
+
+    return status;
+}
+
+static void do_hw_sess_cleanup(st_proxy_session_t *st_ses,
+    st_hw_session_t *hw_ses, enum hw_session_err_mask err)
 {
     if (err & HW_SES_ERR_MASK_BUFFERING)
-        hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
+        hw_ses->fptrs->stop_buffering(hw_ses);
 
     if (err & HW_SES_ERR_MASK_STARTED) {
         hw_ses->fptrs->stop(hw_ses);
@@ -237,43 +2346,49 @@
         hw_ses->fptrs->set_device(hw_ses, false);
 
     if (err & HW_SES_ERR_MASK_REG_SM)
-        hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
+        hw_ses->fptrs->dereg_sm(hw_ses);
 }
 
-static void reg_hal_event_session(st_session_t *p_ses, st_hw_session_t *hw_ses)
+static void reg_hal_event_session(st_session_t *stc_ses,
+    st_hw_session_t *hw_ses)
 {
     struct sound_trigger_event_info event_info;
     /* Pass the pcm information to audio hal for capturing LAB */
-    if (p_ses->lab_enabled && p_ses->stdev->audio_hal_cb) {
-        ALOGD("%s: ST_EVENT_SESSION_REGISTER capture_handle %d p_ses %p",
-              __func__, p_ses->capture_handle, (void *)p_ses);
-        event_info.st_ses.p_ses = (void *)p_ses;
+    if ((stc_ses->rc_config &&
+         stc_ses->rc_config->capture_requested) &&
+        stc_ses->stdev->audio_hal_cb) {
+        ALOGD("%s:[c%d] ST_EVENT_SESSION_REGISTER capture_handle %d",
+           __func__, stc_ses->sm_handle, stc_ses->capture_handle);
+        event_info.st_ses.p_ses = (void *)stc_ses;
         event_info.st_ses.config = hw_ses->config;
-        event_info.st_ses.capture_handle = p_ses->capture_handle;
+        event_info.st_ses.capture_handle = stc_ses->capture_handle;
         /*
          * set pcm to NULL as this version of st_hal doesn't pass pcm to
          * audio HAL
          */
         event_info.st_ses.pcm = NULL;
-        p_ses->stdev->audio_hal_cb(ST_EVENT_SESSION_REGISTER, &event_info);
+        stc_ses->stdev->audio_hal_cb(ST_EVENT_SESSION_REGISTER, &event_info);
     }
 }
 
-static void dereg_hal_event_session(st_session_t *p_ses)
+static void dereg_hal_event_session(st_session_t *stc_ses)
 {
     struct sound_trigger_event_info event_info;
-    /* Indicate to audio hal that buffering is stopped to stop reading LAB data */
-    if (p_ses->lab_enabled && p_ses->stdev->audio_hal_cb) {
-        ALOGD("%s: ST_EVENT_SESSION_DEREGISTER capture_handle %d p_ses %p",
-           __func__, p_ses->capture_handle, p_ses);
-        event_info.st_ses.p_ses = (void *)p_ses;
-        event_info.st_ses.capture_handle = p_ses->capture_handle;
+    /* Indicate to audio hal that to stop reading LAB data */
+    if ((stc_ses->rc_config &&
+         stc_ses->rc_config->capture_requested) &&
+        stc_ses->stdev->audio_hal_cb) {
+        ALOGD("%s:[c%d] ST_EVENT_SESSION_DEREGISTER capture_handle %d",
+           __func__, stc_ses->sm_handle, stc_ses->capture_handle);
+        event_info.st_ses.p_ses = (void *)stc_ses;
+        event_info.st_ses.capture_handle = stc_ses->capture_handle;
         event_info.st_ses.pcm = NULL;
-        p_ses->stdev->audio_hal_cb(ST_EVENT_SESSION_DEREGISTER, &event_info);
+        stc_ses->stdev->audio_hal_cb(ST_EVENT_SESSION_DEREGISTER, &event_info);
     }
 }
 
-static int start_hw_session(st_session_t *st_ses, st_hw_session_t *hw_ses, bool load_sm)
+static int start_hw_session(st_proxy_session_t *st_ses, st_hw_session_t *hw_ses,
+    bool load_sm)
 {
     int status = 0, err = 0;
 
@@ -287,7 +2402,7 @@
         hw_ses->lpi_enable = hw_ses->stdev->lpi_enable;
         if (!load_sm) {
             load_sm = true;
-            status = hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
+            status = hw_ses->fptrs->dereg_sm(hw_ses);
             if (status)
                 ALOGW("%s:[%d] failed to dereg_sm err %d", __func__,
                     st_ses->sm_handle, status);
@@ -295,8 +2410,8 @@
     }
 
     if (load_sm) {
-        status = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_data,
-            st_ses->sm_type);
+        status = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_info.sm_data,
+            st_ses->sm_info.sm_size, st_ses->sm_info.sm_type);
         if (status) {
             ALOGE("%s:[%d] failed to reg_sm err %d", __func__,
                 st_ses->sm_handle, status);
@@ -313,24 +2428,9 @@
     }
     err |= HW_SES_ERR_MASK_DEVICE_SET;
 
-    /*
-     * Check for rc_config update before reg_sm_param,
-     * as hw session can change from transitions,
-     * and hence related config might also need to be updated.
-     */
-    if (hw_ses->rc_config != st_ses->rc_config ||
-        hw_ses->rc_config_update_counter != st_ses->rc_config_update_counter) {
-        status = st_hw_ses_update_config(st_ses, hw_ses);
-        if (status) {
-            ALOGE("%s: ERROR. updating rc_config, returned status %d",
-                  __func__, status);
-            goto cleanup;
-        }
-    }
-
-    status = hw_ses->fptrs->reg_sm_params(hw_ses,
-        st_ses->recognition_mode, st_ses->lab_enabled,
-        st_ses->rc_config, st_ses->sm_type, st_ses->sm_data);
+    status = hw_ses->fptrs->reg_sm_params(hw_ses, st_ses->recognition_mode,
+        st_ses->lab_enabled, st_ses->rc_config, st_ses->sm_info.sm_type,
+        st_ses->sm_info.sm_data);
     if (status) {
         ALOGE("%s:[%d] failed to reg_sm_params err %d", __func__,
             st_ses->sm_handle, status);
@@ -354,7 +2454,8 @@
     return status;
 }
 
-static int stop_hw_session(st_session_t *st_ses, st_hw_session_t *hw_ses, bool unload_sm)
+static int stop_hw_session(st_proxy_session_t *st_ses, st_hw_session_t *hw_ses,
+    bool unload_sm)
 {
     int status = 0;
     int rc = 0;
@@ -380,7 +2481,7 @@
         rc = status;
     }
     if (unload_sm) {
-        status = hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
+        status = hw_ses->fptrs->dereg_sm(hw_ses);
         if (status) {
             ALOGE("%s:[%d] failed to dereg_sm, err %d", __func__,
                 st_ses->sm_handle, status);
@@ -395,79 +2496,75 @@
     return rc;
 }
 
-static int start_session(st_session_t *st_ses, st_hw_session_t *hw_ses, bool load_sm)
+static int start_session(st_proxy_session_t *st_ses, st_hw_session_t *hw_ses,
+    bool load_sm)
 {
     int status = 0;
+
     if (st_ses->hw_session_started) {
         ALOGE("%s:[%d] already started", __func__, st_ses->sm_handle);
         return -1;
     }
-    /*
-     * The reg_hal_event_session call must be after start_hw_session. This is
-     * important for when load_sm is true, because reg_sm sets the correct pcm
-     * config for the current hw session. That pcm config is then sent to audio hal.
-     */
+
     status = start_hw_session(st_ses, hw_ses, load_sm);
-    if (!status)
-        reg_hal_event_session(st_ses, hw_ses);
+    hw_ses->sthw_cfg_updated = false;
+
     return status;
 }
 
-static int restart_session(st_session_t *st_ses, st_hw_session_t *hw_ses)
+static int restart_session(st_proxy_session_t *st_ses, st_hw_session_t *hw_ses)
 {
-    int status = hw_ses->fptrs->restart(hw_ses, st_ses->recognition_mode,
-                        st_ses->lab_enabled, st_ses->rc_config,
-                        st_ses->sm_type, st_ses->sm_data);
+    int status = 0;
+
+    status = hw_ses->fptrs->restart(hw_ses, st_ses->recognition_mode,
+            st_ses->rc_config, st_ses->sm_info.sm_type,
+            st_ses->sm_info.sm_data);
     if (status == 0) {
         st_ses->hw_session_started = true;
     } else {
-        ALOGE("%s:[%d] failed to restart, stop session", __func__, st_ses->sm_handle);
-        /*
-         * lower layers like gcs/lsm need to handle double stop calls properly
-         * to avoid possible crash, as some of the clean ups are already issued
-         * during fptrs->restart() when it's failed.
-         */
-        stop_hw_session(st_ses, hw_ses, true);
+        ALOGE("%s:[%d] failed to restart", __func__, st_ses->sm_handle);
+        st_ses->hw_session_started = false;
     }
+
     return status;
 }
 
-static int stop_session(st_session_t *st_ses, st_hw_session_t *hw_ses, bool unload_sm)
+static int stop_session(st_proxy_session_t *st_ses,
+    st_hw_session_t *hw_ses, bool unload_sm)
 {
     if (!st_ses->hw_session_started) {
         ALOGV("%s:[%d] already stopped", __func__, st_ses->sm_handle);
         return 0;
     }
-    dereg_hal_event_session(st_ses);
-    if (st_ses->detection_requested) {
-        st_ses->detection_requested = false;
-        enable_second_stage_processing(st_ses, hw_ses);
-    }
+    st_ses->detection_requested = false;
     return stop_hw_session(st_ses, hw_ses, unload_sm);
 }
 
 /*
- * This function gets the first stage detection keyword indices, which are needed
- * by the second stage sessions. If the legacy DSP is used, which does not provide
- * keyword indices, set the indices to include the entire keyword duration. This
- * function also gets the user confidence level if there is an active voiceprint
- * session.
+ * This function gets the first stage detection keyword indices, which are
+ * needed by the second stage sessions. If the legacy DSP is used, which does
+ * not provide keyword indices, set the indices to include the entire keyword
+ * duration. This function also gets the user confidence level if there is an
+ * active voiceprint session.
  */
-static int get_first_stage_detection_params(st_session_t *st_ses, void *payload,
-                                            size_t payload_size)
+static int get_first_stage_detection_params(st_proxy_session_t *st_ses,
+    void *payload, size_t payload_size)
 {
     size_t count_size = 0;
     uint8_t *payload_ptr = (uint8_t *)payload;
-    uint32_t key_id = 0, key_payload_size = 0;
+    uint8_t *cf_levels = NULL;
+    uint32_t key_id = 0, key_payload_size = 0, cf_levels_size = 0;
     uint32_t kw_start_ms = 0, kw_end_ms = 0;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
-    struct listnode *node = NULL, *tmp_node = NULL;
+    struct listnode *node = NULL;
     st_arm_second_stage_t *st_sec_stage = NULL;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
     bool is_active_vop_session = false;
 
-    list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
+    list_for_each(node, &stc_ses->second_stage_list) {
         st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-        if (st_sec_stage->ss_info->sm_detection_type == ST_SM_TYPE_USER_VERIFICATION) {
+        if (st_sec_stage->ss_info->sm_detection_type ==
+            ST_SM_TYPE_USER_VERIFICATION) {
             is_active_vop_session = true;
             break;
         }
@@ -486,8 +2583,25 @@
             switch (key_id) {
             case KEY_ID_CONFIDENCE_LEVELS:
                 if (is_active_vop_session) {
-                    hw_ses->user_level = (int32_t)(*(payload_ptr +
-                        GENERIC_DET_EVENT_USER_LEVEL_OFFSET));
+                    /*
+                     * It is expected that VoP is supported with single KW/user
+                     * SVA3.0 model, hence get it directly with hard offset.
+                     */
+                    if (!st_ses->sm_info.sm_merged) {
+                        hw_ses->user_level = (int32_t)(*(payload_ptr +
+                            GENERIC_DET_EVENT_USER_LEVEL_OFFSET));
+                    } else {
+                        /* Extract from first stage merged conf levels */
+                        check_and_extract_det_conf_levels_payload(st_ses,
+                            payload_ptr + (4 * sizeof(uint32_t)),
+                            *((uint32_t *)payload_ptr + 3),
+                            &cf_levels, &cf_levels_size);
+                        if (!cf_levels || !cf_levels_size)
+                            break;
+                        hw_ses->user_level = cf_levels[1];
+                        ALOGV("%s:hw_ses->user_level %d at cf_levels[1]",
+                              __func__, hw_ses->user_level);
+                    }
                 }
                 break;
 
@@ -515,9 +2629,9 @@
          * duration from platform xml will be used.
          */
         hw_ses->kw_start_idx = 0;
-        if (hw_ses->client_req_hist_buf) {
+        if (hw_ses->sthw_cfg.client_req_hist_buf) {
             hw_ses->kw_end_idx =
-                convert_ms_to_bytes(hw_ses->client_req_hist_buf,
+                convert_ms_to_bytes(hw_ses->sthw_cfg.client_req_hist_buf,
                     &hw_ses->config);
         } else {
             hw_ses->kw_end_idx =
@@ -531,7 +2645,7 @@
                 hw_ses->user_level = (int32_t)(*(payload_ptr +
                     GCS_NON_GENERIC_USER_LEVEL_OFFSET));
             } else if ((st_ses->exec_mode == ST_EXEC_MODE_ADSP) ||
-                      !st_ses->stdev->is_gcs) {
+                       !st_ses->stdev->is_gcs) {
                 hw_ses->user_level = (int32_t)(*(payload_ptr +
                     LSM_NON_GENERIC_USER_LEVEL_OFFSET));
             }
@@ -540,64 +2654,1372 @@
 
     kw_start_ms = convert_bytes_to_ms(hw_ses->kw_start_idx, &hw_ses->config);
     kw_end_ms = convert_bytes_to_ms(hw_ses->kw_end_idx, &hw_ses->config);
-    ALOGD("%s:[%d] 1st stage kw_start = %dms, kw_end = %dms, is_generic_event %d",
-        __func__, st_ses->sm_handle, kw_start_ms, kw_end_ms,
-        hw_ses->is_generic_event);
+    ALOGD("%s:[%d] 1st stage kw_start = %dms, kw_end = %dms,"
+          "is_generic_event %d", __func__, st_ses->sm_handle,
+          kw_start_ms, kw_end_ms, hw_ses->is_generic_event);
 
     return 0;
 }
 
-static inline void stop_second_stage_session(st_session_t *st_ses)
+static inline int prepapre_second_stage_for_client(st_session_t *stc_ses)
 {
-    struct listnode *node = NULL, *tmp_node = NULL;
+    struct listnode *node = NULL;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    int status = 0;
+
+    ALOGV("%s:[c%d]", __func__, stc_ses->sm_handle);
+
+    list_for_each(node, &stc_ses->second_stage_list) {
+        st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+        status = st_second_stage_prepare_session(st_sec_stage);
+    }
+    return status;
+}
+
+static inline int start_second_stage_for_client(st_session_t *stc_ses)
+{
+    struct listnode *node = NULL;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    int status = 0;
+
+    ALOGV("%s:[c%d]", __func__, stc_ses->sm_handle);
+
+    list_for_each(node, &stc_ses->second_stage_list) {
+        st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+        status = st_second_stage_start_session(st_sec_stage);
+    }
+    return status;
+}
+
+static inline void stop_second_stage_for_client(st_session_t *stc_ses)
+{
+    struct listnode *node = NULL;
     st_arm_second_stage_t *st_sec_stage = NULL;
 
-    list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
+    ALOGV("%s:[c%d]", __func__, stc_ses->sm_handle);
+
+    list_for_each(node, &stc_ses->second_stage_list) {
         st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
         st_second_stage_stop_session(st_sec_stage);
     }
 }
 
-static int idle_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int generate_legacy_st_phrase_recognition_event
+(
+    const struct sound_trigger_phrase_sound_model *phrase_sm,
+    const struct sound_trigger_recognition_config *rc_config,
+    const void *payload,
+    unsigned int payload_size,
+    struct sound_trigger_phrase_recognition_event **out_rc_event
+)
+{
+    struct sound_trigger_phrase_recognition_event *event;
+    unsigned int i = 0, j = 0, user_id = 0;
+
+    ALOGD("%s: Enter payload_size %d", __func__, payload_size);
+
+    if(!payload || !phrase_sm || !rc_config || !out_rc_event) {
+        ALOGE("%s: Null params", __func__);
+        return -EINVAL;
+    }
+
+    *out_rc_event = NULL;
+    event = calloc(1, sizeof(*event) + payload_size);
+    if (!event) {
+        ALOGE("%s: event allocation failed size %d", __func__, payload_size);
+        return -ENODEV;
+    }
+
+    event->num_phrases = rc_config->num_phrases;
+    event->common.data_offset = sizeof(*event);
+    event->common.data_size = payload_size;
+    memcpy((char *)event + event->common.data_offset, payload, payload_size);
+
+    /* fill confidence levels */
+    for (i = 0; i < rc_config->num_phrases; i++) {
+        event->phrase_extras[i].id = rc_config->phrases[i].id;
+        event->phrase_extras[i].recognition_modes =
+                                 phrase_sm->phrases[0].recognition_mode;
+        event->phrase_extras[i].confidence_level = ((char *)payload)[i];
+        event->phrase_extras[i].num_levels =  rc_config->phrases[i].num_levels;
+        for (j = 0; j < rc_config->phrases[i].num_levels; j++) {
+            user_id = rc_config->phrases[i].levels[j].user_id;
+            event->phrase_extras[i].levels[j].user_id = user_id;
+            event->phrase_extras[i].levels[j].level =
+                                             ((char *)payload)[user_id];
+        }
+    }
+
+    *out_rc_event = event;
+    return 0;
+}
+
+/*
+ * This function sets the opaque data size for the DSP's generic detection
+ * events. This opaque data can now have varying size based on the requested
+ * params.
+ */
+static size_t set_opaque_data_size(char *payload, size_t payload_size,
+    uint32_t version)
+{
+    size_t count_size = 0, opaque_size = 0;
+    uint32_t key_id = 0, key_payload_size = 0;
+
+    while (count_size < payload_size) {
+        key_id = *(uint32_t *)payload;
+        key_payload_size = *((uint32_t *)payload + 1);
+
+        switch (key_id) {
+        case KEY_ID_CONFIDENCE_LEVELS:
+            opaque_size += sizeof(struct st_param_header);
+            if (version != CONF_LEVELS_INTF_VERSION_0002) {
+                opaque_size +=
+                    sizeof(struct st_confidence_levels_info);
+            } else {
+                opaque_size +=
+                    sizeof(struct st_confidence_levels_info_v2);
+            }
+            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
+            payload += count_size;
+            break;
+
+        case KEY_ID_KEYWORD_POSITION_STATS:
+            opaque_size += sizeof(struct st_param_header) +
+                sizeof(struct st_keyword_indices_info);
+            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
+            payload += count_size;
+            break;
+
+        default:
+            ALOGE("%s: Unsupported generic detection event key id", __func__);
+        }
+    }
+
+    opaque_size += sizeof(struct st_param_header) +
+        sizeof(struct st_timestamp_info);
+
+    return opaque_size;
+}
+
+/*
+ * This function packs the updated opaque data confidence levels which are
+ * passed to the client via callback.
+ */
+static int pack_opaque_data_conf_levels(
+    st_proxy_session_t *st_ses, void *opaque_data,
+    uint8_t *payload,
+    unsigned int payload_size)
+{
+    uint8_t *payload_ptr = payload;
+    unsigned int i = 0, j = 0, k = 0, user_id = 0;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    struct listnode *node = NULL;
+    struct st_confidence_levels_info *conf_levels = NULL;
+    struct st_confidence_levels_info_v2 *conf_levels_v2 = NULL;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    int32_t kw_level = 0, user_level = 0;
+
+    list_for_each(node, &stc_ses->second_stage_list) {
+        st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+        if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
+            kw_level = st_sec_stage->ss_session->confidence_score;
+        } else if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_VOP) {
+            user_level = st_sec_stage->ss_session->confidence_score;
+        }
+    }
+
+    if (stc_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002) {
+        conf_levels = (struct st_confidence_levels_info *)opaque_data;
+        for (i = 0; i < conf_levels->num_sound_models; i++) {
+            if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_GMM) {
+                for (j = 0;
+                     j < conf_levels->conf_levels[i].num_kw_levels; j++) {
+                    if (j <= payload_size)
+                            conf_levels->conf_levels[i].kw_levels[j].kw_level =
+                                    payload_ptr[j];
+                    else
+                        ALOGE("%s: unexpected conf size %d < %d", __func__,
+                            payload_size, j);
+                    for (k = 0;
+                         k < conf_levels->conf_levels[i].kw_levels[j].num_user_levels;
+                         k++) {
+                        user_id =
+                            conf_levels->conf_levels[i].kw_levels[j].
+                                user_levels[k].user_id;
+                        if (user_id <= payload_size)
+                            conf_levels->conf_levels[i].kw_levels[j].
+                                user_levels[k].level = payload_ptr[user_id];
+                        else
+                            ALOGE("%s: Unexpected conf size %d < %d", __func__,
+                                payload_size, user_id);
+                    }
+                }
+            } else if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_CNN) {
+                conf_levels->conf_levels[i].kw_levels[0].kw_level = kw_level;
+            } else if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_VOP) {
+                /*
+                 * Fill both the keyword and user confidence level with the
+                 * confidence score returned from the voiceprint algorithm.
+                 */
+                conf_levels->conf_levels[i].kw_levels[0].kw_level =
+                    (uint8_t)user_level;
+                conf_levels->conf_levels[i].kw_levels[0].user_levels[0].level =
+                    (uint8_t)user_level;
+            }
+        }
+    } else {
+        conf_levels_v2 = (struct st_confidence_levels_info_v2 *)opaque_data;
+        for (i = 0; i < conf_levels_v2->num_sound_models; i++) {
+            if (conf_levels_v2->conf_levels[i].sm_id == ST_SM_ID_SVA_GMM) {
+                for (j = 0;
+                     j < conf_levels_v2->conf_levels[i].num_kw_levels; j++) {
+                    if (j <= payload_size)
+                        conf_levels_v2->conf_levels[i].kw_levels[j].kw_level =
+                            payload_ptr[j];
+                    else
+                        ALOGE("%s: unexpected conf size %d < %d", __func__,
+                              payload_size, j);
+
+                    for (k = 0;
+                         k < conf_levels_v2->conf_levels[i].kw_levels[j].num_user_levels;
+                         k++) {
+                        user_id =
+                            conf_levels_v2->conf_levels[i].kw_levels[j].
+                                user_levels[k].user_id;
+                        if (user_id <= payload_size)
+                            conf_levels_v2->conf_levels[i].kw_levels[j].
+                                    user_levels[k].level = payload_ptr[user_id];
+                        else
+                            ALOGE("%s: Unexpected conf size %d < %d", __func__,
+                                payload_size, user_id);
+                    }
+                }
+            } else if (conf_levels_v2->conf_levels[i].sm_id ==
+                       ST_SM_ID_SVA_CNN) {
+                conf_levels_v2->conf_levels[i].kw_levels[0].kw_level = kw_level;
+            } else if (conf_levels_v2->conf_levels[i].sm_id ==
+                       ST_SM_ID_SVA_VOP) {
+                /*
+                 * Fill both the keyword and user confidence level with the
+                 * confidence score returned from the voiceprint algorithm.
+                 */
+                conf_levels_v2->conf_levels[i].kw_levels[0].kw_level =
+                    user_level;
+                conf_levels_v2->conf_levels[i].kw_levels[0].user_levels[0].level =
+                    user_level;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/* This function packs the sound trigger API confidence levels */
+static int pack_recognition_event_conf_levels(
+    st_proxy_session_t *st_ses, uint8_t *payload,
+    unsigned int payload_size,
+    struct sound_trigger_phrase_recognition_event *local_event)
+{
+    unsigned int j = 0, k = 0, user_id = 0;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    struct listnode *node = NULL;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    struct sound_trigger_phrase_sound_model *phrase_sm =
+        (struct sound_trigger_phrase_sound_model *)stc_ses->phrase_sm;
+
+    /*
+     * Fill in the GMM confidence levels to the sound trigger recognition event
+     * APIs first. If any second stage session is enabled, overwrite the APIs
+     * with the second stage confidence levels.
+     */
+    for (j = 0; j < stc_ses->rc_config->num_phrases; j++) {
+        local_event->phrase_extras[j].id = stc_ses->rc_config->phrases[j].id;
+        local_event->phrase_extras[j].recognition_modes =
+            phrase_sm->phrases[j].recognition_mode;
+        local_event->phrase_extras[j].num_levels =
+            stc_ses->rc_config->phrases[j].num_levels;
+                if (j <= payload_size)
+                    local_event->phrase_extras[j].confidence_level = payload[j];
+                else
+                    ALOGE("%s: unexpected conf size %d < %d", __func__,
+                        payload_size, j);
+
+        for (k = 0; k < stc_ses->rc_config->phrases[j].num_levels; k++) {
+            user_id = stc_ses->rc_config->phrases[j].levels[k].user_id;
+            if (user_id <= payload_size) {
+                local_event->phrase_extras[j].levels[k].user_id = user_id;
+                local_event->phrase_extras[j].levels[k].level =
+                    payload[user_id];
+            } else {
+                ALOGE("%s: Unexpected conf size %d < %d", __func__,
+                    payload_size, user_id);
+            }
+        }
+    }
+
+    list_for_each(node, &stc_ses->second_stage_list) {
+        st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+        if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
+            local_event->phrase_extras[0].confidence_level =
+                (uint8_t)st_sec_stage->ss_session->confidence_score;
+        } else if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_VOP) {
+            local_event->phrase_extras[0].levels[0].level =
+                (uint8_t)st_sec_stage->ss_session->confidence_score;
+        }
+    }
+    return 0;
+}
+
+static int parse_generic_event_and_pack_opaque_data(
+    st_proxy_session_t *st_ses, uint8_t *opaque_data,
+    uint8_t *payload, size_t payload_size,
+    struct sound_trigger_phrase_recognition_event *local_event)
+{
+    uint32_t key_id = 0, key_payload_size = 0;
+    struct st_param_header *param_hdr = NULL;
+    struct st_keyword_indices_info *kw_indices = NULL;
+    struct st_timestamp_info *timestamps = NULL;
+    size_t count_size = 0;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    struct listnode *node = NULL;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    int status = 0;
+    unsigned char *cf_levels = NULL;
+    unsigned int cf_levels_size = 0;
+
+    while (count_size < payload_size) {
+        key_id = *(uint32_t *)payload;
+        key_payload_size = *((uint32_t *)payload + 1);
+
+        switch (key_id) {
+        case KEY_ID_CONFIDENCE_LEVELS:
+            /* Pack the opaque data confidence levels structure */
+            param_hdr = (struct st_param_header *)(opaque_data);
+            param_hdr->key_id = ST_PARAM_KEY_CONFIDENCE_LEVELS;
+            opaque_data += sizeof(struct st_param_header);
+            if (stc_ses->conf_levels_intf_version !=
+                CONF_LEVELS_INTF_VERSION_0002) {
+                param_hdr->payload_size =
+                    sizeof(struct st_confidence_levels_info);
+            } else {
+                param_hdr->payload_size =
+                    sizeof(struct st_confidence_levels_info_v2);
+            }
+            check_and_extract_det_conf_levels_payload(st_ses,
+                payload + (4 * sizeof(uint32_t)), *((uint32_t *)payload + 3),
+                &cf_levels, &cf_levels_size);
+            if (!cf_levels || !cf_levels_size) {
+                status = -EINVAL;
+                goto exit;
+            }
+            memcpy(opaque_data, stc_ses->st_conf_levels,
+                param_hdr->payload_size);
+            pack_opaque_data_conf_levels(st_ses, opaque_data,
+                cf_levels, cf_levels_size);
+            pack_recognition_event_conf_levels(st_ses, cf_levels,
+                cf_levels_size, local_event);
+            opaque_data += param_hdr->payload_size;
+            break;
+
+        case KEY_ID_KEYWORD_POSITION_STATS:
+            /* Pack the opaque data keyword indices structure */
+            param_hdr = (struct st_param_header *)(opaque_data);
+            param_hdr->key_id = ST_PARAM_KEY_KEYWORD_INDICES;
+            param_hdr->payload_size = sizeof(struct st_keyword_indices_info);
+            opaque_data += sizeof(struct st_param_header);
+            kw_indices = (struct st_keyword_indices_info *)(opaque_data);
+            kw_indices->version = 0x1;
+            kw_indices->start_index = *((uint32_t *)payload + 3);
+            kw_indices->end_index = *((uint32_t *)payload + 4);
+
+            list_for_each(node, &stc_ses->second_stage_list) {
+                st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                                            list_node);
+                if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
+                    kw_indices->start_index =
+                        st_sec_stage->ss_session->kw_start_idx;
+                    kw_indices->end_index =
+                        st_sec_stage->ss_session->kw_end_idx;
+                }
+            }
+            opaque_data += sizeof(struct st_keyword_indices_info);
+            break;
+
+        default:
+            ALOGE("%s: Unsupported generic detection event key id", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+        count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
+        payload += count_size;
+    }
+
+    /* Pack the opaque data detection timestamp structure */
+    param_hdr = (struct st_param_header *)(opaque_data);
+    param_hdr->key_id = ST_PARAM_KEY_TIMESTAMP;
+    param_hdr->payload_size = sizeof(struct st_timestamp_info);
+    opaque_data += sizeof(struct st_param_header);
+    timestamps = (struct st_timestamp_info *)(opaque_data);
+    timestamps->version = 0x1;
+    timestamps->first_stage_det_event_time =
+    st_ses->hw_ses_current->first_stage_det_event_time;
+    if (!list_empty(&stc_ses->second_stage_list))
+        timestamps->second_stage_det_event_time =
+            st_ses->hw_ses_current->second_stage_det_event_time;
+    opaque_data += sizeof(struct st_timestamp_info);
+
+exit:
+    return status;
+}
+
+static int parse_generic_event_without_opaque_data(
+    st_proxy_session_t *st_ses, uint8_t *payload, size_t payload_size,
+    struct sound_trigger_phrase_recognition_event *local_event)
+{
+    uint32_t key_id = 0, key_payload_size = 0;
+    size_t count_size = 0;
+    int status = 0;
+    unsigned char *cf_levels = NULL;
+    unsigned int cf_levels_size = 0;;
+
+    while (count_size < payload_size) {
+        key_id = *(uint32_t *)payload;
+        key_payload_size = *((uint32_t *)payload + 1);
+
+        switch (key_id) {
+        case KEY_ID_CONFIDENCE_LEVELS:
+            check_and_extract_det_conf_levels_payload(st_ses,
+                payload + (4 * sizeof(uint32_t)), *((uint32_t *)payload + 3),
+                &cf_levels, &cf_levels_size);
+            if (!cf_levels || !cf_levels_size) {
+                status = -EINVAL;
+                return status;
+            }
+            pack_recognition_event_conf_levels(st_ses, cf_levels,
+                cf_levels_size, local_event);
+            return status;
+
+        case KEY_ID_KEYWORD_POSITION_STATS:
+            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
+            payload += count_size;
+            break;
+
+        default:
+            ALOGE("%s: Unsupported generic detection event key id", __func__);
+            status = -EINVAL;
+            return status;
+        }
+    }
+    return status;
+}
+
+/*
+ * This function handles detection payloads in the format of the DSP's
+ * generic detection event.
+ */
+int process_detection_event_keyphrase_v2(
+    st_proxy_session_t *st_ses, int detect_status,
+    void *payload, size_t payload_size,
+    struct sound_trigger_phrase_recognition_event **event)
+{
+    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    unsigned int i = 0, j = 0;
+    int status = 0;
+    uint8_t *opaque_data = NULL;
+    size_t opaque_size = 0;
+    struct sound_trigger_phrase_recognition_event *local_event = NULL;
+
+    if (st_ses->vendor_uuid_info->is_qcva_uuid)
+        opaque_size = set_opaque_data_size(payload, payload_size,
+            stc_ses->conf_levels_intf_version);
+    else
+        opaque_size = payload_size;
+
+    local_event = calloc(1,
+        sizeof(struct sound_trigger_phrase_recognition_event) + opaque_size);
+    if (!local_event) {
+        ALOGE("%s: local_event allocation failed, opaque data size = %d",
+              __func__, (unsigned int)opaque_size);
+        return -ENOMEM;
+    }
+
+    local_event->num_phrases = stc_ses->rc_config->num_phrases;
+    local_event->common.data_offset =
+        sizeof(struct sound_trigger_phrase_recognition_event);
+    local_event->common.data_size = opaque_size;
+    opaque_data = (uint8_t *)local_event + local_event->common.data_offset;
+
+    if (st_ses->vendor_uuid_info->is_qcva_uuid) {
+        if (stc_ses->rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) {
+            status = parse_generic_event_and_pack_opaque_data(st_ses,
+                opaque_data, payload, payload_size, local_event);
+            if (status) {
+                ALOGE("%s: Failed to parse generic detection event with opaque"
+                      "data %d", __func__, status);
+                goto exit;
+            }
+
+            ST_DBG_DECLARE(FILE *opaque_fd = NULL; static int opaque_cnt = 0);
+            ST_DBG_FILE_OPEN_WR(opaque_fd, ST_DEBUG_DUMP_LOCATION,
+                                "detection_opaque_data", "bin", opaque_cnt++);
+            ST_DBG_FILE_WRITE(opaque_fd, opaque_data, opaque_size);
+            ST_DBG_FILE_CLOSE(opaque_fd);
+        } else {
+            status = parse_generic_event_without_opaque_data(st_ses, payload,
+                payload_size, local_event);
+            if (status) {
+                ALOGE("%s: Failed to parse generic detection event without"
+                      "opaque data %d", __func__, status);
+                goto exit;
+            }
+        }
+    } else {
+        local_event = calloc(1, sizeof(*local_event) + payload_size);
+        if (!local_event) {
+            ALOGE("%s: event allocation failed, size %zd", __func__,
+                  payload_size);
+            status = -ENOMEM;
+            goto exit;
+        }
+        memcpy(local_event->phrase_extras,
+            stc_ses->rc_config->phrases, stc_ses->rc_config->num_phrases *
+            sizeof(struct sound_trigger_phrase_recognition_extra));
+        local_event->num_phrases = stc_ses->rc_config->num_phrases;
+        local_event->common.data_offset = sizeof(*local_event);
+        local_event->common.data_size = opaque_size;
+        memcpy(opaque_data, payload, opaque_size);
+        opaque_data += opaque_size;
+    }
+
+    /*
+     * fill the remaining recognition event parameters not specific
+     * to soundmodel lib
+     */
+    local_event->common.status = detect_status;
+    local_event->common.type = stc_ses->phrase_sm->common.type;
+    local_event->common.model = stc_ses->sm_handle;
+    local_event->common.capture_available =
+        stc_ses->rc_config->capture_requested;
+    local_event->common.capture_delay_ms = 0;
+    local_event->common.capture_preamble_ms = 0;
+    local_event->common.audio_config.sample_rate =
+        SOUND_TRIGGER_SAMPLING_RATE_16000;
+    local_event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    local_event->common.audio_config.channel_mask =
+        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
+
+    for (i = 0; i < local_event->num_phrases; ++i) {
+        ALOGV("%s: [%d] kw_id %d level %d", __func__, i,
+              local_event->phrase_extras[i].id,
+              local_event->phrase_extras[i].confidence_level);
+        for (j = 0; j < local_event->phrase_extras[i].num_levels; ++j) {
+            ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
+                  local_event->phrase_extras[i].levels[j].user_id,
+                  local_event->phrase_extras[i].levels[j].level);
+        }
+    }
+
+    ALOGI("%s:[c%d]", __func__, stc_ses->sm_handle);
+
+    ALOGV("%s:[c%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
+          "num_phrases=%d id=%d", __func__, stc_ses->sm_handle,
+          local_event->common.status, local_event->common.type,
+          local_event->common.model, local_event->common.capture_available,
+          local_event->num_phrases, local_event->phrase_extras[0].id);
+
+    *event = local_event;
+    return 0;
+
+exit:
+    if (local_event)
+        free(local_event);
+    return status;
+}
+
+/*
+ * This function handles detection payloads in the format of the DSP's
+ * legacy (non-generic) detection event.
+ * TODO: Deprecate this when DSP for all shared targets of this component
+ * move to generic event.
+ */
+static int process_detection_event_keyphrase(
+    st_proxy_session_t *st_ses, int detect_status,
+    void *payload, size_t payload_size,
+    struct sound_trigger_phrase_recognition_event **event)
+{
+    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    unsigned int i = 0, j = 0;
+    int status = 0;
+    struct sound_trigger_phrase_recognition_event *local_event = NULL;
+    size_t opaque_size = 0;
+    uint8_t *opaque_data = NULL, *payload_ptr = NULL;
+    struct st_param_header *param_hdr = NULL;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    struct listnode *node = NULL;
+    struct st_keyword_indices_info *kw_indices = NULL;
+    struct st_timestamp_info *timestamps = NULL;
+    bool enable_kw_indices = false;
+    unsigned char *cf_levels = NULL;
+    unsigned int cf_levels_size = 0;
+
+    if ((stc_ses->rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
+        st_ses->vendor_uuid_info->is_qcva_uuid) {
+        /*
+         * This logic is for the updated opaque data format. Sound trigger
+         * recognition event APIs are filled along with the opaque data's
+         * confidence levels, keyword indices, and timestamp parameters.
+         */
+        opaque_size = (2 * sizeof(struct st_param_header)) +
+            sizeof(struct st_timestamp_info);
+        if (stc_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002)
+            opaque_size += sizeof(struct st_confidence_levels_info);
+        else
+            opaque_size += sizeof(struct st_confidence_levels_info_v2);
+
+        list_for_each(node, &stc_ses->second_stage_list) {
+            st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
+            if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
+                enable_kw_indices = true;
+                opaque_size += sizeof(struct st_param_header) +
+                    sizeof(struct st_keyword_indices_info);
+                break;
+            }
+        }
+
+        local_event = calloc(1,
+            sizeof(struct sound_trigger_phrase_recognition_event) +
+            opaque_size);
+        if (!local_event) {
+            ALOGE("%s: local_event allocation failed, opaque data size = %d",
+                  __func__, (unsigned int)opaque_size);
+            return -ENOMEM;
+        }
+
+        local_event->num_phrases = stc_ses->rc_config->num_phrases;
+        local_event->common.data_offset =
+            sizeof(struct sound_trigger_phrase_recognition_event);
+        local_event->common.data_size = opaque_size;
+        opaque_data = (uint8_t *)local_event + local_event->common.data_offset;
+        if ((st_ses->exec_mode == ST_EXEC_MODE_CPE) && st_ses->stdev->is_gcs) {
+            payload_ptr = (uint8_t *)payload + 2;
+            payload_size -= 2; /* Re-use */
+        } else if ((st_ses->exec_mode == ST_EXEC_MODE_ADSP) ||
+                   !st_ses->stdev->is_gcs) {
+            payload_ptr = (uint8_t *)payload;
+        } else {
+            ALOGE("%s: Invalid execution mode, exiting", __func__);
+            status = -EINVAL;
+            goto err_exit;
+        }
+
+        /* Pack the opaque data confidence levels structure */
+        param_hdr = (struct st_param_header *)opaque_data;
+        param_hdr->key_id = ST_PARAM_KEY_CONFIDENCE_LEVELS;
+        opaque_data += sizeof(struct st_param_header);
+        if (stc_ses->conf_levels_intf_version !=
+            CONF_LEVELS_INTF_VERSION_0002) {
+            param_hdr->payload_size =
+                sizeof(struct st_confidence_levels_info);
+        } else {
+            param_hdr->payload_size =
+                sizeof(struct st_confidence_levels_info_v2);
+        }
+        check_and_extract_det_conf_levels_payload(st_ses, payload_ptr,
+            payload_size, &cf_levels, &cf_levels_size);
+        if (!cf_levels || !cf_levels_size) {
+            status = -EINVAL;
+            goto err_exit;
+        }
+        memcpy(opaque_data, stc_ses->st_conf_levels, param_hdr->payload_size);
+        pack_opaque_data_conf_levels(st_ses, opaque_data, cf_levels,
+            cf_levels_size);
+        pack_recognition_event_conf_levels(st_ses, cf_levels, cf_levels_size,
+            local_event);
+        opaque_data += param_hdr->payload_size;
+
+        /* Pack the opaque data keyword indices structure */
+        if (enable_kw_indices) {
+            param_hdr = (struct st_param_header *)opaque_data;
+            param_hdr->key_id = ST_PARAM_KEY_KEYWORD_INDICES;
+            param_hdr->payload_size = sizeof(struct st_keyword_indices_info);
+            opaque_data += sizeof(struct st_param_header);
+            kw_indices = (struct st_keyword_indices_info *)opaque_data;
+            kw_indices->version = 0x1;
+            list_for_each(node, &stc_ses->second_stage_list) {
+                st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                list_node);
+                if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
+                    kw_indices->start_index =
+                        st_sec_stage->ss_session->kw_start_idx;
+                    kw_indices->end_index =
+                        st_sec_stage->ss_session->kw_end_idx;
+                }
+            }
+            opaque_data += sizeof(struct st_keyword_indices_info);
+        }
+
+        /* Pack the opaque data detection timestamp structure */
+        param_hdr = (struct st_param_header *)opaque_data;
+        param_hdr->key_id = ST_PARAM_KEY_TIMESTAMP;
+        param_hdr->payload_size = sizeof(struct st_timestamp_info);
+        opaque_data += sizeof(struct st_param_header);
+        timestamps = (struct st_timestamp_info *)opaque_data;
+        timestamps->version = 0x1;
+        timestamps->first_stage_det_event_time =
+            st_hw_ses->first_stage_det_event_time;
+        if (!list_empty(&stc_ses->second_stage_list))
+            timestamps->second_stage_det_event_time =
+                st_hw_ses->second_stage_det_event_time;
+        opaque_data += sizeof(struct st_timestamp_info);
+
+        ST_DBG_DECLARE(FILE *opaque_fd = NULL; static int opaque_cnt = 0);
+        ST_DBG_FILE_OPEN_WR(opaque_fd, ST_DEBUG_DUMP_LOCATION,
+                            "detection_opaque_data", "bin", opaque_cnt++);
+        ST_DBG_FILE_WRITE(opaque_fd, (opaque_data - opaque_size), opaque_size);
+        ST_DBG_FILE_CLOSE(opaque_fd);
+
+    } else {
+        if (st_ses->vendor_uuid_info->is_qcva_uuid ||
+            st_ses->vendor_uuid_info->is_qcmd_uuid) {
+            if (st_ses->stdev->is_gcs &&
+                ST_EXEC_MODE_CPE == st_ses->exec_mode &&
+                !st_hw_ses->is_generic_event) {
+                payload_ptr = payload;
+                payload_ptr += 2; /* Skip minor_version and num_active_models */
+                payload_size -= 2;
+            } else {
+                payload_ptr = payload;
+            }
+            status = generate_legacy_st_phrase_recognition_event(
+                stc_ses->phrase_sm, stc_ses->rc_config, payload_ptr,
+                payload_size, &local_event);
+
+            if (status)
+                goto exit;
+        } else {
+            ALOGD("%s: Send detection payload as is", __func__);
+
+            local_event = calloc(1, sizeof(*local_event) + payload_size);
+            if (!local_event) {
+                ALOGE("%s: event allocation failed, size %zd", __func__,
+                    payload_size);
+                status = -ENOMEM;
+                goto exit;
+            }
+            memcpy(local_event->phrase_extras,
+                stc_ses->rc_config->phrases, stc_ses->rc_config->num_phrases *
+                sizeof(struct sound_trigger_phrase_recognition_extra));
+            local_event->num_phrases = stc_ses->rc_config->num_phrases;
+            local_event->common.data_offset = sizeof(*local_event);
+            local_event->common.data_size = payload_size;
+            memcpy((char *)local_event + local_event->common.data_offset,
+                   payload, payload_size);
+        }
+    }
+
+    /* fill the remaining recognition event parameters not specific
+       to soundmodel lib */
+    local_event->common.status = detect_status;
+    local_event->common.type = stc_ses->phrase_sm->common.type;
+    local_event->common.model = stc_ses->sm_handle;
+    local_event->common.capture_available =
+        stc_ses->rc_config->capture_requested;
+    local_event->common.capture_delay_ms = 0;
+    local_event->common.capture_preamble_ms = 0;
+    local_event->common.audio_config.sample_rate =
+        SOUND_TRIGGER_SAMPLING_RATE_16000;
+    local_event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    local_event->common.audio_config.channel_mask =
+        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
+
+    for (i = 0; i < local_event->num_phrases; ++i) {
+        ALOGV("%s: [%d] kw_id %d level %d", __func__, i,
+            local_event->phrase_extras[i].id,
+            local_event->phrase_extras[i].confidence_level);
+        for (j = 0; j < local_event->phrase_extras[i].num_levels; ++j) {
+            ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
+                local_event->phrase_extras[i].levels[j].user_id,
+                local_event->phrase_extras[i].levels[j].level);
+        }
+    }
+
+    ALOGI("%s:[c%d]", __func__, stc_ses->sm_handle);
+
+    ALOGV("%s:[c%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
+          "num_phrases=%d id=%d", __func__, stc_ses->sm_handle,
+          local_event->common.status, local_event->common.type,
+          local_event->common.model, local_event->common.capture_available,
+          local_event->num_phrases, local_event->phrase_extras[0].id);
+
+    *event = local_event;
+    return 0;
+
+err_exit:
+    if (local_event)
+        free(local_event);
+
+exit:
+    return status;
+}
+
+static int process_detection_event_generic(st_proxy_session_t *st_ses,
+    int detect_status,
+    void *payload, size_t payload_size,
+    struct sound_trigger_recognition_event **event)
+{
+    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
+    st_session_t *stc_ses = st_ses->det_stc_ses;
+    struct st_vendor_info *v_info = st_ses->vendor_uuid_info;
+    int status = 0;
+    struct sound_trigger_recognition_event *local_event = NULL;
+
+    local_event = calloc(1, sizeof(*local_event) + payload_size);
+    if (!local_event) {
+        ALOGE("%s: event allocation failed, size %zd", __func__,
+            payload_size);
+        status = -ENOMEM;
+        goto exit;
+    }
+
+    local_event->status = detect_status;
+    local_event->type = stc_ses->sm_type;
+    local_event->model = stc_ses->sm_handle;
+    local_event->capture_available = stc_ses->rc_config->capture_requested;
+    local_event->capture_delay_ms = 0;
+    local_event->capture_preamble_ms = 0;
+    local_event->audio_config.sample_rate = v_info ?
+    v_info->sample_rate : SOUND_TRIGGER_SAMPLING_RATE_16000;
+    local_event->audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    local_event->audio_config.channel_mask =
+        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
+
+    local_event->data_offset = sizeof(*local_event);
+    local_event->data_size = payload_size;
+    memcpy((char *)local_event + local_event->data_offset,
+        payload, payload_size);
+
+    ALOGI("%s:[%d]", __func__, stc_ses->sm_handle);
+    ALOGV("%s:[c%d] status=%d, type=%d, model=%d, capture_avaiable=%d",
+        __func__, stc_ses->sm_handle, local_event->status,
+        local_event->type, local_event->model,
+        local_event->capture_available);
+
+    *event = local_event;
+
+exit:
+    return status;
+}
+
+static inline int process_detection_event(st_proxy_session_t *st_ses,
+    uint64_t timestamp __unused,
+    int detect_status,
+    void *payload, size_t payload_size,
+    struct sound_trigger_recognition_event **event)
+{
+    int ret;
+    struct sound_trigger_phrase_recognition_event *phrase_event = NULL;
+
+    *event = NULL;
+    if (st_ses->sm_info.sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        if (sthw_extn_check_process_det_ev_support())
+            ret = sthw_extn_process_detection_event_keyphrase(st_ses,
+                timestamp, detect_status, payload, payload_size, &phrase_event);
+        else if (st_ses->hw_ses_current->is_generic_event &&
+                 !st_ses->vendor_uuid_info->is_qcmd_uuid)
+            ret = process_detection_event_keyphrase_v2(st_ses, detect_status,
+                payload, payload_size, &phrase_event);
+        else
+            ret = process_detection_event_keyphrase(st_ses, detect_status,
+                payload, payload_size, &phrase_event);
+        if (phrase_event)
+            *event = &phrase_event->common;
+    } else {
+        ret = process_detection_event_generic(st_ses, detect_status, payload,
+            payload_size, event);
+    }
+    return ret;
+}
+
+
+/*
+ * If the keyword detection session detects before the user verification
+ * session, signal to process user verification. If the keyword detection
+ * session rejects before the user verification session, signal to stop
+ * processing user verification.
+ */
+static void handle_vop_pending_detection(st_arm_ss_session_t *ss_session,
+    unsigned int det_status, unsigned int kw_det_buff_sz)
+{
+    if (det_status & KEYWORD_DETECTION_SUCCESS) {
+        if (kw_det_buff_sz > ss_session->unread_bytes)
+            ss_session->buff_sz = kw_det_buff_sz;
+        else
+            ss_session->buff_sz = ss_session->unread_bytes;
+
+        /*
+         * It is possible that VOP started processing by already consuming
+         * data from unread_bytes while CNN detects. In this case, it does
+         * not need to be signaled.
+         */
+        if (ss_session->unread_bytes >= ss_session->buff_sz) {
+            ALOGD("%s: Processing UV due to KW detection success", __func__);
+            pthread_cond_signal(&ss_session->cond);
+        }
+    } else if (det_status & KEYWORD_DETECTION_REJECT) {
+        ss_session->exit_buffering = true;
+        ALOGD("%s: Exiting from UV due to KW detection rejection", __func__);
+        pthread_cond_signal(&ss_session->cond);
+    }
+}
+
+/*
+ * If the user verification session rejects before the keyword detection
+ * session, signal to stop processing keyword detection.
+ */
+static void handle_cnn_pending_detection(st_arm_ss_session_t *ss_session,
+    unsigned int det_status)
+{
+    if (det_status & USER_VERIFICATION_REJECT) {
+        ss_session->exit_buffering = true;
+        ALOGD("%s: Exiting from KW detection due to UV rejection", __func__);
+        pthread_cond_signal(&ss_session->cond);
+    }
+}
+
+/*
+ * This thread handles detection events from the second stage sessions
+ * and aggregates them into 1 final decision. It will call the client callback
+ * or restart the first stage session based on this decision.
+ */
+static void *aggregator_thread_loop(void *st_session)
+{
+    st_proxy_session_t *st_ses = (st_proxy_session_t *)st_session;
+    st_session_t *stc_ses = NULL;
+    recognition_callback_t callback = NULL;
+    void *cookie = NULL;
+    struct listnode *node = NULL;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    int status = 0, lock_status = 0;
+    unsigned int kw_det_buff_sz = 0, det_status = 0;
+    struct timespec tspec = {0};
+    struct sound_trigger_recognition_event *event = NULL;
+    bool capture_requested = false;
+
+    ALOGV("%s: Enter", __func__);
+
+    /*
+     * For multi-clients it is expected only one of the clients detection
+     * happens at a time. Continue processing on a run time detected client
+     */
+    pthread_mutex_lock(&st_ses->ss_detections_lock);
+    while (!st_ses->exit_aggregator_loop) {
+        det_status = 0;
+        lock_status = 0;
+        ALOGV("%s: waiting on cond", __func__);
+        pthread_cond_wait(&st_ses->ss_detections_cond,
+            &st_ses->ss_detections_lock);
+        ALOGV("%s: done waiting on cond", __func__);
+        if (st_ses->exit_aggregator_loop) {
+            ALOGV("%s: exit", __func__);
+            pthread_mutex_unlock(&st_ses->ss_detections_lock);
+            return NULL;
+        }
+        if (!st_ses->det_stc_ses)
+            continue;
+        stc_ses = st_ses->det_stc_ses;
+
+        list_for_each(node, &stc_ses->second_stage_list) {
+            st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                list_node);
+
+            pthread_mutex_lock(&st_sec_stage->ss_session->lock);
+            det_status |= st_sec_stage->ss_session->det_status;
+            if (st_sec_stage->ss_session->det_status ==
+                KEYWORD_DETECTION_SUCCESS)
+                kw_det_buff_sz = st_sec_stage->ss_session->bytes_processed;
+            pthread_mutex_unlock(&st_sec_stage->ss_session->lock);
+        }
+
+        list_for_each(node, &stc_ses->second_stage_list) {
+            st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                list_node);
+
+            pthread_mutex_lock(&st_sec_stage->ss_session->lock);
+            if ((st_sec_stage->ss_info->sm_detection_type ==
+                 ST_SM_TYPE_USER_VERIFICATION) &&
+                (det_status & USER_VERIFICATION_PENDING)) {
+                handle_vop_pending_detection(st_sec_stage->ss_session,
+                    det_status, kw_det_buff_sz);
+            } else if ((st_sec_stage->ss_info->sm_detection_type ==
+                        ST_SM_TYPE_KEYWORD_DETECTION) &&
+                       (det_status & KEYWORD_DETECTION_PENDING)) {
+                handle_cnn_pending_detection(st_sec_stage->ss_session,
+                    det_status);
+            }
+            pthread_mutex_unlock(&st_sec_stage->ss_session->lock);
+        }
+
+        if (!IS_SS_DETECTION_PENDING(det_status)) {
+            pthread_mutex_lock(&st_ses->lock);
+            /*
+             * If the client stops before 2nd stage finishes processing, or a
+             * transition is in progress, the detection event should not be
+             * handled.
+             */
+            if ((st_ses->current_state != buffering_state_fn) ||
+                (st_ses->exec_mode == ST_EXEC_MODE_NONE)) {
+                ALOGW("%s: First stage is not in a valid state, continuing",
+                    __func__);
+                pthread_mutex_unlock(&st_ses->lock);
+                continue;
+            }
+            if (IS_SS_DETECTION_SUCCESS(det_status)) {
+                clock_gettime(CLOCK_MONOTONIC, &tspec);
+                st_ses->hw_ses_current->second_stage_det_event_time =
+                    get_current_time_ns();
+                ATRACE_ASYNC_END("sthal: detection success",
+                    st_ses->sm_handle);
+
+                status = process_detection_event(st_ses,
+                    st_ses->det_session_ev->payload.detected.timestamp,
+                    st_ses->det_session_ev->payload.detected.detect_status,
+                    st_ses->det_session_ev->payload.detected.detect_payload,
+                    st_ses->det_session_ev->payload.detected.payload_size,
+                    &event);
+                if (status || !event) {
+                    ALOGE("%s:[%d] process_detection_event failed err %d",
+                        __func__, st_ses->sm_handle, status);
+                    /*
+                     * Stop buffering if this is not a successful detection and
+                     * LAB is triggered in hw automatically
+                     */
+                    st_ses->hw_ses_current->fptrs->stop_buffering(
+                        st_ses->hw_ses_current);
+
+                    pthread_mutex_unlock(&st_ses->lock);
+                    if (event) {
+                        free(event);
+                        event = NULL;
+                    }
+                    goto exit;
+                }
+                callback = stc_ses->callback;
+                capture_requested = stc_ses->rc_config->capture_requested;
+                cookie = stc_ses->cookie;
+                ALOGD("%s:[c%d] Second stage detected successfully, "
+                    "calling client callback", __func__, stc_ses->sm_handle);
+                pthread_mutex_unlock(&st_ses->lock);
+                ATRACE_BEGIN("sthal: client detection callback");
+                callback(event, cookie);
+                free(event);
+                ATRACE_END();
+
+                /*
+                 * The client could unload the sound model during the callback,
+                 * which would join this thread and wait for this thread exit
+                 * as part of st_session_deinit() with st_session_lock held. By
+                 * this time, the state is also moved to idle. To avoid
+                 * deadlock, upon return from client callback, try acquiring
+                 * lock only if not in idle state, else exit right away.
+                 */
+                do {
+                    lock_status = pthread_mutex_trylock(&st_ses->lock);
+                } while (lock_status && (st_ses->current_state !=
+                         idle_state_fn));
+
+                if (st_ses->current_state == idle_state_fn) {
+                    ALOGV("%s:[%d] client unloaded after callback"
+                        ", lock status %d", __func__, st_ses->sm_handle,
+                        lock_status);
+                    if (!lock_status)
+                        pthread_mutex_unlock(&st_ses->lock);
+                    goto exit;
+                }
+                /*
+                 * If client has not requested capture data,
+                 * stop hw session buffering here to resume next
+                 * detection
+                 */
+                if (!capture_requested)
+                    st_ses->hw_ses_current->fptrs->stop_buffering(
+                        st_ses->hw_ses_current);
+            } else {
+                ATRACE_ASYNC_END("sthal: detection reject",
+                    st_ses->sm_handle);
+                ALOGD("%s: Second stage did NOT detect, restarting st_session",
+                    __func__);
+                st_ses->hw_ses_current->fptrs->stop_buffering(
+                    st_ses->hw_ses_current);
+                start_second_stage_for_client(stc_ses);
+                st_session_ev_t ev = {.ev_id = ST_SES_EV_RESTART,
+                    .stc_ses = stc_ses};
+                DISPATCH_EVENT(st_ses, ev, status);
+            }
+            pthread_mutex_unlock(&st_ses->lock);
+        } else {
+            ALOGV("%s: There is a second stage session pending, continuing",
+                __func__);
+        }
+    }
+exit:
+    pthread_mutex_unlock(&st_ses->ss_detections_lock);
+    ALOGV("%s: Exit", __func__);
+    return NULL;
+}
+
+static void init_det_event_aggregator(st_proxy_session_t *st_ses)
+{
+    int status = 0;
+    pthread_condattr_t attr;
+
+    ALOGV("%s", __func__);
+
+    st_ses->exit_aggregator_loop = false;
+    pthread_mutex_init(&st_ses->ss_detections_lock, NULL);
+    pthread_condattr_init(&attr);
+    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+    pthread_cond_init(&st_ses->ss_detections_cond, &attr);
+    pthread_condattr_destroy(&attr);
+    status = pthread_create(&st_ses->aggregator_thread, NULL,
+        aggregator_thread_loop, st_ses);
+    if (status) {
+        ALOGE("%s: Error creating aggregator thread. status = %d",
+              __func__, status);
+    } else {
+        st_ses->aggregator_thread_created = true;
+    }
+}
+
+static void destroy_det_event_aggregator(st_proxy_session_t *st_ses)
+{
+    int status = 0;
+
+    ALOGV("%s", __func__);
+
+    st_ses->exit_aggregator_loop = true;
+    pthread_mutex_lock(&st_ses->ss_detections_lock);
+    pthread_cond_signal(&st_ses->ss_detections_cond);
+    pthread_mutex_unlock(&st_ses->ss_detections_lock);
+    status = pthread_join(st_ses->aggregator_thread, NULL);
+    if (status)
+        ALOGE("%s: Error joining aggregator thread. status = %d",
+              __func__, status);
+    pthread_cond_destroy(&st_ses->ss_detections_cond);
+    pthread_mutex_destroy(&st_ses->ss_detections_lock);
+    st_ses->aggregator_thread_created = false;
+}
+
+/* This function is called for multi-client */
+static int handle_load_sm(st_proxy_session_t *st_ses, st_session_t *stc_ses)
+{
+    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
+    int status = 0;
+
+    ALOGV("%s:[c%d-%d]", __func__, stc_ses->sm_handle, st_ses->sm_handle);
+    if (!stc_ses->phrase_sm) {
+        ALOGE("%s:[c%d] sound model data is not initialzed", __func__,
+              stc_ses->sm_handle);
+        return -EINVAL;
+    }
+
+    if (!is_other_client_attached(st_ses, stc_ses)) {
+        ALOGE("%s:[c%d] Unexpected without multi-clients", __func__,
+              stc_ses->sm_handle);
+        return -EINVAL;
+    }
+
+    if (st_ses->current_state == buffering_state_fn)
+        hw_ses->fptrs->stop_buffering(hw_ses);
+
+    if (st_ses->current_state == active_state_fn ||
+        st_ses->current_state == detected_state_fn ||
+        st_ses->current_state == buffering_state_fn) {
+        status = stop_session(st_ses, hw_ses, false);
+        if (status)
+            ALOGE("%s:[%d] stop_session failed %d", __func__, st_ses->sm_handle,
+                  status);
+    }
+
+    status = hw_ses->fptrs->dereg_sm(hw_ses);
+    if (status) {
+        ALOGE("%s:[%d] dereg_sm failed %d", __func__,
+            st_ses->sm_handle, status);
+    }
+    /* Continue updating sound model resulting in merged model */
+    status = update_sound_model(stc_ses, true);
+    if (status) {
+        ALOGE("%s:[c%d] update_sound_model add failed %d", __func__,
+              stc_ses->sm_handle, status);
+        goto exit;
+    }
+    hw_ses->sthw_cfg.conf_levels = st_ses->sm_info.cf_levels;
+    hw_ses->sthw_cfg.num_conf_levels = st_ses->sm_info.cf_levels_size;
+    hw_ses->sthw_cfg_updated = true;
+    /*
+     * Sound model merge would have changed the order of merge conf levels,
+     * which need to be re-updated for all current active clients, if any.
+     */
+    status = update_merge_conf_levels_payload_with_active_clients(st_ses);
+    if (status)
+        goto exit_1;
+
+    /* Load merged sound model */
+    status = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_info.sm_data,
+            st_ses->sm_info.sm_size, st_ses->sm_info.sm_type);
+    if (status) {
+        ALOGE("%s:[%d] reg_sm failed %d", __func__,
+            st_ses->sm_handle, status);
+        goto exit_1;
+    }
+
+    if (st_ses->current_state == active_state_fn ||
+        st_ses->current_state == detected_state_fn ||
+        st_ses->current_state == buffering_state_fn) {
+
+        status = start_session(st_ses, hw_ses, false);
+        if (status)
+            goto exit_2;
+        STATE_TRANSITION(st_ses, active_state_fn);
+    }
+
+    return 0;
+
+exit_2:
+    if (!st_ses->stdev->ssr_offline_received)
+        hw_ses->fptrs->dereg_sm(hw_ses);
+
+exit_1:
+    if (!st_ses->stdev->ssr_offline_received) {
+        update_sound_model(stc_ses, false);
+        update_merge_conf_levels_payload_with_active_clients(st_ses);
+    }
+
+exit:
+    if (st_ses->stdev->ssr_offline_received) {
+        STATE_TRANSITION(st_ses, ssr_state_fn);
+        status = 0;
+    }
+    return status;
+}
+
+/* This function is called for multi-client */
+static int handle_unload_sm(st_proxy_session_t *st_ses, st_session_t *stc_ses)
+{
+    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
+    int status = 0;
+
+    ALOGV("%s:[c%d-%d]", __func__, stc_ses->sm_handle, st_ses->sm_handle);
+
+    if (!is_other_client_attached(st_ses, stc_ses)) {
+        ALOGE("%s:[c%d] Unexpected without multi-clients", __func__,
+            stc_ses->sm_handle);
+        return -EINVAL;
+    }
+
+    if (st_ses->current_state == buffering_state_fn)
+        hw_ses->fptrs->stop_buffering(hw_ses);
+
+    if (st_ses->current_state == active_state_fn ||
+        st_ses->current_state == detected_state_fn ||
+        st_ses->current_state == buffering_state_fn) {
+        status = stop_session(st_ses, hw_ses, false);
+        if (status)
+            ALOGE("%s:[%d] stop_session failed %d", __func__,
+                st_ses->sm_handle, status);
+    }
+
+    status = hw_ses->fptrs->dereg_sm(hw_ses);
+    if (status)
+        ALOGE("%s:[%d] dereg_sm failed %d", __func__, st_ses->sm_handle, status);
+
+    /* Continue deleting this model */
+    status = update_sound_model(stc_ses, false);
+    if (status)
+        ALOGE("%s:[c%d] update_sound_model delete failed %d", __func__,
+            stc_ses->sm_handle, status);
+
+    hw_ses->sthw_cfg.conf_levels = st_ses->sm_info.cf_levels;
+    hw_ses->sthw_cfg.num_conf_levels = st_ses->sm_info.cf_levels_size;
+    hw_ses->sthw_cfg_updated = true;
+    /*
+     * Sound model merge would have changed the order of merge conf levels,
+     * which need to be re-updated for all current active clients, if any.
+     */
+    update_merge_conf_levels_payload_with_active_clients(st_ses);
+
+    /* Load remaining merged sound model */
+    status = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_info.sm_data,
+        st_ses->sm_info.sm_size, st_ses->sm_info.sm_type);
+    if (status) {
+        ALOGE("%s:[%d] reg_sm failed %d", __func__,
+            st_ses->sm_handle, status);
+        goto exit;
+    }
+
+    if (st_ses->current_state == active_state_fn ||
+        st_ses->current_state == detected_state_fn ||
+        st_ses->current_state == buffering_state_fn) {
+
+        status = start_session(st_ses, hw_ses, false);
+        if (status)
+            goto exit;
+        STATE_TRANSITION(st_ses, active_state_fn);
+    }
+    return 0;
+
+exit:
+    if (st_ses->stdev->ssr_offline_received) {
+        STATE_TRANSITION(st_ses, ssr_state_fn);
+        status = 0;
+    }
+    return status;
+}
+
+static int idle_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
     int ret = 0;
+    st_session_t *stc_ses = ev->stc_ses;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    st_arm_second_stage_t *st_sec_stage = NULL;
 
     /* skip parameter check as this is an internal funciton */
-    ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle, ev->ev_id);
+    ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+        st_ses->sm_handle, ev->ev_id);
 
     switch (ev->ev_id) {
     case ST_SES_EV_LOAD_SM:
-        if (!st_ses->sm_data) {
+        if (!stc_ses->phrase_sm) {
             ALOGE("%s: sound model data is not initialzed", __func__);
             status = -EINVAL;
             break;
         }
+        status = update_sound_model(stc_ses, true);
+        if (status) {
+            ALOGE("%s:[c%d] update sound model add failed %d", __func__,
+                stc_ses->sm_handle, status);
+            status = -EINVAL;
+            break;
+        }
 
         /*
-         * Do retry to handle a corner case that when ADSP SSR ONLINE is received,
-         * sometimes ADSP is still not ready to receive cmd from HLOS and thus
-         * fails, so try more times to recover the session from SSR state.
+         * Do retry to handle a corner case that when ADSP SSR ONLINE is
+         * received, sometimes ADSP is still not ready to receive cmd from HLOS
+         * and thus fails, so try more times to recover the session from SSR
+         * state.
          */
         for (int i = 0; i < REG_SM_RETRY_CNT; i++) {
-            status = ret = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_data,
-                st_ses->sm_type);
+            status = ret = hw_ses->fptrs->reg_sm(hw_ses, st_ses->sm_info.sm_data,
+                st_ses->sm_info.sm_size, st_ses->sm_info.sm_type);
             if (ret) {
                 if (st_ses->stdev->ssr_offline_received) {
-                    st_ses->client_req_state = ST_STATE_LOADED;
                     STATE_TRANSITION(st_ses, ssr_state_fn);
-                    /*
-                     * Send success to client because the failure is recovered
-                     * internally from SSR.
-                     */
                     status = 0;
                     break;
                 } else {
-                    ALOGE("%s:[%d] failed to reg sm, err %d, retry cnt %d", __func__,
-                        st_ses->sm_handle, status, i);
+                    ALOGE("%s:[%d] failed to reg sm, err %d, retry cnt %d",
+                          __func__, st_ses->sm_handle, status, i);
                     usleep(REG_SM_WAIT_TIME_MS * 1000);
                 }
             } else {
@@ -607,20 +4029,14 @@
         if (ret)
             break;
 
-        if (st_ses->enable_second_stage) {
-            hw_ses->enable_second_stage = true;
-            hw_ses->second_stage_list = &(st_ses->second_stage_list);
-            list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-                st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                st_sec_stage->ss_session->st_ses = st_ses;
-                st_second_stage_prepare_session(st_sec_stage);
-            }
-        }
-
         STATE_TRANSITION(st_ses, loaded_state_fn);
         break;
 
     case ST_SES_EV_SET_EXEC_MODE:
+        stc_ses->exec_mode = ev->payload.exec_mode;
+        if (ev->payload.exec_mode == st_ses->exec_mode)
+            break;
+
         st_ses->exec_mode = ev->payload.exec_mode;
         if (ST_EXEC_MODE_CPE == st_ses->exec_mode)
             st_ses->hw_ses_current = st_ses->hw_ses_cpe;
@@ -630,11 +4046,11 @@
         break;
 
     case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
+        stc_ses->paused = true;
         break;
 
     case ST_SES_EV_RESUME:
-        st_ses->paused = false;
+        stc_ses->paused = false;
         break;
 
     case ST_SES_EV_SSR_OFFLINE:
@@ -663,164 +4079,158 @@
     return status;
 }
 
-static int loaded_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int loaded_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
+    st_session_t *stc_ses = ev->stc_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
     st_hw_session_t *new_hw_ses = NULL;
     st_exec_mode_t new_exec_mode = 0;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    st_arm_second_stage_t *st_sec_stage = NULL;
 
-    /* skip parameter check as this is an internal funciton */
-    ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle, ev->ev_id);
+    /* skip parameter check as this is an internal function */
+    ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+                st_ses->sm_handle, ev->ev_id);
 
     switch (ev->ev_id) {
-    case ST_SES_EV_RESUME:
-        if (!st_ses->paused)
-            break;
-        st_ses->paused = false;
-        if (st_ses->client_req_state != ST_STATE_ACTIVE)
-            break;
-        /* If the session is paused and client_req_state is active, fall through
-         * and handle similarly to start/restart.
-         */
-    case ST_SES_EV_START:
-    case ST_SES_EV_RESTART:
-        st_ses->client_req_state = ST_STATE_ACTIVE;
-        if (!st_ses->paused) {
-            /*
-             *  There is a need to be able to differentiate between LAB due to a client
-             *  request, and LAB due to second stage enablement.
-             */
-            st_ses->capture_requested = st_ses->rc_config->capture_requested;
-            st_ses->lab_enabled =
-                (st_ses->capture_requested || st_ses->enable_second_stage);
-
-            status = start_session(st_ses, hw_ses, false);
-            if (status) {
-                if (st_ses->stdev->ssr_offline_received) {
-                    if (st_ses->enable_second_stage)
-                        stop_second_stage_session(st_ses);
-                    hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-                    STATE_TRANSITION(st_ses, ssr_state_fn);
-                    /* Send success to client because the failure is recovered
-                     * internally from SSR.
-                     */
-                    status = 0;
-                } else {
-                    ALOGE("%s:[%d] failed to start session, err %d", __func__,
-                        st_ses->sm_handle, status);
-                }
-                break;
-            }
-
-            if (st_ses->enable_second_stage) {
-                list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-                    st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                    status = st_second_stage_start_session(st_sec_stage);
-                    if (status) {
-                        ALOGE("%s: Failed to start second stage session, exiting", __func__);
-                        status = -EINVAL;
-                        break;
-                    }
-                }
-            }
-            STATE_TRANSITION(st_ses, active_state_fn);
-        }
+    case ST_SES_EV_LOAD_SM:
+        /* Valid only in multi-client session usecase */
+        status = handle_load_sm(st_ses, stc_ses);
         break;
 
     case ST_SES_EV_UNLOAD_SM:
-        if (st_ses->enable_second_stage)
-            stop_second_stage_session(st_ses);
-        status = hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-        if (status) {
-            /* since this is a teardown scenario dont fail here */
-            ALOGE("%s:[%d] dereg_sm failed with err %d", __func__,
-                st_ses->sm_handle, status);
-            status = 0;
+        if (is_other_client_attached(st_ses, stc_ses)) {
+            status = handle_unload_sm(st_ses, stc_ses);
+            break;
         }
+
+        status = hw_ses->fptrs->dereg_sm(hw_ses);
+        if (status)
+            ALOGE("%s:[%d] dereg_sm failed %d", __func__,
+                st_ses->sm_handle, status);
+
+        status = update_sound_model(stc_ses, false);
+        if (status)
+            ALOGE("%s:[c%d] update_sound_model failed %d", __func__,
+                stc_ses->sm_handle, status);
+
+        /* since this is a teardown scenario dont fail here */
+        status = 0;
         STATE_TRANSITION(st_ses, idle_state_fn);
         break;
 
+    case ST_SES_EV_RESUME:
+        stc_ses->paused = false;
+        if (!is_any_client_in_state(st_ses, ST_STATE_ACTIVE))
+            break;
+        /* Fall through */
+    case ST_SES_EV_START:
+    case ST_SES_EV_RESTART:
+        if (ev->ev_id == ST_SES_EV_RESTART)
+            update_hw_config_on_restart(st_ses, stc_ses);
+
+        /*
+        * During Resume, the first active client will start the hw sesison.
+        * During Start, check for any paused sessions to delay actual start
+        * to Resume.
+        */
+        if ((ev->ev_id != ST_SES_EV_RESUME) && is_any_client_paused(st_ses))
+            break;
+
+        status = start_session(st_ses, hw_ses, false);
+        if (status) {
+            if (st_ses->stdev->ssr_offline_received) {
+                hw_ses->fptrs->dereg_sm(hw_ses);
+                STATE_TRANSITION(st_ses, ssr_state_fn);
+                status = 0;
+            } else {
+                ALOGE("%s:[%d] failed to start session, err %d", __func__,
+                    st_ses->sm_handle, status);
+            }
+            break;
+        }
+        STATE_TRANSITION(st_ses, active_state_fn);
+        break;
+
+    case ST_SES_EV_STOP:
+        /*
+         * Valid in multi-client case.
+         * Reconfig based off other active clients, if any, so that RESUME
+         * can apply this reconfig.
+         */
+        update_hw_config_on_stop(st_ses, stc_ses);
+        break;
+
     case ST_SES_EV_SSR_OFFLINE:
-        if (st_ses->enable_second_stage)
-            stop_second_stage_session(st_ses);
         /* exec mode can be none if ssr occurs during a transition */
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE)
-            hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-        /*
-         * When the session is first loaded, the client_req_state remains
-         * in idle state. In this case, client_req_state must be set to
-         * loaded before the SSR handling. In other usecases,
-         * client_req_state can be either loaded or active, so it should
-         * not be changed here.
-         */
-        if (st_ses->client_req_state == ST_STATE_IDLE)
-            st_ses->client_req_state = ST_STATE_LOADED;
+            hw_ses->fptrs->dereg_sm(hw_ses);
         STATE_TRANSITION(st_ses, ssr_state_fn);
         break;
 
     case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
-        break;
-
-    case ST_SES_EV_STOP:
-        st_ses->client_req_state = ST_STATE_LOADED;
+        stc_ses->paused = true;
         break;
 
     case ST_SES_EV_SET_EXEC_MODE:
         new_exec_mode = ev->payload.exec_mode;
 
-        if ((st_ses->exec_mode != new_exec_mode) &&
-            st_ses->enable_trans) {
-
-            if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
-                st_ses->exec_mode = ST_EXEC_MODE_NONE;
-                /* unload sm for current hw session */
-                status = hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-                if (status) {
-                    ALOGE("%s:[%d] dereg_sm failed with err %d", __func__,
-                        st_ses->sm_handle, status);
-                    break;
-                }
-            }
-
-            if (new_exec_mode == ST_EXEC_MODE_NONE)
-                break;
-
-            /* load sm to new hw_ses */
-            if (ST_EXEC_MODE_CPE == new_exec_mode) {
-                new_hw_ses = st_ses->hw_ses_cpe;
-                st_ses->hw_ses_cpe->enable_second_stage =
-                    st_ses->hw_ses_adsp->enable_second_stage;
-                st_ses->hw_ses_cpe->second_stage_list =
-                    st_ses->hw_ses_adsp->second_stage_list;
-            } else if (ST_EXEC_MODE_ADSP == new_exec_mode) {
-                new_hw_ses = st_ses->hw_ses_adsp;
-                st_ses->hw_ses_adsp->enable_second_stage =
-                    st_ses->hw_ses_cpe->enable_second_stage;
-                st_ses->hw_ses_adsp->second_stage_list =
-                    st_ses->hw_ses_cpe->second_stage_list;
-            } else {
-                ALOGE("%s: unknown execution mode %d", __func__,
-                    new_exec_mode);
-                status = -EINVAL;
-                break;
-            }
-
-            status = new_hw_ses->fptrs->reg_sm(new_hw_ses,
-                        st_ses->sm_data, st_ses->sm_type);
-            if (status) {
-                ALOGE("%s:[%d] reg_sm failed with err %d", __func__,
-                       st_ses->sm_handle, status);
-                break;
-            }
-            /* switch hw sessions only if successful*/
-            st_ses->exec_mode = new_exec_mode;
-            st_ses->hw_ses_current = new_hw_ses;
-            /* remain in current state */
+        if (new_exec_mode == st_ses->exec_mode) {
+            stc_ses->exec_mode = st_ses->exec_mode;
+            break;
         }
+
+        if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
+            st_ses->exec_mode = ST_EXEC_MODE_NONE;
+            list_for_each(node, &st_ses->clients_list) {
+                c_ses = node_to_item(node, st_session_t, hw_list_node);
+                c_ses->exec_mode = ST_EXEC_MODE_NONE;
+            }
+            /* unload sm for current hw session */
+            status = hw_ses->fptrs->dereg_sm(hw_ses);
+            if (status) {
+                ALOGE("%s:[%d] dereg_sm failed with err %d", __func__,
+                    st_ses->sm_handle, status);
+                break;
+            }
+        }
+
+        if (new_exec_mode == ST_EXEC_MODE_NONE)
+            break;
+
+        /* load sm to new hw_ses */
+        if (ST_EXEC_MODE_CPE == new_exec_mode) {
+            new_hw_ses = st_ses->hw_ses_cpe;
+        } else if (ST_EXEC_MODE_ADSP == new_exec_mode) {
+            new_hw_ses = st_ses->hw_ses_adsp;
+        } else {
+            ALOGE("%s: unknown execution mode %d", __func__,
+                new_exec_mode);
+            status = -EINVAL;
+            break;
+        }
+
+        status = new_hw_ses->fptrs->reg_sm(new_hw_ses,
+            st_ses->sm_info.sm_data, st_ses->sm_info.sm_size,
+            st_ses->sm_info.sm_type);
+        if (status) {
+            ALOGE("%s:[%d] reg_sm failed with err %d", __func__,
+                st_ses->sm_handle, status);
+            break;
+        }
+        /* switch hw sessions only if successful*/
+        list_for_each(node, &st_ses->clients_list) {
+            c_ses = node_to_item(node, st_session_t, hw_list_node);
+            c_ses->exec_mode = new_exec_mode;
+            if (c_ses->state == ST_STATE_ACTIVE) {
+                dereg_hal_event_session(c_ses);
+                reg_hal_event_session(c_ses, new_hw_ses);
+            }
+        }
+        st_ses->exec_mode = new_exec_mode;
+        st_ses->hw_ses_current = new_hw_ses;
+        /* remain in current state */
         break;
 
     case ST_SES_EV_SET_DEVICE:
@@ -850,9 +4260,9 @@
 
     case ST_SES_EV_GET_PARAM_DATA:
         status = hw_ses->fptrs->get_param_data(hw_ses,
-                      ev->payload.getparam.param, ev->payload.getparam.payload,
-                      ev->payload.getparam.payload_size,
-                      ev->payload.getparam.param_data_size);
+            ev->payload.getparam.param, ev->payload.getparam.payload,
+            ev->payload.getparam.payload_size,
+            ev->payload.getparam.param_data_size);
         break;
 
     case ST_SES_EV_REQUEST_DET:
@@ -864,35 +4274,79 @@
     default:
         ALOGD("%s:[%d] unhandled event", __func__, st_ses->sm_handle);
         break;
-
     };
 
     return status;
 }
 
-static int active_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int active_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
+    st_session_t *stc_ses = ev->stc_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
-    st_session_ev_t deferred_ev =  { .ev_id = ST_SES_EV_DEFERRED_STOP };
     st_hw_session_t *new_hw_ses = NULL;
     st_exec_mode_t new_exec_mode;
+    st_arm_second_stage_t *st_sec_stage = NULL;
+    struct sound_trigger_recognition_event *event = NULL;
+    recognition_callback_t callback = NULL;
+    void *cookie = NULL;
+    bool lab_enabled = false, enable_second_stage = false, active = false;
 
     /* skip parameter check as this is an internal funciton */
     ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle, ev->ev_id);
 
     switch (ev->ev_id) {
+    case ST_SES_EV_LOAD_SM:
+        /* Valid in multi-client usecase */
+        status = handle_load_sm(st_ses, stc_ses);
+        break;
+
+    case ST_SES_EV_UNLOAD_SM:
+        /* Valid in multi-client usecase */
+        status = handle_unload_sm(st_ses, stc_ses);
+        break;
+
+    case ST_SES_EV_RESTART:
+        /* Valid in multi-client usecase */
+        update_hw_config_on_restart(st_ses, stc_ses);
+        /* Fall through */
+    case ST_SES_EV_START:
+        /* Valid in multi-client usecase */
+        status = stop_session(st_ses, hw_ses, false);
+        if (!status) {
+            status = start_session(st_ses, hw_ses, false);
+            if (status)
+                ALOGE("%s:[%d] start_session failed %d", __func__,
+                    st_ses->sm_handle, status);
+        } else {
+            ALOGE("%s:[%d] stop_session failed %d", __func__,
+                st_ses->sm_handle, status);
+        }
+        if (status & st_ses->stdev->ssr_offline_received) {
+            hw_ses->fptrs->dereg_sm(hw_ses);
+            STATE_TRANSITION(st_ses, ssr_state_fn);
+            status = 0;
+        }
+        break;
+
     case ST_SES_EV_SET_EXEC_MODE:
         new_exec_mode = ev->payload.exec_mode;
 
-        /* if no change in mode or dynamic transition not enabled then noop */
-        if ((new_exec_mode == st_ses->exec_mode) || !st_ses->enable_trans)
+        if (new_exec_mode == st_ses->exec_mode) {
+            stc_ses->exec_mode = st_ses->exec_mode;
             break;
+        }
 
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
             ALOGV("%s: disable current session", __func__);
             st_ses->exec_mode = ST_EXEC_MODE_NONE;
             status = stop_session(st_ses, hw_ses, true);
+            list_for_each(node, &st_ses->clients_list) {
+                c_ses = node_to_item(node, st_session_t, hw_list_node);
+                c_ses->exec_mode = ST_EXEC_MODE_NONE;
+            }
             if (status)
                 break;
         }
@@ -902,22 +4356,26 @@
 
         if (ST_EXEC_MODE_CPE == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_cpe;
-            st_ses->hw_ses_cpe->enable_second_stage =
-                st_ses->hw_ses_adsp->enable_second_stage;
-            st_ses->hw_ses_cpe->second_stage_list =
-                st_ses->hw_ses_adsp->second_stage_list;
         } else if (ST_EXEC_MODE_ADSP == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_adsp;
-            st_ses->hw_ses_adsp->enable_second_stage =
-                st_ses->hw_ses_cpe->enable_second_stage;
-            st_ses->hw_ses_adsp->second_stage_list =
-                st_ses->hw_ses_cpe->second_stage_list;
         } else {
             ALOGE("%s: unknown execution mode %d", __func__,
                 new_exec_mode);
             status = -EINVAL;
             break;
         }
+        /*
+         * hw session changed to/from WDSP/ADSP, hence update the
+         * related config.
+         * Not applicable for LPI<->non-LPI transtions as hw session
+         * doesn't change.
+         */
+        status = update_hw_config_on_start(stc_ses, new_hw_ses);
+        if (status) {
+            ALOGE("%s: Update_hw_config_on_start failed %d",
+                __func__, status);
+            break;
+        }
 
         ALOGV("%s: enable current session", __func__);
         status = start_session(st_ses, new_hw_ses, true);
@@ -925,216 +4383,298 @@
             break;
 
         /* set new exec mode and current session */
+        list_for_each(node, &st_ses->clients_list) {
+            c_ses = node_to_item(node, st_session_t, hw_list_node);
+            c_ses->exec_mode = new_exec_mode;
+            if (c_ses->state == ST_STATE_ACTIVE) {
+                dereg_hal_event_session(c_ses);
+                reg_hal_event_session(c_ses, new_hw_ses);
+            }
+        }
         st_ses->exec_mode = new_exec_mode;
         st_ses->hw_ses_current = new_hw_ses;
+
         ALOGV("%s: end transition", __func__);
         break;
 
     case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
-        /* Fall through to handle pause events similarly to stop events. */
-    case ST_SES_EV_STOP:
-        if (st_ses->paused)
-            st_ses->client_req_state = ST_STATE_ACTIVE;
-        else
-            st_ses->client_req_state = ST_STATE_LOADED;
+        /*
+         * For multi-client, the first active pausing client stops the hw
+         * session and moves to loaded state.
+         */
+        stc_ses->paused = true;
+        if (stc_ses->state != ST_STATE_ACTIVE)
+                break;
+
         status = stop_session(st_ses, hw_ses, false);
+
         if (status) {
             if (st_ses->stdev->ssr_offline_received) {
                 STATE_TRANSITION(st_ses, ssr_state_fn);
-                if (st_ses->enable_second_stage)
-                    stop_second_stage_session(st_ses);
-                hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-                /* Send success to client because the failure is recovered
-                 * internally from SSR.
-                 */
+                hw_ses->fptrs->dereg_sm(hw_ses);
                 status = 0;
-                break;
             } else {
                 ALOGE("%s:[%d] failed to stop session, err %d", __func__,
                     st_ses->sm_handle, status);
+                /* Move anyway to loaded state */
+                STATE_TRANSITION(st_ses, loaded_state_fn);
             }
+            break;
         }
-
         STATE_TRANSITION(st_ses, loaded_state_fn);
         break;
 
-    case ST_SES_EV_DETECTED:
-        {
-            size_t payload_size = ev->payload.detected.payload_size;
-            struct sound_trigger_recognition_event *event = NULL;
-            recognition_callback_t callback;
-            bool lab_enabled = false, enable_second_stage = false;
+    case ST_SES_EV_STOP:
+        status = stop_session(st_ses, hw_ses, false);
+        if (status)
+            ALOGE("%s:[%d] start_session failed %d", __func__,
+                st_ses->sm_handle, status);
 
-            if (!st_ses->enable_second_stage ||
-                st_ses->detection_requested) {
-                status = process_detection_event(st_ses,
-                    ev->payload.detected.timestamp,
-                    ev->payload.detected.detect_status,
-                    ev->payload.detected.detect_payload,
-                    payload_size, &event);
-                if (status || !event) {
-                    ALOGE("%s:[%d] process_detection_event failed err %d", __func__,
+        /* Continue to reconfig based off other active clients, if any */
+        active = update_hw_config_on_stop(st_ses, stc_ses);
+
+        if (!status) {
+            if (active) {
+                ALOGD("%s: client c%d stopped, start %d due to reconfig",
+                    __func__, stc_ses->sm_handle, st_ses->sm_handle);
+                status = start_session(st_ses, hw_ses, false);
+                if (status)
+                    ALOGE("%s:[%d] start_session failed %d", __func__,
                         st_ses->sm_handle, status);
-                    /* Stop buffering if this is not a successful detection and
-                        LAB is triggered in hw automatically */
-                    hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
-
-                    if (event)
-                        free(event);
-                    break;
-                }
+                /* Stay in active state */
             } else {
-                enable_second_stage = true;
-                st_ses->sent_detection_to_client = false;
-                get_first_stage_detection_params(st_ses,
-                    ev->payload.detected.detect_payload, payload_size);
-                memcpy(st_ses->det_session_ev, ev, sizeof(st_session_ev_t));
+                STATE_TRANSITION(st_ses, loaded_state_fn);
             }
+        }
 
-            /*
-             * change to new state before invoking user callback, this will
-             * ensure that if user calls start_recognition immediately from the
-             * callback it will be handled by one of the two states below
-             */
-            if (!status && st_ses->lab_enabled) {
-                ST_DBG_FILE_OPEN_WR(st_ses->lab_fp, ST_DEBUG_DUMP_LOCATION, "lab_capture",
-                    "bin", file_cnt++);
-                STATE_TRANSITION(st_ses, buffering_state_fn);
-            } else {
-                STATE_TRANSITION(st_ses, detected_state_fn);
-            }
-
-            if (!st_ses->callback) {
-                ALOGE("%s:[%d] received detection event but no callback",
-                    __func__, st_ses->sm_handle);
-                status = -EINVAL;
-                if (event && !enable_second_stage)
-                    free(event);
-                break;
-            }
-            callback = st_ses->callback;
-
-            /*
-             * store the current capture requested in-case a new start comes
-             * once we exist the critical-section.
-             * In this case we continue to operate based on previous capture requested
-             * setting untill the new session start is processed and resets the state
-             */
-            lab_enabled = st_ses->lab_enabled;
-            pthread_mutex_unlock(&st_ses->lock);
-
-            /*
-             * callback to user, assumption is that client does not
-             * block in the callback waiting for data otherwise will be a deadlock
-             */
-            if (!enable_second_stage) {
-                ALOGD("%s:[%d] invoking the client callback",
-                    __func__, st_ses->sm_handle);
-                ATRACE_ASYNC_END("sthal: detection success",
-                    st_ses->sm_handle);
-                ATRACE_BEGIN("sthal: client detection callback");
-                callback(event, st_ses->cookie);
-                ATRACE_END();
-            }
-
-            /*
-             * TODO: Add RECOGNITION_STATUS_GET_STATE_RESPONSE to
-             * the SoundTrigger API header.
-             */
-            if (lab_enabled &&
-                ((ev->payload.detected.detect_status ==
-                  RECOGNITION_STATUS_SUCCESS) ||
-                 (ev->payload.detected.detect_status == 3))) {
-                /* Cache lab data to internal buffers (blocking call) */
-                hw_ses->fptrs->process_lab_capture(hw_ses);
-            }
-
-            /*
-             * It is possible that the client may start/stop/unload the session
-             * with the same lock held, before we aqcuire lock here.
-             * We need further processing only if client starts in detected state
-             * or buffering state if lab was enabled, else return gracefully.
-             */
-             do {
-                status = pthread_mutex_trylock(&st_ses->lock);
-             } while (status && ((st_ses->current_state == detected_state_fn) ||
-                      (st_ses->current_state == buffering_state_fn)));
-
-            if (st_ses->current_state != detected_state_fn) {
-                ALOGV("%s:[%d] client not in detected state, lock status %d",
-                      __func__, st_ses->sm_handle, status);
-                if (!status) {
-                    /*
-                     * Stop session if still in buffering state and no pending
-                     * stop to be handled i.e. internally buffering was stopped.
-                     * This is required to avoid further detections in wrong state.
-                     * Client is expected to issue start recognition for current
-                     * detection event which will restart the session.
-                     */
-                    if ((st_ses->current_state == buffering_state_fn) &&
-                        !st_ses->pending_stop) {
-                        ALOGD("%s:[%d] buffering stopped internally, stop session",
-                              __func__, st_ses->sm_handle);
-                        stop_session(st_ses, hw_ses, false);
-                        STATE_TRANSITION(st_ses, loaded_state_fn);
-                    }
-
-                    pthread_mutex_unlock(&st_ses->lock);
-                }
-
-                if (!st_ses->enable_second_stage)
-                    free(event);
+        if (status) {
+            if (st_ses->stdev->ssr_offline_received) {
+                hw_ses->fptrs->dereg_sm(hw_ses);
+                STATE_TRANSITION(st_ses, ssr_state_fn);
                 status = 0;
-                if (st_ses->detection_requested) {
-                    st_ses->detection_requested = false;
-                    enable_second_stage_processing(st_ses, hw_ses);
-                }
+            } else {
+                STATE_TRANSITION(st_ses, loaded_state_fn);
+            }
+        }
+
+        break;
+
+    case ST_SES_EV_DETECTED:
+        /*
+         * Find which client is this detection for.
+         * Note that only one keyword detection can happen at a time.
+         */
+        stc_ses = get_detected_client(st_ses,
+            ev->payload.detected.detect_payload,
+            ev->payload.detected.payload_size);
+
+        if (!stc_ses) {
+            ALOGW("%s:[%d] Couldn't find a matching client for detection",
+                __func__, st_ses->sm_handle);
+            /*
+             * Though we set higest conf level 100 for inactive client in merged
+             * sound model, it may be possible it still detects. In case the lab
+             * is enabled due to other active client, stop hw buffering.
+             */
+            if (st_ses->lab_enabled)
+                hw_ses->fptrs->stop_buffering(hw_ses);
+            break;
+        }
+        st_ses->det_stc_ses = stc_ses;
+        st_ses->hw_ses_current->enable_second_stage = false; /* Initialize */
+
+        if (list_empty(&stc_ses->second_stage_list) ||
+            st_ses->detection_requested) {
+            st_ses->detection_requested = false;
+            status = process_detection_event(st_ses,
+                ev->payload.detected.timestamp,
+                ev->payload.detected.detect_status,
+                ev->payload.detected.detect_payload,
+                ev->payload.detected.payload_size,
+                &event);
+            if (status || !event) {
+                ALOGE("%s:[%d] process_detection_event failed err %d", __func__,
+                    st_ses->sm_handle, status);
+                /*
+                 * Stop buffering if this is not a successful detection and
+                 * LAB is triggered in hw automatically
+                 */
+                hw_ses->fptrs->stop_buffering(hw_ses);
+                if (event)
+                    free(event);
                 break;
             }
+        } else {
+            ALOGV("%s:[c%d] second stage enabled, list_empty %d,"
+                  "det_requested %d", __func__, stc_ses->sm_handle,
+                  list_empty(&stc_ses->second_stage_list),
+                  st_ses->detection_requested);
 
-            if (st_ses->detection_requested) {
-                st_ses->detection_requested = false;
-                enable_second_stage_processing(st_ses, hw_ses);
-            }
+            enable_second_stage = true;
             /*
-             * If we are not buffering (i.e capture is not requested), then
-             * trigger a deferred stop. Most applications issue (re)start
-             * almost immediately. Delaying stop allows unnecessary teardown
-             * and reinitialization of backend.
+             * Before first stage starts buffering, update the second stage info
+             * to first stage layer for further communication between first and
+             * second stage layers.
              */
-            if (!lab_enabled) {
-                /*
-                 * Note that this event will only be posted to the detected state
-                 * The current state may switch to active if the client
-                 * issues start/restart before control of the callback thread
-                 * reaches this point.
-                 */
-                DISPATCH_EVENT(st_ses, deferred_ev, status);
-            } else {
-                ALOGE("%s:[%d] capture is requested but state is still detected!?",
-                      __func__, st_ses->sm_handle);
-            }
+            st_ses->hw_ses_current->enable_second_stage = true;
+            st_ses->hw_ses_current->second_stage_list =
+                &(stc_ses->second_stage_list);
 
-            if (!enable_second_stage)
+            list_for_each(node, &stc_ses->second_stage_list) {
+                st_sec_stage = node_to_item(node, st_arm_second_stage_t,
+                                            list_node);
+                st_sec_stage->ss_session->st_ses = st_ses;
+            }
+            get_first_stage_detection_params(st_ses,
+                ev->payload.detected.detect_payload,
+                ev->payload.detected.payload_size);
+            memcpy(st_ses->det_session_ev, ev, sizeof(st_session_ev_t));
+        }
+        /*
+         * change to new state before invoking user callback, this will
+         * ensure that if user calls start_recognition immediately from the
+         * callback it will be handled by one of the two states below
+         */
+        if (!status && st_ses->lab_enabled) {
+            if (stc_ses->rc_config->capture_requested ||
+                !list_empty(&stc_ses->second_stage_list)) {
+                ST_DBG_FILE_OPEN_WR(st_ses->lab_fp, ST_DEBUG_DUMP_LOCATION,
+                    "lab_capture", "bin", file_cnt++);
+                STATE_TRANSITION(st_ses, buffering_state_fn);
+                lab_enabled = true;
+            } else {
+                /*
+                 * for merged model case the client detected may not have
+                 * requested the capture nor enabled the second stage, but the
+                 * hw lab could be enabled due to other client requested capture
+                 * or enabled second stage.
+                 */
+                ALOGV("%s: stop buffering as c%d doesn't need", __func__,
+                    stc_ses->sm_handle);
+                hw_ses->fptrs->stop_buffering(hw_ses);
+                STATE_TRANSITION(st_ses, detected_state_fn);
+                lab_enabled = false;
+            }
+        } else {
+            STATE_TRANSITION(st_ses, detected_state_fn);
+        }
+
+        if (!stc_ses->callback) {
+            ALOGE("%s:[c%d] received detection event but no callback",
+                __func__, stc_ses->sm_handle);
+            status = -EINVAL;
+            if (event)
                 free(event);
             break;
         }
+        /*
+         * callback to user, assumption is that client does not
+         * block in the callback waiting for data otherwise will be a deadlock.
+         * If second stage is enabled, the detection will be sent later when
+         * second stage successfully detects.
+         */
+        if (!enable_second_stage) {
+            callback = stc_ses->callback;
+            cookie = stc_ses->cookie;
+            ALOGD("%s:[c%d] invoking the client callback",
+                __func__, stc_ses->sm_handle);
+            ATRACE_ASYNC_END("sthal: detection success",
+                st_ses->sm_handle);
+            pthread_mutex_unlock(&st_ses->lock);
+            ATRACE_BEGIN("sthal: client detection callback");
+            callback(event, cookie);
+            ATRACE_END();
+        } else {
+            pthread_mutex_unlock(&st_ses->lock);
+        }
+        if (event)
+            free(event);
+
+        /*
+         * TODO: Add RECOGNITION_STATUS_GET_STATE_RESPONSE to
+         * the SoundTrigger API header.
+         */
+        if (lab_enabled &&
+            ((ev->payload.detected.detect_status ==
+              RECOGNITION_STATUS_SUCCESS) ||
+             (ev->payload.detected.detect_status == 3))) {
+            /* Cache lab data to internal buffers (blocking call) */
+            hw_ses->fptrs->process_lab_capture(hw_ses);
+        }
+
+        /*
+         * It is possible that the client may start/stop/unload the session
+         * with the same lock held, before we aqcuire lock here.
+         * We need further processing only if client starts in detected state
+         * or buffering state if lab was enabled, else return gracefully.
+         * For multi-client scenario, only one client is assumed to be
+         * detected/bufffering, so the logic remains same.
+         */
+         do {
+             status = pthread_mutex_trylock(&st_ses->lock);
+         } while (status && ((st_ses->current_state == detected_state_fn) ||
+                  (st_ses->current_state == buffering_state_fn)));
+
+        if (st_ses->current_state != detected_state_fn) {
+            ALOGV("%s:[%d] client not in detected state, lock status %d",
+                __func__, st_ses->sm_handle, status);
+            if (!status) {
+                /*
+                 * Stop session if still in buffering state and no pending
+                 * stop to be handled i.e. internally buffering was stopped.
+                 * This is required to avoid further detections in wrong state.
+                 * Client is expected to issue start recognition for current
+                 * detection event which will restart the session.
+                 */
+                if ((st_ses->current_state == buffering_state_fn) &&
+                    !stc_ses->pending_stop) {
+                    ALOGD("%s:[%d] buffering stopped internally, post c%d stop",
+                        __func__, st_ses->sm_handle,
+                        st_ses->det_stc_ses->sm_handle);
+                    status = hw_session_notifier_enqueue(stc_ses->sm_handle,
+                        ST_SES_EV_DEFERRED_STOP,
+                        ST_SES_DEFERRED_STOP_SS_DELAY_MS);
+                    if (!status)
+                        stc_ses->pending_stop = true;
+                }
+                pthread_mutex_unlock(&st_ses->lock);
+            }
+            status = 0;
+            break;
+        }
+
+        /*
+         * If we are not buffering (i.e capture is not requested), then
+         * trigger a deferred stop. Most applications issue (re)start
+         * almost immediately. Delaying stop allows unnecessary teardown
+         * and reinitialization of backend.
+         */
+        if (!lab_enabled) {
+            /*
+             * Note that this event will only be posted to the detected state
+             * The current state may switch to active if the client
+             * issues start/restart before control of the callback thread
+             * reaches this point.
+             */
+            st_session_ev_t deferred_ev = { .ev_id = ST_SES_EV_DEFERRED_STOP,
+                .stc_ses = stc_ses};
+            DISPATCH_EVENT(st_ses, deferred_ev, status);
+        } else {
+            ALOGE("%s:[%d] capture is requested but state is still detected!?",
+                __func__, st_ses->sm_handle);
+        }
+        break;
 
     case ST_SES_EV_SSR_OFFLINE:
-        STATE_TRANSITION(st_ses, ssr_state_fn);
-        if (st_ses->enable_second_stage)
-            stop_second_stage_session(st_ses);
         /* exec mode can be none if ssr occurs during a transition */
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE)
-            stop_hw_session(st_ses, hw_ses, true /* unload_sm */);
-
-        st_ses->client_req_state = ST_STATE_ACTIVE;
+            stop_session(st_ses, hw_ses, true);
+        STATE_TRANSITION(st_ses, ssr_state_fn);
         break;
 
     case ST_SES_EV_SEND_CHMIX_COEFF:
         status = hw_ses->fptrs->send_custom_chmix_coeff(hw_ses,
-                                  ev->payload.chmix_coeff_str);
+            ev->payload.chmix_coeff_str);
         break;
 
     case ST_SES_EV_SET_DEVICE:
@@ -1143,6 +4683,10 @@
         else
             status = hw_ses->fptrs->enable_device(hw_ses, true);
 
+        if (status && st_ses->stdev->ssr_offline_received) {
+            STATE_TRANSITION(st_ses, ssr_state_fn);
+            status = 0;
+        }
         break;
 
     case ST_SES_EV_READ_PCM:
@@ -1157,93 +4701,117 @@
 
     case ST_SES_EV_GET_PARAM_DATA:
         status = hw_ses->fptrs->get_param_data(hw_ses,
-                      ev->payload.getparam.param, ev->payload.getparam.payload,
-                      ev->payload.getparam.payload_size,
-                      ev->payload.getparam.param_data_size);
+            ev->payload.getparam.param, ev->payload.getparam.payload,
+            ev->payload.getparam.payload_size,
+            ev->payload.getparam.param_data_size);
         break;
 
     case ST_SES_EV_REQUEST_DET:
-        status = hw_ses->fptrs->send_detection_request(hw_ses);
-        if (!status) {
-            st_ses->detection_requested = true;
-            /*
-             * Disable second stage processing if a forced detection is
-             * requested.
-             */
-            disable_second_stage_processing(st_ses, hw_ses);
+        if (!list_empty(&stc_ses->second_stage_list)) {
+            ALOGE("%s:[%d] Event not supported with second stage enabled",
+                __func__, st_ses->sm_handle);
+            status = -EINVAL;
+            break;
         }
+        status = hw_ses->fptrs->send_detection_request(hw_ses);
+        if (!status)
+            st_ses->detection_requested = true;
         break;
 
     default:
         ALOGD("%s:[%d] unhandled event", __func__, st_ses->sm_handle);
         break;
-
     };
 
     return status;
 }
 
-static int detected_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int detected_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
-    st_exec_mode_t new_exec_mode;
+    st_exec_mode_t new_exec_mode = ST_EXEC_MODE_NONE;
+    st_session_t *stc_ses = ev->stc_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
     st_hw_session_t *new_hw_ses = NULL;
 
     /* skip parameter check as this is an internal funciton */
-    ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle, ev->ev_id);
+    ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+        st_ses->sm_handle, ev->ev_id);
 
     switch (ev->ev_id) {
+    case ST_SES_EV_LOAD_SM:
+        /* Valid event only in multi-client usecase */
+        /*
+         * If a detected client deffered stop event is not handled yet,
+         * handle here before moving out of detected state
+         */
+        if (st_ses->det_stc_ses && !st_ses->det_stc_ses->pending_stop) {
+            ALOGD("%s:[%d] post deferred stop for c%d", __func__,
+                st_ses->sm_handle, st_ses->det_stc_ses->sm_handle);
+            status = hw_session_notifier_enqueue(
+               st_ses->det_stc_ses->sm_handle, ST_SES_EV_DEFERRED_STOP,
+               ST_SES_DEFERRED_STOP_DELAY_MS);
+            if (!status)
+                st_ses->det_stc_ses->pending_stop = true;
+        }
+        status = handle_load_sm(st_ses, stc_ses);
+        break;
+
+    case ST_SES_EV_UNLOAD_SM:
+        /* Valid event only in multi-client usecase */
+        /*
+         * If a detected client deffered stop event is not handled yet,
+         * handle here before moving out of detected state
+         */
+        if (st_ses->det_stc_ses && !st_ses->det_stc_ses->pending_stop) {
+            ALOGD("%s:[%d] post deferred stop for client c%d", __func__,
+                st_ses->sm_handle, st_ses->det_stc_ses->sm_handle);
+            status = hw_session_notifier_enqueue(
+               st_ses->det_stc_ses->sm_handle, ST_SES_EV_DEFERRED_STOP,
+               ST_SES_DEFERRED_STOP_DELAY_MS);
+            if (!status)
+                st_ses->det_stc_ses->pending_stop = true;
+        }
+        status = handle_unload_sm(st_ses, stc_ses);
+        break;
+
     case ST_SES_EV_START:
-        /* session already started but config has changed stop and restart */
-        status = stop_session(st_ses, hw_ses, false);
-        if (status)
-            break;
-        status = start_session(st_ses, hw_ses, false);
-        if (status)
-            break;
+        /* For multi-client, other loaded client may start */
         STATE_TRANSITION(st_ses, active_state_fn);
+        DISPATCH_EVENT(st_ses, *ev, status);
         break;
 
     case ST_SES_EV_RESTART:
-        /* session already restarted without any config changes */
-        restart_session(st_ses, hw_ses);
-        STATE_TRANSITION(st_ses, active_state_fn);
+        status = restart_session(st_ses, hw_ses);
+        if (status && !st_ses->stdev->ssr_offline_received)
+            ALOGE("%s:[%d] failed to start session, err %d", __func__,
+                st_ses->sm_handle, status);
+
+        if (st_ses->stdev->ssr_offline_received) {
+            stop_session(st_ses, hw_ses, true);
+            STATE_TRANSITION(st_ses, ssr_state_fn);
+            status = 0;
+        } else {
+            /* Move anyways to allow client unload */
+            STATE_TRANSITION(st_ses, active_state_fn);
+        }
         break;
+
     case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
-        st_ses->client_req_state = ST_STATE_LOADED;
-        /* Fall through to handle pause events similarly to stop event. */
     case ST_SES_EV_STOP:
         /*
          * It is possible that the client can issue stop after detection
          * callback. This even can be issued internally as part of
-         * deferred stop as well.
+         * deferred stop as well. For multi-client, it could be current
+         * detected  client stop or other client stop.
          */
-        status = stop_session(st_ses, hw_ses, false);
-        if (status) {
-            if (st_ses->stdev->ssr_offline_received) {
-                STATE_TRANSITION(st_ses, ssr_state_fn);
-                if (st_ses->enable_second_stage)
-                    stop_second_stage_session(st_ses);
-                hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-                st_ses->client_req_state = ST_STATE_LOADED;
-                /* Send success to client because the failure is recovered
-                 * internally from SSR.
-                 */
-                status = 0;
-            } else {
-                ALOGE("%s:[%d] failed to stop session, err %d", __func__,
-                    st_ses->sm_handle, status);
-            }
-            break;
-        }
-
-        STATE_TRANSITION(st_ses, loaded_state_fn);
+        STATE_TRANSITION(st_ses, active_state_fn);
+        DISPATCH_EVENT(st_ses, *ev, status);
         break;
+
     case ST_SES_EV_SSR_OFFLINE:
-        if (st_ses->enable_second_stage)
-            stop_second_stage_session(st_ses);
         /*
          * Ignore return status during SSR handling
          * as the ADSP or CPE might be down so these
@@ -1251,22 +4819,25 @@
          * ssr occurs during a transition.
          */
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE)
-            stop_hw_session(st_ses, hw_ses, true /* unload sm */);
-
-        st_ses->client_req_state = ST_STATE_ACTIVE;
+            stop_session(st_ses, hw_ses, true);
         STATE_TRANSITION(st_ses, ssr_state_fn);
         break;
 
     case ST_SES_EV_SET_EXEC_MODE:
         new_exec_mode = ev->payload.exec_mode;
 
-        /* if no change in mode or dynamic transition not enabled then noop */
-        if ((new_exec_mode == st_ses->exec_mode) || !st_ses->enable_trans)
+        if (new_exec_mode == st_ses->exec_mode) {
+            stc_ses->exec_mode = st_ses->exec_mode;
             break;
+        }
 
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
             st_ses->exec_mode = ST_EXEC_MODE_NONE;
             status = stop_session(st_ses, hw_ses, true);
+            list_for_each(node, &st_ses->clients_list) {
+                c_ses = node_to_item(node, st_session_t, hw_list_node);
+                c_ses->exec_mode = new_exec_mode;
+            }
             if (status)
                 break;
         }
@@ -1277,16 +4848,8 @@
         /* switch to new hw session */
         if (ST_EXEC_MODE_CPE == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_cpe;
-            st_ses->hw_ses_cpe->enable_second_stage =
-                st_ses->hw_ses_adsp->enable_second_stage;
-            st_ses->hw_ses_cpe->second_stage_list =
-                st_ses->hw_ses_adsp->second_stage_list;
         } else if (ST_EXEC_MODE_ADSP == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_adsp;
-            st_ses->hw_ses_adsp->enable_second_stage =
-                st_ses->hw_ses_cpe->enable_second_stage;
-            st_ses->hw_ses_adsp->second_stage_list =
-                st_ses->hw_ses_cpe->second_stage_list;
         } else {
             ALOGE("%s: unknown execution mode %d", __func__,
                 new_exec_mode);
@@ -1295,6 +4858,18 @@
         }
 
         /*
+         * hw session changed to/from WDSP/ADSP, hence update the
+         * related config.
+         * Not applicable for LPI<->non-LPI transtions as hw session
+         * doesn't change.
+         */
+        status = update_hw_config_on_start(stc_ses, new_hw_ses);
+        if (status) {
+            ALOGE("%s: Update_hw_config_on_start failed %d",
+                __func__, status);
+            break;
+        }
+        /*
          * start new hw session and stay in detected state as
          * client restart and stop concurrency scenarios are handled
          * in this state
@@ -1303,6 +4878,14 @@
         if (status)
             break;
 
+        list_for_each(node, &st_ses->clients_list) {
+            c_ses = node_to_item(node, st_session_t, hw_list_node);
+            c_ses->exec_mode = new_exec_mode;
+            if (c_ses->state == ST_STATE_ACTIVE) {
+                dereg_hal_event_session(c_ses);
+                reg_hal_event_session(c_ses, new_hw_ses);
+            }
+        }
         st_ses->exec_mode = new_exec_mode;
         st_ses->hw_ses_current = new_hw_ses;
         break;
@@ -1327,18 +4910,17 @@
 
     case ST_SES_EV_GET_PARAM_DATA:
         status = hw_ses->fptrs->get_param_data(hw_ses,
-                      ev->payload.getparam.param, ev->payload.getparam.payload,
-                      ev->payload.getparam.payload_size,
-                      ev->payload.getparam.param_data_size);
+            ev->payload.getparam.param, ev->payload.getparam.payload,
+            ev->payload.getparam.payload_size,
+            ev->payload.getparam.param_data_size);
         break;
     case ST_SES_EV_DEFERRED_STOP:
         ALOGD("%s:[%d] post deferred stop from detected state", __func__,
-              st_ses->sm_handle);
-        status = hw_session_notifier_enqueue(st_ses->sm_handle,
-                                             ST_SES_EV_DEFERRED_STOP,
-                                             ST_SES_DEFERRED_STOP_DELAY_MS);
+            st_ses->sm_handle);
+        status = hw_session_notifier_enqueue(stc_ses->sm_handle,
+            ST_SES_EV_DEFERRED_STOP, ST_SES_DEFERRED_STOP_DELAY_MS);
         if (!status)
-            st_ses->pending_stop = true;
+            stc_ses->pending_stop = true;
         break;
     case ST_SES_EV_REQUEST_DET:
         ALOGE("%s:[%d] Event not supported in this state",
@@ -1348,26 +4930,41 @@
     default:
         ALOGD("%s:[%d] unhandled event", __func__, st_ses->sm_handle);
         break;
-
     };
-
     return status;
 }
 
-static int buffering_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int buffering_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
+    st_session_t *stc_ses = ev->stc_ses;
+    struct listnode *node = NULL;
+    st_session_t *c_ses = NULL;
     st_hw_session_t *hw_ses = st_ses->hw_ses_current;
-    st_exec_mode_t new_exec_mode = 0;
+    st_exec_mode_t new_exec_mode = ST_EXEC_MODE_NONE;
     st_hw_session_t *new_hw_ses = NULL;
-    st_session_ev_t set_dev_ev =  { .ev_id = ST_SES_EV_SET_DEVICE };
 
     /* skip parameter check as this is an internal function */
-    ALOGVV("%s:[%d] handle event id %d", __func__, st_ses->sm_handle,
-        ev->ev_id);
-    switch (ev->ev_id) {
-    case ST_SES_EV_READ_PCM:
+    ALOGVV("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+         st_ses->sm_handle, ev->ev_id);
 
+    switch (ev->ev_id) {
+    case ST_SES_EV_LOAD_SM:
+        /* Valid in multi-client usecase */
+        ALOGD("%s:[c%d-%d] load sm", __func__, stc_ses->sm_handle,
+            st_ses->sm_handle);
+
+        status = handle_load_sm(st_ses, stc_ses);
+        break;
+
+    case ST_SES_EV_UNLOAD_SM:
+        ALOGD("%s:[c%d-%d] unload sm", __func__, stc_ses->sm_handle,
+            st_ses->sm_handle);
+
+        status = handle_unload_sm(st_ses, stc_ses);
+        break;
+
+    case ST_SES_EV_READ_PCM:
         /* Note: this function may block if there is no PCM data ready*/
         hw_ses->fptrs->read_pcm(hw_ses, ev->payload.readpcm.out_buff,
             ev->payload.readpcm.out_buff_size);
@@ -1375,61 +4972,48 @@
             ev->payload.readpcm.out_buff_size);
         break;
     case ST_SES_EV_END_BUFFERING:
-        hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
-        if (!st_ses->pending_stop) {
-            ALOGD("%s:[%d] post deferred stop on buffering end", __func__,
-                  st_ses->sm_handle);
-            status = hw_session_notifier_enqueue(st_ses->sm_handle,
-                                                 ST_SES_EV_DEFERRED_STOP,
-                                                 ST_SES_DEFERRED_STOP_DELAY_MS);
-            if (!status)
-                st_ses->pending_stop = true;
-        } else {
-            ALOGD("%s:[%d] skip deferred stop on buffering as already set", __func__,
-                  st_ses->sm_handle);
+        if (stc_ses == st_ses->det_stc_ses) {
+            hw_ses->fptrs->stop_buffering(hw_ses);
+            if (!stc_ses->pending_stop) {
+                ALOGD("%s:[c%d] post deferred stop on buffering end", __func__,
+                    stc_ses->sm_handle);
+                status = hw_session_notifier_enqueue(stc_ses->sm_handle,
+                    ST_SES_EV_DEFERRED_STOP, ST_SES_DEFERRED_STOP_DELAY_MS);
+                if (!status)
+                    stc_ses->pending_stop = true;
+            } else {
+                ALOGD("%s:[c%d] skip deferred stop on buffering as already set",
+                      __func__, stc_ses->sm_handle);
+            }
         }
         break;
-    case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
-        /* Fall through to handle pause events similarly to stop event. */
-    case ST_SES_EV_STOP:
-        ALOGD("%s:[%d] handle event STOP %s", __func__, st_ses->sm_handle,
-              st_ses->paused ? "(paused)" : "");
-        /*
-         * These events are related to a tear down sequence, so transition to
-         * loaded state even if there is a failure.
-         */
-        status = hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
-        if (status)
-            ALOGE("%s:[%d] failed to stop_buffering err %d", __func__,
-                st_ses->sm_handle, status);
-        /*
-         * We treat pause during buffering with the same semantics as stop.
-         * With second stage enabled, the session can be in buffering state
-         * without the app requesting it. So if the PAUSE event comes when 
-         * session is in buffering state, client_req_state needs to be set to
-         * active to ensure the session can make it back to the active state.
-         */
-        if (st_ses->paused)
-            st_ses->client_req_state = ST_STATE_ACTIVE;
-        else
-            st_ses->client_req_state = ST_STATE_LOADED;
-        status = stop_session(st_ses, hw_ses, false);
-        if (status)
-            ALOGE("%s:[%d] failed to stop session, err %d", __func__,
-                st_ses->sm_handle, status);
 
+    case ST_SES_EV_STOP:
+         ALOGD("%s:[c%d-%d] handle event STOP", __func__, stc_ses->sm_handle,
+             st_ses->sm_handle);
+        if (stc_ses != st_ses->det_stc_ses) {
+            ALOGD("%s: c%d buffering, delay c%d stop", __func__,
+                st_ses->det_stc_ses->sm_handle, stc_ses->sm_handle);
+            update_hw_config_on_stop(st_ses, stc_ses);
+            break;
+        }
+        /* Fall through */
+    case ST_SES_EV_PAUSE:
+        hw_ses->fptrs->stop_buffering(hw_ses);
+        STATE_TRANSITION(st_ses, active_state_fn);
+        DISPATCH_EVENT(st_ses, *ev, status);
         ST_DBG_FILE_CLOSE(st_ses->lab_fp);
-        STATE_TRANSITION(st_ses, loaded_state_fn);
         break;
 
     case ST_SES_EV_SET_DEVICE:
+        ALOGD("%s:[c%d-%d] handle SET_DEVICE", __func__, stc_ses->sm_handle,
+            st_ses->sm_handle);
         /*
          * Device switch will not wait for buffering to finish. It will instead
          * interrupt and stop the buffering and transition to the loaded state.
          * The loaded state will then take care of the device switch.
          */
-        hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
+        hw_ses->fptrs->stop_buffering(hw_ses);
         status = stop_session(st_ses, hw_ses, false);
         if (status && !st_ses->stdev->ssr_offline_received) {
             ALOGE("%s:[%d] failed to stop session, err %d", __func__,
@@ -1437,13 +5021,30 @@
             break;
         }
         STATE_TRANSITION(st_ses, loaded_state_fn);
-        DISPATCH_EVENT(st_ses, set_dev_ev, status);
+        DISPATCH_EVENT(st_ses, *ev, status);
+        /*
+         * The current detected client may or may not read buffer/restart
+         * recognition. If no other clients are active, get to loaded state.
+         * Otherwise, if any other client than the detected client is active,
+         * we should move to active state for other client detections.
+         */
+        st_session_ev_t start_ev = {.ev_id = ST_SES_EV_START};
+        if (check_and_get_other_active_client(st_ses, st_ses->det_stc_ses)) {
+            start_ev.stc_ses = stc_ses;
+            DISPATCH_EVENT(st_ses, start_ev, status);
+        }
         break;
 
     case ST_SES_EV_START:
     case ST_SES_EV_RESTART:
-        ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle,
-            ev->ev_id);
+        ALOGD("%s:[c%d-%d] handle event START/RESTART", __func__,
+            stc_ses->sm_handle, st_ses->sm_handle);
+
+        if (stc_ses != st_ses->det_stc_ses) {
+            ALOGD("%s: c%d buffering, delay c%d start", __func__,
+                st_ses->det_stc_ses->sm_handle, stc_ses->sm_handle);
+            break;
+        }
         /*
          * Client starts detection again.
          * This implies a previous deferred stop hasn't completed yet as
@@ -1451,67 +5052,46 @@
          * For a restart event, issue stop buffering and restart the session
          * For a start event, stop buffering then stop and start the session
          * so that any new parameters take effect.
+         * For multi-client case while the detected is buffering,
+         * the other client stop/start would have been deferred by updating
+         * the config, and later when current detected client restarts after
+         * buffreing is completed, check if hw config is updated due to other
+         * client and stop->start the hw session to apply updated config.
          */
-        hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
-        if (ev->ev_id == ST_SES_EV_START) {
+        hw_ses->fptrs->stop_buffering(hw_ses);
+        if (hw_ses->sthw_cfg_updated || ev->ev_id == ST_SES_EV_START) {
             status = stop_session(st_ses, hw_ses, false);
-            if (status && !st_ses->stdev->ssr_offline_received) {
+            if (status) {
                 ALOGE("%s:[%d] failed to stop session, err %d", __func__,
-                      st_ses->sm_handle, status);
-                break;
+                    st_ses->sm_handle, status);
+            } else {
+                status = start_session(st_ses, hw_ses, false);
+                if (status) {
+                    ALOGE("%s:[%d] failed to start session, err %d", __func__,
+                        st_ses->sm_handle, status);
+                }
             }
-            status = start_session(st_ses, hw_ses, false);
         } else {
             status = restart_session(st_ses, hw_ses);
         }
 
         if (status) {
             if (st_ses->stdev->ssr_offline_received) {
-                if (st_ses->enable_second_stage)
-                    stop_second_stage_session(st_ses);
-                hw_ses->fptrs->dereg_sm(hw_ses, st_ses->lab_enabled);
-                st_ses->client_req_state = ST_STATE_ACTIVE;
+                hw_ses->fptrs->dereg_sm(hw_ses);
                 STATE_TRANSITION(st_ses, ssr_state_fn);
-                /* Send success to client because the failure is recovered
-                 * internally from SSR.
-                 */
                 status = 0;
             } else {
-                ALOGE("%s:[%d] failed to start session, err %d", __func__,
-                      st_ses->sm_handle, status);
                 /* move to active anyways to allow unload sm */
                 STATE_TRANSITION(st_ses, active_state_fn);
             }
         } else {
-            /* Move state back to active for restart and start events */
             STATE_TRANSITION(st_ses, active_state_fn);
         }
         break;
 
     case ST_SES_EV_SSR_OFFLINE:
-        ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle,
-            ev->ev_id);
-
-        if (st_ses->enable_second_stage) {
-            /*
-             * Second stage sessions will always buffer in order to be
-             * able to make detections. If the SSR occurs before the
-             * client is notified of a detection, it needs to recover to
-             * the active state. If the SSR occurs after this, or if the
-             * second stage detection is rejected, then recovering to
-             * the loaded state is sufficient.
-             */
-            if (!st_ses->sent_detection_to_client || st_ses->paused)
-                st_ses->client_req_state = ST_STATE_ACTIVE;
-            else
-                st_ses->client_req_state = ST_STATE_LOADED;
-        } else {
-            if (st_ses->paused)
-                st_ses->client_req_state = ST_STATE_ACTIVE;
-            else
-                st_ses->client_req_state = ST_STATE_LOADED;
-        }
-
+        ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+            st_ses->sm_handle, ev->ev_id);
         /*
          *  Ignore return status during SSR handling
          *  as the ADSP or CPE might be down so these
@@ -1519,35 +5099,35 @@
          *  ssr occurs during a transition.
          */
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
-            hw_ses->fptrs->stop_buffering(hw_ses,
-                                          st_ses->lab_enabled);
-            if (st_ses->enable_second_stage)
-                stop_second_stage_session(st_ses);
-            stop_hw_session(st_ses, hw_ses, true /* unload sm */);
+            hw_ses->fptrs->stop_buffering(hw_ses);
+            stop_session(st_ses, hw_ses, true);
         }
         STATE_TRANSITION(st_ses, ssr_state_fn);
         break;
 
     case ST_SES_EV_SET_EXEC_MODE:
-        ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle,
-            ev->ev_id);
+        ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+            st_ses->sm_handle, ev->ev_id);
 
         new_exec_mode = ev->payload.exec_mode;
-
-        /* if no change in mode or dynamic transition not enabled then noop */
-        if ((new_exec_mode == st_ses->exec_mode) || !st_ses->enable_trans)
+        if (new_exec_mode == st_ses->exec_mode) {
+            stc_ses->exec_mode = st_ses->exec_mode;
             break;
+        }
 
         if (st_ses->exec_mode != ST_EXEC_MODE_NONE) {
             st_ses->exec_mode = ST_EXEC_MODE_NONE;
-            status = hw_ses->fptrs->stop_buffering(hw_ses, st_ses->lab_enabled);
+            status = hw_ses->fptrs->stop_buffering(hw_ses);
             if (status) {
                 ALOGE("%s:[%d] failed to stop_buffering err %d", __func__,
                     st_ses->sm_handle, status);
                 break;
             }
-
             status = stop_session(st_ses, hw_ses, true);
+            list_for_each(node, &st_ses->clients_list) {
+                c_ses = node_to_item(node, st_session_t, hw_list_node);
+                c_ses->exec_mode = new_exec_mode;
+            }
             if (status) {
                 ALOGE("%s:[%d] failed to stop session, err %d", __func__,
                     st_ses->sm_handle, status);
@@ -1561,29 +5141,40 @@
         /* switch to new hw session */
         if (ST_EXEC_MODE_CPE == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_cpe;
-            st_ses->hw_ses_cpe->enable_second_stage =
-                st_ses->hw_ses_adsp->enable_second_stage;
-            st_ses->hw_ses_cpe->second_stage_list =
-                st_ses->hw_ses_adsp->second_stage_list;
         } else if (ST_EXEC_MODE_ADSP == new_exec_mode) {
             new_hw_ses = st_ses->hw_ses_adsp;
-            st_ses->hw_ses_adsp->enable_second_stage =
-                st_ses->hw_ses_cpe->enable_second_stage;
-            st_ses->hw_ses_adsp->second_stage_list =
-                st_ses->hw_ses_cpe->second_stage_list;
         } else {
             ALOGE("%s: unknown execution mode %d", __func__,
                 new_exec_mode);
             status = -EINVAL;
             break;
         }
-
+        /*
+         * hw session changed to/from WDSP/ADSP, hence update the
+         * related config.
+         * Not applicable for LPI<->non-LPI transtions as hw session
+         * doesn't change.
+         */
+        status = update_hw_config_on_start(stc_ses, new_hw_ses);
+        if (status) {
+            ALOGE("%s: Update_hw_config_on_start failed %d",
+                __func__, status);
+            break;
+        }
         status = start_session(st_ses, new_hw_ses, true);
         if (status) {
             ALOGE("%s:[%d] failed to start hw ses, err %d", __func__,
                 st_ses->sm_handle, status);
             break;
         }
+        list_for_each(node, &st_ses->clients_list) {
+            c_ses = node_to_item(node, st_session_t, hw_list_node);
+            c_ses->exec_mode = new_exec_mode;
+            if (c_ses->state == ST_STATE_ACTIVE) {
+                dereg_hal_event_session(c_ses);
+                reg_hal_event_session(c_ses, new_hw_ses);
+            }
+        }
         st_ses->exec_mode = new_exec_mode;
         st_ses->hw_ses_current = new_hw_ses;
         STATE_TRANSITION(st_ses, active_state_fn);
@@ -1591,14 +5182,14 @@
 
     case ST_SES_EV_SEND_CHMIX_COEFF:
         status = hw_ses->fptrs->send_custom_chmix_coeff(hw_ses,
-                                  ev->payload.chmix_coeff_str);
+            ev->payload.chmix_coeff_str);
         break;
 
     case ST_SES_EV_GET_PARAM_DATA:
         status = hw_ses->fptrs->get_param_data(hw_ses,
-                      ev->payload.getparam.param, ev->payload.getparam.payload,
-                      ev->payload.getparam.payload_size,
-                      ev->payload.getparam.param_data_size);
+            ev->payload.getparam.param, ev->payload.getparam.payload,
+            ev->payload.getparam.payload_size,
+            ev->payload.getparam.param_data_size);
         break;
 
     case ST_SES_EV_REQUEST_DET:
@@ -1611,94 +5202,122 @@
         ALOGD("%s:[%d] unhandled event, id %d", __func__, st_ses->sm_handle,
             ev->ev_id);
         break;
-
     };
 
     return status;
 }
 
-static int ssr_state_fn(st_session_t *st_ses, st_session_ev_t *ev)
+static int ssr_state_fn(st_proxy_session_t *st_ses, st_session_ev_t *ev)
 {
     int status = 0;
-    st_session_ev_t load_ev =  { .ev_id = ST_SES_EV_LOAD_SM };
-    st_session_ev_t start_ev =  { .ev_id = ST_SES_EV_START };
-    st_session_ev_t exec_mode_ev =  { .ev_id = ST_SES_EV_SET_EXEC_MODE };
+    st_session_t *stc_ses = ev->stc_ses;
+    st_session_ev_t load_ev =  {.ev_id = ST_SES_EV_LOAD_SM};
+    st_session_ev_t start_ev =  {.ev_id = ST_SES_EV_START};
+    st_session_ev_t exec_mode_ev =  {.ev_id = ST_SES_EV_SET_EXEC_MODE};
+    bool active = false;
 
-    /* skip parameter check as this is an internal funciton */
-    ALOGD("%s:[%d] handle event id %d", __func__, st_ses->sm_handle, ev->ev_id);
+    /* skip parameter check as this is an internal function */
+    ALOGD("%s:[c%d-%d] handle event id %d", __func__, stc_ses->sm_handle,
+        st_ses->sm_handle, ev->ev_id);
 
     switch (ev->ev_id) {
     case ST_SES_EV_SSR_ONLINE:
         ALOGV("%s:[%d] SSR ONLINE received", __func__, st_ses->sm_handle);
+        /*
+         * Load and start all clients at once instead of unload/loading
+         * due to each subsequent client dispatch. It is expected the
+         * upper layer calls SSR_ONLINE for all clients.
+         */
+        stc_ses->pending_load = true;
+        if (is_any_client_not_pending_load(st_ses))
+            break;
+
+        reset_clients_pending_load(st_ses);
 
         STATE_TRANSITION(st_ses, idle_state_fn);
 
-        if ((st_ses->ssr_transit_exec_mode == ST_EXEC_MODE_CPE) ||
-            (st_ses->ssr_transit_exec_mode == ST_EXEC_MODE_ADSP)) {
-            exec_mode_ev.payload.exec_mode = st_ses->ssr_transit_exec_mode;
+        if ((stc_ses->ssr_transit_exec_mode == ST_EXEC_MODE_CPE) ||
+            (stc_ses->ssr_transit_exec_mode == ST_EXEC_MODE_ADSP)) {
+            exec_mode_ev.stc_ses = stc_ses;
+            exec_mode_ev.payload.exec_mode = stc_ses->ssr_transit_exec_mode;
             DISPATCH_EVENT(st_ses, exec_mode_ev, status);
-            st_ses->ssr_transit_exec_mode = ST_EXEC_MODE_NONE;
+            if (status)
+                break;
+            stc_ses->ssr_transit_exec_mode = ST_EXEC_MODE_NONE;
         }
-
-        if ((ST_STATE_ACTIVE == st_ses->client_req_state) ||
-            (ST_STATE_LOADED == st_ses->client_req_state)) {
+        active = is_any_client_in_state(st_ses, ST_STATE_ACTIVE);
+        if (active || is_any_client_in_state(st_ses, ST_STATE_LOADED)) {
+            load_ev.stc_ses = stc_ses;
             DISPATCH_EVENT(st_ses, load_ev, status);
             if (status)
                 break;
         }
-
-        if ((ST_STATE_ACTIVE == st_ses->client_req_state) &&
-            !st_ses->paused) {
+        if (active) {
+            start_ev.stc_ses = stc_ses;
             DISPATCH_EVENT(st_ses, start_ev, status);
-            if (status)
-                break;
         }
 
         break;
 
     case ST_SES_EV_LOAD_SM:
-        if (ST_STATE_IDLE == st_ses->client_req_state) {
-            st_ses->client_req_state = ST_STATE_LOADED;
+        if (ST_STATE_IDLE == stc_ses->state) {
+            status = update_sound_model(stc_ses, true);
+            if (status) {
+                ALOGE("%s:[c%d] update sound model add failed %d", __func__,
+                    stc_ses->sm_handle, status);
+                status = -EINVAL;
+                break;
+            }
+            prepapre_second_stage_for_client(stc_ses);
+            stc_ses->state = ST_STATE_LOADED;
         } else {
-            ALOGE("%s: received unexpected event, client_req_state = %d",
-                __func__, st_ses->client_req_state);
+            ALOGE("%s: received unexpected event, client state = %d",
+                __func__, stc_ses->state);
         }
         break;
 
     case ST_SES_EV_UNLOAD_SM:
-        if (ST_STATE_LOADED == st_ses->client_req_state) {
-            st_ses->client_req_state = ST_STATE_IDLE;
+        if (ST_STATE_LOADED == stc_ses->state) {
+            status = update_sound_model(stc_ses, false);
+            if (status)
+                ALOGE("%s:[c%d] update sound_model failed %d", __func__,
+                    stc_ses->sm_handle, status);
+            stop_second_stage_for_client(stc_ses);
+            stc_ses->state = ST_STATE_IDLE;
         } else {
-            ALOGE("%s: received unexpected event, client_req_state = %d",
-                __func__, st_ses->client_req_state);
+            ALOGE("%s: received unexpected event, client state = %d",
+                __func__, stc_ses->state);
         }
         break;
 
     case ST_SES_EV_START:
     case ST_SES_EV_RESTART:
-        if (ST_STATE_LOADED == st_ses->client_req_state) {
-            st_ses->client_req_state = ST_STATE_ACTIVE;
+        if (ST_STATE_LOADED == stc_ses->state) {
+            if (ev->ev_id == ST_SES_EV_RESTART)
+                update_hw_config_on_restart(st_ses, stc_ses);
+            stc_ses->state = ST_STATE_ACTIVE;
         } else {
-            ALOGE("%s: received unexpected event, client_req_state = %d",
-                __func__, st_ses->client_req_state);
+            ALOGE("%s: received unexpected event, client state = %d",
+                __func__, stc_ses->state);
         }
         break;
 
     case ST_SES_EV_STOP:
-        if (ST_STATE_ACTIVE == st_ses->client_req_state) {
-            st_ses->client_req_state = ST_STATE_LOADED;
+        if (ST_STATE_ACTIVE == stc_ses->state) {
+            update_hw_config_on_stop(st_ses, stc_ses);
+            stc_ses->state = ST_STATE_LOADED;
         } else {
-            ALOGE("%s: received unexpected event, client_req_state = %d",
-                __func__, st_ses->client_req_state);
+            ALOGE("%s: received unexpected event, client state = %d",
+                __func__, stc_ses->state);
         }
         break;
 
     case ST_SES_EV_PAUSE:
-        st_ses->paused = true;
+        stc_ses->paused = true;
         break;
 
     case ST_SES_EV_RESUME:
-        st_ses->paused = false;
+        stc_ses->paused = false;
         break;
 
     case ST_SES_EV_READ_PCM:
@@ -1714,7 +5333,12 @@
         break;
 
     case ST_SES_EV_SET_EXEC_MODE:
+        stc_ses->exec_mode = ev->payload.exec_mode;
+        if (ev->payload.exec_mode == st_ses->exec_mode)
+            break;
+
         st_ses->exec_mode = ev->payload.exec_mode;
+
         if (ST_EXEC_MODE_CPE == st_ses->exec_mode)
             st_ses->hw_ses_current = st_ses->hw_ses_cpe;
         else if (ST_EXEC_MODE_ADSP == st_ses->exec_mode)
@@ -1736,849 +5360,138 @@
     return status;
 }
 
-/*
- * This function sets the opaque data size for the DSP's generic detection
- * events. This opaque data can now have varying size based on the requested
- * params.
- */
-static size_t set_opaque_data_size(void *payload, size_t payload_size,
-    uint32_t version)
-{
-    size_t count_size = 0, opaque_size = 0;
-    uint8_t *payload_ptr = (uint8_t *)payload;
-    uint32_t key_id = 0, key_payload_size = 0;
-
-    while (count_size < payload_size) {
-        key_id = *(uint32_t *)payload_ptr;
-        key_payload_size = *((uint32_t *)payload_ptr + 1);
-
-        switch (key_id) {
-        case KEY_ID_CONFIDENCE_LEVELS:
-            opaque_size += sizeof(struct st_param_header);
-            if (version != CONF_LEVELS_INTF_VERSION_0002) {
-                opaque_size +=
-                    sizeof(struct st_confidence_levels_info);
-            } else {
-                opaque_size +=
-                    sizeof(struct st_confidence_levels_info_v2);
-            }
-            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
-            payload_ptr += count_size;
-            break;
-
-        case KEY_ID_KEYWORD_POSITION_STATS:
-            opaque_size += sizeof(struct st_param_header) +
-                sizeof(struct st_keyword_indices_info);
-            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
-            payload_ptr += count_size;
-            break;
-
-        default:
-            ALOGE("%s: Unsupported generic detection event key id", __func__);
-        }
-    }
-
-    opaque_size += sizeof(struct st_param_header) + sizeof(struct st_timestamp_info);
-
-    return opaque_size;
-}
-
-/*
- * This function packs the updated opaque data confidence levels which are
- * passed to the client via callback.
- */
-static int pack_opaque_data_conf_levels(
-    st_session_t *st_ses, void *opaque_data,
-    uint8_t *payload)
-{
-    uint8_t *payload_ptr = payload;
-    unsigned int i, j, k, user_id;
-    st_arm_second_stage_t *st_sec_stage;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    struct st_confidence_levels_info *conf_levels = NULL;
-    struct st_confidence_levels_info_v2 *conf_levels_v2 = NULL;
-    int32_t kw_level = 0, user_level = 0;
-
-    if (st_ses->enable_second_stage) {
-        list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-            st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-            if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
-                kw_level = st_sec_stage->ss_session->confidence_score;
-            } else if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_VOP) {
-                user_level = st_sec_stage->ss_session->confidence_score;
-            }
-        }
-    }
-    if (st_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002) {
-        conf_levels = (struct st_confidence_levels_info *)opaque_data;
-        for (i = 0; i < conf_levels->num_sound_models; i++) {
-            if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_GMM) {
-                for (j = 0;
-                     j < conf_levels->conf_levels[i].num_kw_levels; j++) {
-                    conf_levels->conf_levels[i].kw_levels[j].kw_level =
-                        payload_ptr[j];
-                    for (k = 0;
-                         k < conf_levels->conf_levels[i].kw_levels[j].num_user_levels;
-                         k++) {
-                        user_id =
-                            conf_levels->conf_levels[i].kw_levels[j].
-                                user_levels[k].user_id;
-                        conf_levels->conf_levels[i].kw_levels[j].
-                            user_levels[k].level = payload_ptr[user_id];
-                    }
-                }
-            } else if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_CNN) {
-                conf_levels->conf_levels[i].kw_levels[0].kw_level = kw_level;
-            } else if (conf_levels->conf_levels[i].sm_id == ST_SM_ID_SVA_VOP) {
-                /*
-                 * Fill both the keyword and user confidence level with the
-                 * confidence score returned from the voiceprint algorithm.
-                 */
-                conf_levels->conf_levels[i].kw_levels[0].kw_level =
-                    (uint8_t)user_level;
-                conf_levels->conf_levels[i].kw_levels[0].user_levels[0].level =
-                    (uint8_t)user_level;
-            }
-        }
-    } else {
-        conf_levels_v2 = (struct st_confidence_levels_info_v2 *)opaque_data;
-        for (i = 0; i < conf_levels_v2->num_sound_models; i++) {
-            if (conf_levels_v2->conf_levels[i].sm_id == ST_SM_ID_SVA_GMM) {
-                for (j = 0;
-                     j < conf_levels_v2->conf_levels[i].num_kw_levels; j++) {
-                    conf_levels_v2->conf_levels[i].kw_levels[j].kw_level =
-                        payload_ptr[j];
-                    for (k = 0;
-                         k < conf_levels_v2->conf_levels[i].kw_levels[j].num_user_levels;
-                         k++) {
-                        user_id =
-                            conf_levels_v2->conf_levels[i].kw_levels[j].
-                                user_levels[k].user_id;
-                        conf_levels_v2->conf_levels[i].kw_levels[j].
-                            user_levels[k].level = payload_ptr[user_id];
-                    }
-                }
-            } else if (conf_levels_v2->conf_levels[i].sm_id == ST_SM_ID_SVA_CNN) {
-                conf_levels_v2->conf_levels[i].kw_levels[0].kw_level = kw_level;
-            } else if (conf_levels_v2->conf_levels[i].sm_id == ST_SM_ID_SVA_VOP) {
-                /*
-                 * Fill both the keyword and user confidence level with the
-                 * confidence score returned from the voiceprint algorithm.
-                 */
-                conf_levels_v2->conf_levels[i].kw_levels[0].kw_level = user_level;
-                conf_levels_v2->conf_levels[i].kw_levels[0].user_levels[0].level =
-                    user_level;
-            }
-        }
-    }
-
-    return 0;
-}
-
-/* This function packs the sound trigger API confidence levels */
-static int pack_recognition_event_conf_levels(
-    st_session_t *st_ses, uint8_t *payload_ptr,
-    struct sound_trigger_phrase_recognition_event *local_event)
-{
-    unsigned int j = 0, k = 0, user_id = 0;
-    st_arm_second_stage_t *st_sec_stage;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    struct sound_trigger_phrase_sound_model *phrase_sm =
-        (struct sound_trigger_phrase_sound_model *)st_ses->sm_data;
-
-    /*
-     * Fill in the GMM confidence levels to the sound trigger recognition event
-     * APIs first. If any second stage session is enabled, overwrite the APIs
-     * with the second stage confidence levels.
-     */
-    for (j = 0; j < st_ses->rc_config->num_phrases; j++) {
-        local_event->phrase_extras[j].id = st_ses->rc_config->phrases[j].id;
-        local_event->phrase_extras[j].recognition_modes =
-            phrase_sm->phrases[j].recognition_mode;
-        local_event->phrase_extras[j].num_levels =
-            st_ses->rc_config->phrases[j].num_levels;
-        local_event->phrase_extras[j].confidence_level = payload_ptr[j];
-        for (k = 0; k < st_ses->rc_config->phrases[j].num_levels; k++) {
-            user_id = st_ses->rc_config->phrases[j].levels[k].user_id;
-            local_event->phrase_extras[j].levels[k].user_id = user_id;
-            local_event->phrase_extras[j].levels[k].level =
-                payload_ptr[user_id];
-        }
-    }
-
-    if (st_ses->enable_second_stage) {
-        list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-            st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-            if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
-                local_event->phrase_extras[0].confidence_level =
-                    (uint8_t)st_sec_stage->ss_session->confidence_score;
-            } else if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_VOP) {
-                local_event->phrase_extras[0].levels[0].level =
-                    (uint8_t)st_sec_stage->ss_session->confidence_score;
-            }
-        }
-    }
-
-    return 0;
-}
-
-static int parse_generic_event_and_pack_opaque_data(
-    st_session_t *st_ses, uint8_t **opaque_data,
-    uint8_t *payload_ptr, size_t payload_size,
-    struct sound_trigger_phrase_recognition_event *local_event)
-{
-    uint32_t key_id = 0, key_payload_size = 0;
-    struct st_param_header *param_hdr = NULL;
-    struct st_keyword_indices_info *kw_indices = NULL;
-    struct st_timestamp_info *timestamps = NULL;
-    size_t count_size = 0;
-    st_arm_second_stage_t *st_sec_stage;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
-    int status = 0;
-    uint8_t *conf_payload_ptr = NULL;
-
-    while (count_size < payload_size) {
-        key_id = *(uint32_t *)payload_ptr;
-        key_payload_size = *((uint32_t *)payload_ptr + 1);
-
-        switch (key_id) {
-        case KEY_ID_CONFIDENCE_LEVELS:
-            /* Pack the opaque data confidence levels structure */
-            param_hdr = (struct st_param_header *)(*opaque_data);
-            param_hdr->key_id = ST_PARAM_KEY_CONFIDENCE_LEVELS;
-            *opaque_data += sizeof(struct st_param_header);
-            if (st_ses->conf_levels_intf_version !=
-                CONF_LEVELS_INTF_VERSION_0002) {
-                param_hdr->payload_size =
-                    sizeof(struct st_confidence_levels_info);
-            } else {
-                param_hdr->payload_size =
-                    sizeof(struct st_confidence_levels_info_v2);
-            }
-            memcpy(*opaque_data, st_hw_ses->conf_levels_info,
-                param_hdr->payload_size);
-            conf_payload_ptr = payload_ptr + (4 * sizeof(uint32_t));
-            pack_opaque_data_conf_levels(st_ses, (void *)*opaque_data,
-                conf_payload_ptr);
-            pack_recognition_event_conf_levels(st_ses, conf_payload_ptr,
-                local_event);
-            *opaque_data += param_hdr->payload_size;
-            break;
-
-        case KEY_ID_KEYWORD_POSITION_STATS:
-            /* Pack the opaque data keyword indices structure */
-            param_hdr = (struct st_param_header *)(*opaque_data);
-            param_hdr->key_id = ST_PARAM_KEY_KEYWORD_INDICES;
-            param_hdr->payload_size = sizeof(struct st_keyword_indices_info);
-            *opaque_data += sizeof(struct st_param_header);
-            kw_indices = (struct st_keyword_indices_info *)(*opaque_data);
-            kw_indices->version = 0x1;
-            kw_indices->start_index = *((uint32_t *)payload_ptr + 3);
-            kw_indices->end_index = *((uint32_t *)payload_ptr + 4);
-
-            if (st_ses->enable_second_stage) {
-                list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-                    st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                    if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
-                        kw_indices->start_index = st_sec_stage->ss_session->kw_start_idx;
-                        kw_indices->end_index = st_sec_stage->ss_session->kw_end_idx;
-                    }
-                }
-            }
-
-            *opaque_data += sizeof(struct st_keyword_indices_info);
-            break;
-
-        default:
-            ALOGE("%s: Unsupported generic detection event key id", __func__);
-            status = -EINVAL;
-            goto exit;
-        }
-        count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
-        payload_ptr += count_size;
-    }
-
-    /* Pack the opaque data detection timestamp structure */
-    param_hdr = (struct st_param_header *)(*opaque_data);
-    param_hdr->key_id = ST_PARAM_KEY_TIMESTAMP;
-    param_hdr->payload_size = sizeof(struct st_timestamp_info);
-    *opaque_data += sizeof(struct st_param_header);
-    timestamps = (struct st_timestamp_info *)(*opaque_data);
-    timestamps->version = 0x1;
-    timestamps->first_stage_det_event_time =
-        st_ses->hw_ses_current->first_stage_det_event_time;
-    if (st_ses->enable_second_stage)
-        timestamps->second_stage_det_event_time =
-            st_ses->hw_ses_current->second_stage_det_event_time;
-    *opaque_data += sizeof(struct st_timestamp_info);
-
-exit:
-    return status;
-}
-
-static int parse_generic_event_without_opaque_data(
-    st_session_t *st_ses, uint8_t *payload_ptr, size_t payload_size,
-    struct sound_trigger_phrase_recognition_event *local_event)
-{
-    uint32_t key_id = 0, key_payload_size = 0;
-    size_t count_size = 0;
-    int status = 0;
-    uint8_t *conf_payload_ptr;
-
-    while (count_size < payload_size) {
-        key_id = *(uint32_t *)payload_ptr;
-        key_payload_size = *((uint32_t *)payload_ptr + 1);
-
-        switch (key_id) {
-        case KEY_ID_CONFIDENCE_LEVELS:
-            conf_payload_ptr = payload_ptr + (4 * sizeof(uint32_t));
-            pack_recognition_event_conf_levels(st_ses, conf_payload_ptr,
-                local_event);
-            return status;
-
-        case KEY_ID_KEYWORD_POSITION_STATS:
-            count_size += GENERIC_DET_EVENT_HEADER_SIZE + key_payload_size;
-            payload_ptr += count_size;
-            break;
-
-        default:
-            ALOGE("%s: Unsupported generic detection event key id", __func__);
-            status = -EINVAL;
-            return status;
-        }
-    }
-
-    return status;
-}
-
-/*
- * This function handles detection payloads in the format of the DSP's
- * generic detection event.
- */
-int process_detection_event_keyphrase_v2(
-    st_session_t *st_ses, int detect_status,
-    void *payload, size_t payload_size,
-    struct sound_trigger_phrase_recognition_event **event)
-{
-    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
-    unsigned int i, j;
-    int status = 0;
-    uint8_t *opaque_data = NULL, *payload_ptr = NULL;
-    size_t opaque_size = 0;
-    struct sound_trigger_phrase_recognition_event *local_event = NULL;
-
-    if (st_ses->vendor_uuid_info->is_qcva_uuid)
-        opaque_size = set_opaque_data_size(payload, payload_size,
-            st_ses->conf_levels_intf_version);
-    else
-        opaque_size = payload_size;
-
-    local_event = calloc(1, sizeof(struct sound_trigger_phrase_recognition_event) + opaque_size);
-    if (!local_event) {
-        ALOGE("%s: local_event allocation failed, opaque data size = %d", __func__,
-            (unsigned int)opaque_size);
-        return -ENOMEM;
-    }
-
-    local_event->num_phrases = st_ses->rc_config->num_phrases;
-    local_event->common.data_offset = sizeof(struct sound_trigger_phrase_recognition_event);
-    local_event->common.data_size = opaque_size;
-    opaque_data = (uint8_t *)local_event + local_event->common.data_offset;
-    payload_ptr = (uint8_t *)payload;
-
-    if (st_ses->vendor_uuid_info->is_qcva_uuid) {
-        if (st_ses->rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) {
-            status = parse_generic_event_and_pack_opaque_data(st_ses, &opaque_data,
-                payload_ptr, payload_size, local_event);
-            if (status) {
-                ALOGE("%s: Failed to parse generic detection event with opaque data %d",
-                    __func__, status);
-                goto exit;
-            }
-
-            ST_DBG_DECLARE(FILE *opaque_fd = NULL; static int opaque_cnt = 0);
-            ST_DBG_FILE_OPEN_WR(opaque_fd, ST_DEBUG_DUMP_LOCATION,
-                                "detection_opaque_data", "bin", opaque_cnt++);
-            ST_DBG_FILE_WRITE(opaque_fd, (uint8_t *)local_event +
-                local_event->common.data_offset, opaque_size);
-            ST_DBG_FILE_CLOSE(opaque_fd);
-        } else {
-            status = parse_generic_event_without_opaque_data(st_ses, payload_ptr,
-                payload_size, local_event);
-            if (status) {
-                ALOGE("%s: Failed to parse generic detection event without opaque data %d",
-                    __func__, status);
-                goto exit;
-            }
-        }
-
-    } else {
-        local_event = calloc(1, sizeof(*local_event) + payload_size);
-        if (!local_event) {
-            ALOGE("%s: event allocation failed, size %zd", __func__,
-                payload_size);
-            status = -ENOMEM;
-            goto exit;
-        }
-        memcpy(local_event->phrase_extras,
-            st_ses->rc_config->phrases, st_ses->rc_config->num_phrases *
-            sizeof(struct sound_trigger_phrase_recognition_extra));
-        local_event->num_phrases = st_ses->rc_config->num_phrases;
-        local_event->common.data_offset = sizeof(*local_event);
-        local_event->common.data_size = payload_size;
-        memcpy((char *)local_event + local_event->common.data_offset, payload,
-            payload_size);
-        opaque_data += opaque_size;
-    }
-
-    /* fill the remaining recognition event parameters not specific
-       to soundmodel lib */
-    local_event->common.status = detect_status;
-    local_event->common.type = st_ses->sm_data->common.type;
-    local_event->common.model = st_ses->sm_handle;
-    local_event->common.capture_available = st_ses->capture_requested;
-    local_event->common.capture_delay_ms = 0;
-    local_event->common.capture_preamble_ms = 0;
-    local_event->common.audio_config.sample_rate =
-        SOUND_TRIGGER_SAMPLING_RATE_16000;
-    local_event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
-    local_event->common.audio_config.channel_mask =
-        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
-
-    for (i = 0; i < local_event->num_phrases; ++i) {
-        ALOGV("%s: [%d] kw_id %d level %d", __func__, i,
-            local_event->phrase_extras[i].id,
-            local_event->phrase_extras[i].confidence_level);
-        for (j = 0; j < local_event->phrase_extras[i].num_levels; ++j) {
-            ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
-                local_event->phrase_extras[i].levels[j].user_id,
-                local_event->phrase_extras[i].levels[j].level);
-        }
-    }
-
-    ALOGI("%s:[%d]", __func__, st_ses->sm_handle);
-
-    ALOGV("%s:[%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
-        "num_phrases=%d id=%d", __func__,
-        st_ses->sm_handle, local_event->common.status, local_event->common.type,
-        local_event->common.model, local_event->common.capture_available,
-        local_event->num_phrases, local_event->phrase_extras[0].id);
-
-    *event = local_event;
-    return 0;
-
-exit:
-
-    if (local_event)
-        free(local_event);
-    return status;
-}
-
-/*
- * This function handles detection payloads in the format of the DSP's
- * legacy (non-generic) detection event.
- */
-static int process_detection_event_keyphrase(
-    st_session_t *st_ses, int detect_status,
-    void *payload, size_t payload_size,
-    struct sound_trigger_phrase_recognition_event **event)
-{
-    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
-    unsigned int i, j;
-    int status = 0;
-    struct sound_trigger_phrase_recognition_event *local_event = NULL;
-    size_t opaque_size = 0;
-    uint8_t *opaque_data = NULL, *payload_ptr = NULL;
-    struct st_param_header *param_hdr = NULL;
-    st_arm_second_stage_t *st_sec_stage;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    struct st_keyword_indices_info *kw_indices = NULL;
-    struct st_timestamp_info *timestamps = NULL;
-    bool enable_kw_indices = false;
-
-    if ((st_ses->rc_config->data_size > CUSTOM_CONFIG_OPAQUE_DATA_SIZE) &&
-            st_ses->vendor_uuid_info->is_qcva_uuid) {
-        /*
-         * This logic is for the updated opaque data format. Sound trigger recognition
-         * event APIs are filled along with the opaque data's confidence levels, keyword
-         * indices, and timestamp parameters.
-         */
-        opaque_size = (2 * sizeof(struct st_param_header)) +
-            sizeof(struct st_timestamp_info);
-        if (st_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002)
-            opaque_size += sizeof(struct st_confidence_levels_info);
-        else
-            opaque_size += sizeof(struct st_confidence_levels_info_v2);
-
-        if (st_ses->enable_second_stage) {
-            list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-                st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
-                    enable_kw_indices = true;
-                    opaque_size += sizeof(struct st_param_header) +
-                        sizeof(struct st_keyword_indices_info);
-                    break;
-                }
-            }
-        }
-
-        local_event = calloc(1,
-            sizeof(struct sound_trigger_phrase_recognition_event) + opaque_size);
-        if (!local_event) {
-            ALOGE("%s: local_event allocation failed, opaque data size = %d", __func__,
-                (unsigned int)opaque_size);
-            return -ENOMEM;
-        }
-
-        local_event->num_phrases = st_ses->rc_config->num_phrases;
-        local_event->common.data_offset = sizeof(struct sound_trigger_phrase_recognition_event);
-        local_event->common.data_size = opaque_size;
-        opaque_data = (uint8_t *)local_event + local_event->common.data_offset;
-        if ((st_ses->exec_mode == ST_EXEC_MODE_CPE) && st_ses->stdev->is_gcs) {
-            payload_ptr = (uint8_t *)payload + 2;
-        } else if ((st_ses->exec_mode == ST_EXEC_MODE_ADSP) || !st_ses->stdev->is_gcs) {
-            payload_ptr = (uint8_t *)payload;
-        } else {
-            ALOGE("%s: Invalid execution mode, exiting", __func__);
-            status = -EINVAL;
-            goto err_exit;
-        }
-
-        /* Pack the opaque data confidence levels structure */
-        param_hdr = (struct st_param_header *)opaque_data;
-        param_hdr->key_id = ST_PARAM_KEY_CONFIDENCE_LEVELS;
-        opaque_data += sizeof(struct st_param_header);
-        if (st_ses->conf_levels_intf_version != CONF_LEVELS_INTF_VERSION_0002) {
-            param_hdr->payload_size =
-                sizeof(struct st_confidence_levels_info);
-        } else {
-            param_hdr->payload_size =
-                sizeof(struct st_confidence_levels_info_v2);
-        }
-        memcpy(opaque_data, st_hw_ses->conf_levels_info,
-            param_hdr->payload_size);
-        pack_opaque_data_conf_levels(st_ses, (void *)opaque_data, payload_ptr);
-        pack_recognition_event_conf_levels(st_ses, payload_ptr,
-            local_event);
-        opaque_data += param_hdr->payload_size;
-
-        /* Pack the opaque data keyword indices structure */
-        if (enable_kw_indices) {
-            param_hdr = (struct st_param_header *)opaque_data;
-            param_hdr->key_id = ST_PARAM_KEY_KEYWORD_INDICES;
-            param_hdr->payload_size = sizeof(struct st_keyword_indices_info);
-            opaque_data += sizeof(struct st_param_header);
-            kw_indices = (struct st_keyword_indices_info *)opaque_data;
-            kw_indices->version = 0x1;
-
-            if (st_ses->enable_second_stage) {
-                list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-                    st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
-                    if (st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN) {
-                        kw_indices->start_index = st_sec_stage->ss_session->kw_start_idx;
-                        kw_indices->end_index = st_sec_stage->ss_session->kw_end_idx;
-                    }
-                }
-            }
-            opaque_data += sizeof(struct st_keyword_indices_info);
-        }
-
-        /* Pack the opaque data detection timestamp structure */
-        param_hdr = (struct st_param_header *)opaque_data;
-        param_hdr->key_id = ST_PARAM_KEY_TIMESTAMP;
-        param_hdr->payload_size = sizeof(struct st_timestamp_info);
-        opaque_data += sizeof(struct st_param_header);
-        timestamps = (struct st_timestamp_info *)opaque_data;
-        timestamps->version = 0x1;
-        timestamps->first_stage_det_event_time =
-            st_ses->hw_ses_current->first_stage_det_event_time;
-        if (st_ses->enable_second_stage)
-            timestamps->second_stage_det_event_time =
-                st_ses->hw_ses_current->second_stage_det_event_time;
-        opaque_data += sizeof(struct st_timestamp_info);
-
-        ST_DBG_DECLARE(FILE *opaque_fd = NULL; static int opaque_cnt = 0);
-        ST_DBG_FILE_OPEN_WR(opaque_fd, ST_DEBUG_DUMP_LOCATION,
-                            "detection_opaque_data", "bin", opaque_cnt++);
-        ST_DBG_FILE_WRITE(opaque_fd, (opaque_data - opaque_size), opaque_size);
-        ST_DBG_FILE_CLOSE(opaque_fd);
-
-    } else {
-        /* This logic is for the legacy opaque data format or third party vendors */
-        if (st_ses->vendor_uuid_info &&
-            st_ses->vendor_uuid_info->smlib_handle) {
-            /* if smlib is present, get the event from it else send the
-               DSP recieved payload as it is to App */
-            /* TODO: checking is_gcs should be avoided here */
-            if (st_ses->stdev->is_gcs &&
-                ST_EXEC_MODE_CPE == st_ses->exec_mode &&
-                !st_ses->hw_ses_current->is_generic_event) {
-                ALOGD("%s: about to call generate_st_phrase_recognition_event_v2",
-                      __func__);
-                status = st_ses->vendor_uuid_info->
-                    generate_st_phrase_recognition_event_v2(st_ses->sm_data,
-                    st_ses->rc_config, payload, payload_size, &local_event);
-            } else {
-                status = st_ses->vendor_uuid_info->
-                    generate_st_phrase_recognition_event(st_ses->sm_data,
-                    st_ses->rc_config, payload, payload_size, &local_event);
-            }
-
-            if (status) {
-                ALOGW("%s: smlib fill recognition event failed, status %d",
-                    __func__, status);
-                goto exit;
-            }
-        } else if (!st_ses->vendor_uuid_info &&
-                   st_ses->stdev->smlib_handle) {
-            /* This is SVA non topology solution */
-            /* TODO: checking is_gcs should be avoided here */
-            if (st_ses->stdev->is_gcs) {
-                status = st_ses->stdev->generate_st_phrase_recognition_event_v2(
-                    st_ses->sm_data, st_ses->rc_config, payload, payload_size,
-                    &local_event);
-            } else {
-                status = st_ses->stdev->generate_st_phrase_recognition_event(
-                    st_ses->sm_data, st_ses->rc_config, payload,  payload_size,
-                    &local_event);
-            }
-
-            if (status) {
-                ALOGW("%s: SVA smlib fill recognition event failed, status\
-                    %d", __func__, status);
-                goto exit;
-            }
-        } else {
-            local_event = calloc(1, sizeof(*local_event) + payload_size);
-            if (!local_event) {
-                ALOGE("%s: event allocation failed, size %zd", __func__,
-                    payload_size);
-                status = -ENOMEM;
-                goto exit;
-            }
-            memcpy(local_event->phrase_extras,
-                st_ses->rc_config->phrases, st_ses->rc_config->num_phrases *
-                sizeof(struct sound_trigger_phrase_recognition_extra));
-            local_event->num_phrases = st_ses->rc_config->num_phrases;
-            local_event->common.data_offset = sizeof(*local_event);
-            local_event->common.data_size = payload_size;
-            memcpy((char *)local_event + local_event->common.data_offset, payload,
-                payload_size);
-        }
-    }
-
-    /* fill the remaining recognition event parameters not specific
-       to soundmodel lib */
-    local_event->common.status = detect_status;
-    local_event->common.type = st_ses->sm_data->common.type;
-    local_event->common.model = st_ses->sm_handle;
-    local_event->common.capture_available = st_ses->capture_requested;
-    local_event->common.capture_delay_ms = 0;
-    local_event->common.capture_preamble_ms = 0;
-    local_event->common.audio_config.sample_rate =
-        SOUND_TRIGGER_SAMPLING_RATE_16000;
-    local_event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
-    local_event->common.audio_config.channel_mask =
-        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
-
-    for (i = 0; i < local_event->num_phrases; ++i) {
-        ALOGV("%s: [%d] kw_id %d level %d", __func__, i,
-            local_event->phrase_extras[i].id,
-            local_event->phrase_extras[i].confidence_level);
-        for (j = 0; j < local_event->phrase_extras[i].num_levels; ++j) {
-            ALOGV("%s: [%d] user_id %d level %d ", __func__, i,
-                local_event->phrase_extras[i].levels[j].user_id,
-                local_event->phrase_extras[i].levels[j].level);
-        }
-    }
-
-    ALOGI("%s:[%d]", __func__, st_ses->sm_handle);
-
-    ALOGV("%s:[%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
-        "num_phrases=%d id=%d", __func__,
-        st_ses->sm_handle, local_event->common.status, local_event->common.type,
-        local_event->common.model, local_event->common.capture_available,
-        local_event->num_phrases, local_event->phrase_extras[0].id);
-
-    *event = local_event;
-    return 0;
-
-err_exit:
-    if (local_event)
-        free(local_event);
-
-exit:
-    return status;
-}
-
-static int process_detection_event_generic(st_session_t *st_ses,
-    int detect_status,
-    void *payload, size_t payload_size,
-    struct sound_trigger_recognition_event **event)
-{
-    st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
-    struct st_vendor_info *v_info = st_ses->vendor_uuid_info;
-    int status = 0;
-    struct sound_trigger_recognition_event *local_event = NULL;
-
-    local_event = calloc(1, sizeof(*local_event) + payload_size);
-    if (!local_event) {
-        ALOGE("%s: event allocation failed, size %zd", __func__,
-            payload_size);
-        status = -ENOMEM;
-        goto exit;
-    }
-
-    local_event->status = detect_status;
-    local_event->type = st_ses->sm_type;
-    local_event->model = st_ses->sm_handle;
-    local_event->capture_available = st_ses->capture_requested;
-    local_event->capture_delay_ms = 0;
-    local_event->capture_preamble_ms = 0;
-    local_event->audio_config.sample_rate = v_info ?
-        v_info->sample_rate : SOUND_TRIGGER_SAMPLING_RATE_16000;
-    local_event->audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
-    local_event->audio_config.channel_mask =
-        audio_channel_in_mask_from_count(st_hw_ses->config.channels);
-
-    local_event->data_offset = sizeof(*local_event);
-    local_event->data_size = payload_size;
-    memcpy((char *)local_event + local_event->data_offset,
-        payload, payload_size);
-
-    ALOGI("%s:[%d]", __func__, st_ses->sm_handle);
-    ALOGV("%s:[%d] status=%d, type=%d, model=%d, capture_avaiable=%d",
-        __func__, st_ses->sm_handle, local_event->status,
-        local_event->type, local_event->model,
-        local_event->capture_available);
-
-    *event = local_event;
-
-exit:
-    return status;
-}
-
-static inline int process_detection_event(st_session_t *st_ses,
-    uint64_t timestamp __unused,
-    int detect_status,
-    void *payload, size_t payload_size,
-    struct sound_trigger_recognition_event **event)
-{
-    int ret;
-    struct sound_trigger_phrase_recognition_event *phrase_event = NULL;
-
-    *event = NULL;
-    if (st_ses->sm_type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        if (sthw_extn_check_process_det_ev_support())
-            ret = sthw_extn_process_detection_event_keyphrase(st_ses,
-                                      timestamp, detect_status,
-                                      payload, payload_size, &phrase_event);
-        else if (st_ses->hw_ses_current->is_generic_event &&
-                 !st_ses->vendor_uuid_info->is_qcmd_uuid)
-            ret = process_detection_event_keyphrase_v2(st_ses, detect_status,
-                                                    payload, payload_size,
-                                                    &phrase_event);
-        else
-            ret = process_detection_event_keyphrase(st_ses, detect_status,
-                                                    payload, payload_size,
-                                                    &phrase_event);
-        if (phrase_event)
-            *event = &phrase_event->common;
-    } else {
-        ret = process_detection_event_generic(st_ses, detect_status, payload,
-                                            payload_size, event);
-    }
-    return ret;
-}
-
-int st_session_load_sm(st_session_t *st_ses)
+int st_session_load_sm(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
-    /* SM is stored in the session by st_device, hence set NULL below */
-    st_session_loadsm_payload_t payload = { NULL };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_loadsm_payload_t payload = { .phrase_sm = stc_ses->phrase_sm };
     st_session_ev_t ev = { .ev_id = ST_SES_EV_LOAD_SM,
-        .payload.loadsm = payload };
-
-    /*
-     * no need to lock mutex when loading sm as session is just being
-     * being created and handle not returned to caller yet
-     */
-    DISPATCH_EVENT(st_ses, ev, status);
-    return status;
-}
-
-int st_session_unload_sm(st_session_t *st_ses)
-{
-    int status = 0;
-    if (!st_ses)
-        return -EINVAL;
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_UNLOAD_SM };
+        .payload.loadsm = payload, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
     DISPATCH_EVENT(st_ses, ev, status);
+    if (!status) {
+        prepapre_second_stage_for_client(stc_ses);
+        stc_ses->state = ST_STATE_LOADED;
+    }
     pthread_mutex_unlock(&st_ses->lock);
 
     return status;
 }
 
-int st_session_start(st_session_t *st_ses)
+int st_session_unload_sm(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_START };
-
-    /* lock to serialize event handling */
-    pthread_mutex_lock(&st_ses->lock);
-    DISPATCH_EVENT(st_ses, ev, status);
-    pthread_mutex_unlock(&st_ses->lock);
-    return status;
-}
-
-int st_session_stop(st_session_t *st_ses)
-{
-    int status = 0;
-    if (!st_ses)
-        return -EINVAL;
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_STOP };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_UNLOAD_SM, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
     DISPATCH_EVENT(st_ses, ev, status);
+    stop_second_stage_for_client(stc_ses);
+    stc_ses->state = ST_STATE_IDLE;
     pthread_mutex_unlock(&st_ses->lock);
 
     return status;
 }
 
-int st_session_restart(st_session_t *st_ses)
+int st_session_start(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_RESTART };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_START, .stc_ses = stc_ses };
 
-    /* lock to serialize event handling */
     pthread_mutex_lock(&st_ses->lock);
+    if (stc_ses->pending_stop) {
+        ALOGV("%s:[c%d] cancel ST_SES_EV_DEFERRED_STOP", __func__,
+            stc_ses->sm_handle);
+        hw_session_notifier_cancel(stc_ses->sm_handle, ST_SES_EV_DEFERRED_STOP);
+        stc_ses->pending_stop = false;
+    }
+
     DISPATCH_EVENT(st_ses, ev, status);
+    if (!status) {
+        reg_hal_event_session(stc_ses, st_ses->hw_ses_current);
+        start_second_stage_for_client(stc_ses);
+        stc_ses->state = ST_STATE_ACTIVE;
+    }
     pthread_mutex_unlock(&st_ses->lock);
     return status;
 }
 
-int st_session_ssr_offline(st_session_t *st_ses,
+int st_session_stop(st_session_t *stc_ses)
+{
+    int status = 0;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return -EINVAL;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_STOP, .stc_ses = stc_ses };
+
+    pthread_mutex_lock(&st_ses->lock);
+    DISPATCH_EVENT(st_ses, ev, status);
+    dereg_hal_event_session(stc_ses);
+    stc_ses->pending_stop = false;
+    stc_ses->state = ST_STATE_LOADED;
+    pthread_mutex_unlock(&st_ses->lock);
+
+    return status;
+}
+
+int st_session_restart(st_session_t *stc_ses)
+{
+    int status = 0;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return -EINVAL;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_RESTART, .stc_ses = stc_ses };
+
+    pthread_mutex_lock(&st_ses->lock);
+    if (stc_ses->pending_stop) {
+        ALOGV("%s:[c%d] cancel ST_SES_EV_DEFERRED_STOP", __func__,
+            stc_ses->sm_handle);
+        hw_session_notifier_cancel(stc_ses->sm_handle, ST_SES_EV_DEFERRED_STOP);
+        stc_ses->pending_stop = false;
+    }
+
+    DISPATCH_EVENT(st_ses, ev, status);
+    if (!status) {
+        start_second_stage_for_client(stc_ses);
+        stc_ses->state = ST_STATE_ACTIVE;
+    } else {
+        dereg_hal_event_session(stc_ses);
+        stc_ses->state = ST_STATE_LOADED;
+    }
+    pthread_mutex_unlock(&st_ses->lock);
+
+    return status;
+}
+
+int st_session_ssr_offline(st_session_t *stc_ses,
     enum ssr_event_status ssr_type)
 {
     int status = 0;
 
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_session_ev_t ev = { .ev_id = ST_SES_EV_SSR_OFFLINE,
-        .payload.ssr = ssr_type };
+        .payload.ssr = ssr_type, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
     /*
@@ -2597,9 +5510,9 @@
         ((ST_EXEC_MODE_ADSP == st_ses->exec_mode) &&
          (SND_CARD_STATUS_OFFLINE == ssr_type)) ||
         ((ST_EXEC_MODE_NONE == st_ses->exec_mode) &&
-         (((ST_EXEC_MODE_CPE == st_ses->ssr_transit_exec_mode) &&
+         (((ST_EXEC_MODE_CPE == stc_ses->ssr_transit_exec_mode) &&
            (SND_CARD_STATUS_OFFLINE == ssr_type)) ||
-          ((ST_EXEC_MODE_ADSP == st_ses->ssr_transit_exec_mode) &&
+          ((ST_EXEC_MODE_ADSP == stc_ses->ssr_transit_exec_mode) &&
            (CPE_STATUS_OFFLINE == ssr_type)))) ||
         ((ST_EXEC_MODE_CPE == st_ses->exec_mode) &&
          (SND_CARD_STATUS_OFFLINE == ssr_type) &&
@@ -2610,15 +5523,17 @@
     return status;
 }
 
-int st_session_ssr_online(st_session_t *st_ses,
+int st_session_ssr_online(st_session_t *stc_ses,
     enum ssr_event_status ssr_type)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_session_ev_t ev = { .ev_id = ST_SES_EV_SSR_ONLINE,
-        .payload.ssr = ssr_type };
+        .payload.ssr = ssr_type, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
     /*
@@ -2637,37 +5552,45 @@
         ((ST_EXEC_MODE_ADSP == st_ses->exec_mode) &&
          (SND_CARD_STATUS_ONLINE == ssr_type)) ||
         ((ST_EXEC_MODE_NONE == st_ses->exec_mode) &&
-         (((ST_EXEC_MODE_CPE == st_ses->ssr_transit_exec_mode) &&
+         (((ST_EXEC_MODE_CPE == stc_ses->ssr_transit_exec_mode) &&
            (SND_CARD_STATUS_ONLINE == ssr_type)) ||
-          ((ST_EXEC_MODE_ADSP == st_ses->ssr_transit_exec_mode) &&
+          ((ST_EXEC_MODE_ADSP == stc_ses->ssr_transit_exec_mode) &&
            (CPE_STATUS_ONLINE == ssr_type)))) ||
         ((ST_EXEC_MODE_CPE == st_ses->exec_mode) &&
          (SND_CARD_STATUS_ONLINE == ssr_type) &&
          (st_ses->stdev->bg_kwd)))
         DISPATCH_EVENT(st_ses, ev, status);
+
     pthread_mutex_unlock(&st_ses->lock);
 
     return status;
 }
 
-int st_session_pause(st_session_t *st_ses)
+int st_session_pause(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_PAUSE };
-    pthread_mutex_lock(&st_ses->lock);
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_PAUSE, .stc_ses = stc_ses };
+
+    pthread_mutex_lock(&stc_ses->lock);
     DISPATCH_EVENT(st_ses, ev, status);
-    pthread_mutex_unlock(&st_ses->lock);
+    pthread_mutex_unlock(&stc_ses->lock);
     return status;
 }
 
-int st_session_resume(st_session_t *st_ses)
+int st_session_resume(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_RESUME };
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = { .ev_id = ST_SES_EV_RESUME, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
     DISPATCH_EVENT(st_ses, ev, status);
@@ -2675,41 +5598,70 @@
     return status;
 }
 
-int st_session_disable_device(st_session_t *st_ses)
+int st_session_disable_device(st_session_t *stc_ses)
 {
     int status = 0;
 
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = {.ev_id = ST_SES_EV_SET_DEVICE,
+        .payload.enable = false, .stc_ses = stc_ses};
+
+    pthread_mutex_lock(&st_ses->lock);
+    /*
+     * Avoid dispatching for each attached multi-client, instead
+     * defer it until last client
+     */
+    stc_ses->pending_set_device = true;
+    if (is_any_client_not_pending_set_device(st_ses)) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return status;
+    }
+    reset_clients_pending_set_device(st_ses);
+
+    DISPATCH_EVENT(st_ses, ev, status);
+    pthread_mutex_unlock(&st_ses->lock);
+    return status;
+}
+
+int st_session_enable_device(st_session_t *stc_ses)
+{
+    int status = 0;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return -EINVAL;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_session_ev_t ev = { .ev_id = ST_SES_EV_SET_DEVICE,
-        .payload.enable = false };
+        .payload.enable = true, .stc_ses = stc_ses };
 
     pthread_mutex_lock(&st_ses->lock);
+    /*
+     * Avoid dispatching for each attached multi-client, instead
+     * defer it until last client
+     */
+    stc_ses->pending_set_device = true;
+    if (is_any_client_not_pending_set_device(st_ses)) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return status;
+    }
+    reset_clients_pending_set_device(st_ses);
+
     DISPATCH_EVENT(st_ses, ev, status);
     pthread_mutex_unlock(&st_ses->lock);
     return status;
 }
 
-int st_session_enable_device(st_session_t *st_ses)
+bool st_session_is_detected(st_session_t *stc_ses)
 {
-    int status = 0;
+    bool ret = false;
 
-    if (!st_ses)
-        return -EINVAL;
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return ret;
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_SET_DEVICE,
-        .payload.enable = true };
-
-    pthread_mutex_lock(&st_ses->lock);
-    DISPATCH_EVENT(st_ses, ev, status);
-    pthread_mutex_unlock(&st_ses->lock);
-    return status;
-}
-
-bool st_session_is_detected(st_session_t *st_ses)
-{
-    bool ret;
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
 
     pthread_mutex_lock(&st_ses->lock);
     ret = (st_ses->current_state == detected_state_fn) ? true : false;
@@ -2718,9 +5670,14 @@
     return ret;
 }
 
-bool st_session_is_active(st_session_t *st_ses)
+bool st_session_is_active(st_session_t *stc_ses)
 {
-    bool ret;
+    bool ret = false;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return ret;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
 
     pthread_mutex_lock(&st_ses->lock);
     ret = (st_ses->current_state == active_state_fn) ? true : false;
@@ -2729,9 +5686,14 @@
     return ret;
 }
 
-bool st_session_is_buffering(st_session_t *st_ses)
+bool st_session_is_buffering(st_session_t *stc_ses)
 {
-    bool ret;
+    bool ret = false;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return ret;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
 
     pthread_mutex_lock(&st_ses->lock);
     ret = (st_ses->current_state == buffering_state_fn) ? true : false;
@@ -2740,9 +5702,14 @@
     return ret;
 }
 
-bool st_session_is_ssr_state(st_session_t *st_ses)
+bool st_session_is_ssr_state(st_session_t *stc_ses)
 {
-    bool ret;
+    bool ret = false;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return ret;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
 
     pthread_mutex_lock(&st_ses->lock);
     ret = (st_ses->current_state == ssr_state_fn) ? true : false;
@@ -2751,31 +5718,40 @@
     return ret;
 }
 
-int st_session_read_pcm(st_session_t *st_ses, uint8_t *buff,
+int st_session_read_pcm(st_session_t *stc_ses, uint8_t *buff,
     size_t buff_size, size_t *read_size)
 {
     int status = 0;
-    if (!st_ses || !buff || buff_size == 0 || read_size == 0)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses || !buff || buff_size == 0 ||
+        read_size == 0)
         return -EINVAL;
 
-    st_session_readpcm_payload_t payload = { .out_buff = buff,
-        .out_buff_size = buff_size, .actual_read_size = read_size };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_readpcm_payload_t payload = {.out_buff = buff,
+        .out_buff_size = buff_size, .actual_read_size = read_size};
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_READ_PCM,
-        .payload.readpcm = payload };
+    st_session_ev_t ev = {.ev_id = ST_SES_EV_READ_PCM,
+        .payload.readpcm = payload, .stc_ses = stc_ses};
 
-    /* Do not lock when handling this event, this event
-     can go in parallel with other events */
+    /*
+     * Do not lock when handling this event, this event
+     * can go in parallel with other events as multiple
+     * sessions can buffer in parallel.
+     */
     DISPATCH_EVENT(st_ses, ev, status);
     return status;
 }
 
-int st_session_stop_lab(st_session_t *st_ses)
+int st_session_stop_lab(st_session_t *stc_ses)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_END_BUFFERING };
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = {.ev_id = ST_SES_EV_END_BUFFERING, .stc_ses = stc_ses};
 
     pthread_mutex_lock(&st_ses->lock);
     DISPATCH_EVENT(st_ses, ev, status);
@@ -2783,32 +5759,69 @@
     return status;
 }
 
-int st_session_set_exec_mode(st_session_t *st_ses, st_exec_mode_t exec)
+int st_session_set_exec_mode(st_session_t *stc_ses, st_exec_mode_t exec)
 {
     int status = 0;
-    if (!st_ses)
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
     ALOGV("%s: exec mode %d", __func__, exec);
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_SET_EXEC_MODE,
-        .payload.exec_mode = exec };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = {.ev_id = ST_SES_EV_SET_EXEC_MODE,
+        .payload.exec_mode = exec, .stc_ses = stc_ses};
 
     pthread_mutex_lock(&st_ses->lock);
-    DISPATCH_EVENT(st_ses, ev, status);
+    if (st_ses->enable_trans)
+        DISPATCH_EVENT(st_ses, ev, status);
     pthread_mutex_unlock(&st_ses->lock);
     return status;
 }
 
-int st_session_send_custom_chmix_coeff(st_session_t *st_ses, char *str)
+int st_session_update_recongition_config(st_session_t *stc_ses)
+{
+
+    int status = 0;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return -EINVAL;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+
+    pthread_mutex_lock(&st_ses->lock);
+    status = update_hw_config_on_start(stc_ses, st_ses->hw_ses_current);
+    pthread_mutex_unlock(&st_ses->lock);
+    return status;
+
+}
+
+int st_session_get_preroll(st_session_t *stc_ses)
+{
+    int val = 0;
+
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
+        return 0;
+
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+
+    pthread_mutex_lock(&st_ses->lock);
+    val = st_ses->hw_ses_current->sthw_cfg.client_req_preroll;
+    pthread_mutex_unlock(&st_ses->lock);
+
+    return val;
+}
+
+int st_session_send_custom_chmix_coeff(st_session_t *stc_ses, char *str)
 {
     int status = 0;
 
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_session_ev_t ev = { .ev_id = ST_SES_EV_SEND_CHMIX_COEFF,
-        .payload.chmix_coeff_str = str};
+        .payload.chmix_coeff_str = str, .stc_ses = stc_ses};
 
     pthread_mutex_lock(&st_ses->lock);
     if (ST_EXEC_MODE_ADSP == st_ses->exec_mode)
@@ -2817,32 +5830,35 @@
     return status;
 }
 
-int st_session_get_config(st_session_t *st_ses, struct pcm_config *config)
+int st_session_get_config(st_session_t *stc_ses, struct pcm_config *config)
 {
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
-    st_hw_session_t *hw_ses = st_ses->hw_ses_current;
-    memcpy(config, &hw_ses->config, sizeof(struct pcm_config));
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+
+    pthread_mutex_lock(&st_ses->lock);
+    memcpy(config, &st_ses->hw_ses_current->config, sizeof(struct pcm_config));
+    pthread_mutex_unlock(&st_ses->lock);
 
     return 0;
 }
 
-int st_session_get_param_data(st_session_t *st_ses, const char *param,
-                              void *payload, size_t payload_size,
-                              size_t *param_data_size)
+int st_session_get_param_data(st_session_t *stc_ses, const char *param,
+    void *payload, size_t payload_size, size_t *param_data_size)
 {
     int status = 0;
 
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_session_getparam_payload_t getparam_payload = { .param = param,
         .payload = payload,
         .payload_size = payload_size,
         .param_data_size = param_data_size};
     st_session_ev_t ev = { .ev_id = ST_SES_EV_GET_PARAM_DATA,
-        .payload.getparam = getparam_payload};
+        .payload.getparam = getparam_payload, .stc_ses = stc_ses};
 
     pthread_mutex_lock(&st_ses->lock);
     /* Currently get param data supported for ARM & ADSP mode */
@@ -2854,310 +5870,105 @@
     return status;
 }
 
-/*
- * If the keyword detection session detects before the user verification
- * session, signal to process user verification. If the keyword detection
- * session rejects before the user verification session, signal to stop
- * processing user verification.
- */
-static void handle_vop_pending_detection(st_arm_ss_session_t *ss_session,
-                                   unsigned int det_status,
-                                   unsigned int kw_det_buff_sz)
+int st_session_ss_init(st_session_t *stc_ses)
 {
-    if (det_status & KEYWORD_DETECTION_SUCCESS) {
-        if (kw_det_buff_sz > ss_session->unread_bytes)
-            ss_session->buff_sz = kw_det_buff_sz;
-        else
-            ss_session->buff_sz = ss_session->unread_bytes;
-
-        /*
-         * It is possible that VOP started processing by already consuming
-         * data from unread_bytes while CNN detects. In this case, it does
-         * not need to be signaled.
-         */
-        if (ss_session->unread_bytes >= ss_session->buff_sz) {
-            ALOGD("%s: Processing UV due to KW detection success", __func__);
-            pthread_cond_signal(&ss_session->cond);
-        }
-    } else if (det_status & KEYWORD_DETECTION_REJECT) {
-        ss_session->exit_buffering = true;
-        ALOGD("%s: Exiting from UV due to KW detection rejection", __func__);
-        pthread_cond_signal(&ss_session->cond);
-    }
-}
-
-/*
- * If the user verification session rejects before the keyword detection
- * session, signal to stop processing keyword detection.
- */
-static void handle_cnn_pending_detection(st_arm_ss_session_t *ss_session,
-                                   unsigned int det_status)
-{
-    if (det_status & USER_VERIFICATION_REJECT) {
-        ss_session->exit_buffering = true;
-        ALOGD("%s: Exiting from KW detection due to UV rejection", __func__);
-        pthread_cond_signal(&ss_session->cond);
-    }
-}
-
-/*
- * This thread handles detection events from the second stage sessions
- * and aggregates them into 1 final decision. It will call the client callback
- * or restart the first stage session based on this decision.
- */
-static void *aggregator_thread_loop(void *st_session)
-{
-    st_session_t *st_ses = (st_session_t *)st_session;
-    recognition_callback_t callback = NULL;
-    struct listnode *node = NULL, *tmp_node = NULL;
+    int status = 0;
+    struct listnode *node = NULL;
     st_arm_second_stage_t *st_sec_stage = NULL;
-    int status = 0, lock_status = 0;
-    unsigned int kw_det_buff_sz = 0, det_status = 0;
-    struct timespec tspec = {0};
-    struct sound_trigger_recognition_event *event = NULL;
-    st_session_ev_t restart_ev = { .ev_id = ST_SES_EV_RESTART };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
 
-    ALOGV("%s: Enter", __func__);
-
-    pthread_mutex_lock(&st_ses->ss_detections_lock);
-    while (!st_ses->exit_aggregator_loop) {
-        det_status = 0;
-        lock_status = 0;
-        ALOGV("%s: waiting on cond", __func__);
-        pthread_cond_wait(&st_ses->ss_detections_cond,
-            &st_ses->ss_detections_lock);
-        ALOGV("%s: done waiting on cond", __func__);
-        if (st_ses->exit_aggregator_loop) {
-            pthread_mutex_unlock(&st_ses->ss_detections_lock);
-            return NULL;
-        }
-
-        list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-            st_sec_stage = node_to_item(node, st_arm_second_stage_t,
-                list_node);
-
-            pthread_mutex_lock(&st_sec_stage->ss_session->lock);
-            det_status |= st_sec_stage->ss_session->det_status;
-            if (st_sec_stage->ss_session->det_status ==
-                KEYWORD_DETECTION_SUCCESS)
-                kw_det_buff_sz = st_sec_stage->ss_session->bytes_processed;
-            pthread_mutex_unlock(&st_sec_stage->ss_session->lock);
-        }
-
-        list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
-            st_sec_stage = node_to_item(node, st_arm_second_stage_t,
-                list_node);
-
-            pthread_mutex_lock(&st_sec_stage->ss_session->lock);
-            if ((st_sec_stage->ss_info->sm_detection_type ==
-                    ST_SM_TYPE_USER_VERIFICATION) &&
-                (det_status & USER_VERIFICATION_PENDING)) {
-                handle_vop_pending_detection(st_sec_stage->ss_session,
-                    det_status, kw_det_buff_sz);
-            } else if ((st_sec_stage->ss_info->sm_detection_type ==
-                    ST_SM_TYPE_KEYWORD_DETECTION) &&
-                (det_status & KEYWORD_DETECTION_PENDING)) {
-                handle_cnn_pending_detection(st_sec_stage->ss_session,
-                    det_status);
-            }
-            pthread_mutex_unlock(&st_sec_stage->ss_session->lock);
-        }
-
-        if (!IS_SS_DETECTION_PENDING(det_status)) {
-            pthread_mutex_lock(&st_ses->lock);
-            /*
-             * If the client stops before 2nd stage finishes processing, or a
-             * transition is in progress, the detection event should not be
-             * handled.
-             */
-            if ((st_ses->current_state != buffering_state_fn) ||
-                (st_ses->exec_mode == ST_EXEC_MODE_NONE)) {
-                ALOGW("%s: First stage is not in a valid state, continuing",
-                    __func__);
-                pthread_mutex_unlock(&st_ses->lock);
-                continue;
-            }
-            if (IS_SS_DETECTION_SUCCESS(det_status)) {
-                clock_gettime(CLOCK_MONOTONIC, &tspec);
-                st_ses->hw_ses_current->second_stage_det_event_time =
-                    get_current_time_ns();
-                ATRACE_ASYNC_END("sthal: detection success",
-                    st_ses->sm_handle);
-                status = process_detection_event(st_ses,
-                    st_ses->det_session_ev->payload.detected.timestamp,
-                    st_ses->det_session_ev->payload.detected.detect_status,
-                    st_ses->det_session_ev->payload.detected.detect_payload,
-                    st_ses->det_session_ev->payload.detected.payload_size,
-                    &event);
-                if (status || !event) {
-                    ALOGE("%s:[%d] process_detection_event failed err %d",
-                        __func__, st_ses->sm_handle, status);
-                    /* Stop buffering if this is not a successful detection and
-                        LAB is triggered in hw automatically */
-                    st_ses->hw_ses_current->fptrs->stop_buffering(
-                        st_ses->hw_ses_current, st_ses->lab_enabled);
-
-                    pthread_mutex_unlock(&st_ses->lock);
-                    if (event) {
-                        free(event);
-                        event = NULL;
-                    }
-                    goto exit;
-                }
-                callback = st_ses->callback;
-                ALOGD("%s: Second stage detected successfully"
-                    ", calling client callback", __func__);
-                st_ses->sent_detection_to_client = true;
-                pthread_mutex_unlock(&st_ses->lock);
-                ATRACE_BEGIN("sthal: client detection callback");
-                callback(event, st_ses->cookie);
-                ATRACE_END();
-
-                /*
-                 * The client could unload the sound model during the callback,
-                 * which would join this thread and wait for this thread exit
-                 * as part of st_session_deinit() with st_session_lock held. By
-                 * this time, the state is also moved to idle. To avoid
-                 * deadlock, upon return from client callback, try acquiring
-                 * lock only if not in idle state, else exit right away.
-                 */
-                do {
-                    lock_status = pthread_mutex_trylock(&st_ses->lock);
-                } while (lock_status && (st_ses->current_state !=
-                         idle_state_fn));
-
-                if (st_ses->current_state == idle_state_fn) {
-                    ALOGV("%s:[%d] client unloaded after callback"
-                        ", lock status %d", __func__, st_ses->sm_handle,
-                        lock_status);
-                    if (!lock_status)
-                        pthread_mutex_unlock(&st_ses->lock);
-                    free(event);
-                    event = NULL;
-                    goto exit;
-                }
-
-                if (!st_ses->capture_requested)
-                    st_ses->hw_ses_current->fptrs->stop_buffering(
-                        st_ses->hw_ses_current, st_ses->lab_enabled);
-                free(event);
-                event = NULL;
-            } else {
-                ATRACE_ASYNC_END("sthal: detection reject",
-                    st_ses->sm_handle);
-                ALOGD("%s: Second stage did NOT detect, restarting st_session",
-                    __func__);
-                st_ses->hw_ses_current->fptrs->stop_buffering(
-                    st_ses->hw_ses_current, st_ses->lab_enabled);
-                DISPATCH_EVENT(st_ses, restart_ev, status);
-            }
-            pthread_mutex_unlock(&st_ses->lock);
-        } else {
-            ALOGV("%s: There is a second stage session pending, continuing",
-                __func__);
-        }
-    }
-exit:
-    pthread_mutex_unlock(&st_ses->ss_detections_lock);
-    ALOGV("%s: Exit", __func__);
-    return NULL;
-}
-
-static void init_det_event_aggregator(st_session_t *st_ses)
-{
-    int status = 0;
-    pthread_condattr_t attr;
-
-    st_ses->exit_aggregator_loop = false;
-    pthread_mutex_init(&(st_ses->ss_detections_lock), NULL);
-    pthread_condattr_init(&attr);
-    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
-    pthread_cond_init(&(st_ses->ss_detections_cond), &attr);
-    pthread_condattr_destroy(&attr);
-    status = pthread_create(&st_ses->aggregator_thread, NULL,
-        aggregator_thread_loop, st_ses);
-    if (status)
-        ALOGE("%s: Error creating aggregator thread. status = %d",
-            __func__, status);
-
-}
-
-static void destroy_det_event_aggregator(st_session_t *st_ses)
-{
-    int status = 0;
-
-    st_ses->exit_aggregator_loop = true;
-    pthread_mutex_lock(&st_ses->ss_detections_lock);
-    pthread_cond_signal(&st_ses->ss_detections_cond);
-    pthread_mutex_unlock(&st_ses->ss_detections_lock);
-    status = pthread_join(st_ses->aggregator_thread, NULL);
-    if (status)
-        ALOGE("%s: Error joining aggregator thread. status = %d",
-            __func__, status);
-    pthread_cond_destroy(&(st_ses->ss_detections_cond));
-    pthread_mutex_destroy(&(st_ses->ss_detections_lock));
-}
-
-int st_session_ss_init(st_session_t *st_ses)
-{
-    int status = 0;
-    struct listnode *node = NULL, *tmp_node = NULL;
-    st_arm_second_stage_t *st_sec_stage = NULL;
-
-    init_det_event_aggregator(st_ses);
-    list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
+    list_for_each(node, &stc_ses->second_stage_list) {
         st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
         status = st_second_stage_module_init(st_sec_stage,
             (void *)st_sec_stage->ss_info->lib_name);
         if (status) {
-            ALOGE("%s: initializing second stage session failed %d",
-                __func__, status);
+            ALOGE("%s:[c%d] initializing second stage session failed %d",
+                __func__, stc_ses->sm_handle, status);
             goto ss_cleanup;
         }
     }
 
+    pthread_mutex_lock(&st_ses->lock);
+    if (st_ses->aggregator_thread_created) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return 0;
+    }
+    /*
+     * Aggregator is not maintatined per client as there is only one
+     * client keyword detection happens at a time in multi-client scenario.
+     * Instead use single aggregator thread at proxy level, processing the
+     * second stage for detected client at run time.
+     */
+    init_det_event_aggregator(st_ses);
+
     st_ses->det_session_ev = calloc(1, sizeof(st_session_ev_t));
     if (!st_ses->det_session_ev) {
         ALOGE("%s: Failed to allocate st_session_ev_t, exiting", __func__);
         status = -ENOMEM;
         goto ss_cleanup;
     }
+    pthread_mutex_unlock(&st_ses->lock);
     return 0;
 
 ss_cleanup:
     destroy_det_event_aggregator(st_ses);
-    list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
+    list_for_each(node, &stc_ses->second_stage_list) {
         st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
         st_second_stage_module_deinit(st_sec_stage);
     }
+    pthread_mutex_unlock(&st_ses->lock);
     return status;
 }
 
-int st_session_ss_deinit(st_session_t *st_ses)
+int st_session_ss_deinit(st_session_t *stc_ses)
 {
-    struct listnode *node = NULL, *tmp_node = NULL;
+    struct listnode *node = NULL;
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
     st_arm_second_stage_t *st_sec_stage = NULL;
+    st_session_t *c_ses = NULL;
+    bool aggregator_needed = false;
 
-    destroy_det_event_aggregator(st_ses);
-    list_for_each_safe(node, tmp_node, &st_ses->second_stage_list) {
+    list_for_each(node, &stc_ses->second_stage_list) {
         st_sec_stage = node_to_item(node, st_arm_second_stage_t, list_node);
         st_second_stage_module_deinit(st_sec_stage);
     }
 
+    pthread_mutex_lock(&st_ses->lock);
+    if (!st_ses->aggregator_thread_created) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return 0;
+    }
+    /* If other client has second stage enabled, keep the aggregator */
+    list_for_each(node, &st_ses->clients_list) {
+        c_ses = node_to_item(node, st_session_t, hw_list_node);
+        if (c_ses != stc_ses && !list_empty(&c_ses->second_stage_list)) {
+            aggregator_needed = true;
+            break;
+        }
+    }
+    if (aggregator_needed) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return 0;
+    }
+
+    destroy_det_event_aggregator(st_ses);
+
     if (st_ses->det_session_ev)
         free(st_ses->det_session_ev);
+    pthread_mutex_unlock(&st_ses->lock);
 
     return 0;
 }
 
-int st_session_request_detection(st_session_t *st_ses)
+int st_session_request_detection(st_session_t *stc_ses)
 {
     int status = 0;
 
-    if (!st_ses)
+    if (!stc_ses || !stc_ses->hw_proxy_ses)
         return -EINVAL;
 
-    st_session_ev_t ev = { .ev_id = ST_SES_EV_REQUEST_DET };
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+    st_session_ev_t ev = {.ev_id = ST_SES_EV_REQUEST_DET, .stc_ses = stc_ses};
 
     /* lock to serialize event handling */
     pthread_mutex_lock(&st_ses->lock);
@@ -3166,28 +5977,33 @@
     return status;
 }
 
-int st_session_init(st_session_t *st_ses, struct sound_trigger_device *stdev,
+int st_session_init(st_session_t *stc_ses, struct sound_trigger_device *stdev,
     st_exec_mode_t exec_mode, sound_model_handle_t sm_handle)
 {
     int status = 0;
-    struct st_vendor_info *v_info;
+    struct st_vendor_info *v_info = NULL;
+    st_proxy_session_t *st_ses = NULL;
+    struct listnode *node = NULL;
+    struct st_session *c_ses = NULL;
     pthread_mutexattr_t attr;
 
-    if (!st_ses || !stdev) {
-        status = -EINVAL;
-        return status;
-    }
-    st_ses->stdev = stdev;
+    if (!stc_ses || !stdev)
+        return -EINVAL;
 
-    /* caller must set vendor_uuid_info directly if present */
-    v_info = st_ses->vendor_uuid_info;
+    st_ses = calloc(1, sizeof(st_proxy_session_t));
+    if (!st_ses) {
+        ALOGE("%s: hw_proxy_ses allocation failed", __func__);
+        return -ENOMEM;
+    }
+    st_ses->stdev = stc_ses->stdev = stdev;
+    v_info = stc_ses->vendor_uuid_info;
 
     if (v_info && (EXEC_MODE_CFG_DYNAMIC == v_info->exec_mode_cfg)) {
         st_ses->enable_trans = true;
-
         if (stdev->is_gcs) {
             /* alloc and init cpe session*/
-            st_ses->hw_ses_cpe = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_gcs_t));
+            st_ses->hw_ses_cpe =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_gcs_t));
             if (!st_ses->hw_ses_cpe) {
                 status = -ENOMEM;
                 goto cleanup;
@@ -3201,7 +6017,8 @@
             }
 
             /* alloc and init adsp session*/
-            st_ses->hw_ses_adsp = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+            st_ses->hw_ses_adsp =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
             if (!st_ses->hw_ses_adsp) {
                 st_hw_sess_gcs_deinit(st_ses->hw_ses_cpe);
                 status = -ENOMEM;
@@ -3218,7 +6035,8 @@
 
         } else {
             /* alloc and init cpe session*/
-            st_ses->hw_ses_cpe = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+            st_ses->hw_ses_cpe =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
             if (!st_ses->hw_ses_cpe) {
                 status = -ENOMEM;
                 goto cleanup;
@@ -3231,13 +6049,13 @@
                 goto cleanup;
             }
             /* alloc and init adsp session*/
-            st_ses->hw_ses_adsp = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+            st_ses->hw_ses_adsp =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
             if (!st_ses->hw_ses_adsp) {
                 status = -ENOMEM;
                 st_hw_sess_lsm_deinit(st_ses->hw_ses_cpe);
                 goto cleanup;
             }
-
             status = st_hw_sess_lsm_init(st_ses->hw_ses_adsp, hw_sess_cb,
                 (void *)st_ses, ST_EXEC_MODE_ADSP, v_info, sm_handle, stdev);
             if (status) {
@@ -3246,18 +6064,17 @@
                 goto cleanup;
             }
         }
-
         /* set current hw_session */
         if (exec_mode == ST_EXEC_MODE_CPE)
             st_ses->hw_ses_current = st_ses->hw_ses_cpe;
         else if (exec_mode == ST_EXEC_MODE_ADSP)
             st_ses->hw_ses_current = st_ses->hw_ses_adsp;
-
     } else if (v_info && (EXEC_MODE_CFG_CPE == v_info->exec_mode_cfg)) {
         st_ses->enable_trans = false;
         if (stdev->is_gcs) {
             ALOGD("%s: initializing gcs hw session", __func__);
-            st_ses->hw_ses_cpe = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_gcs_t));
+            st_ses->hw_ses_cpe =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_gcs_t));
             if (!st_ses->hw_ses_cpe) {
                 status = -ENOMEM;
                 goto cleanup;
@@ -3270,7 +6087,8 @@
                 goto cleanup;
             }
         } else {
-            st_ses->hw_ses_cpe = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+            st_ses->hw_ses_cpe =
+                (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
             if (!st_ses->hw_ses_cpe) {
                 status = -ENOMEM;
                 goto cleanup;
@@ -3284,22 +6102,50 @@
             }
         }
         st_ses->hw_ses_current = st_ses->hw_ses_cpe;
-
     } else if (v_info && (EXEC_MODE_CFG_APE == v_info->exec_mode_cfg)) {
-        st_ses->enable_trans = false;
-        st_ses->hw_ses_adsp = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
-        if (!st_ses->hw_ses_adsp) {
-            status = -ENOMEM;
-            goto cleanup;
-        }
-        status = st_hw_sess_lsm_init(st_ses->hw_ses_adsp, hw_sess_cb,
-            (void *)st_ses, exec_mode, v_info, sm_handle, stdev);
-        if (status) {
-            ALOGE("%s: initializing lsm hw session failed %d",
-                __func__, status);
-            goto cleanup;
-        }
-        st_ses->hw_ses_current = st_ses->hw_ses_adsp;
+        /*
+         * Check for merge sound model support and return the existing hw
+         * session. If any other clients have already created it.
+         */
+        if (v_info->merge_fs_soundmodels) {
+            if (!v_info->is_qcva_uuid) {
+               ALOGE("%s: merge sound model not supported for non SVA engines",
+                     __func__);
+               status = -ENOSYS;
+               goto cleanup;
+            }
+            list_for_each(node, &stdev->sound_model_list) {
+                c_ses = node_to_item(node, st_session_t, list_node);
+                if ((c_ses != stc_ses) &&
+                    c_ses->vendor_uuid_info->is_qcva_uuid &&
+                    c_ses->vendor_uuid_info->merge_fs_soundmodels) {
+                    stc_ses->hw_proxy_ses = c_ses->hw_proxy_ses;
+                    list_add_tail(&stc_ses->hw_proxy_ses->clients_list,
+                        &stc_ses->hw_list_node);
+                    ALOGD("%s: another client attached: h%d <-- c%d", __func__,
+                        stc_ses->hw_proxy_ses->sm_handle, sm_handle);
+                    free(st_ses);
+                    st_ses = NULL;
+                    break;
+                }
+            }
+         }
+         if (st_ses) { /* If no other client exist */
+             st_ses->hw_ses_adsp =
+                 (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+             if (!st_ses->hw_ses_adsp) {
+                 status = -ENOMEM;
+                 goto cleanup;
+             }
+             status = st_hw_sess_lsm_init(st_ses->hw_ses_adsp, hw_sess_cb,
+                (void *)st_ses, exec_mode, v_info, sm_handle, stdev);
+             if (status) {
+                 ALOGE("%s: initializing lsm hw session failed %d",
+                     __func__, status);
+                 goto cleanup;
+             }
+             st_ses->hw_ses_current = st_ses->hw_ses_adsp;
+         }
     } else if (v_info && (EXEC_MODE_CFG_ARM == v_info->exec_mode_cfg)) {
         st_ses->enable_trans = false;
         st_ses->hw_ses_arm = calloc(1, sizeof(st_hw_session_pcm_t));
@@ -3316,7 +6162,8 @@
         }
         st_ses->hw_ses_current = st_ses->hw_ses_arm;
     } else if (!v_info) {
-        st_ses->hw_ses_cpe = (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
+        st_ses->hw_ses_cpe =
+            (st_hw_session_t *)calloc(1, sizeof(st_hw_session_lsm_t));
         if (!st_ses->hw_ses_cpe) {
             status = -ENOMEM;
             goto cleanup;
@@ -3332,46 +6179,81 @@
 
     pthread_mutexattr_init(&attr);
     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-    pthread_mutex_init(&st_ses->lock, (const pthread_mutexattr_t *)&attr);
+    pthread_mutex_init(&stc_ses->lock, (const pthread_mutexattr_t *)&attr);
 
-    st_ses->exec_mode = exec_mode;
-    st_ses->sm_handle = sm_handle;
-    st_ses->ssr_transit_exec_mode = ST_EXEC_MODE_NONE;
-    st_ses->lab_fp = NULL;
+    stc_ses->exec_mode = exec_mode;
+    stc_ses->sm_handle = sm_handle;
+    stc_ses->ssr_transit_exec_mode = ST_EXEC_MODE_NONE;
+    stc_ses->client_req_det_mode = ST_DET_UNKNOWN_MODE;
+    stc_ses->state = ST_STATE_IDLE;
 
-    /* start in idle state */
-    STATE_TRANSITION(st_ses, idle_state_fn);
+    if (st_ses) { /* Could get freed if other client exists */
+        st_ses ->vendor_uuid_info = v_info;
+        st_ses->exec_mode = exec_mode;
+        st_ses->sm_handle = sm_handle;
+        st_ses->lab_fp = NULL;
+        pthread_mutex_init(&st_ses->lock, (const pthread_mutexattr_t *)&attr);
 
+        stc_ses->hw_proxy_ses = st_ses;
+        list_init(&st_ses->clients_list);
+        list_add_tail(&st_ses->clients_list, &stc_ses->hw_list_node);
+        ALOGD("%s: client attached: h%d <-- c%d", __func__,
+            st_ses->sm_handle, sm_handle);
+
+        if (!stdev->ssr_offline_received) {
+            STATE_TRANSITION(st_ses, idle_state_fn);
+        } else {
+            STATE_TRANSITION(st_ses, ssr_state_fn);
+            status = 0;
+        }
+    }
     return status;
 
 cleanup:
-    if (st_ses->hw_ses_cpe)
-        free(st_ses->hw_ses_cpe);
-    if (st_ses->hw_ses_adsp)
-        free(st_ses->hw_ses_adsp);
+    if (st_ses) {
+        if (st_ses->hw_ses_cpe)
+            free(st_ses->hw_ses_cpe);
+        if (st_ses->hw_ses_adsp)
+            free(st_ses->hw_ses_adsp);
+        free(st_ses);
+    }
     return status;
 }
 
-int st_session_deinit(st_session_t *st_ses)
+int st_session_deinit(st_session_t *stc_ses)
 {
+    st_proxy_session_t *st_ses = stc_ses->hw_proxy_ses;
+
+    pthread_mutex_lock(&st_ses->lock);
+    list_remove(&stc_ses->hw_list_node);
+    ALOGV("%s: client detatched: h%d <-- c%d", __func__, st_ses->sm_handle,
+        stc_ses->sm_handle);
+    if (stc_ses == st_ses->det_stc_ses)
+        st_ses->det_stc_ses = NULL;
+
+    if (!list_empty(&st_ses->clients_list)) {
+        pthread_mutex_unlock(&st_ses->lock);
+        return 0;
+    }
     /* deinit cpe session */
     if (st_ses->hw_ses_cpe) {
         if (st_ses->stdev->is_gcs)
             st_hw_sess_gcs_deinit(st_ses->hw_ses_cpe);
         else
             st_hw_sess_lsm_deinit(st_ses->hw_ses_cpe);
-        free((void *)st_ses->hw_ses_cpe);
+        free(st_ses->hw_ses_cpe);
         st_ses->hw_ses_cpe = NULL;
     }
-
     /* deinit adsp session */
     if (st_ses->hw_ses_adsp) {
         st_hw_sess_lsm_deinit(st_ses->hw_ses_adsp);
-        free((void *)st_ses->hw_ses_adsp);
+        free(st_ses->hw_ses_adsp);
         st_ses->hw_ses_adsp = NULL;
     }
-
+    pthread_mutex_unlock(&st_ses->lock);
     pthread_mutex_destroy(&st_ses->lock);
+    free(stc_ses->hw_proxy_ses);
+    stc_ses->hw_proxy_ses = NULL;
 
     return 0;
 }
diff --git a/st_session.h b/st_session.h
index 774e2a4..c37751b 100644
--- a/st_session.h
+++ b/st_session.h
@@ -41,8 +41,6 @@
 #include "sound_trigger_platform.h"
 #include "st_common_defs.h"
 
-#define MAX_STATE_NAME_LEN 50
-
 /* Below are the states that can be requested from the client */
 enum client_states_t {
     ST_STATE_IDLE,
@@ -50,6 +48,12 @@
     ST_STATE_ACTIVE
 };
 
+typedef enum {
+    ST_DET_LOW_POWER_MODE,
+    ST_DET_HIGH_PERF_MODE,
+    ST_DET_UNKNOWN_MODE = 0xFF,
+}  st_det_perf_mode_t;
+
 typedef enum st_session_event_id {
     ST_SES_EV_LOAD_SM,
     ST_SES_EV_UNLOAD_SM,
@@ -76,12 +80,30 @@
 typedef struct st_session_ev st_session_ev_t;
 
 typedef struct st_session st_session_t;
-typedef int (*st_session_state_fn_t)(st_session_t*, st_session_ev_t *ev);
+typedef struct st_proxy_session st_proxy_session_t;
+typedef int (*st_proxy_session_state_fn_t)(st_proxy_session_t*,
+                                           st_session_ev_t *ev);
+
+struct sound_model_info {
+    unsigned char *sm_data;
+    unsigned int sm_size;
+    sound_trigger_sound_model_type_t sm_type;
+    unsigned int num_keyphrases;
+    unsigned int num_users;
+    char **keyphrases;
+    char **users;
+    char **cf_levels_kw_users;
+    unsigned char *cf_levels;
+    unsigned char *det_cf_levels;
+    unsigned int cf_levels_size;
+    bool sm_merged;
+};
 
 struct st_session {
     /* TODO: decouple device below from session */
     struct listnode list_node;
     struct listnode transit_list_node;
+    struct listnode hw_list_node;
 
     struct sound_trigger_device *stdev;
     struct st_vendor_info *vendor_uuid_info;
@@ -89,23 +111,49 @@
     pthread_mutex_t lock;
     st_exec_mode_t exec_mode;
     st_exec_mode_t ssr_transit_exec_mode;
-    bool enable_trans;
-    struct sound_trigger_phrase_sound_model *sm_data;
+    struct sound_trigger_phrase_sound_model *phrase_sm;
     struct sound_trigger_recognition_config *rc_config;
-
     sound_trigger_sound_model_type_t sm_type;
-
     sound_model_handle_t sm_handle;
     recognition_callback_t callback;
     void *cookie;
     audio_io_handle_t capture_handle;
-
     bool capture_requested;
-    bool lab_enabled;
 
     unsigned int num_phrases;
     unsigned int num_users;
     unsigned int recognition_mode;
+    enum client_states_t state;
+    bool paused;
+    bool pending_stop;
+    bool pending_load;
+    bool pending_set_device;
+    st_det_perf_mode_t client_req_det_mode;
+    unsigned int hist_buf_duration;
+    unsigned int preroll_duration;
+
+    struct listnode second_stage_list;
+    uint32_t conf_levels_intf_version;
+    void *st_conf_levels;
+
+    st_proxy_session_t *hw_proxy_ses;
+    struct sound_model_info sm_info;
+};
+
+struct st_proxy_session {
+    struct listnode clients_list; /* Attached client sessions */
+    struct sound_trigger_device *stdev;
+    struct st_vendor_info *vendor_uuid_info;
+
+    pthread_mutex_t lock;
+    st_exec_mode_t exec_mode;
+    bool enable_trans;
+
+    struct sound_trigger_recognition_config *rc_config;
+    sound_trigger_sound_model_type_t sm_type;
+    sound_model_handle_t sm_handle;
+    bool lab_enabled;
+    unsigned int recognition_mode;
 
     st_hw_session_t *hw_ses_cpe; /* cpe hw session */
     st_hw_session_t *hw_ses_adsp; /* adsp hw session */
@@ -113,29 +161,31 @@
     st_hw_session_t *hw_ses_current; /* current hw session, this is set every
         time there is an exec_mode change and points to one of the above
         hw sessions */
-    bool paused;
-    bool hw_session_started;
-    /* flag gets set if user restarts
-        session right after detection before we have a chance to stop the
-        session */
+    st_hw_session_t *hw_ses_prev; /* cached hw_ses_current,
+        used for WDSP<->ADSP transitions */
+    st_session_t *det_stc_ses; /* Current detected client */
 
-    st_session_state_fn_t current_state;
-    enum client_states_t client_req_state; /* holds the state that was requested by
-        user this is used for recovering from SSR */
+    /*
+     * flag gets set if user restarts
+     * session right after detection before we have a chance to stop the
+     * session
+     */
+    bool hw_session_started;
+
+    st_proxy_session_state_fn_t current_state;
     bool device_disabled;
-    bool pending_stop;
 
     pthread_t aggregator_thread;
     pthread_mutex_t ss_detections_lock;
     pthread_cond_t ss_detections_cond;
-    bool sent_detection_to_client;
+    bool aggregator_thread_created;
     bool exit_aggregator_loop;
-    struct listnode second_stage_list;
     bool enable_second_stage;
     st_session_ev_t *det_session_ev;
     int rc_config_update_counter;
     bool detection_requested;
-    uint32_t conf_levels_intf_version;
+
+    struct sound_model_info sm_info;
     FILE *lab_fp;
 };
 
@@ -169,7 +219,6 @@
    enum ssr_event_status ssr_type);
 int st_session_pause(st_session_t *st_ses);
 int st_session_resume(st_session_t *st_ses);
-void st_session_query_state(st_session_t *st_ses, char *state_name, size_t len);
 int st_session_restart(st_session_t *st_ses);
 int st_session_send_custom_chmix_coeff(st_session_t *st_ses, char *str);
 int st_session_get_config(st_session_t *st_ses, struct pcm_config *config);
@@ -182,10 +231,13 @@
 int st_session_set_exec_mode(st_session_t *st_ses, st_exec_mode_t exec);
 int st_session_get_param_data(st_session_t *st_ses, const char *param,
     void *payload, size_t payload_size, size_t *param_data_size);
+int st_session_request_detection(st_session_t *st_ses);
+int st_session_update_recongition_config(st_session_t *st_ses);
+int st_session_get_preroll(st_session_t *st_ses);
+
 int process_detection_event_keyphrase_v2(
-    st_session_t *st_ses, int detect_status,
+    st_proxy_session_t *st_ses, int detect_status,
     void *payload, size_t payload_size,
     struct sound_trigger_phrase_recognition_event **event);
-int st_session_request_detection(st_session_t *st_ses);
 
 #endif /* ST_SESSION_H */