audio: enable SoundMonitor to handle ADSP SSR events

Enable SoundMonitor in audio hal to handle ADSP SSR events,
do required cleanup and restore any active sessions gracefully.

Change-Id: I83270b7c12095e56a6677f41efe0f5618a27e0e5
diff --git a/configs/msm8909/msm8909.mk b/configs/msm8909/msm8909.mk
index 3405db7..ee80b83 100755
--- a/configs/msm8909/msm8909.mk
+++ b/configs/msm8909/msm8909.mk
@@ -35,6 +35,7 @@
 AUDIO_FEATURE_ENABLED_DYNAMIC_LOG := true
 MM_AUDIO_ENABLED_FTM := true
 TARGET_USES_QCOM_MM_AUDIO := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 
 ##AUDIO_FEATURE_FLAGS
 
diff --git a/configs/msm8937/msm8937.mk b/configs/msm8937/msm8937.mk
index 4b26d6c..0f10b33 100644
--- a/configs/msm8937/msm8937.mk
+++ b/configs/msm8937/msm8937.mk
@@ -56,6 +56,7 @@
 AUDIO_FEATURE_ENABLED_SOURCE_TRACKING := true
 BOARD_SUPPORTS_QAHW := true
 AUDIO_FEATURE_ENABLED_DYNAMIC_LOG := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/configs/msm8953/msm8953.mk b/configs/msm8953/msm8953.mk
index 1adc471..9a778b9 100644
--- a/configs/msm8953/msm8953.mk
+++ b/configs/msm8953/msm8953.mk
@@ -56,6 +56,7 @@
 AUDIO_FEATURE_ENABLED_SOURCE_TRACKING := true
 BOARD_SUPPORTS_QAHW := true
 AUDIO_FEATURE_ENABLED_DYNAMIC_LOG := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/configs/msm8996/msm8996.mk b/configs/msm8996/msm8996.mk
index 7f8d6ec..4b3af40 100644
--- a/configs/msm8996/msm8996.mk
+++ b/configs/msm8996/msm8996.mk
@@ -55,6 +55,7 @@
 AUDIO_FEATURE_ENABLED_GEF_SUPPORT := true
 BOARD_SUPPORTS_QAHW := true
 AUDIO_FEATURE_ENABLED_DYNAMIC_LOG := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/configs/msm8998/msm8998.mk b/configs/msm8998/msm8998.mk
index 524582a..cddd66e 100644
--- a/configs/msm8998/msm8998.mk
+++ b/configs/msm8998/msm8998.mk
@@ -63,6 +63,7 @@
 BOARD_SUPPORTS_QAHW := true
 AUDIO_FEATURE_ENABLED_RAS := true
 AUDIO_FEATURE_ENABLED_DYNAMIC_LOG := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/configs/sdm660/sdm660.mk b/configs/sdm660/sdm660.mk
index 62fe5c8..460a284 100644
--- a/configs/sdm660/sdm660.mk
+++ b/configs/sdm660/sdm660.mk
@@ -62,6 +62,7 @@
 AUDIO_FEATURE_ENABLED_SOURCE_TRACKING := true
 AUDIO_FEATURE_ENABLED_GEF_SUPPORT := true
 AUDIO_FEATURE_ENABLED_RAS := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/configs/sdm845/sdm845.mk b/configs/sdm845/sdm845.mk
index 19802b4..3c2cab7 100644
--- a/configs/sdm845/sdm845.mk
+++ b/configs/sdm845/sdm845.mk
@@ -59,6 +59,7 @@
 AUDIO_FEATURE_ENABLED_GEF_SUPPORT := true
 BOARD_SUPPORTS_QAHW := false
 AUDIO_FEATURE_ENABLED_RAS := true
+AUDIO_FEATURE_ENABLED_SND_MONITOR := true
 ##AUDIO_FEATURE_FLAGS
 
 #Audio Specific device overlays
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 62b661e..84814f1 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -250,13 +250,6 @@
     }
 }
 
-#ifndef FM_POWER_OPT
-#define audio_extn_fm_set_parameters(adev, parms) (0)
-#else
-void audio_extn_fm_set_parameters(struct audio_device *adev,
-                                   struct str_parms *parms);
-#endif
-
 #ifndef SOURCE_TRACKING_ENABLED
 #define audio_extn_source_track_set_parameters(adev, parms) (0)
 #define audio_extn_source_track_get_parameters(adev, query, reply) (0)
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 42719f4..7c86d18 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -836,6 +836,13 @@
                                    struct sound_focus_param *payload);
 #endif
 
+#ifndef FM_POWER_OPT
+#define audio_extn_fm_set_parameters(adev, parms) (0)
+#else
+void audio_extn_fm_set_parameters(struct audio_device *adev,
+                                   struct str_parms *parms);
+#endif
+
 #ifndef APTX_DECODER_ENABLED
 #define audio_extn_aptx_dec_set_license(adev); (0)
 #define audio_extn_set_aptx_dec_bt_addr(adev, parms); (0)
