st-hal: Add support for SVA sound models merging

Added support for merging the SVA GMM sound models to be passed to
ADSP. Two or more different client Application keywords targetted to
the same SVA ADSP engine can be merged and single ADSP session can be
opened, though multiple framework sessions exist from each Application.
STHAL manages merging the incoming sound models and routing detections
to the appropriate framework session. Only GMM sound models for ADSP
are merged, the second stage sound models are not merged.

Change-Id: Ib11146e8f7e3d9e3d660a5a0521502132a87fed8
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 2cbc4a5..d06f365 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 b762f34..f308077 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;
@@ -175,14 +233,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 2a13d2c..579614b 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"
@@ -410,45 +410,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;
 }
@@ -2316,16 +2333,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);
@@ -2502,6 +2509,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;
@@ -3575,9 +3590,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 &&
@@ -3647,8 +3671,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);
     }
@@ -3704,8 +3726,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 68f1b4a..0c519b5 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 dbd21b8..5685c81 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 a597952..01c8b92 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,
@@ -1012,13 +1011,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);
         }
 
@@ -1085,7 +1086,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;
@@ -1114,7 +1115,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;
@@ -1358,7 +1359,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);
@@ -1395,13 +1396,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);
         }
 
@@ -1471,7 +1474,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;
@@ -1523,6 +1526,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);
@@ -1647,6 +1651,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++);
@@ -1853,8 +1860,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);
@@ -1865,13 +1873,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 +
@@ -1917,12 +1925,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);
@@ -2031,15 +2037,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];
@@ -2050,7 +2052,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);
@@ -2171,16 +2173,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);
 
@@ -2257,7 +2252,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;
@@ -2270,18 +2265,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;
@@ -2302,7 +2285,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;
@@ -2315,7 +2298,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);
@@ -2334,7 +2317,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.
@@ -2394,8 +2377,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;
@@ -2459,8 +2443,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");
@@ -2489,6 +2473,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;
@@ -2497,18 +2483,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);
             {
@@ -2540,7 +2526,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++];
@@ -2599,29 +2585,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;
@@ -2843,7 +2834,7 @@
                 goto error_exit_1;
         }
     }
-
+    p_ses->lab_enabled = capture_requested;
     return status;
 
 error_exit_1:
@@ -2970,12 +2961,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;
@@ -3079,16 +3069,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;
@@ -3130,7 +3112,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;
@@ -3140,18 +3122,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;
@@ -3163,7 +3133,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);
@@ -3208,8 +3178,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;
@@ -3247,8 +3218,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");
@@ -3286,24 +3257,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;
@@ -3326,7 +3297,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) {
@@ -3347,29 +3318,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;
@@ -3405,7 +3378,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:
@@ -3488,7 +3461,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 =
@@ -3515,7 +3488,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);
@@ -3526,9 +3499,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,
@@ -3545,12 +3518,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;
 }
@@ -3563,7 +3535,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)
@@ -3581,7 +3552,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;
@@ -3629,6 +3600,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;
@@ -3674,9 +3650,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,
@@ -3693,9 +3669,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)
@@ -3704,7 +3680,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)
 {
     /*
@@ -3719,8 +3695,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;
@@ -3741,7 +3716,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;
@@ -4146,7 +4121,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 1afbee0..2884ccf 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 */