Merge "configs: add quad mic profile for Lahaina target"
diff --git a/configs/lito/lito.mk b/configs/lito/lito.mk
index 3bc4e47..07615fb 100644
--- a/configs/lito/lito.mk
+++ b/configs/lito/lito.mk
@@ -215,7 +215,7 @@
     $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/lito/audio_policy_configuration_odm.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 else
 PRODUCT_COPY_FILES += \
-    $(TOPDIR)frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
+    $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/common/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 endif
 
 PRODUCT_COPY_FILES += \
diff --git a/configs/msmnile/msmnile.mk b/configs/msmnile/msmnile.mk
index 5a6796b..fa9c210 100644
--- a/configs/msmnile/msmnile.mk
+++ b/configs/msmnile/msmnile.mk
@@ -193,7 +193,7 @@
     $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/msmnile/audio_policy_configuration_odm.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 else
 PRODUCT_COPY_FILES += \
-    $(TOPDIR)frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
+    $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/common/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 endif
 
 PRODUCT_COPY_FILES += \
diff --git a/configs/msmsteppe/msmsteppe.mk b/configs/msmsteppe/msmsteppe.mk
index 512f378..5b53343 100644
--- a/configs/msmsteppe/msmsteppe.mk
+++ b/configs/msmsteppe/msmsteppe.mk
@@ -193,7 +193,7 @@
     $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/msmsteppe/audio_policy_configuration_odm.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 else
 PRODUCT_COPY_FILES += \