diff --git a/hal/audio_extn/fm.c b/hal/audio_extn/fm.c
index 5da494d..b34bb22 100644
--- a/hal/audio_extn/fm.c
+++ b/hal/audio_extn/fm.c
@@ -62,7 +62,7 @@
     bool is_fm_muted;
     float fm_volume;
     bool restart_fm;
-    int scard_state;
+    card_status_t card_status;
 };
 
 static struct fm_module fmmod = {
@@ -72,7 +72,7 @@
   .is_fm_running = 0,
   .is_fm_muted = 0,
   .restart_fm = 0,
-  .scard_state = SND_CARD_STATE_ONLINE,
+  .card_status = CARD_STATUS_ONLINE,
 };
 
 static int32_t fm_set_volume(struct audio_device *adev, float value, bool persist)
@@ -242,14 +242,14 @@
     if (ret >= 0) {
         char *snd_card_status = value+2;
         if (strstr(snd_card_status, "OFFLINE")) {
-            fmmod.scard_state = SND_CARD_STATE_OFFLINE;
+            fmmod.card_status = CARD_STATUS_OFFLINE;
         }
         else if (strstr(snd_card_status, "ONLINE")) {
-            fmmod.scard_state = SND_CARD_STATE_ONLINE;
+            fmmod.card_status = CARD_STATUS_ONLINE;
         }
     }
     if(fmmod.is_fm_running) {
-        if (fmmod.scard_state == SND_CARD_STATE_OFFLINE) {
+        if (fmmod.card_status == CARD_STATUS_OFFLINE) {
             ALOGD("sound card is OFFLINE, stop FM");
             fm_stop(adev);
             fmmod.restart_fm = 1;
@@ -263,7 +263,7 @@
                 select_devices(adev, USECASE_AUDIO_PLAYBACK_FM);
         }
     }
-    if (fmmod.restart_fm && (fmmod.scard_state == SND_CARD_STATE_ONLINE)) {
+    if (fmmod.restart_fm && (fmmod.card_status == CARD_STATUS_ONLINE)) {
         ALOGD("sound card is ONLINE, restart FM");
         fmmod.restart_fm = 0;
         fm_start(adev);
diff --git a/hal/audio_extn/qaf.c b/hal/audio_extn/qaf.c
index bf731f6..8cb80e9 100644
--- a/hal/audio_extn/qaf.c
+++ b/hal/audio_extn/qaf.c
@@ -629,7 +629,6 @@
 {
     int ret = 0;
     struct audio_device *adev = out->dev;
-    int snd_card_status = get_snd_card_state(adev);
 
     if ((out->usecase < 0) || (out->usecase >= AUDIO_USECASE_MAX)) {
         ret = -EINVAL;
@@ -641,7 +640,8 @@
           __func__, &out->stream, out->usecase, use_case_table[out->usecase],
           out->devices);
 
-    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
+    if (CARD_STATUS_OFFLINE == out->card_status ||
+        CARD_STATUS_OFFLINE == adev->card_status) {
         ALOGE("%s: sound card is not active/SSR returning error", __func__);
         ret = -EIO;
         usleep(50000);
diff --git a/hal/audio_extn/soundtrigger.c b/hal/audio_extn/soundtrigger.c
index 94a8a2b..a38ca17 100644
--- a/hal/audio_extn/soundtrigger.c
+++ b/hal/audio_extn/soundtrigger.c
@@ -97,6 +97,15 @@
     return NULL;
 }
 
+static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms)
+{
+    if (!parms)
+        return;
+
+    audio_extn_sound_trigger_set_parameters(NULL, parms);
+    return;
+}
+
 int audio_hw_call_back(sound_trigger_event_type_t event,
                        sound_trigger_event_info_t* config)
 {
@@ -426,6 +435,7 @@
 
     st_dev->adev = adev;
     list_init(&st_dev->st_ses_list);
+    audio_extn_snd_mon_register_listener(st_dev, stdev_snd_mon_cb);
 
     return 0;
 
@@ -442,6 +452,7 @@
 {
     ALOGI("%s: Enter", __func__);
     if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
+        audio_extn_snd_mon_unregister_listener(st_dev);
         dlclose(st_dev->lib_handle);
         free(st_dev);
         st_dev = NULL;
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 68f5978..0b3592a 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -423,6 +423,26 @@
         adev->adm_abandon_focus(adev->adm_data, in->capture_handle);
 }
 
+static int parse_snd_card_status(struct str_parms *parms, int *card,
+                                 card_status_t *status)
+{
+    char value[32]={0};
+    char state[32]={0};
+
+    int  ret = str_parms_get_str(parms, "SND_CARD_STATUS", value, sizeof(value));
+    if (ret < 0)
+        return -1;
+
+    // sscanf should be okay as value is of max length 32.
+    // same as sizeof state.
+    if (sscanf(value, "%d,%s", card, state) < 2)
+        return -1;
+
+    *status = !strcmp(state, "ONLINE") ? CARD_STATUS_ONLINE :
+                                         CARD_STATUS_OFFLINE;
+    return 0;
+}
+
 __attribute__ ((visibility ("default")))
 bool audio_hw_send_gain_dep_calibration(int level) {
     bool ret_val = false;
@@ -542,35 +562,6 @@
            (uc_id == USECASE_AUDIO_PLAYBACK_AFE_PROXY);
 }
 
-int get_snd_card_state(struct audio_device *adev)
-{
-    int snd_scard_state;
-
-    if (!adev)
-        return SND_CARD_STATE_OFFLINE;
-
-    pthread_mutex_lock(&adev->snd_card_status.lock);
-    snd_scard_state = adev->snd_card_status.state;
-    pthread_mutex_unlock(&adev->snd_card_status.lock);
-
-    return snd_scard_state;
-}
-
-static int set_snd_card_state(struct audio_device *adev, int snd_scard_state)
-{
-    if (!adev)
-        return -ENOSYS;
-
-    pthread_mutex_lock(&adev->snd_card_status.lock);
-    if (adev->snd_card_status.state != snd_scard_state) {
-        adev->snd_card_status.state = snd_scard_state;
-        platform_snd_card_update(adev->platform, snd_scard_state);
-    }
-    pthread_mutex_unlock(&adev->snd_card_status.lock);
-
-    return 0;
-}
-
 static int enable_audio_route_for_voice_usecases(struct audio_device *adev,
                                                  struct audio_usecase *uc_info)
 {
@@ -1793,7 +1784,6 @@
     int ret = 0;
     struct audio_usecase *uc_info;
     struct audio_device *adev = in->dev;
-    int snd_card_status = get_snd_card_state(adev);
 
     int usecase = platform_update_usecase_from_source(in->source,in->usecase);
     if (get_usecase_from_list(adev, usecase) == NULL)
@@ -1802,8 +1792,9 @@
           __func__, &in->stream, in->usecase, use_case_table[in->usecase]);
 
 
-    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
-        ALOGE("%s: sound card is not active/SSR returning error", __func__);
+    if (CARD_STATUS_OFFLINE == in->card_status||
+        CARD_STATUS_OFFLINE == adev->card_status) {
+        ALOGW("in->card_status or adev->card_status offline, try again");
         ret = -EIO;
         goto error_config;
     }
@@ -2111,7 +2102,7 @@
                 event = STREAM_CBK_EVENT_DRAIN_READY;
                 ALOGV("copl(%p):send drain callback, ret %d", out, ret);
             } else
-                ALOGE("%s: Block drain ready event during SSR", __func__);
+                ALOGI("%s: Block drain ready event during SSR", __func__);
             break;
         case OFFLOAD_CMD_DRAIN:
             ALOGD("copl(%p):calling compress_drain", out);
@@ -2120,6 +2111,11 @@
             send_callback = true;
             event = STREAM_CBK_EVENT_DRAIN_READY;
             break;
+        case OFFLOAD_CMD_ERROR:
+            ALOGD("copl(%p): sending error callback to AF", out);
+            send_callback = true;
+            event = STREAM_CBK_EVENT_ERROR;
+            break;
         default:
             ALOGE("%s unknown command received: %d", __func__, cmd->cmd);
             break;
@@ -2230,7 +2226,6 @@
     int ret = 0;
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
-    int snd_card_status = get_snd_card_state(adev);
     char mixer_ctl_name[128];
     struct mixer_ctl *ctl = NULL;
     char* perf_mode[] = {"ULL", "ULL_PP", "LL"};
@@ -2244,8 +2239,9 @@
           __func__, &out->stream, out->usecase, use_case_table[out->usecase],
           out->devices);
 
-    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
-        ALOGE("%s: sound card is not active/SSR returning error", __func__);
+    if (CARD_STATUS_OFFLINE == out->card_status ||
+        CARD_STATUS_OFFLINE == adev->card_status) {
+        ALOGW("out->card_status or adev->card_status offline, try again");
         ret = -EIO;
         goto error_config;
     }
@@ -2679,6 +2675,27 @@
     return 0;
 }
 
+static int out_on_error(struct audio_stream *stream)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+    bool do_standby = false;
+
+    lock_output_stream(out);
+    if (!out->standby) {
+        if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+            stop_compressed_output_l(out);
+            send_offload_cmd_l(out, OFFLOAD_CMD_ERROR);
+        } else
+            do_standby = true;
+    }
+    pthread_mutex_unlock(&out->lock);
+
+    if (do_standby)
+        return out_standby(&out->stream.common);
+
+    return 0;
+}
+
 static int out_dump(const struct audio_stream *stream __unused,
                     int fd __unused)
 {
@@ -2717,6 +2734,43 @@
     return out == adev->primary_output || out == adev->voice_tx_output;
 }
 
+// note: this call is safe only if the stream_cb is
+// removed first in close_output_stream (as is done now).
+static void out_snd_mon_cb(void * stream, struct str_parms * parms)
+{
+    if (!stream || !parms)
+        return;
+
+    struct stream_out *out = (struct stream_out *)stream;
+    struct audio_device *adev = out->dev;
+
+    card_status_t status;
+    int card;
+    if (parse_snd_card_status(parms, &card, &status) < 0)
+        return;
+
+    pthread_mutex_lock(&adev->lock);
+    bool valid_cb = (card == adev->snd_card);
+    pthread_mutex_unlock(&adev->lock);
+
+    if (!valid_cb)
+        return;
+
+    lock_output_stream(out);
+    if (out->card_status != status)
+        out->card_status = status;
+    pthread_mutex_unlock(&out->lock);
+
+    ALOGI("out_snd_mon_cb for card %d usecase %s, status %s", card,
+          use_case_table[out->usecase],
+          status == CARD_STATUS_OFFLINE ? "offline" : "online");
+
+    if (status == CARD_STATUS_OFFLINE)
+        out_on_error(stream);
+
+    return;
+}
+
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
     struct stream_out *out = (struct stream_out *)stream;
@@ -3102,12 +3156,11 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
     struct audio_device *adev = out->dev;