-    $(TOPDIR)frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
+    $(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/common/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml
 endif
 
 PRODUCT_COPY_FILES += \
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
index 00e581f..8422846 100644
--- a/hal/audio_extn/a2dp.c
+++ b/hal/audio_extn/a2dp.c
@@ -2875,8 +2875,10 @@
                     goto param_handled;
                 list_for_each(node, &a2dp.adev->usecase_list) {
                     uc_info = node_to_item(node, struct audio_usecase, list);
-                    if (uc_info->stream.out && uc_info->type == PCM_PLAYBACK &&
-                        is_a2dp_out_device_type(&uc_info->stream.out->device_list)) {
+                    if (uc_info->type == PCM_PLAYBACK &&
+                        (uc_info->out_snd_device == SND_DEVICE_OUT_BT_A2DP ||
+                         uc_info->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP ||
+                         uc_info->out_snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)) {
                         pthread_mutex_unlock(&a2dp.adev->lock);
                         fp_check_a2dp_restore(a2dp.adev, uc_info->stream.out, false);
                         pthread_mutex_lock(&a2dp.adev->lock);
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 786b7181..24dd068 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -5485,6 +5485,11 @@
                                   struct str_parms *);
 static maxxaudio_set_parameters_t maxxaudio_set_parameters;
 
+typedef void (*maxxaudio_get_parameters_t)(struct audio_device *,
+                                  struct str_parms *,
+                                  struct str_parms *);
+static maxxaudio_get_parameters_t maxxaudio_get_parameters;
+
 typedef bool (*maxxaudio_supported_usb_t)();
 static maxxaudio_supported_usb_t maxxaudio_supported_usb;
 
@@ -5512,6 +5517,8 @@
                  (maxxaudio_set_device_t)dlsym(maxxaudio_lib_handle, "ma_set_device")) ||
             !(maxxaudio_set_parameters =
                  (maxxaudio_set_parameters_t)dlsym(maxxaudio_lib_handle, "ma_set_parameters")) ||
+            !(maxxaudio_get_parameters =
+                 (maxxaudio_get_parameters_t)dlsym(maxxaudio_lib_handle, "ma_get_parameters")) ||
             !(maxxaudio_supported_usb =
                  (maxxaudio_supported_usb_t)dlsym(
                                     maxxaudio_lib_handle, "ma_supported_usb"))) {
@@ -5533,6 +5540,7 @@
     maxxaudio_set_state = NULL;
     maxxaudio_set_device = NULL;
     maxxaudio_set_parameters = NULL;
+    maxxaudio_get_parameters = NULL;
     maxxaudio_supported_usb = NULL;
     ALOGW(":: %s: ---- Feature MAXX_AUDIO is disabled ----", __func__);
     return -ENOSYS;
@@ -5580,6 +5588,14 @@
         maxxaudio_set_parameters(adev, parms);
 }
 
+void audio_extn_ma_get_parameters(struct audio_device *adev,
+                                  struct str_parms *query,
+                                  struct str_parms *reply)
+{
+    if (maxxaudio_get_parameters)
+        maxxaudio_get_parameters(adev, query, reply);
+}
+
 bool audio_extn_ma_supported_usb()
 {
     return (maxxaudio_supported_usb ? maxxaudio_supported_usb(): false);
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c71fbc3..c01954d 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -361,6 +361,9 @@
 void audio_extn_ma_set_device(struct audio_usecase *usecase);
 void audio_extn_ma_set_parameters(struct audio_device *adev,
                                   struct str_parms *parms);
+void audio_extn_ma_get_parameters(struct audio_device *adev,
+                                  struct str_parms *query,
+                                  struct str_parms *reply);
 bool audio_extn_ma_supported_usb();
 bool audio_extn_is_maxx_audio_enabled();
 // --- Function pointers from audio_extn needed by MAXX_AUDIO
diff --git a/hal/audio_extn/maxxaudio.c b/hal/audio_extn/maxxaudio.c
index 88de2f2..f1393f6 100644
--- a/hal/audio_extn/maxxaudio.c
+++ b/hal/audio_extn/maxxaudio.c
@@ -50,10 +50,14 @@
 #define MA_QDSP_SET_VOL         "maxxaudio_qdsp_set_volume"
 #define MA_QDSP_SET_VOLT        "maxxaudio_qdsp_set_volume_table"
 #define MA_QDSP_SET_PARAM       "maxxaudio_qdsp_set_parameter"
+#define MA_QDSP_SET_COMMAND     "maxxaudio_qdsp_set_command"
+#define MA_QDSP_GET_COMMAND     "maxxaudio_qdsp_get_command"
 
 #define SUPPORT_DEV "18d1:5033" // Blackbird usbid
 #define SUPPORTED_USB 0x01
 
+#define WAVES_COMMAND_SIZE 10240
+
 typedef unsigned int effective_scope_flag_t;
 const effective_scope_flag_t EFFECTIVE_SCOPE_RTC = 1 << 0;   /* RTC  */
 const effective_scope_flag_t EFFECTIVE_SCOPE_ACDB = 1 << 1;  /* ACDB */
@@ -145,6 +149,16 @@
                                const struct ma_audio_cal_settings *,
                                unsigned int, double);
 
+typedef bool (*ma_set_cmd_t)(ma_audio_cal_handle_t handle,
+                             const struct ma_audio_cal_settings *,
+                             const char*);
+
+typedef bool (*ma_get_cmd_t)(ma_audio_cal_handle_t handle,
+                             const struct ma_audio_cal_settings *,
+                             const char *,
+                             char *,
+                             uint32_t);
+
 struct ma_platform_data {
     void *waves_handle;
     void *platform;
@@ -158,6 +172,8 @@
     ma_set_volume_t          ma_set_volume;
     ma_set_volume_table_t    ma_set_volume_table;
     ma_set_param_t           ma_set_param;
+    ma_set_cmd_t             ma_set_cmd;
+    ma_get_cmd_t             ma_get_cmd;
     bool speaker_lr_swap;
     bool orientation_used;
     int dispaly_orientation;
@@ -167,6 +183,9 @@
 static uint16_t g_supported_dev = 0;
 static struct ma_state ma_cur_state_table[STREAM_MAX_TYPES];
 static struct ma_platform_data *my_data = NULL;
+static char ma_command_data[WAVES_COMMAND_SIZE];
+static char ma_reply_data[WAVES_COMMAND_SIZE];
+
 // --- external function dependency ---
 fp_platform_set_parameters_t fp_platform_set_parameters;
 fp_audio_extn_get_snd_card_split_t fp_audio_extn_get_snd_card_split;
@@ -276,7 +295,6 @@
 
 static bool check_and_send_all_audio_cal(struct audio_device *adev, ma_cmd_t cmd)
 {
-    int i = 0;
     bool ret = false;
     struct listnode *node;
     struct audio_usecase *usecase;
@@ -476,71 +494,78 @@
 
     my_data->waves_handle = dlopen(lib_path, RTLD_NOW);
     if (my_data->waves_handle == NULL) {
-         ALOGE("%s: DLOPEN failed for %s, %s", __func__, LIB_MA_PARAM, dlerror());
-         goto error;
+        ALOGE("%s: DLOPEN failed for %s, %s", __func__, LIB_MA_PARAM, dlerror());
+        goto error;
     } else {
-         ALOGV("%s: DLOPEN successful for %s", __func__, LIB_MA_PARAM);
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_MA_PARAM);
 
-         my_data->ma_param_init = (ma_param_init_t)dlsym(my_data->waves_handle,
-                                   MA_QDSP_PARAM_INIT);
-         if (!my_data->ma_param_init) {
-             ALOGE("%s: dlsym error %s for ma_param_init", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_param_init = (ma_param_init_t)dlsym(my_data->waves_handle, MA_QDSP_PARAM_INIT);
+        if (!my_data->ma_param_init) {
+            ALOGE("%s: dlsym error %s for ma_param_init", __func__, dlerror());
+            goto error;
+        }
 
-         my_data->ma_param_deinit = (ma_param_deinit_t)dlsym(
-                                     my_data->waves_handle, MA_QDSP_PARAM_DEINIT);
-         if (!my_data->ma_param_deinit) {
-             ALOGE("%s: dlsym error %s for ma_param_deinit", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_param_deinit = (ma_param_deinit_t)dlsym(my_data->waves_handle,
+                                                            MA_QDSP_PARAM_DEINIT);
+        if (!my_data->ma_param_deinit) {
+            ALOGE("%s: dlsym error %s for ma_param_deinit", __func__, dlerror());
+            goto error;
+        }
 
         my_data->ma_is_feature_used = (ma_is_feature_used_t)dlsym(my_data->waves_handle,
-                                    MA_QDSP_IS_FEATURE_USED);
+                                                                  MA_QDSP_IS_FEATURE_USED);
         if (!my_data->ma_is_feature_used) {
             ALOGV("%s: dlsym error %s for ma_is_feature_used", __func__, dlerror());
         }
 
         my_data->ma_set_orientation = (ma_set_orientation_t)dlsym(my_data->waves_handle,
-                                        MA_QDSP_SET_ORIENTATION);
+                                                                  MA_QDSP_SET_ORIENTATION);
         if (!my_data->ma_set_orientation) {
             ALOGV("%s: dlsym error %s for ma_set_orientation", __func__, dlerror());
         }
 
-         my_data->ma_set_lr_swap = (ma_set_lr_swap_t)dlsym(my_data->waves_handle,
-                                    MA_QDSP_SET_LR_SWAP);
-         if (!my_data->ma_set_lr_swap) {
-             ALOGE("%s: dlsym error %s for ma_set_lr_swap", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_set_lr_swap = (ma_set_lr_swap_t)dlsym(my_data->waves_handle,
+                                                          MA_QDSP_SET_LR_SWAP);
+        if (!my_data->ma_set_lr_swap) {
+            ALOGE("%s: dlsym error %s for ma_set_lr_swap", __func__, dlerror());
+            goto error;
+        }
 
-         my_data->ma_set_sound_mode = (ma_set_sound_mode_t)dlsym(
-                                       my_data->waves_handle, MA_QDSP_SET_MODE);
-         if (!my_data->ma_set_sound_mode) {
-             ALOGE("%s: dlsym error %s for ma_set_sound_mode", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_set_sound_mode = (ma_set_sound_mode_t)dlsym(my_data->waves_handle,
+                                                                MA_QDSP_SET_MODE);
+        if (!my_data->ma_set_sound_mode) {
+            ALOGE("%s: dlsym error %s for ma_set_sound_mode", __func__, dlerror());
+            goto error;
+        }
 
-         my_data->ma_set_volume = (ma_set_volume_t)dlsym(my_data->waves_handle,
-                                   MA_QDSP_SET_VOL);
-         if (!my_data->ma_set_volume) {
-             ALOGE("%s: dlsym error %s for ma_set_volume", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_set_volume = (ma_set_volume_t)dlsym(my_data->waves_handle, MA_QDSP_SET_VOL);
+        if (!my_data->ma_set_volume) {
+            ALOGE("%s: dlsym error %s for ma_set_volume", __func__, dlerror());
+            goto error;
+        }
 
-         my_data->ma_set_volume_table = (ma_set_volume_table_t)dlsym(
-                                         my_data->waves_handle, MA_QDSP_SET_VOLT);
-         if (!my_data->ma_set_volume_table) {
-             ALOGE("%s: dlsym error %s for ma_set_volume_table", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_set_volume_table = (ma_set_volume_table_t)dlsym(my_data->waves_handle,
+                                                                    MA_QDSP_SET_VOLT);
+        if (!my_data->ma_set_volume_table) {
+            ALOGE("%s: dlsym error %s for ma_set_volume_table", __func__, dlerror());
+            goto error;
+        }
 
-         my_data->ma_set_param = (ma_set_param_t)dlsym(
-                                  my_data->waves_handle, MA_QDSP_SET_PARAM);
-         if (!my_data->ma_set_param) {
-             ALOGE("%s: dlsym error %s for ma_set_param", __func__, dlerror());
-             goto error;
-         }
+        my_data->ma_set_param = (ma_set_param_t)dlsym(my_data->waves_handle, MA_QDSP_SET_PARAM);
+        if (!my_data->ma_set_param) {
+            ALOGE("%s: dlsym error %s for ma_set_param", __func__, dlerror());
+            goto error;
+        }
+
+        my_data->ma_set_cmd = (ma_set_cmd_t)dlsym(my_data->waves_handle, MA_QDSP_SET_COMMAND);
+        if (!my_data->ma_set_cmd) {
+            ALOGE("%s: dlsym error %s for ma_set_cmd", __func__, dlerror());
+        }
+
+        my_data->ma_get_cmd = (ma_get_cmd_t)dlsym(my_data->waves_handle, MA_QDSP_GET_COMMAND);
+        if (!my_data->ma_get_cmd) {
+            ALOGE("%s: dlsym error %s for ma_get_cmd", __func__, dlerror());
+        }
     }
 
     /* get preset table */
@@ -679,7 +704,6 @@
 
 void ma_set_device(struct audio_usecase *usecase)
 {
-    int i = 0;
     struct ma_audio_cal_settings ma_cal;
 
     if (!my_data) {
@@ -731,8 +755,101 @@
     pthread_mutex_unlock(&my_data->lock);
 }
 
-void ma_set_parameters(struct audio_device *adev,
-                                  struct str_parms *parms)
+static bool ma_set_command(struct ma_audio_cal_settings *audio_cal_settings, char *cmd_data)
+{
+    if (my_data->ma_set_cmd)
+        return my_data->ma_set_cmd(g_ma_audio_cal_handle, audio_cal_settings, cmd_data);
+    return false;
+}
+
+static bool ma_get_command(struct ma_audio_cal_settings *audio_cal_settings, char *cmd_data,
+                           char *reply_data, uint32_t reply_size)
+{
+    if (my_data->ma_get_cmd)
+        return my_data->ma_get_cmd(g_ma_audio_cal_handle, audio_cal_settings, cmd_data, reply_data,
+                                   reply_size);
+    return false;
+}
+
+static bool ma_fill_apptype_and_device_from_params(struct str_parms *parms, uint32_t *app_type,
+                                                   struct listnode *devices)
+{
+    int ret;
+    char value[128];
+
+    ret = str_parms_get_str(parms, "cal_apptype", value, sizeof(value));
+
+    if (ret >= 0) {
+        *app_type = (uint32_t)atoi(value);
+        ret = str_parms_get_str(parms, "cal_devid", value, sizeof(value));
+        if (ret >= 0) {
+            update_device_list(devices, (uint32_t)atoi(value), "", true);
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool ma_add_apptype_and_device_to_params(struct str_parms *parms, uint32_t app_type,
+                                                struct listnode *devices)
+{
+    if (0 <= str_parms_add_int(parms, "cal_apptype", app_type)) {
+        if (0 <= str_parms_add_int(parms, "cal_devid", get_device_types(devices))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool ma_get_command_parameters(struct str_parms *query, struct str_parms *reply)
+{
+    struct ma_audio_cal_settings ma_cal;
+    int ret;
+
+    ret = str_parms_get_str(query, "waves_data", ma_command_data, sizeof(ma_command_data));
+    if (ret >= 0) {
+        ma_cal_init(&ma_cal);
+        if (ma_fill_apptype_and_device_from_params(query, &ma_cal.common.app_type,
+                &ma_cal.common.devices)) {
+            ma_add_apptype_and_device_to_params(reply, ma_cal.common.app_type,
+                                                &ma_cal.common.devices);
+            ALOGV("%s: before - command=%s", __func__, (char *)ma_command_data);
+            if (ma_get_command(&ma_cal, ma_command_data, ma_reply_data, sizeof(ma_reply_data))) {
+                str_parms_add_str(reply, "waves_data", ma_reply_data);
+                ALOGV("%s: after - command=%s", __func__, (char *)ma_reply_data);
+                return true;
+            } else {
+                str_parms_add_str(reply, "waves_data", "");
+            }
+        }
+    }
+    return false;
+}
+
+static bool ma_set_command_parameters(struct str_parms *parms)
+{
+    struct ma_audio_cal_settings ma_cal;
+    int ret;
+
+    ret = str_parms_get_str(parms, "waves_data", ma_command_data, sizeof(ma_command_data));
+    if (ret >= 0) {
+        ma_cal_init(&ma_cal);
+        if (ma_fill_apptype_and_device_from_params(parms, &ma_cal.common.app_type,
+                &ma_cal.common.devices)) {
+            return ma_set_command(&ma_cal, ma_command_data);
+        }
+    }
+    return false;
+}
+
+void ma_get_parameters(struct audio_device *adev, struct str_parms *query,
+                       struct str_parms *reply)
+{
+    (void)adev;
+    ma_get_command_parameters(query, reply);
+}
+
+void ma_set_parameters(struct audio_device *adev, struct str_parms *parms)
 {
     int ret;
     int val;
@@ -791,6 +908,8 @@
             }
         }
     }
+
+    ma_set_command_parameters(parms);
 }
 
 bool ma_supported_usb()
diff --git a/hal/audio_extn/sndmonitor.c b/hal/audio_extn/sndmonitor.c
index 4f27db9..ff234fc 100644
--- a/hal/audio_extn/sndmonitor.c
+++ b/hal/audio_extn/sndmonitor.c
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+* Copyright (c) 2016-2020, 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
@@ -224,6 +224,7 @@
             (strncasecmp(card_id, "apq", 3) != 0) &&
             (strncasecmp(card_id, "sa", 2) != 0) &&
             (strncasecmp(card_id, "kona", 4) != 0) &&
+            (strncasecmp(card_id, "lahaina", 7) != 0) &&
             (strncasecmp(card_id, "atoll", 5) != 0) &&
             (strncasecmp(card_id, "bengal", 6) != 0) &&
             (strncasecmp(card_id, "lito", 4) != 0)) {
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index fc75fd1..13910f6 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -65,6 +65,7 @@
 #include <hardware/audio_alsaops.h>
 #include <system/thread_defs.h>
 #include <tinyalsa/asoundlib.h>
+#include <utils/Timers.h> // systemTime
 #include <audio_effects/effect_aec.h>
 #include <audio_effects/effect_ns.h>
 #include <audio_utils/format.h>
@@ -1957,9 +1958,10 @@
     reset_hdmi_sink_caps(out);
 
     /* Cache ext disp type */
-    if (platform_get_ext_disp_type_v2(adev->platform,
+    ret = platform_get_ext_disp_type_v2(adev->platform,
                                       out->extconn.cs.controller,
-                                      out->extconn.cs.stream <= 0)) {
+                                      out->extconn.cs.stream);
+    if(ret < 0) {
         ALOGE("%s: Failed to query disp type, ret:%d", __func__, ret);
         return -EINVAL;
     }
@@ -2642,7 +2644,11 @@
                                  is_single_device_type_equal(&vc_usecase->device_list,
                                                         AUDIO_DEVICE_OUT_HEARING_AID) ||
                                  is_single_device_type_equal(&usecase->device_list,
-                                                     AUDIO_DEVICE_IN_VOICE_CALL))) {
+                                                     AUDIO_DEVICE_IN_VOICE_CALL) ||
+                                 (is_single_device_type_equal(&usecase->device_list,
+                                                     AUDIO_DEVICE_IN_USB_HEADSET) &&
+                                 is_single_device_type_equal(&vc_usecase->device_list,
+                                                        AUDIO_DEVICE_OUT_USB_HEADSET)))) {
                 in_snd_device = vc_usecase->in_snd_device;
                 out_snd_device = vc_usecase->out_snd_device;
             }
@@ -4549,6 +4555,17 @@
     dprintf(fd, "      Standby: %s\n", out->standby ? "yes" : "no");
     dprintf(fd, "      Frames written: %lld\n", (long long)out->written);
 
+    char buffer[256]; // for statistics formatting
+    if (!is_offload_usecase(out->usecase)) {
+        simple_stats_to_string(&out->fifo_underruns, buffer, sizeof(buffer));
+        dprintf(fd, "      Fifo frame underruns: %s\n", buffer);
+    }
+
+    if (out->start_latency_ms.n > 0) {
+        simple_stats_to_string(&out->start_latency_ms, buffer, sizeof(buffer));
+        dprintf(fd, "      Start latency ms: %s\n", buffer);
+    }
+
     if (locked) {
         pthread_mutex_unlock(&out->lock);
     }
@@ -4870,7 +4887,7 @@
 
     err = platform_get_controller_stream_from_params(parms, &ext_controller,
                                                        &ext_stream);
-    if (err >= 0) {
+    if (err == 0) {
         out->extconn.cs.controller = ext_controller;
         out->extconn.cs.stream = ext_stream;
         ALOGD("%s: usecase(%s) new controller/stream (%d/%d)", __func__,
@@ -5705,6 +5722,8 @@
 
     if (out->standby) {
         out->standby = false;
+        const int64_t startNs = systemTime(SYSTEM_TIME_MONOTONIC);
+
         pthread_mutex_lock(&adev->lock);
         if (out->usecase == USECASE_COMPRESS_VOIP_CALL)
             ret = voice_extn_compress_voip_start_output_stream(out);
@@ -5717,6 +5736,7 @@
             goto exit;
         }
         out->started = 1;
+        out->last_fifo_valid = false; // we're coming out of standby, last_fifo isn't valid.
         if (last_known_cal_step != -1) {
             ALOGD("%s: retry previous failed cal level set", __func__);
             audio_hw_send_gain_dep_calibration(last_known_cal_step);
@@ -5731,6 +5751,10 @@
         }
         if (out->set_dual_mono)
             audio_extn_send_dual_mono_mixing_coefficients(out);
+
+        // log startup time in ms.
+        simple_stats_log(
+                &out->start_latency_ms, (systemTime(SYSTEM_TIME_MONOTONIC) - startNs) * 1e-6);
     }
 
     if (adev->is_channel_status_set == false &&
@@ -5885,6 +5909,30 @@
                     bytes_to_write /= 2;
                 }
             }
+
+            // Note: since out_get_presentation_position() is called alternating with out_write()
+            // by AudioFlinger, we can check underruns using the prior timestamp read.
+            // (Alternately we could check if the buffer is empty using pcm_get_htimestamp().
+            if (out->last_fifo_valid) {
+                // compute drain to see if there is an underrun.
+                const int64_t current_ns = systemTime(SYSTEM_TIME_MONOTONIC); // sys call
+                const int64_t frames_by_time =
+                        (current_ns - out->last_fifo_time_ns) * out->config.rate / NANOS_PER_SECOND;
+                const int64_t underrun = frames_by_time - out->last_fifo_frames_remaining;
+
+                if (underrun > 0) {
+                    simple_stats_log(&out->fifo_underruns, underrun);
+
+                    ALOGW("%s: underrun(%lld) "
+                            "frames_by_time(%lld) > out->last_fifo_frames_remaining(%lld)",
+                            __func__,
+                            (long long)out->fifo_underruns.n,
+                            (long long)frames_by_time,
+                            (long long)out->last_fifo_frames_remaining);
+                }
+                out->last_fifo_valid = false;  // we're writing below, mark fifo info as stale.
+            }
+
             ALOGVV("%s: writing buffer (%zu bytes) to pcm device", __func__, bytes);
 
             long ns = 0;
@@ -6114,15 +6162,26 @@
         if (out->pcm) {
             unsigned int avail;
             if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
-                size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
-
                 uint64_t signed_frames = 0;
                 uint64_t frames_temp = 0;
 
-                frames_temp = (kernel_buffer_size > avail) ? (kernel_buffer_size - avail) : 0;
+                if (out->kernel_buffer_size > avail) {
+                    frames_temp = out->last_fifo_frames_remaining = out->kernel_buffer_size - avail;
+                } else {
+                    ALOGW("%s: avail:%u > kernel_buffer_size:%zu clamping!",
+                            __func__, avail, out->kernel_buffer_size);
+                    avail = out->kernel_buffer_size;
+                    frames_temp = out->last_fifo_frames_remaining = 0;
+                }
+                out->last_fifo_valid = true;
+                out->last_fifo_time_ns = audio_utils_ns_from_timespec(timestamp);
+
                 if (out->written >= frames_temp)
                     signed_frames = out->written - frames_temp;
 
+                ALOGVV("%s: frames:%lld  avail:%u  kernel_buffer_size:%zu",
+                        __func__, (long long)signed_frames, avail, out->kernel_buffer_size);
+
                 // This adjustment accounts for buffering after app processor.
                 // It is based on estimated DSP latency per use case, rather than exact.
                 frames_temp = platform_render_latency(out->usecase) * out->sample_rate / 1000000LL;
@@ -6141,7 +6200,9 @@
                 *frames = signed_frames;
                 ret = 0;
             }
-        } else if (out->card_status == CARD_STATUS_OFFLINE) {
+        } else if (out->card_status == CARD_STATUS_OFFLINE ||
+            // audioflinger still needs position updates when A2DP is suspended
+            (is_a2dp_out_device_type(&out->device_list) && audio_extn_a2dp_source_is_suspended())) {
             *frames = out->written;
             clock_gettime(CLOCK_MONOTONIC, timestamp);
             if (is_offload_usecase(out->usecase))
@@ -6609,6 +6670,12 @@
     dprintf(fd, "      Frames read: %lld\n", (long long)in->frames_read);
     dprintf(fd, "      Frames muted: %lld\n", (long long)in->frames_muted);
 
+    char buffer[256]; // for statistics formatting
+    if (in->start_latency_ms.n > 0) {
+        simple_stats_to_string(&in->start_latency_ms, buffer, sizeof(buffer));
+        dprintf(fd, "      Start latency ms: %s\n", buffer);
+    }
+
     if (locked) {
         pthread_mutex_unlock(&in->lock);
     }
@@ -6870,6 +6937,8 @@
     }
 
     if (in->standby) {
+        const int64_t startNs = systemTime(SYSTEM_TIME_MONOTONIC);
+
         pthread_mutex_lock(&adev->lock);
         if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
             ret = voice_extn_compress_voip_start_input_stream(in);
@@ -6884,6 +6953,10 @@
             goto exit;
         }
         in->standby = 0;
+
+        // log startup time in ms.
+        simple_stats_log(
+                &in->start_latency_ms, (systemTime(SYSTEM_TIME_MONOTONIC) - startNs) * 1e-6);
     }
 
     /* Avoid read if capture_stopped is set */
@@ -8256,6 +8329,8 @@
     else
         out->af_period_multiplier = 1;
 
+    out->kernel_buffer_size = out->config.period_size * out->config.period_count;
+
     out->standby = 1;
     /* out->muted = false; by calloc() */
     /* out->written = 0; by calloc() */
@@ -8772,6 +8847,7 @@
     voice_get_parameters(adev, query, reply);
     audio_extn_a2dp_get_parameters(query, reply);
     platform_get_parameters(adev->platform, query, reply);
+    audio_extn_ma_get_parameters(adev, query, reply);
     pthread_mutex_unlock(&adev->lock);
 
 exit:
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index cc9e602..5e3c1cb 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -48,6 +48,8 @@
 
 #include <audio_route/audio_route.h>
 #include <audio_utils/ErrorLog.h>
+#include <audio_utils/Statistics.h>
+#include <audio_utils/clock.h>
 #include "audio_defs.h"
 #include "voice.h"
 #include "audio_hw_extn_api.h"
@@ -454,6 +456,16 @@
             int stream;
         } cs;
     } extconn;
+
+    size_t kernel_buffer_size;  // cached value of the alsa buffer size, const after open().
+
+    // last out_get_presentation_position() cached info.
+    bool         last_fifo_valid;
+    unsigned int last_fifo_frames_remaining;
+    int64_t      last_fifo_time_ns;
+
+    simple_stats_t fifo_underruns;  // TODO: keep a list of the last N fifo underrun times.
+    simple_stats_t start_latency_ms;
 };
 
 struct stream_in {
@@ -507,6 +519,8 @@
     int64_t frames_muted; /* total frames muted, not cleared when entering standby */
 
     error_log_t *error_log;
+
+    simple_stats_t start_latency_ms;
 };
 
 typedef enum {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 235ae1f..b1ba217 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -5269,13 +5269,13 @@
         goto done_key_audcal;
     }
 
-    memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
-    /* parse audio calibration keys */
-    ret = parse_audiocal_cfg(parms, &cal);
-
     /* handle audio calibration data now */
     err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, len);
     if (err >= 0) {
+        memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
+        /* parse audio calibration keys */
+        ret = parse_audiocal_cfg(parms, &cal);
+
         str_parms_del(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA);
         dlen = strlen(value);
         if(dlen <= 0) {
@@ -5604,15 +5604,16 @@
         goto done;
     }
 
-    memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
-    /* parse audiocal configuration keys */
-    ret = parse_audiocal_cfg(query, &cal);
-    if(ret == 0) {
-        /* No calibration keys found */
-        goto done;
-    }
     err = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, sizeof(value));
     if (err >= 0) {
+        memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
+        /* parse audiocal configuration keys */
+        ret = parse_audiocal_cfg(query, &cal);
+        if (ret == 0) {
+            /* No calibration keys found */
+            goto done;
+        }
+
         str_parms_del(query, AUDIO_PARAMETER_KEY_AUD_CALDATA);
     } else {
         goto done;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index a1fdc39..6764e19 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -1525,9 +1525,6 @@
 #define ULL_PLATFORM_DELAY         (3*1000LL)
 #define MMAP_PLATFORM_DELAY        (3*1000LL)
 
-static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
-static bool is_tmus = false;
-
 static bool is_usb_snd_dev(snd_device_t snd_device)
 {
     if (snd_device < SND_DEVICE_IN_BEGIN) {
@@ -1549,7 +1546,7 @@
     return false;
 }
 
-static void check_operator()
+bool is_operator_tmus()
 {
     char value[PROPERTY_VALUE_MAX];
     int mccmnc;
@@ -1574,17 +1571,12 @@
     case 310210:
     case 310200:
     case 310160:
-        is_tmus = true;
-        break;
+        return true;
+    default:
+        return false;
     }
 }
 
-bool is_operator_tmus()
-{
-    pthread_once(&check_op_once_ctl, check_operator);
-    return is_tmus;
-}
-
 static char *get_current_operator()
 {
     struct listnode *node;
@@ -1929,7 +1921,8 @@
         else if (compare_device_type(out_devices, AUDIO_DEVICE_OUT_EARPIECE))
             strlcat(ec_ref_mixer_path, " handset",
                     MIXER_PATH_MAX_LENGTH);
-        else if (compare_device_type(out_devices, AUDIO_DEVICE_OUT_WIRED_HEADPHONE))
+        else if (compare_device_type(out_devices, AUDIO_DEVICE_OUT_WIRED_HEADPHONE) ||
+                 compare_device_type(out_devices, AUDIO_DEVICE_OUT_WIRED_HEADSET))
             strlcat(ec_ref_mixer_path, " headphones",
                     MIXER_PATH_MAX_LENGTH);
         else if (compare_device_type(out_devices, AUDIO_DEVICE_OUT_USB_HEADSET))
@@ -6007,7 +6000,7 @@
         }
 
         disp_type = mixer_ctl_get_value(ctl, 0);
-        if (disp_type == EXT_DISPLAY_TYPE_NONE) {
+        if (disp_type <= EXT_DISPLAY_TYPE_NONE) {
              ALOGE("%s: Invalid external display type: %d", __func__, disp_type);
              return -EINVAL;
         }
@@ -7731,13 +7724,13 @@
         goto done_key_audcal;
     }
 
-    memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
-    /* parse audio calibration keys */
-    ret = parse_audiocal_cfg(parms, &cal);
-
     /* handle audio calibration data now */
     err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, len);
     if (err >= 0) {
+        memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
+        /* parse audio calibration keys */
+        ret = parse_audiocal_cfg(parms, &cal);
+
         str_parms_del(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA);
         dlen = strlen(value);
         if(dlen <= 0) {
@@ -8331,15 +8324,18 @@
         goto done;
     }
 
+    // init cal
     memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
-    /* parse audiocal configuration keys */
-    ret = parse_audiocal_cfg(query, &cal);
-    if(ret == 0) {
-        /* No calibration keys found */
-        goto done;
-    }
+
     err = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, sizeof(value));
     if (err >= 0) {
+        /* parse audiocal configuration keys */
+        ret = parse_audiocal_cfg(query, &cal);
+        if (ret == 0) {
+            /* No calibration keys found */
+            goto done;
+        }
+
         str_parms_del(query, AUDIO_PARAMETER_KEY_AUD_CALDATA);
     } else {
         goto done;
diff --git a/hal/voice_extn/compress_voip.c b/hal/voice_extn/compress_voip.c
index dffe8ed..2ba1617 100644
--- a/hal/voice_extn/compress_voip.c
+++ b/hal/voice_extn/compress_voip.c
@@ -768,7 +768,7 @@
               __func__, adev->snd_card, pcm_dev_tx_id);
         voip_data.pcm_tx = pcm_open(adev->snd_card,
                                     pcm_dev_tx_id,
-                                    PCM_IN, voip_config);
+                                    PCM_IN|PCM_MONOTONIC, voip_config);
         if (voip_data.pcm_tx && !pcm_is_ready(voip_data.pcm_tx)) {
             ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_tx));
             pcm_close(voip_data.pcm_tx);