-    int snd_scard_state = get_snd_card_state(adev);
     ssize_t ret = 0;
 
     lock_output_stream(out);
 
-    if (SND_CARD_STATE_OFFLINE == snd_scard_state) {
+    if (CARD_STATUS_OFFLINE == out->card_status) {
 
         if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
             /*during SSR for compress usecase we should return error to flinger*/
@@ -3216,9 +3269,8 @@
             send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER);
         } else if (-ENETRESET == ret) {
             ALOGE("copl %s: received sound card offline state on compress write", __func__);
-            set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
+            out->card_status = CARD_STATUS_OFFLINE;
             pthread_mutex_unlock(&out->lock);
-            out_standby(&out->stream.common);
             return ret;
         }
         if ( ret == (ssize_t)bytes && !out->non_blocking)
@@ -3309,12 +3361,9 @@
     }
 
 exit:
-    /* ToDo: There may be a corner case when SSR happens back to back during
-       start/stop. Need to post different error to handle that. */
     if (-ENETRESET == ret) {
-        set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
+        out->card_status = CARD_STATUS_OFFLINE;
     }
-
     pthread_mutex_unlock(&out->lock);
 
     if (ret != 0) {
@@ -3326,7 +3375,7 @@
             pthread_mutex_unlock(&adev->lock);
             out->standby = true;
         }
-        out_standby(&out->stream.common);
+        out_on_error(&out->stream.common);
         if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))
             usleep((uint64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) /
                             out_get_sample_rate(&out->stream.common));
@@ -3338,7 +3387,6 @@
                                    uint32_t *dsp_frames)
 {
     struct stream_out *out = (struct stream_out *)stream;
-    struct audio_device *adev = out->dev;
 
     if (dsp_frames == NULL)
         return -EINVAL;
@@ -3365,24 +3413,25 @@
             ALOGVV("%s rendered frames %d sample_rate %d",
                     __func__, *dsp_frames, out->sample_rate);
         }
-        pthread_mutex_unlock(&out->lock);
         if (-ENETRESET == ret) {
             ALOGE(" ERROR: sound card not active Unable to get time stamp from compress driver");
-            set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
-            return -EINVAL;
+            out->card_status = CARD_STATUS_OFFLINE;
+            ret = -EINVAL;
         } else if(ret < 0) {
             ALOGE(" ERROR: Unable to get time stamp from compress driver");
-            return -EINVAL;
-        } else if (get_snd_card_state(adev) == SND_CARD_STATE_OFFLINE){
+            ret = -EINVAL;
+        } else if (out->card_status == CARD_STATUS_OFFLINE) {
             /*
              * Handle corner case where compress session is closed during SSR
              * and timestamp is queried
              */
             ALOGE(" ERROR: sound card not active, return error");
-            return -EINVAL;
+            ret = -EINVAL;
         } else {
-            return 0;
+            ret = 0;
         }
+        pthread_mutex_unlock(&out->lock);
+        return ret;
     } else if (audio_is_linear_pcm(out->format)) {
         *dsp_frames = out->written;
         return 0;
@@ -3440,7 +3489,7 @@
             ret = -errno;
         if (-ENETRESET == ret) {
             ALOGE(" ERROR: sound card not active Unable to get time stamp from compress driver");
-            set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
+            out->card_status = CARD_STATUS_OFFLINE;
             ret = -EINVAL;
         } else
             ret = 0;
@@ -3463,7 +3512,7 @@
                     ret = 0;
                 }
             }
-        } else if (adev->snd_card_status.state == SND_CARD_STATE_OFFLINE) {
+        } else if (out->card_status == CARD_STATUS_OFFLINE) {
             *frames = out->written;
             clock_gettime(CLOCK_MONOTONIC, timestamp);
             ret = 0;
@@ -3505,10 +3554,7 @@
         ALOGD("copl(%p):pause compress driver", out);
         lock_output_stream(out);
         if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PLAYING) {
-            struct audio_device *adev = out->dev;
-            int snd_scard_state = get_snd_card_state(adev);
-
-            if (SND_CARD_STATE_ONLINE == snd_scard_state)
+            if (out->card_status != CARD_STATUS_OFFLINE)
                 status = compress_pause(out->compr);
 
             out->offload_state = OFFLOAD_STATE_PAUSED;
@@ -3538,15 +3584,7 @@
         status = 0;
         lock_output_stream(out);
         if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PAUSED) {
-            struct audio_device *adev = out->dev;
-            int snd_scard_state = get_snd_card_state(adev);
-
-            if (SND_CARD_STATE_ONLINE == snd_scard_state) {
-                if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-                    pthread_mutex_lock(&out->dev->lock);
-                    ALOGV("offload resume, check and set hdmi backend again");
-                    pthread_mutex_unlock(&out->dev->lock);
-                }
+            if (out->card_status != CARD_STATUS_OFFLINE) {
                 status = compress_resume(out->compr);
             }
             if (!status) {
@@ -3692,6 +3730,43 @@
     return 0;
 }
 
+static void in_snd_mon_cb(void * stream, struct str_parms * parms)
+{
+    if (!stream || !parms)
+        return;
+
+    struct stream_in *in = (struct stream_in *)stream;
+    struct audio_device *adev = in->dev;
+
+    card_status_t status;
+    int card;
+    if (parse_snd_card_status(parms, &card, &status) < 0)
+        return;
+
+    pthread_mutex_lock(&adev->lock);
+    bool valid_cb = (card == adev->snd_card);
+    pthread_mutex_unlock(&adev->lock);
+
+    if (!valid_cb)
+        return;
+
+    lock_input_stream(in);
+    if (in->card_status != status)
+        in->card_status = status;
+    pthread_mutex_unlock(&in->lock);
+
+    ALOGW("in_snd_mon_cb for card %d usecase %s, status %s", card,
+          use_case_table[in->usecase],
+          status == CARD_STATUS_OFFLINE ? "offline" : "online");
+
+    // a better solution would be to report error back to AF and let
+    // it put the stream to standby
+    if (status == CARD_STATUS_OFFLINE)
+        in_standby(&in->stream.common);
+
+    return;
+}
+
 static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
     struct stream_in *in = (struct stream_in *)stream;
@@ -3808,7 +3883,6 @@
     struct stream_in *in = (struct stream_in *)stream;
     struct audio_device *adev = in->dev;
     int ret = -1;
-    int snd_scard_state = get_snd_card_state(adev);
     size_t bytes_read = 0;
 
     lock_input_stream(in);
@@ -3821,12 +3895,6 @@
         return bytes;
     }
 
-    if (SND_CARD_STATE_OFFLINE == snd_scard_state) {
-        ALOGD(" %s: sound card is not active/SSR state", __func__);
-        ret= -EIO;;
-        goto exit;
-    }
-
     if (in->standby) {
         pthread_mutex_lock(&adev->lock);
         if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
@@ -3887,11 +3955,8 @@
         memset(buffer, 0, bytes);
 
 exit:
-    /* ToDo: There may be a corner case when SSR happens back to back during
-       start/stop. Need to post different error to handle that. */
     if (-ENETRESET == ret)
-        set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
-
+        in->card_status = CARD_STATUS_OFFLINE;
     pthread_mutex_unlock(&in->lock);
 
     if (ret != 0) {
@@ -3981,12 +4046,6 @@
 
     *stream_out = NULL;
 
-    if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
-             (SND_CARD_STATE_OFFLINE == get_snd_card_state(adev))) {
-        ALOGE("sound card is not active rejecting compress output open request");
-        return -EINVAL;
-    }
-
     out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
 
     ALOGD("%s: enter: format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)\
@@ -4046,6 +4105,16 @@
         }
     } else if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) ||
                (out->flags == AUDIO_OUTPUT_FLAG_DIRECT)) {
+        pthread_mutex_lock(&adev->lock);
+        bool offline = (adev->card_status == CARD_STATUS_OFFLINE);
+        pthread_mutex_unlock(&adev->lock);
+
+        // reject offload during card offline to allow
+        // fallback to s/w paths
+        if (offline) {
+            ret = -ENODEV;
+            goto error_open;
+        }
 
         if (config->offload_info.version != AUDIO_INFO_INITIALIZER.version ||
             config->offload_info.size != AUDIO_INFO_INITIALIZER.size) {
@@ -4424,6 +4493,18 @@
     config->channel_mask = out->stream.common.get_channels(&out->stream.common);
     config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
 
+    /*
+       By locking output stream before registering, we allow the callback
+       to update stream's state only after stream's initial state is set to
+       adev state.
+    */
+    lock_output_stream(out);
+    audio_extn_snd_mon_register_listener(out, out_snd_mon_cb);
+    pthread_mutex_lock(&adev->lock);
+    out->card_status = adev->card_status;
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&out->lock);
+
     *stream_out = &out->stream;
     ALOGD("%s: Stream (%p) picks up usecase (%s)", __func__, &out->stream,
            use_case_table[out->usecase]);
@@ -4474,6 +4555,10 @@
 
     ALOGD("%s: enter:stream_handle(%p)",__func__, out);
 
+    // must deregister from sndmonitor first to prevent races
+    // between the callback and close_stream
+    audio_extn_snd_mon_unregister_listener(out);
+
     /* close adsp hdrl session before standby */
     if (out->adsp_hdlr_stream_handle) {
         ret = audio_extn_adsp_hdlr_stream_close(out->adsp_hdlr_stream_handle);
@@ -4522,29 +4607,6 @@
     ALOGV("%s: exit", __func__);
 }
 
-static void close_compress_sessions(struct audio_device *adev)
-{
-    struct stream_out *out;
-    struct listnode *node, *tempnode;
-    struct audio_usecase *usecase;
-    pthread_mutex_lock(&adev->lock);
-
-    list_for_each_safe(node, tempnode, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        if (is_offload_usecase(usecase->id)) {
-            if (usecase->stream.out) {
-                ALOGI(" %s closing compress session %d on OFFLINE state", __func__, usecase->id);
-                out = usecase->stream.out;
-                pthread_mutex_unlock(&adev->lock);
-                out_standby(&out->stream.common);
-                pthread_mutex_lock(&adev->lock);
-                tempnode = list_head(&adev->usecase_list);
-            }
-       }
-    }
-    pthread_mutex_unlock(&adev->lock);
-}
-
 static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
 {
     struct audio_device *adev = (struct audio_device *)dev;
@@ -4559,21 +4621,6 @@
 
     if (!parms)
         goto error;
-    ret = str_parms_get_str(parms, "SND_CARD_STATUS", value, sizeof(value));
-    if (ret >= 0) {
-        char *snd_card_status = value+2;
-        if (strstr(snd_card_status, "OFFLINE")) {
-            ALOGD("Received sound card OFFLINE status");
-            set_snd_card_state(adev,SND_CARD_STATE_OFFLINE);
-            //close compress sessions on OFFLINE status
-            close_compress_sessions(adev);
-        } else if (strstr(snd_card_status, "ONLINE")) {
-            ALOGD("Received sound card ONLINE status");
-            set_snd_card_state(adev,SND_CARD_STATE_ONLINE);
-            //send dts hpx license if enabled
-            audio_extn_dts_eagle_send_lic();
-        }
-    }
 
     pthread_mutex_lock(&adev->lock);
     status = voice_set_parameters(adev, parms);
@@ -4765,17 +4812,6 @@
         return NULL;
     }
 
-    ret = str_parms_get_str(query, "SND_CARD_STATUS", value,
-                            sizeof(value));
-    if (ret >=0) {
-        int val = 1;
-        pthread_mutex_lock(&adev->snd_card_status.lock);
-        if (SND_CARD_STATE_OFFLINE == adev->snd_card_status.state)
-            val = 0;
-        pthread_mutex_unlock(&adev->snd_card_status.lock);
-        str_parms_add_int(reply, "SND_CARD_STATUS", val);
-        goto exit;
-    }
     //handle vr audio getparam
 
     ret = str_parms_get_str(query,
@@ -5102,6 +5138,13 @@
        get sound trigger pcm if present */
     audio_extn_sound_trigger_check_and_get_session(in);
 
+    lock_input_stream(in);
+    audio_extn_snd_mon_register_listener(in, in_snd_mon_cb);
+    pthread_mutex_lock(&adev->lock);
+    in->card_status = adev->card_status;
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&in->lock);
+
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
     return ret;
@@ -5121,6 +5164,10 @@
 
     ALOGD("%s: enter:stream_handle(%p)",__func__, in);
 
+    // must deregister from sndmonitor first to prevent races
+    // between the callback and close_stream
+    audio_extn_snd_mon_unregister_listener(stream);
+
     /* Disable echo reference while closing input stream */
     platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE);
 
@@ -5169,6 +5216,7 @@
     pthread_mutex_lock(&adev_init_lock);
 
     if ((--audio_device_ref_count) == 0) {
+        audio_extn_snd_mon_unregister_listener(adev);
         audio_extn_sound_trigger_deinit(adev);
         audio_extn_listen_deinit(adev);
         audio_extn_utils_release_streams_cfg_lists(
@@ -5184,6 +5232,7 @@
             adev->adm_deinit(adev->adm_data);
         qahwi_deinit(device);
         audio_extn_adsp_hdlr_deinit();
+        audio_extn_snd_mon_deinit();
         free(device);
         adev = NULL;
     }
@@ -5210,6 +5259,40 @@
     }
 }
 
+static void adev_snd_mon_cb(void *cookie, struct str_parms *parms)
+{
+    bool is_snd_card_status = false;
+    bool is_ext_device_status = false;
+    char value[32];
+    int card = -1;
+    card_status_t status;
+
+    if (cookie != adev || !parms)
+        return;
+
+    if (!parse_snd_card_status(parms, &card, &status)) {
+        is_snd_card_status = true;
+    } else if (0 < str_parms_get_str(parms, "ext_audio_device", value, sizeof(value))) {
+        is_ext_device_status = true;
+    } else {
+        // not a valid event
+        return;
+    }
+
+    pthread_mutex_lock(&adev->lock);
+    if (card == adev->snd_card || is_ext_device_status) {
+        if (is_snd_card_status && adev->card_status != status) {
+            adev->card_status = status;
+            platform_snd_card_update(adev->platform, status);
+            audio_extn_fm_set_parameters(adev, parms);
+        } else if (is_ext_device_status) {
+            platform_set_parameters(adev->platform, parms);
+        }
+    }
+    pthread_mutex_unlock(&adev->lock);
+    return;
+}
+
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
@@ -5283,8 +5366,6 @@
     adev->perf_lock_opts[1] = 0x20E;
     adev->perf_lock_opts_size = 2;
 
-    pthread_mutex_init(&adev->snd_card_status.lock, (const pthread_mutexattr_t *) NULL);
-    adev->snd_card_status.state = SND_CARD_STATE_OFFLINE;
     /* Loads platform specific libraries dynamically */
     adev->platform = platform_init(adev);
     if (!adev->platform) {
@@ -5294,7 +5375,6 @@
         *device = NULL;
         pthread_mutex_unlock(&adev_init_lock);
         pthread_mutex_destroy(&adev->lock);
-        pthread_mutex_destroy(&adev->snd_card_status.lock);
         return -EINVAL;
     }
 
@@ -5313,8 +5393,6 @@
         adev->device.close_output_stream = audio_extn_qaf_close_output_stream;
     }
 
-    adev->snd_card_status.state = SND_CARD_STATE_ONLINE;
-
     if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
         adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
         if (adev->visualizer_lib == NULL) {
@@ -5331,7 +5409,6 @@
     }
     audio_extn_init(adev);
     audio_extn_listen_init(adev, adev->snd_card);
-    audio_extn_sound_trigger_init(adev);
     audio_extn_gef_init(adev);
 
     if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
@@ -5443,6 +5520,14 @@
     qahwi_init(*device);
     audio_extn_perf_lock_init();
     audio_extn_adsp_hdlr_init(adev->mixer);
+
+    audio_extn_snd_mon_init();
+    pthread_mutex_lock(&adev->lock);
+    audio_extn_snd_mon_register_listener(adev, adev_snd_mon_cb);
+    adev->card_status = CARD_STATUS_ONLINE;
+    pthread_mutex_unlock(&adev->lock);
+    audio_extn_sound_trigger_init(adev); /* dependent on snd_mon_init() */
+
     ALOGV("%s: exit", __func__);
     return 0;
 }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 5044551..00013bc 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -81,9 +81,6 @@
 #define DEFAULT_HDMI_OUT_SAMPLE_RATE 48000
 #define DEFAULT_HDMI_OUT_FORMAT AUDIO_FORMAT_PCM_16_BIT
 
-#define SND_CARD_STATE_OFFLINE 0
-#define SND_CARD_STATE_ONLINE 1
-
 #define MAX_PERF_LOCK_OPTS 20
 
 #define MAX_STREAM_PROFILE_STR_LEN 32
@@ -260,6 +257,7 @@
     bool realtime;
     int af_period_multiplier;
     struct audio_device *dev;
+    card_status_t card_status;
 
     void* qaf_stream_handle;
     pthread_cond_t qaf_offload_cond;
@@ -303,6 +301,7 @@
     qahwi_stream_in_t qahwi_in;
 
     struct audio_device *dev;
+    card_status_t card_status;
 };
 
 typedef enum {
@@ -330,11 +329,6 @@
     union stream_ptr stream;
 };
 
-struct sound_card_status {
-    pthread_mutex_t lock;
-    int state;
-};
-
 struct stream_format {
     struct listnode list;
     audio_format_t format;
@@ -402,6 +396,7 @@
     bool allow_afe_proxy_usage;
 
     int snd_card;
+    card_status_t card_status;
     unsigned int cur_codec_backend_samplerate;
     unsigned int cur_codec_backend_bit_width;
     bool is_channel_status_set;
@@ -414,7 +409,6 @@
     int (*offload_effects_start_output)(audio_io_handle_t, int, struct mixer *);
     int (*offload_effects_stop_output)(audio_io_handle_t, int);
 
-    struct sound_card_status snd_card_status;
     int (*offload_effects_set_hpx_state)(bool);
 
     void *adm_data;
@@ -470,7 +464,6 @@
 
 int pcm_ioctl(struct pcm *pcm, int request, ...);
 
-int get_snd_card_state(struct audio_device *adev);
 audio_usecase_t get_usecase_id_from_usecase_type(const struct audio_device *adev,
                                                  usecase_type_t type);
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c20cd3d..eba8e97 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2579,11 +2579,11 @@
     return my_data->is_acdb_initialized;
 }
 
-void platform_snd_card_update(void *platform, int snd_scard_state)
+void platform_snd_card_update(void *platform, card_status_t card_status)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
 
-    if (snd_scard_state == SND_CARD_STATE_ONLINE) {
+    if (card_status == CARD_STATUS_ONLINE) {
         if (!platform_is_acdb_initialized(my_data)) {
             if(platform_acdb_init(my_data))
                 ALOGE("%s: acdb initialization is failed", __func__);
@@ -4915,7 +4915,7 @@
         }
 
         if ((prop_playback_enabled && (voice_is_in_call(my_data->adev))) ||
-             (SND_CARD_STATE_OFFLINE == get_snd_card_state(my_data->adev))) {
+             (CARD_STATUS_OFFLINE == my_data->adev->card_status)) {
             char *decoder_mime_type = value;
 
             //check if unsupported mime type or not
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 840c75a..6f4bc0a 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2273,11 +2273,11 @@
     return my_data->is_acdb_initialized;
 }
 
-void platform_snd_card_update(void *platform, int snd_scard_state)
+void platform_snd_card_update(void *platform, card_status_t card_status)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
 
-    if (snd_scard_state == SND_CARD_STATE_ONLINE) {
+    if (card_status == CARD_STATUS_ONLINE) {
         if (!platform_is_acdb_initialized(my_data)) {
             if(platform_acdb_init(my_data))
                 ALOGE("%s: acdb initialization is failed", __func__);
@@ -4699,7 +4699,7 @@
         }
 
         if ((prop_playback_enabled && (voice_is_in_call(my_data->adev))) ||
-             (SND_CARD_STATE_OFFLINE == get_snd_card_state(my_data->adev))) {
+             (CARD_STATUS_OFFLINE == my_data->adev->card_status)) {
             char *decoder_mime_type = value;
 
             //check if unsupported mime type or not
diff --git a/hal/platform_api.h b/hal/platform_api.h
index a5ba7bf..b31ed94 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -149,7 +149,7 @@
 /* From platform_info.c */
 int platform_info_init(const char *filename, void *, caller_t);
 
-void platform_snd_card_update(void *platform, int snd_scard_state);
+void platform_snd_card_update(void *platform, card_status_t scard_status);
 
 struct audio_offload_info_t;
 uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info);
diff --git a/hal/voice_extn/compress_voip.c b/hal/voice_extn/compress_voip.c
index 5fff2d5..0ab3fc0 100644
--- a/hal/voice_extn/compress_voip.c
+++ b/hal/voice_extn/compress_voip.c
@@ -531,11 +531,11 @@
     int ret = 0;
     struct audio_device *adev = out->dev;
     struct audio_usecase *uc_info;
-    int snd_card_status = get_snd_card_state(adev);
 
     ALOGD("%s: enter", __func__);
 
-    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
+    if (CARD_STATUS_OFFLINE == out->card_status ||
+        CARD_STATUS_OFFLINE == adev->card_status) {
         ret = -ENETRESET;
         ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret);
         goto error;
@@ -565,11 +565,11 @@
 {
     int ret = 0;
     struct audio_device *adev = in->dev;
-    int snd_card_status = get_snd_card_state(adev);
 
     ALOGD("%s: enter", __func__);
 
-    if (SND_CARD_STATE_OFFLINE == snd_card_status) {
+    if (CARD_STATUS_OFFLINE == in->card_status ||
+        CARD_STATUS_OFFLINE == adev->card_status) {
         ret = -ENETRESET;
         ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret);
         goto error;