Merge "audio: hal: Add new get_param_data api" into audio-userspace.lnx.2.2-dev
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index e6dfb66..fb668d5 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1089,10 +1089,19 @@
     struct audio_usecase *usecase;
     bool switch_device[AUDIO_USECASE_MAX];
     int i, num_uc_to_switch = 0;
+    int backend_check_cond = AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND;
 
     bool force_routing = platform_check_and_set_capture_codec_backend_cfg(adev, uc_info,
                          snd_device);
     ALOGD("%s:becf: force routing %d", __func__, force_routing);
+
+    /*
+     * Make sure out devices is checked against out codec backend device and
+     * also in devices against in codec backend. Checking out device against in
+     * codec backend or vice versa causes issues.
+     */
+    if (uc_info->type == PCM_CAPTURE)
+        backend_check_cond = AUDIO_DEVICE_IN_ALL_CODEC_BACKEND;
     /*
      * This function is to make sure that all the active capture usecases
      * are always routed to the same input sound device.
@@ -1108,10 +1117,13 @@
 
     list_for_each(node, &adev->usecase_list) {
         usecase = node_to_item(node, struct audio_usecase, list);
+        /*
+         * TODO: Enhance below condition to handle BT sco/USB multi recording
+         */
         if (usecase->type != PCM_PLAYBACK &&
                 usecase != uc_info &&
                 (usecase->in_snd_device != snd_device || force_routing) &&
-                ((uc_info->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) &&
+                ((uc_info->devices & backend_check_cond) &&
                  (((usecase->devices & ~AUDIO_DEVICE_BIT_IN) & AUDIO_DEVICE_IN_ALL_CODEC_BACKEND) ||
                   (usecase->type == VOICE_CALL) || (usecase->type == VOIP_CALL))) &&
                 (usecase->id != USECASE_AUDIO_SPKR_CALIB_TX)) {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index a6de6e9..c2bcbe0 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2607,6 +2607,8 @@
         if (backend_tag_table[snd_device] != NULL) {
                 if (strcmp(backend_tag_table[snd_device], "usb-headset-mic") == 0)
                         port = USB_AUDIO_TX_BACKEND;
+                else if (strstr(backend_tag_table[snd_device], "bt-sco") != NULL)
+                        port = BT_SCO_TX_BACKEND;
         }
     } else {
         ALOGW("%s:napb: Invalid device - %d ", __func__, snd_device);
@@ -3324,6 +3326,10 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     struct audio_device *adev = my_data->adev;
+    /*
+     * TODO: active_input always points to last opened input. Source returned will
+     * be wrong if more than one active inputs are present.
+     */
     audio_source_t  source = (adev->active_input == NULL) ?
                                 AUDIO_SOURCE_DEFAULT : adev->active_input->source;
 
@@ -4377,8 +4383,8 @@
           __func__, bit_width, sample_rate, channels,backend_idx,
           platform_get_snd_device_name(snd_device));
 
-    if (bit_width !=
-        my_data->current_backend_cfg[backend_idx].bit_width) {
+    if ((my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl) &&
+        (bit_width != my_data->current_backend_cfg[backend_idx].bit_width)) {
 
         struct  mixer_ctl *ctl = NULL;
         ctl = mixer_get_ctl_by_name(adev->mixer,
@@ -4416,7 +4422,8 @@
      */
     // TODO: This has to be more dynamic based on policy file
 
-    if ((sample_rate != my_data->current_backend_cfg[(int)backend_idx].sample_rate) &&
+    if ((my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl) &&
+        (sample_rate != my_data->current_backend_cfg[(int)backend_idx].sample_rate) &&
             (my_data->hifi_audio)) {
             /*
              * sample rate update is needed only for hifi audio enabled platforms
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index a0ae7d0..24c793e 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -240,6 +240,7 @@
     SLIMBUS_0_TX,
     DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
     USB_AUDIO_TX_BACKEND,
+    BT_SCO_TX_BACKEND,
     MAX_CODEC_BACKENDS
 };
 
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 7bfc5de..81741fc 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -2370,6 +2370,8 @@
         if (backend_tag_table[snd_device] != NULL) {
                 if (strcmp(backend_tag_table[snd_device], "usb-headset-mic") == 0)
                         port = USB_AUDIO_TX_BACKEND;
+                else if (strstr(backend_tag_table[snd_device], "bt-sco") != NULL)
+                        port = BT_SCO_TX_BACKEND;
         }
     } else {
         ALOGW("%s:napb: Invalid device - %d ", __func__, snd_device);
@@ -3025,6 +3027,10 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     struct audio_device *adev = my_data->adev;
+    /*
+     * TODO: active_input always points to last opened input. Source returned will
+     * be wrong if more than one active inputs are present.
+     */
     audio_source_t  source = (adev->active_input == NULL) ?
                                 AUDIO_SOURCE_DEFAULT : adev->active_input->source;
 
@@ -4357,8 +4363,8 @@
           sample_rate, channels, backend_idx,
           platform_get_snd_device_name(snd_device));
 
-    if (bit_width !=
-        my_data->current_backend_cfg[backend_idx].bit_width) {
+    if ((my_data->current_backend_cfg[backend_idx].bitwidth_mixer_ctl) &&
+        (bit_width != my_data->current_backend_cfg[backend_idx].bit_width)) {
 
         struct  mixer_ctl *ctl = NULL;
         ctl = mixer_get_ctl_by_name(adev->mixer,
@@ -4392,8 +4398,8 @@
         ret = 0;
     }
 
-    if (sample_rate !=
-       my_data->current_backend_cfg[backend_idx].sample_rate) {
+    if ((my_data->current_backend_cfg[backend_idx].samplerate_mixer_ctl) &&
+        (sample_rate != my_data->current_backend_cfg[backend_idx].sample_rate)) {
             char *rate_str = NULL;
             struct  mixer_ctl *ctl = NULL;
 
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 6f55c2c..eb34293 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -231,6 +231,7 @@
     SLIMBUS_0_TX,
     DEFAULT_CODEC_TX_BACKEND = SLIMBUS_0_TX,
     USB_AUDIO_TX_BACKEND,
+    BT_SCO_TX_BACKEND,
     MAX_CODEC_BACKENDS
 };
 
diff --git a/qahw_api/inc/qahw_api.h b/qahw_api/inc/qahw_api.h
index 17f6f5f..5db0fdd 100644
--- a/qahw_api/inc/qahw_api.h
+++ b/qahw_api/inc/qahw_api.h
@@ -395,6 +395,12 @@
  */
 int qahw_set_mode(qahw_module_handle_t *hw_module, audio_mode_t mode);
 
+/* Mute/unmute mic during voice/voip/HFP call */
+int qahw_set_mic_mute(qahw_module_handle_t *hw_module, bool state);
+
+/* Get mute/unmute status of mic during voice call */
+int qahw_get_mic_mute(qahw_module_handle_t *hw_module, bool *state);
+
 /* set/get global audio parameters */
 int qahw_set_parameters(qahw_module_handle_t *hw_module, const char *kv_pairs);
 
diff --git a/qahw_api/src/qahw.c b/qahw_api/src/qahw.c
index 1a109e6..48df887 100644
--- a/qahw_api/src/qahw.c
+++ b/qahw_api/src/qahw.c
@@ -1087,6 +1087,64 @@
     return rc;
 }
 
+int qahw_set_mic_mute(qahw_module_handle_t *hw_module, bool state)
+{
+    int rc = -EINVAL;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    if (qahw_module->audio_device->set_mic_mute) {
+        rc = qahw_module->audio_device->set_mic_mute(qahw_module->audio_device,
+                                                 state);
+    } else {
+        rc = -ENOSYS;
+        ALOGW("%s not supported", __func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+    return rc;
+}
+
+int qahw_get_mic_mute(qahw_module_handle_t *hw_module, bool *state)
+{
+    size_t rc = 0;
+    qahw_module_t *qahw_module = (qahw_module_t *)hw_module;
+    qahw_module_t *qahw_module_temp;
+    audio_hw_device_t *audio_device;
+
+    pthread_mutex_lock(&qahw_module_init_lock);
+    qahw_module_temp = get_qahw_module_by_ptr(qahw_module);
+    pthread_mutex_unlock(&qahw_module_init_lock);
+    if (qahw_module_temp == NULL) {
+        ALOGE("%s:: invalid hw module %p", __func__, qahw_module);
+        goto exit;
+    }
+
+    pthread_mutex_lock(&qahw_module->lock);
+    audio_device = qahw_module->audio_device;
+    if (qahw_module->audio_device->get_mic_mute) {
+        rc = audio_device->get_mic_mute(qahw_module->audio_device,
+                                                 &state);
+    } else {
+        rc = -ENOSYS;
+        ALOGW("%s not supported", __func__);
+    }
+    pthread_mutex_unlock(&qahw_module->lock);
+
+exit:
+    return rc;
+}
+
 /* set/get global audio parameters */
 int qahw_set_parameters(qahw_module_handle_t *hw_module, const char *kv_pairs)
 {
diff --git a/qahw_api/test/Makefile.am b/qahw_api/test/Makefile.am
index 3da1435..c269557 100644
--- a/qahw_api/test/Makefile.am
+++ b/qahw_api/test/Makefile.am
@@ -5,6 +5,7 @@
 
 hal_play_test_SOURCES = qahw_playback_test.c
 hal_play_test_CPPFLAGS = $(PLAY_CPPFLAGS) $(PLAY_INCLUDES)
+hal_play_test_CPPFLAGS += -D__unused=__attribute__\(\(__unused__\)\)
 hal_play_test_LDADD = -lutils ../libqahw.la
 
 
diff --git a/qahw_api/test/qahw_multi_record_test.c b/qahw_api/test/qahw_multi_record_test.c
index 07dede7..96107c9 100644
--- a/qahw_api/test/qahw_multi_record_test.c
+++ b/qahw_api/test/qahw_multi_record_test.c
@@ -18,37 +18,74 @@
 
 /* Test app to record multiple audio sessions at the HAL layer */
 
+#include <getopt.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <cutils/list.h>
 #include "qahw_api.h"
 #include "qahw_defs.h"
 
+#define nullptr NULL
+#define LATENCY_NODE "/sys/kernel/debug/audio_in_latency_measurement_node"
+#define LATENCY_NODE_INIT_STR "1"
+
+static bool kpi_mode;
+FILE * log_file = NULL;
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t riff_fmt;
+    uint32_t fmt_id;
+    uint32_t fmt_sz;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;       /* sample_rate * num_channels * bps / 8 */
+    uint16_t block_align;     /* num_channels * bps / 8 */
+    uint16_t bits_per_sample;
+    uint32_t data_id;
+    uint32_t data_sz;
+};
+
 struct audio_config_params {
     qahw_module_handle_t *qahw_mod_handle;
     audio_io_handle_t handle;
     audio_devices_t input_device;
-    audio_config_t config;
     audio_input_flags_t flags;
-    const char* kStreamName ;
-    audio_source_t kInputSource;
-    char output_filename[256];
-    double loopTime;
+    audio_config_t config;
+    audio_source_t source;
+    int channels;
+    double record_delay;
+    double record_length;
     char profile[50];
 };
 
+struct timed_params {
+    struct listnode list;
+    char param[256];
+    int param_delay;
+};
+
+static pthread_mutex_t glock;
+static volatile int tests_running;
+static volatile int tests_completed;
+
 #define SOUNDFOCUS_PARAMS "SoundFocus.start_angles;SoundFocus.enable_sectors;" \
                           "SoundFocus.gain_step"
 #define SOURCETRACK_PARAMS "SourceTrack.vad;SourceTrack.doa_speech;SourceTrack.doa_noise;"\
                            "SourceTrack.polar_activity;ssr.noise_level;ssr.noise_level_after_ns"
-int sourcetrack_done = 0;
-static pthread_mutex_t glock;
-pthread_cond_t gcond;
-int tests_running;
-bool gerror;
 
 void *read_sourcetrack_data(void* data)
 {
@@ -58,14 +95,13 @@
     char *token = NULL;
     char choice = '\0';
     int i =0;
-    qahw_module_handle_t *qawh_module_handle =
-                (qahw_module_handle_t *)data;
+    qahw_module_handle_t *qawh_module_handle = (qahw_module_handle_t *) data;
 
-    while (1) {
-        printf("\nGet SoundFocus Params from app");
+    while (true) {
+        fprintf(log_file, "\nGet SoundFocus Params from app");
         string = qahw_get_parameters(qawh_module_handle, kvpair_soundfocus);
         if (!string) {
-            printf("Error.Failed Get SoundFocus Params\n");
+            fprintf(log_file, "Error.Failed Get SoundFocus Params\n");
         } else {
             token = strtok (string , "=");
             while (token) {
@@ -76,14 +112,14 @@
                 }
                 switch (choice) {
                     case 'g':
-                        printf ("\nSoundFocus.gain_step=%s",token);
+                        fprintf(log_file, "\nSoundFocus.gain_step=%s",token);
                         break;
                     case 'e':
-                        printf ("\nSoundFocus.enable_sectors[%d]=%s",i,token);
+                        fprintf(log_file, "\nSoundFocus.enable_sectors[%d]=%s",i,token);
                         i++;
                         break;
                     case 's':
-                        printf ("\nSoundFocus.start_angles[%d]=%s",i,token);
+                        fprintf(log_file, "\nSoundFocus.start_angles[%d]=%s",i,token);
                         i++;
                         break;
                 }
@@ -91,10 +127,10 @@
             }
         }
         choice = '\0';
-        printf ("\nGet SourceTracking Params from app");
+        fprintf(log_file, "\nGet SourceTracking Params from app");
         string = qahw_get_parameters(qawh_module_handle, kvpair_sourcetrack);
         if (!string) {
-            printf ("Error.Failed Get SourceTrack Params\n");
+            fprintf(log_file, "Error.Failed Get SourceTrack Params\n");
         } else {
             token = strtok (string , "=");
             while (token) {
@@ -107,382 +143,671 @@
                 }
                 switch (choice) {
                     case 'p':
-                        printf ("\nSourceTrack.polar_activity=%s,",token);
+                        fprintf(log_file, "\nSourceTrack.polar_activity=%s,",token);
                         choice = '\0';
                         break;
                     case 'v':
-                        printf ("\nSourceTrack.vad[%d]=%s",i,token);
+                        fprintf(log_file, "\nSourceTrack.vad[%d]=%s",i,token);
                         i++;
                         break;
                     case 's':
-                        printf ("\nSourceTrack.doa_speech=%s",token);
+                        fprintf(log_file, "\nSourceTrack.doa_speech=%s",token);
                         break;
                     case 'n':
-                        printf ("\nSourceTrack.doa_noise[%d]=%s",i,token);
+                        fprintf(log_file, "\nSourceTrack.doa_noise[%d]=%s",i,token);
                         i++;
                         break;
                     default :
-                        printf ("%s,",token);
+                        fprintf(log_file, "%s,",token);
                         break;
                 }
                 token = strtok (NULL,",;=");
             }
         }
-        if (sourcetrack_done == 1)
+        if (tests_completed > 0 && tests_running == 0)
             return NULL;
     }
 }
 
+void test_begin()
+{
+      pthread_mutex_lock(&glock);
+      tests_running++;
+      pthread_mutex_unlock(&glock);
+}
+
+void test_end()
+{
+      pthread_mutex_lock(&glock);
+      tests_running--;
+      tests_completed++;
+      pthread_mutex_unlock(&glock);
+}
+
 void *start_input(void *thread_param)
 {
-  int rc = 0;
+  int rc = 0, ret = 0, count = 0;
+  FILE *fdLatencyNode = nullptr;
+  struct timespec tsColdI, tsColdF, tsCont;
+  uint64_t tCold, tCont, tsec, tusec;
+  char latencyBuf[200] = {0};
+  time_t start_time = time(0);
+  double time_elapsed = 0;
+  ssize_t bytes_read = -1;
+  char param[100] = "audio_stream_profile=";
+  char file_name[256] = "/data/rec";
+  int data_sz = 0, name_len = strlen(file_name);
+  qahw_in_buffer_t in_buf;
+
   struct audio_config_params* params = (struct audio_config_params*) thread_param;
   qahw_module_handle_t *qahw_mod_handle = params->qahw_mod_handle;
 
-  // Open audio input stream.
+  /* convert/check params before use */
+  switch(params->channels) {
+  case 1:
+      params->config.channel_mask = AUDIO_CHANNEL_IN_MONO;
+      break;
+  case 2:
+      params->config.channel_mask = AUDIO_CHANNEL_IN_STEREO;
+      break;
+  case 4:
+      params->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_4;
+      break;
+  default:
+      fprintf(log_file, "ERROR :::: channle count %d not supported, handle(%d)", params->channels, params->handle);
+      if (log_file != stdout)
+          fprintf(stdout, "ERROR :::: channle count %d not supported, handle(%d)", params->channels, params->handle);
+      pthread_exit(0);
+  }
+
+  /* setup debug node if in kpi mode */
+  if (kpi_mode) {
+      fdLatencyNode = fopen(LATENCY_NODE,"r+");
+      if (fdLatencyNode) {
+          ret = fwrite(LATENCY_NODE_INIT_STR, sizeof(LATENCY_NODE_INIT_STR), 1, fdLatencyNode);
+          if (ret < 1)
+              fprintf(log_file, "error(%d) writing to debug node!, handle(%d)", ret, params->handle);
+          fflush(fdLatencyNode);
+      } else {
+          fprintf(log_file, "debug node(%s) open failed!, handle(%d)", LATENCY_NODE, params->handle);
+          if (log_file != stdout)
+              fprintf(stdout, "debug node(%s) open failed!, handle(%d)", LATENCY_NODE, params->handle);
+          pthread_exit(0);
+      }
+  }
+
+  test_begin();
+  /* Open audio input stream */
   qahw_stream_handle_t* in_handle = NULL;
 
   rc = qahw_open_input_stream(qahw_mod_handle,
                               params->handle, params->input_device,
                               &params->config, &in_handle,
-                              params->flags, params->kStreamName,
-                              params->kInputSource);
+                              params->flags, "input_stream",
+                              params->source);
   if (rc) {
-      printf("ERROR :::: Could not open input stream.\n" );
-      pthread_mutex_lock(&glock);
-      gerror = true;
-      pthread_cond_signal(&gcond);
-      pthread_mutex_unlock(&glock);
+      fprintf(log_file, "ERROR :::: Could not open input stream, handle(%d)\n", params->handle);
+      if (log_file != stdout)
+          fprintf(stdout, "ERROR :::: Could not open input stream, handle(%d)\n", params->handle);
+      test_end();
       pthread_exit(0);
   }
 
-  // Get buffer size to get upper bound on data to read from the HAL.
-  size_t buffer_size;
-      buffer_size = qahw_in_get_buffer_size(in_handle);
-  char *buffer;
-  buffer = (char *)calloc(1, buffer_size);
+  /* Get buffer size to get upper bound on data to read from the HAL */
+  size_t buffer_size = qahw_in_get_buffer_size(in_handle);
+  char *buffer = (char *)calloc(1, buffer_size);
   if (buffer == NULL) {
-     printf("calloc failed!!\n");
-     pthread_mutex_lock(&glock);
-     gerror = true;
-     pthread_cond_signal(&gcond);
-     pthread_mutex_unlock(&glock);
-     pthread_exit(0);
+      fprintf(log_file, "calloc failed!!, handle(%d)\n", params->handle);
+      if (log_file != stdout)
+          fprintf(stdout, "calloc failed!!, handle(%d)\n", params->handle);
+      test_end();
+      pthread_exit(0);
   }
 
-  printf("input opened, buffer = %p, size %zun",
-         buffer, buffer_size);
-
-  int num_channels = audio_channel_count_from_in_mask(params->config.channel_mask);
-
-  time_t start_time = time(0);
-  ssize_t bytes_read = -1;
-  char param[100] = "audio_stream_profile=";
-  qahw_in_buffer_t in_buf;
-
-  // set profile for the recording session
+  fprintf(log_file, " input opened, buffer  %p, size %zu, handle(%d)", buffer, buffer_size, params->handle);
+  /* set profile for the recording session */
   strlcat(param, params->profile, sizeof(param));
   qahw_in_set_parameters(in_handle, param);
 
-  printf("\nPlease speak into the microphone for %lf seconds.\n", params->loopTime);
+  fprintf(log_file, "\n Please speak into the microphone for %lf seconds, handle(%d)\n", params->record_length, params->handle);
+  if (log_file != stdout)
+      fprintf(stdout, "\n Please speak into the microphone for %lf seconds, handle(%d)\n", params->record_length, params->handle);
 
-  FILE *fd = fopen(params->output_filename,"w");
+  snprintf(file_name + name_len, sizeof(file_name) - name_len, "%d.wav", (0x99A - params->handle));
+  FILE *fd = fopen(file_name,"w");
   if (fd == NULL) {
-     printf("File open failed \n");
-     pthread_mutex_lock(&glock);
-     gerror = true;
-     pthread_cond_signal(&gcond);
-     pthread_mutex_unlock(&glock);
-     pthread_exit(0);
+      fprintf(log_file, "File open failed \n");
+      if (log_file != stdout)
+          fprintf(stdout, "File open failed \n");
+      test_end();
+      pthread_exit(0);
   }
-  pthread_mutex_lock(&glock);
-  tests_running++;
-  pthread_cond_signal(&gcond);
-  pthread_mutex_unlock(&glock);
-  memset(&in_buf,0, sizeof(qahw_in_buffer_t));
+  int bps = 16;
 
+  switch(params->config.format) {
+  case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+      bps = 24;
+      break;
+  case AUDIO_FORMAT_PCM_8_24_BIT:
+  case AUDIO_FORMAT_PCM_32_BIT:
+      bps = 32;
+      break;
+  case AUDIO_FORMAT_PCM_16_BIT:
+  default:
+      bps = 16;
+  }
+
+  struct wav_header hdr;
+  hdr.riff_id = ID_RIFF;
+  hdr.riff_sz = 0;
+  hdr.riff_fmt = ID_WAVE;
+  hdr.fmt_id = ID_FMT;
+  hdr.fmt_sz = 16;
+  hdr.audio_format = FORMAT_PCM;
+  hdr.num_channels = params->channels;
+  hdr.sample_rate = params->config.sample_rate;
+  hdr.byte_rate = hdr.sample_rate * hdr.num_channels * (bps/8);
+  hdr.block_align = hdr.num_channels * (bps/8);
+  hdr.bits_per_sample = bps;
+  hdr.data_id = ID_DATA;
+  hdr.data_sz = 0;
+  fwrite(&hdr, 1, sizeof(hdr), fd);
+
+  memset(&in_buf,0, sizeof(qahw_in_buffer_t));
+  start_time = time(0);
   while(true) {
+      if(time_elapsed < params->record_delay) {
+          usleep(1000000*(params->record_delay - time_elapsed));
+          continue;
+      } else if (time_elapsed > params->record_delay + params->record_length) {
+          fprintf(log_file, "\n Test for session with handle(%d) completed.\n", params->handle);
+          if (log_file != stdout)
+              fprintf(stdout, "\n Test for session with handle(%d) completed.\n", params->handle);
+          break;
+      }
+
+      if (kpi_mode && count == 0) {
+          ret = clock_gettime(CLOCK_REALTIME, &tsColdI);
+          if (ret)
+              fprintf(log_file, "error(%d) getting current time before first read!, handle(%d)", ret, params->handle);
+      }
+
       in_buf.buffer = buffer;
       in_buf.bytes = buffer_size;
       bytes_read = qahw_in_read(in_handle, &in_buf);
-      fwrite(in_buf.buffer, sizeof(char), buffer_size, fd);
-      if(difftime(time(0), start_time) > params->loopTime) {
-          printf("\nTest completed.\n");
-          break;
+
+      if (kpi_mode) {
+          if (count == 0) {
+              ret = clock_gettime(CLOCK_REALTIME, &tsColdF);
+              if (ret)
+                  fprintf(log_file, "error(%d) getting current time after first read!, handle(%d)", ret, params->handle);
+          } else if (count == 8) {
+          /* 8th read done time is captured in kernel which would have trigger 9th read in DSP
+           * 9th read is received by usersace at this time
+           */
+              ret = clock_gettime(CLOCK_REALTIME, &tsCont);
+              if (ret)
+                  fprintf(log_file, "error(%d) getting current time after 8th read!, handle(%d)", ret, params->handle);
+          }
+          count++;
       }
+
+      time_elapsed = difftime(time(0), start_time);
+      fwrite(in_buf.buffer, 1, buffer_size, fd);
+      data_sz += buffer_size;
   }
 
-  printf("closing input");
+  /* update lengths in header */
+  hdr.data_sz = data_sz;
+  hdr.riff_sz = data_sz + 44 - 8;
+  fseek(fd, 0, SEEK_SET);
+  fwrite(&hdr, 1, sizeof(hdr), fd);
+  fclose(fd);
 
-  // Close output stream and device.
+  /* capture latency kpis if required */
+  if (kpi_mode) {
+      tCold = tsColdF.tv_sec*1000 - tsColdI.tv_sec*1000 +
+              tsColdF.tv_nsec/1000000 - tsColdI.tv_nsec/1000000;
+
+      fread((void *) latencyBuf, 100, 1, fdLatencyNode);
+      fclose(fdLatencyNode);
+      sscanf(latencyBuf, " %llu,%llu", &tsec, &tusec);
+      tCont = tsCont.tv_sec*1000 - tsec*1000 + tsCont.tv_nsec/1000000 - tusec/1000;
+      if (log_file != stdout) {
+          fprintf(stdout, "\n cold latency %llums, continuous latency %llums, handle(%d)\n", tCold, tCont, params->handle);
+          fprintf(stdout, " **Note: please add DSP Pipe/PP latency numbers to this, for final latency values\n");
+      }
+      fprintf(log_file, "\n values from debug node %s, handle(%d)\n", latencyBuf, params->handle);
+      fprintf(log_file, "\n cold latency %llums, continuous latency %llums, handle(%d)\n", tCold, tCont, params->handle);
+      fprintf(log_file, " **Note: please add DSP Pipe/PP latency numbers to this, for final latency values\n");
+  }
+
+  fprintf(log_file, " closing input, handle(%d)", params->handle);
+
+  /* Close output stream and device. */
   rc = qahw_in_standby(in_handle);
   if (rc) {
-      printf("out standby failed %d \n",rc);
+      fprintf(log_file, "out standby failed %d, handle(%d)\n",rc, params->handle);
+      if (log_file != stdout)
+          fprintf(stdout, "out standby failed %d, handle(%d)\n",rc, params->handle);
   }
 
   rc = qahw_close_input_stream(in_handle);
   if (rc) {
-      printf("could not close input stream %d \n",rc);
+      fprintf(log_file, "could not close input stream %d, handle(%d)\n",rc, params->handle);
+      if (log_file != stdout)
+          fprintf(stdout, "could not close input stream %d, handle(%d)\n",rc, params->handle);
   }
 
-  // Print instructions to access the file.
-  printf("\nThe audio recording has been saved to %s. Please use adb pull to get "
+  /* Print instructions to access the file. */
+  fprintf(log_file, "\n\n The audio recording has been saved to %s. Please use adb pull to get "
          "the file and play it using audacity. The audio data has the "
-         "following characteristics:\nsample rate: %i\nformat: %d\n"
-         "num channels: %i\n",
-         params->output_filename, params->config.sample_rate,
-         params->config.format, num_channels);
+         "following characteristics:\n Sample rate: %i\n Format: %d\n "
+         "Num channels: %i, handle(%d)\n\n",
+         file_name, params->config.sample_rate, params->config.format, params->channels, params->handle);
+  if (log_file != stdout)
+      fprintf(stdout, "\n\n The audio recording has been saved to %s. Please use adb pull to get "
+         "the file and play it using audacity. The audio data has the "
+         "following characteristics:\n Sample rate: %i\n Format: %d\n "
+         "Num channels: %i, handle(%d)\n\n",
+         file_name, params->config.sample_rate, params->config.format, params->channels, params->handle);
 
-  pthread_mutex_lock(&glock);
-  tests_running--;
-  pthread_cond_signal(&gcond);
-  pthread_mutex_unlock(&glock);
-  pthread_exit(0);
+  test_end();
   return NULL;
 }
 
-int read_config_params_from_user(struct audio_config_params *thread_param, int rec_session) {
-    int channels = 0, format = 0, sample_rate = 0,source = 0, device = 0;
-
-    thread_param->kStreamName = "input_stream";
-
+int read_config_params_from_user(struct audio_config_params *thread_param) {
     printf(" \n Enter input device (4->built-in mic, 16->wired_headset .. etc) ::::: ");
-    scanf(" %d", &device);
-    if (device & AUDIO_DEVICE_IN_BUILTIN_MIC)
-        thread_param->input_device = AUDIO_DEVICE_IN_BUILTIN_MIC;
-    else if (device & AUDIO_DEVICE_IN_WIRED_HEADSET)
-        thread_param->input_device = AUDIO_DEVICE_IN_WIRED_HEADSET;
+    scanf(" %d", &thread_param->input_device);
+    thread_param->input_device |= AUDIO_DEVICE_BIT_IN;
 
-    printf(" \n Enter the channels (1 -mono, 2 -stereo and 4 -quad channels) ::::: ");
-    scanf(" %d", &channels);
-    if (channels == 1) {
-        thread_param->config.channel_mask = AUDIO_CHANNEL_IN_MONO;
-    } else if (channels == 2) {
-        thread_param->config.channel_mask = AUDIO_CHANNEL_IN_STEREO;
-    } else if (channels == 4) {
-        thread_param->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_4;
-    } else {
-        gerror = true;
-        printf("\nINVALID channels");
-        return -1;
-    }
+    printf(" \n Enter the format (1 ->16 bit pcm recording, 6 -> 24 bit packed pcm recording) ::::: ");
+    scanf(" %d", &thread_param->config.format);
 
-    printf(" \n Enter the format (16 - 16 bit recording, 24 - 24 bit recording) ::::: ");
-    scanf(" %d", &format);
-    if (format == 16) {
-        thread_param->config.format = AUDIO_FORMAT_PCM_16_BIT;
-    } else if (format == 24) {
-        thread_param->config.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
-    } else {
-        gerror = true;
-        printf("\n INVALID format");
-        return -1;
-    }
+    printf(" \n Enter input flag to be used (0 -> none, 1 -> fast ...) ::::: ");
+    scanf(" %d", &thread_param->flags);
 
     printf(" \n Enter the sample rate (48000, 16000 etc) :::: ");
-    scanf(" %d", &sample_rate);
-    thread_param->config.sample_rate = sample_rate;
+    scanf(" %d", &thread_param->config.sample_rate);
 
-#ifdef MULTIRECORD_SUPPOT
+    printf(" \n Enter the channels (1 -mono, 2 -stereo and 4 -quad channels) ::::: ");
+    scanf(" %d", &thread_param->channels);
+
     printf(" \n Enter profile (none, record_fluence, record_mec, record_unprocessed etc) :::: ");
     scanf(" %s", thread_param->profile);
-#else
-    thread_param->flags = (audio_input_flags_t)AUDIO_INPUT_FLAG_NONE;
-#endif
-    printf("\n Enter the audio source ( ref: system/media/audio/include/system/audio.h) :::: ");
-    scanf(" %d", &source);
-    thread_param->kInputSource = (audio_source_t)source;
 
-    if (rec_session == 1) {
-        thread_param->handle = 0x999;
-        strcpy(thread_param->output_filename, "/data/rec1.raw");
-    } else if (rec_session == 2) {
-        thread_param->handle = 0x998;
-        strcpy(thread_param->output_filename, "/data/rec2.raw");
-    } else if (rec_session == 3) {
-        thread_param->handle = 0x997;
-        strcpy(thread_param->output_filename, "/data/rec3.raw");
-    } else if (rec_session == 4) {
-        thread_param->handle = 0x996;
-        strcpy(thread_param->output_filename, "/data/rec4.raw");
-    }
+    printf("\n Enter the audio source ( ref: system/media/audio/include/system/audio.h) :::: ");
+    scanf(" %d", &thread_param->source);
 
     printf("\n Enter the record duration in seconds ::::  ");
-    scanf(" %lf", &thread_param->loopTime);
+    scanf(" %lf", &thread_param->record_length);
+
+    printf("\n Enter the start delay for the session in seconds ::::  ");
+    scanf(" %lf", &thread_param->record_delay);
+
     return 0;
 }
 
-int main() {
-    int max_recordings_requested = 0, source_track = 0;
+void fill_default_params(struct audio_config_params *thread_param, int rec_session) {
+    memset(thread_param,0, sizeof(struct audio_config_params));
+
+    thread_param->input_device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+    thread_param->config.format = AUDIO_FORMAT_PCM_16_BIT;
+    thread_param->channels = 2;
+    thread_param->flags = (audio_input_flags_t)AUDIO_INPUT_FLAG_NONE;
+    thread_param->config.sample_rate = 48000;
+    thread_param->source = 1;
+    thread_param->record_length = 8 /*sec*/;
+    thread_param->record_delay = 0 /*sec*/;
+
+    if (rec_session == 1) {
+        thread_param->handle = 0x999;
+    } else if (rec_session == 2) {
+        thread_param->handle = 0x998;
+    } else if (rec_session == 3) {
+        thread_param->handle = 0x997;
+    } else if (rec_session == 4) {
+        thread_param->handle = 0x996;
+    }
+}
+
+void usage() {
+    printf(" \n Command \n");
+    printf(" \n hal_rec_test <options>\n");
+    printf(" \n Options\n");
+    printf(" -d  --device <int>              - see system/media/audio/include/system/audio.h for device values\n");
+    printf("                                             Optional Argument and Default value is 4, i.e Built-in MIC\n\n");
+    printf(" -f  --format <int>                        - Integer value of format in which data needs to be recorded\n\n");
+    printf(" -F  --flags  <int>                        - Integer value of flags to be used for opening input stream\n\n");
+    printf(" -r  --sample-rate <8000-96000>            - Sampling rate to be used\n\n");
+    printf(" -c  --channels <int>                      - Number of input channels needed\n\n");
+    printf(" -s  --source <int>                        - Input Source type\n\n");
+    printf(" -p  --profile <string>                    - Input profile tag, used for profile based app_type selection.\n\n");
+    printf(" -t  --recording-time <in seconds>         - Time duration for the recording\n\n");
+    printf(" -D --recording-delay <in seconds>         - Delay in seconds after which recording should be started\n\n");
+    printf(" -l  --log-file <FILEPATH>                 - File path for debug msg, to print\n");
+    printf("                                             on console use stdout or 1 \n\n");
+    printf(" -K  --kpi-mode                            - Use this flag to measure latency KPIs for this recording\n\n");
+    printf(" -i  --interactive-mode                    - Use this flag if prefer configuring streams using interactive mode\n");
+    printf("                                             All other flags passed would be ignore if this flag is used\n\n");
+    printf(" -S  --source-tracking                     - Use this flag to show capture source tracking params for recordings\n\n");
+    printf(" -h  --help                                - Show this help\n\n");
+    printf(" \n Examples \n");
+    printf(" hal_rec_test     -> start a recording stream with default configurations\n\n");
+    printf(" hal_rec_test -i  -> start a recording stream and get configurations from user interactively\n\n");
+    printf(" hal_rec_test -d 2 -f 1 -r 44100 -c 2 -t 8 -D 2 -S -> start a recording session, with device 2[built-in-mic],\n");
+    printf("                                           format 1[AUDIO_FORMAT_PCM_16_BIT], sample rate 44100, \n");
+    printf("                                           channels 2[AUDIO_CHANNEL_IN_STEREO], record data for 8 secs\n");
+    printf("                                           start recording after 2 secs, and capture source tracking params.\n\n");
+    printf(" hal_rec_test -F 1 --kpi-mode -> start a recording with low latency input flag and calculate latency KPIs\n\n");
+}
+
+int main(int argc, char* argv[]) {
+    int max_recordings_requested = 0, status = 0;
     int thread_active[4] = {0};
     qahw_module_handle_t *qahw_mod_handle;
     const  char *mod_name = "audio.primary";
+    struct audio_config_params params[4];
+    bool interactive_mode = false, source_tracking = false;
+    struct listnode param_list;
+    char log_filename[256] = "stdout";
 
-    pthread_cond_init(&gcond, (const pthread_condattr_t *) NULL);
+    log_file = stdout;
+    list_init(&param_list);
+    fill_default_params(&params[0], 1);
+    struct option long_options[] = {
+        /* These options set a flag. */
+        {"device",          required_argument,    0, 'd'},
+        {"format",          required_argument,    0, 'f'},
+        {"flags",           required_argument,    0, 'F'},
+        {"sample-rate",     required_argument,    0, 'r'},
+        {"channels",        required_argument,    0, 'c'},
+        {"source",          required_argument,    0, 's'},
+        {"profile",         required_argument,    0, 'p'},
+        {"recording-time",  required_argument,    0, 't'},
+        {"recording-delay", required_argument,    0, 'D'},
+        {"log-file",        required_argument,    0, 'l'},
+        {"kpi-mode",        no_argument,          0, 'K'},
+        {"interactive",     no_argument,          0, 'i'},
+        {"source-tracking", no_argument,          0, 'S'},
+        {"help",            no_argument,          0, 'h'},
+        {0, 0, 0, 0}
+    };
+
+    int opt = 0;
+    int option_index = 0;
+    while ((opt = getopt_long(argc,
+                              argv,
+                              "-d:f:F:r:c:s:p:t:D:l:KiSh",
+                              long_options,
+                              &option_index)) != -1) {
+            switch (opt) {
+            case 'd':
+                params[0].input_device = atoll(optarg);
+                break;
+            case 'f':
+                params[0].config.format = atoll(optarg);
+                break;
+            case 'F':
+                params[0].flags = atoll(optarg);
+                break;
+            case 'r':
+                params[0].config.sample_rate = atoi(optarg);
+                break;
+            case 'c':
+                params[0].channels = atoi(optarg);
+                break;
+            case 's':
+                params[0].source = atoi(optarg);
+                break;
+            case 'p':
+                snprintf(params[0].profile, sizeof(params[0].profile), "%s", optarg);
+                break;
+            case 't':
+                params[0].record_length = atoi(optarg);
+                break;
+            case 'D':
+                params[0].record_delay = atoi(optarg);
+                break;
+            case 'l':
+                snprintf(log_filename, sizeof(log_filename), "%s", optarg);
+                break;
+            case 'K':
+                kpi_mode = true;
+                break;
+            case 'i':
+                interactive_mode = true;
+                break;
+            case 'S':
+                source_tracking = true;
+                break;
+            case 'h':
+                usage();
+                return 0;
+                break;
+         }
+    }
 
     qahw_mod_handle = qahw_load_module(mod_name);
     if(qahw_mod_handle == NULL) {
-        printf(" qahw_load_module failed");
+        fprintf(log_file, " qahw_load_module failed");
         return -1;
     }
-#ifdef MULTIRECORD_SUPPOT
-    printf("Starting audio hal multi recording test. \n");
-    printf(" Enter number of record sessions to be started \n");
-    printf("             (Maximum of 4 record sessions are allowed)::::  ");
-    scanf(" %d", &max_recordings_requested);
-#else
-    max_recordings_requested = 1;
-#endif
-    printf(" \n Source Tracking enabled ??? ( 1 - Enable 0 - Disable)::: ");
-    scanf(" %d", &source_track);
-
-    struct audio_config_params thread1_params, thread2_params;
-    struct audio_config_params thread3_params, thread4_params;
+    fprintf(log_file, " Starting audio hal multi recording test. \n");
+    if (interactive_mode) {
+        printf(" Enter logfile path (stdout or 1 for console out)::: \n");
+        scanf(" %s", log_filename);
+        printf(" Enter number of record sessions to be started \n");
+        printf("             (Maximum of 4 record sessions are allowed)::::  ");
+        scanf(" %d", &max_recordings_requested);
+    } else {
+        max_recordings_requested = 1;
+    }
+    if (strcasecmp(log_filename, "stdout") && strcasecmp(log_filename, "1")) {
+        if ((log_file = fopen(log_filename,"wb"))== NULL) {
+            fprintf(stderr, "Cannot open log file %s\n", log_filename);
+            /* continue to log to std out */
+            log_file = stdout;
+        }
+    }
 
     switch (max_recordings_requested) {
         case 4:
-            printf(" Enter the config params for fourth record session \n");
-            thread4_params.qahw_mod_handle = qahw_mod_handle;
-            read_config_params_from_user( &thread4_params, 4);
+            if (interactive_mode) {
+                printf(" Enter the config params for fourth record session \n");
+                fill_default_params(&params[3], 4);
+                read_config_params_from_user(&params[3]);
+            }
+            params[3].qahw_mod_handle = qahw_mod_handle;
             thread_active[3] = 1;
             printf(" \n");
         case 3:
-            printf(" Enter the config params for third record session \n");
-            thread3_params.qahw_mod_handle = qahw_mod_handle;
-            read_config_params_from_user( &thread3_params, 3);
+            if (interactive_mode) {
+                printf(" Enter the config params for third record session \n");
+                fill_default_params(&params[2], 3);
+                read_config_params_from_user(&params[2]);
+            }
+            params[2].qahw_mod_handle = qahw_mod_handle;
             thread_active[2] = 1;
             printf(" \n");
         case 2:
-            printf(" Enter the config params for second record session \n");
-            thread2_params.qahw_mod_handle = qahw_mod_handle;
-            read_config_params_from_user( &thread2_params, 2);
+            if (interactive_mode) {
+                printf(" Enter the config params for second record session \n");
+                fill_default_params(&params[1], 2);
+                read_config_params_from_user(&params[1]);
+            }
+            params[1].qahw_mod_handle = qahw_mod_handle;
             thread_active[1] = 1;
             printf(" \n");
         case 1:
-            printf(" Enter the config params for first record session \n");
-            thread1_params.qahw_mod_handle = qahw_mod_handle;
-            read_config_params_from_user( &thread1_params, 1);
+            if (interactive_mode) {
+                printf(" Enter the config params for first record session \n");
+                fill_default_params(&params[0], 1);
+                read_config_params_from_user(&params[0]);
+            }
+            params[0].qahw_mod_handle = qahw_mod_handle;
             thread_active[0] = 1;
             printf(" \n");
             break;
         default:
-            printf(" INVALID input -- Max record sessions supported is 4 -exit \n");
-            gerror = true;
+            fprintf(log_file, " INVALID input -- Max record sessions supported is 4 -exit \n");
+            if (log_file != stdout)
+                fprintf(stdout, " INVALID input -- Max record sessions supported is 4 -exit \n");
+            status = -1;
             break;
     }
+    if (interactive_mode && status == 0) {
+        int option = 0;
+
+        printf(" \n Source Tracking enabled ??? ( 1 - Enable 0 - Disable)::: ");
+        scanf(" %d", &option);
+        source_tracking = option ? true : false;
+
+        printf(" \n Measure latency KPI values ??? ( 1 - Enable 0 - Disable)::: ");
+        scanf(" %d", &option);
+        kpi_mode = option ? true : false;
+
+        while(true) {
+            char ch = 'y';
+            printf(" \n SetParam command required ??? (y/n)::: ");
+            scanf(" %c", &ch);
+            if (ch != 'y' && ch != 'Y')
+                break;
+            struct timed_params *param = (struct timed_params *)
+                                 calloc(1, sizeof(struct timed_params));
+            if (param == NULL) {
+                fprintf(log_file, " \n Failed to alloc memory for param, ignoring param conf\n\n");
+                if (log_file != stdout)
+                    fprintf(stdout, " \n Failed to alloc memory for param, ignoring param conf\n\n");
+                continue;
+            }
+            printf(" \n Enter param kv pair :::: ");
+            scanf(" %s", param->param);
+            printf(" \n Enter param delay in sec (time after which param need to be set):::: ");
+            scanf(" %d", &param->param_delay);
+
+            list_add_tail(&param_list, &param->list);
+        }
+    }
 
     pthread_t tid[4];
     pthread_t sourcetrack_thread;
     int ret = -1;
 
     if (thread_active[0] == 1) {
-        printf("\n Create first record thread \n");
-        ret = pthread_create(&tid[0], NULL, start_input, (void *)&thread1_params);
+        fprintf(log_file, "\n Create first record thread \n");
+        ret = pthread_create(&tid[0], NULL, start_input, (void *)&params[0]);
         if (ret) {
-            gerror = true;
-            printf(" Failed to create first record thread \n ");
+            status = -1;
+            fprintf(log_file, " Failed to create first record thread \n ");
+            if (log_file != stdout)
+                fprintf(stdout, " Failed to create first record thread \n ");
             thread_active[0] = 0;
         }
     }
     if (thread_active[1] == 1) {
-        printf("Create second record thread \n");
-        ret = pthread_create(&tid[1], NULL, start_input, (void *)&thread2_params);
+        fprintf(log_file, "Create second record thread \n");
+        ret = pthread_create(&tid[1], NULL, start_input, (void *)&params[1]);
         if (ret) {
-            gerror = true;
-            printf(" Failed to create second record thread \n ");
+            status = -1;
+            fprintf(log_file, " Failed to create second record thread \n ");
+            if (log_file != stdout)
+                fprintf(stdout, " Failed to create second record thread \n ");
             thread_active[1] = 0;
         }
     }
     if (thread_active[2] == 1) {
-        printf("Create third record thread \n");
-        ret = pthread_create(&tid[2], NULL, start_input, (void *)&thread3_params);
+        fprintf(log_file, "Create third record thread \n");
+        ret = pthread_create(&tid[2], NULL, start_input, (void *)&params[2]);
         if (ret) {
-            gerror = true;
-            printf(" Failed to create third record thread \n ");
+            status = -1;
+            fprintf(log_file, " Failed to create third record thread \n ");
+            if (log_file != stdout)
+                fprintf(stdout, " Failed to create third record thread \n ");
             thread_active[2] = 0;
         }
     }
     if (thread_active[3] == 1) {
-        printf("Create fourth record thread \n");
-        ret = pthread_create(&tid[3], NULL, start_input, (void *)&thread4_params);
+        fprintf(log_file, "Create fourth record thread \n");
+        ret = pthread_create(&tid[3], NULL, start_input, (void *)&params[3]);
         if (ret) {
-            gerror = true;
-            printf(" Failed to create fourth record thread \n ");
+            status = -1;
+            fprintf(log_file, " Failed to create fourth record thread \n ");
+            if (log_file != stdout)
+                fprintf(stdout, " Failed to create fourth record thread \n ");
             thread_active[3] = 0;
         }
     }
-    if (source_track && max_recordings_requested) {
-        printf("Create source tracking thread \n");
+    if (source_tracking && max_recordings_requested) {
+        fprintf(log_file, "Create source tracking thread \n");
         ret = pthread_create(&sourcetrack_thread,
                 NULL, read_sourcetrack_data,
                 (void *)qahw_mod_handle);
         if (ret) {
-            printf(" Failed to create source tracking thread \n ");
-            source_track = 0;
+            fprintf(log_file, " Failed to create source tracking thread \n ");
+            if (log_file != stdout)
+                fprintf(stdout, " Failed to create source tracking thread \n ");
+            source_tracking = 0;
         }
     }
+    fprintf(log_file, " All threads started \n");
+    if (log_file != stdout)
+        fprintf(stdout, " All threads started \n");
 
-    // set bad mic param
-    while (max_recordings_requested && !source_track) {
-        bool test_completed = false;
-
-        pthread_mutex_lock(&glock);
-        if (!tests_running && !gerror)
-            pthread_cond_wait(&gcond, &glock);
-        test_completed = (tests_running == 0);
-        gerror = true;
-        pthread_mutex_unlock(&glock);
-
-        if (test_completed)
-            break;
-#ifdef MULTIRECORD_SUPPOT
-        char ch;
-        printf("\n Bad mic test required (y/n):::");
-        scanf(" %c", &ch);
-        if (ch == 'y' || ch == 'Y') {
-            int bad_mic_ch_index, ret;
-            char param[100] = "bad_mic_channel_index=";
-            printf("\nEnter bad mic channel index (1, 2, 4 ...):::");
-            scanf(" %d", &bad_mic_ch_index);
-            snprintf(param, sizeof(param), "%s%d", param, bad_mic_ch_index);
-            ret = qahw_set_parameters(qahw_mod_handle, param);
-            printf("param %s set to hal with return value %d\n", param, ret);
-        } else {
-            break;
+    /* set params if queued */
+    time_t start_time = time(0);
+    while (!list_empty(&param_list)) {
+        struct listnode *node, *tempnode;
+        struct timed_params *param;
+        time_t curr_time = time(0);
+        list_for_each_safe(node, tempnode, &param_list) {
+            param = node_to_item(node, struct timed_params, list);
+            if (curr_time - start_time > param->param_delay ||
+                (tests_completed > 0 && tests_running == 0)) {
+                if (curr_time - start_time > param->param_delay) {
+                    ret = qahw_set_parameters(qahw_mod_handle, param->param);
+                    fprintf(log_file, " param %s set to hal with return value %d\n", param->param, ret);
+                }
+                list_remove(&param->list);
+                free(param);
+            }
         }
-#endif
+        usleep(10000);
     }
 
-    printf(" Waiting for threads exit \n");
+    fprintf(log_file, " Waiting for threads exit \n");
+    if (log_file != stdout)
+        fprintf(stdout, " Waiting for threads exit \n");
     if (thread_active[0] == 1) {
         pthread_join(tid[0], NULL);
-        printf("after first record thread exit \n");
+        fprintf(log_file, " after first record thread exit \n");
     }
     if (thread_active[1] == 1) {
         pthread_join(tid[1], NULL);
-        printf("after second record thread exit \n");
+        fprintf(log_file, " after second record thread exit \n");
     }
     if (thread_active[2] == 1) {
         pthread_join(tid[2], NULL);
-        printf("after third record thread exit \n");
+        fprintf(log_file, " after third record thread exit \n");
     }
     if (thread_active[3] == 1) {
         pthread_join(tid[3], NULL);
-        printf("after fourth record thread exit \n");
+        fprintf(log_file, " after fourth record thread exit \n");
     }
-    if (source_track) {
-        sourcetrack_done = 1;
+    if (source_tracking) {
         pthread_join(sourcetrack_thread,NULL);
-        printf("after source tracking thread exit \n");
+        fprintf(log_file, " after source tracking thread exit \n");
     }
 
     ret = qahw_unload_module(qahw_mod_handle);
     if (ret) {
-        printf("could not unload hal %d \n",ret);
+        fprintf(log_file, "could not unload hal %d \n",ret);
     }
 
-
-    printf("Done with hal record test \n");
-    pthread_cond_destroy(&gcond);
+    fprintf(log_file, "\n Done with hal record test \n");
+    if (log_file != stdout) {
+        fprintf(stdout, "\n Done with hal record test \n");
+        fclose(log_file);
+    }
     return 0;
 }
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index ddfb31c..aaa61c0 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -24,13 +24,61 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 #include "qahw_api.h"
 #include "qahw_defs.h"
 
 #define nullptr NULL
+
+#define LATENCY_NODE "/sys/kernel/debug/audio_out_latency_measurement_node"
+#define LATENCY_NODE_INIT_STR "1"
+
+#define AFE_PROXY_SAMPLING_RATE 48000
+#define AFE_PROXY_CHANNEL_COUNT 2
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t riff_fmt;
+    uint32_t fmt_id;
+    uint32_t fmt_sz;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+    uint32_t data_id;
+    uint32_t data_sz;
+};
+
+struct audio_config_params {
+    qahw_module_handle_t *qahw_mod_handle;
+    audio_io_handle_t handle;
+    audio_devices_t input_device;
+    audio_config_t config;
+    audio_input_flags_t flags;
+    const char* kStreamName ;
+    audio_source_t kInputSource;
+    char *file_name;
+    volatile bool thread_exit;
+};
+
+struct proxy_data {
+    struct audio_config_params acp;
+    struct wav_header hdr;
+};
 FILE * log_file = NULL;
 const char *log_filename = NULL;
 float vol_level = 0.01;
+pthread_t proxy_thread;
 
 enum {
     FILE_WAV = 1,
@@ -127,8 +175,8 @@
     }
 }
 
-int async_callback(qahw_stream_callback_event_t event, void *param,
-                  void *cookie)
+int async_callback(qahw_stream_callback_event_t event, void *param __unused,
+                  void *cookie __unused)
 {
     switch (event) {
     case QAHW_STREAM_CBK_EVENT_WRITE_READY:
@@ -148,6 +196,76 @@
     return 0;
 }
 
+void *proxy_read (void* data)
+{
+    struct proxy_data* params = (struct proxy_data*) data;
+    qahw_module_handle_t *qahw_mod_handle = params->acp.qahw_mod_handle;
+    qahw_in_buffer_t in_buf;
+    char *buffer;
+    int rc = 0;
+    int bytes_to_read, bytes_written = 0;
+    FILE *fp = NULL;
+    qahw_stream_handle_t* in_handle = nullptr;
+
+    rc = qahw_open_input_stream(qahw_mod_handle, params->acp.handle,
+              params->acp.input_device, &params->acp.config, &in_handle,
+              params->acp.flags, params->acp.kStreamName, params->acp.kInputSource);
+    if (rc) {
+        fprintf(log_file, "Could not open input stream %d \n",rc);
+        fprintf(stderr, "Could not open input stream %d \n",rc);
+        pthread_exit(0);
+     }
+
+    if (in_handle != NULL) {
+        bytes_to_read = qahw_in_get_buffer_size(in_handle);
+        buffer = (char *) calloc(1, bytes_to_read);
+        if (buffer == NULL) {
+            fprintf(log_file, "calloc failed!!\n");
+            fprintf(stderr, "calloc failed!!\n");
+            pthread_exit(0);
+        }
+
+        if ((fp = fopen(params->acp.file_name,"w"))== NULL) {
+            fprintf(log_file, "Cannot open file to dump proxy data\n");
+            fprintf(stderr, "Cannot open file to dump proxy data\n");
+            pthread_exit(0);
+        }
+        else {
+          params->hdr.num_channels = audio_channel_count_from_in_mask(params->acp.config.channel_mask);
+          params->hdr.sample_rate = params->acp.config.sample_rate;
+          params->hdr.byte_rate = params->hdr.sample_rate * params->hdr.num_channels * 2;
+          params->hdr.block_align = params->hdr.num_channels * 2;
+          params->hdr.bits_per_sample = 16;
+          fwrite(&params->hdr, 1, sizeof(params->hdr), fp);
+        }
+        memset(&in_buf,0, sizeof(qahw_in_buffer_t));
+        in_buf.buffer = buffer;
+        in_buf.bytes = bytes_to_read;
+
+        while (!(params->acp.thread_exit)) {
+            rc = qahw_in_read(in_handle, &in_buf);
+            if (rc > 0) {
+                bytes_written += fwrite((char *)(in_buf.buffer), sizeof(char), (int)in_buf.bytes, fp);
+            }
+        }
+        params->hdr.data_sz = bytes_written;
+        params->hdr.riff_sz = bytes_written + 36; //sizeof(hdr) - sizeof(riff_id) - sizeof(riff_sz)
+        fseek(fp, 0L , SEEK_SET);
+        fwrite(&params->hdr, 1, sizeof(params->hdr), fp);
+        fclose(fp);
+        rc = qahw_in_standby(in_handle);
+        if (rc) {
+            fprintf(log_file, "in standby failed %d \n", rc);
+            fprintf(stderr, "in standby failed %d \n", rc);
+        }
+        rc = qahw_close_input_stream(in_handle);
+        if (rc) {
+            fprintf(log_file, "could not close input stream %d \n", rc);
+            fprintf(stderr, "could not close input stream %d \n", rc);
+        }
+        fprintf(log_file, "pcm data saved to file %s", params->acp.file_name);
+    }
+}
 
 int write_to_hal(qahw_stream_handle_t* out_handle, char *data,
               size_t bytes)
@@ -162,10 +280,10 @@
 
     ret = qahw_out_write(out_handle, &out_buf);
     if (ret < 0 || ret == bytes) {
-        fprintf(log_file, "Writing data to hal failed or full write %ld, %ld\n",
+        fprintf(log_file, "Writing data to hal failed or full write %zd, %zd\n",
             ret, bytes);
     } else if (ret != bytes) {
-        fprintf(log_file, "ret %ld, bytes %ld\n", ret, bytes);
+        fprintf(log_file, "ret %zd, bytes %zd\n", ret, bytes);
         fprintf(log_file, "Waiting for event write ready\n");
         pthread_cond_wait(&write_cond, &write_lock);
         fprintf(log_file, "out of wait for event write ready\n");
@@ -196,7 +314,6 @@
     size_t bytes_written = 0;
     size_t bytes_read = 0;
     char  *data = NULL;
-    qahw_out_buffer_t out_buf;
     bool exit = false;
 
     if (is_offload) {
@@ -218,17 +335,15 @@
     while (!exit) {
         if (!bytes_remaining) {
             bytes_read = fread(data, 1, bytes_wanted, in_file);
-            fprintf(log_file, "fread from file %ld\n", bytes_read);
+            fprintf(log_file, "fread from file %zd\n", bytes_read);
             if (bytes_read <= 0) {
                 if (feof(in_file)) {
-                    fprintf(log_file, "End of file");
+                    fprintf(log_file, "End of file\n");
                     if (is_offload) {
                         pthread_mutex_lock(&drain_lock);
-                        if (is_offload) {
-                            qahw_out_drain(out_handle, QAHW_DRAIN_ALL);
-                            pthread_cond_wait(&drain_cond, &drain_lock);
-                            fprintf(log_file, "Out of compress drain\n");
-                        }
+                        qahw_out_drain(out_handle, QAHW_DRAIN_ALL);
+                        pthread_cond_wait(&drain_cond, &drain_lock);
+                        fprintf(log_file, "Out of compress drain\n");
                         pthread_mutex_unlock(&drain_lock);
                     }
                 } else {
@@ -242,11 +357,11 @@
         }
 
         offset = write_length - bytes_remaining;
-        fprintf(log_file, "bytes_remaining %ld, offset %d, write length %ld\n",
+        fprintf(log_file, "bytes_remaining %zd, offset %d, write length %zd\n",
                 bytes_remaining, offset, write_length);
         bytes_written = write_to_hal(out_handle, data+offset, bytes_remaining);
         bytes_remaining -= bytes_written;
-        fprintf(log_file, "bytes_written %ld, bytes_remaining %ld\n",
+        fprintf(log_file, "bytes_written %zd, bytes_remaining %zd\n",
                 bytes_written, bytes_remaining);
     }
 
@@ -314,10 +429,92 @@
     return aac_format;
 }
 
+int measure_kpi_values(qahw_stream_handle_t* out_handle, bool is_offload) {
+    int rc = 0;
+    int offset = 0;
+    size_t bytes_wanted = 0;
+    size_t write_length = 0;
+    size_t bytes_remaining = 0;
+    size_t bytes_written = 0;
+    char  *data = NULL;
+    int ret = 0, count = 0;
+    struct timespec ts_cold, ts_cont;
+    uint64_t tcold, tcont, scold = 0, uscold = 0, scont = 0, uscont = 0;
+
+    if (is_offload) {
+        fprintf(log_file, "Set callback for offload stream\n");
+        qahw_out_set_callback(out_handle, async_callback, NULL);
+    }
+
+    FILE *fd_latency_node = fopen(LATENCY_NODE, "r+");
+    if (fd_latency_node) {
+        ret = fwrite(LATENCY_NODE_INIT_STR, sizeof(LATENCY_NODE_INIT_STR), 1, fd_latency_node);
+        if (ret<1)
+            fprintf(log_file, "error(%d) writing to debug node!", ret);
+        fflush(fd_latency_node);
+    } else {
+        fprintf(log_file, "debug node(%s) open failed!", LATENCY_NODE);
+        return -1;
+    }
+
+    bytes_wanted = qahw_out_get_buffer_size(out_handle);
+    data = (char *) calloc (1, bytes_wanted);
+    if (data == NULL) {
+        fprintf(log_file, "calloc failed!!\n");
+        return -ENOMEM;
+    }
+
+    while (count < 64) {
+        if (!bytes_remaining) {
+            bytes_remaining = write_length = bytes_wanted;
+        }
+        if (count == 0) {
+            ret = clock_gettime(CLOCK_REALTIME, &ts_cold);
+            if (ret) {
+                fprintf(log_file, "error(%d) fetching start time for cold latency", ret);
+                return -1;
+            }
+        } else if (count == 16) {
+            int *d = (int *)data;
+            d[0] = 0x01010000;
+            ret = clock_gettime(CLOCK_REALTIME, &ts_cont);
+            if (ret) {
+                fprintf(log_file, "error(%d) fetching start time for continuous latency", ret);
+                return -1;
+            }
+        }
+
+        offset = write_length - bytes_remaining;
+        bytes_written = write_to_hal(out_handle, data+offset, bytes_remaining);
+        bytes_remaining -= bytes_written;
+        fprintf(log_file, "bytes_written %zd, bytes_remaining %zd\n",
+                bytes_written, bytes_remaining);
+
+        if (count == 16) {
+            int *i = (int *)data;
+            i[0] = 0x00000000;
+        }
+        count++;
+    }
+
+    char latency_buf[200] = {0};
+    fread((void *) latency_buf, 100, 1, fd_latency_node);
+    fclose(fd_latency_node);
+    sscanf(latency_buf, " %llu,%llu,%*llu,%*llu,%llu,%llu", &scold, &uscold, &scont, &uscont);
+    tcold = scold*1000 - ts_cold.tv_sec*1000 + uscold/1000 - ts_cold.tv_nsec/1000000;
+    tcont = scont*1000 - ts_cont.tv_sec*1000 + uscont/1000 - ts_cont.tv_nsec/1000000;
+    fprintf(log_file, "\n values from debug node %s\n", latency_buf);
+    fprintf(log_file, " cold latency %llums, continuous latency %llums,\n", tcold, tcont);
+    fprintf(log_file, " **Note: please add DSP Pipe/PP latency numbers to this, for final latency values\n");
+    return rc;
+}
+
 void usage() {
     printf(" \n Command \n");
-    printf(" \n hal_play_test <file path>    - path of file to be played\n");
+    printf(" \n hal_play_test -f file-path <options>   - Plays audio file from the path provided\n");
     printf(" \n Options\n");
+    printf(" -f  --file-path <file path>               - file path to be used for playback.\n");
+    printf("                                             file path must be provided unless -K(--kpi) is used\n\n");
     printf(" -r  --sample-rate <sampling rate>         - Required for Non-WAV streams\n");
     printf("                                             For AAC-HE pls specify half the sample rate\n\n");
     printf(" -c  --channel count <channels>            - Required for Non-WAV streams\n\n");
@@ -331,42 +528,52 @@
     printf("                                             1: LC 2: HE_V1 3: HE_V2\n\n");
     printf(" -k  --kvpairs <values>                    - Metadata information of clip\n");
     printf("                                             See Example for more info\n\n");
-    printf(" -l  --log-file <FILEPATH>                 - File path for debug msg, to print\n");
+    printf(" -l  --log-file <ABSOLUTE FILEPATH>        - File path for debug msg, to print\n");
     printf("                                             on console use stdout or 1 \n\n");
+    printf(" -D  --dump-file <ABSOLUTE FILEPATH>       - File path to dump pcm data from proxy\n");
+    printf(" -F  --flags <int value for output flags>  - Output flag to be used\n\n");
+    printf(" -k  --kpi-mode                            - Required for Latency KPI measurement\n");
+    printf("                                             file path is not used here as file playback is not done in this mode\n");
+    printf("                                             file path and other file specific options would be ignored in this mode.\n\n");
     printf(" \n Examples \n");
-    printf(" hal_play_test /etc/Anukoledenadu.wav     -> plays Wav stream with default params\n\n");
-    printf(" hal_play_test /etc/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
+    printf(" hal_play_test -f /etc/Anukoledenadu.wav     -> plays Wav stream with default params\n\n");
+    printf(" hal_play_test -f /etc/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
     printf("                                          -> plays MP3 stream(-t = 2) on speaker device(-d = 2)\n");
     printf("                                          -> 2 channels and 44100 sample rate\n\n");
-    printf(" hal_play_test /etc/AACLC-71-48000Hz-384000bps.aac  -t 4 -d 2 -v 0.05 -r 48000 -c 2 -a 1 \n");
+    printf(" hal_play_test -f /etc/v1-CBR-32kHz-stereo-40kbps.mp3 -t 2 -d 128 -v 0.01 -r 32000 -c 2 -D /data/proxy_dump.wav\n");
+    printf("                                          -> plays MP3 stream(-t = 2) on BT device(-d = 128)\n");
+    printf("                                          -> 2 channels and 32000 sample rate\n");
+    printf("                                          -> dumps pcm data to file at /data/proxy_dump.wav\n\n");
+    printf(" hal_play_test -f /etc/AACLC-71-48000Hz-384000bps.aac  -t 4 -d 2 -v 0.05 -r 48000 -c 2 -a 1 \n");
     printf("                                          -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
     printf("                                          -> AAC format type is LC(-a = 1)\n");
     printf("                                          -> 2 channels and 48000 sample rate\n\n");
-    printf(" hal_play_test /etc/AACHE-adts-stereo-32000KHz-128000Kbps.aac  -t 4 -d 2 -v 0.05 -r 16000 -c 2 -a 3 \n");
+    printf(" hal_play_test -f /etc/AACHE-adts-stereo-32000KHz-128000Kbps.aac  -t 4 -d 2 -v 0.05 -r 16000 -c 2 -a 3 \n");
     printf("                                          -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
     printf("                                          -> AAC format type is HE V2(-a = 3)\n");
     printf("                                          -> 2 channels and 16000 sample rate\n");
     printf("                                          -> note that the sample rate is half the actual sample rate\n\n");
-    printf(" hal_play_test /etc/2.0_16bit_48khz.m4a -k 1536000,16,0,0,4096,14,16388,0,10,2,40,48000,1536000,48000 -t 6 -r 48000 -c 2 -v 0.5 \n");
+    printf(" hal_play_test -f /etc/2.0_16bit_48khz.m4a -k 1536000,16,0,0,4096,14,16388,0,10,2,40,48000,1536000,48000 -t 6 -r 48000 -c 2 -v 0.5 \n");
     printf("                                          -> Play alac clip (-t = 6)\n");
     printf("                                          -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n");
     printf("                                          ->alac_avg_bit_rate,alac_bit_depth,alac_channel_layout,alac_compatible_version,\n");
     printf("                                          ->alac_frame_length,alac_kb,alac_max_frame_bytes,alac_max_run,alac_mb,\n");
     printf("                                          ->alac_num_channels,alac_pb,alac_sampling_rate,avg_bit_rate,sample_rate\n\n");
-    printf(" hal_play_test /etc/DIL CHAHTA HAI.flac -k 0,4096,13740,4096,14 -t 5 -r 48000 -c 2 -v 0.5 \n");
+    printf(" hal_play_test -f /etc/DIL CHAHTA HAI.flac -k 0,4096,13740,4096,14 -t 5 -r 48000 -c 2 -v 0.5 \n");
     printf("                                          -> Play flac clip (-t = 5)\n");
     printf("                                          -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n");
     printf("                                          ->avg_bit_rate,flac_max_blk_size,flac_max_frame_size\n");
     printf("                                          ->flac_min_blk_size,flac_min_frame_size,sample_rate\n");
-    printf(" hal_play_test /etc/vorbis.mka -k 500000,48000,1 -t 7 -r 48000 -c 2 -v 0.5 \n");
+    printf(" hal_play_test -f /etc/vorbis.mka -k 500000,48000,1 -t 7 -r 48000 -c 2 -v 0.5 \n");
     printf("                                          -> Play vorbis clip (-t = 7)\n");
     printf("                                          -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n");
     printf("                                          ->avg_bit_rate,sample_rate,vorbis_bitstream_fmt\n");
-    printf(" hal_play_test /etc/file.wma -k 192000,48000,16,8192,3,15,353 -t 8 -r 48000 -c 2 -v 0.5 \n");
+    printf(" hal_play_test -f /etc/file.wma -k 192000,48000,16,8192,3,15,353 -t 8 -r 48000 -c 2 -v 0.5 \n");
     printf("                                          -> Play wma clip (-t = 8)\n");
     printf("                                          -> kvpair(-k) values represent media-info of clip & values should be in below mentioned sequence\n");
     printf("                                          ->avg_bit_rate,sample_rate,wma_bit_per_sample,wma_block_align\n");
     printf("                                          ->wma_channel_mask,wma_encode_option,wma_format_tag\n");
+    printf(" hal_play_test -K -F 4                    -> Measure latency KPIs for low latency output\n\n");
 }
 
 int main(int argc, char* argv[]) {
@@ -380,40 +587,65 @@
     int rc = 0;
     char *kvpair_values = NULL;
     char kvpair[1000] = {0};
+    struct proxy_data proxy_params;
 
     /*
      * Default values
      */
+    bool kpi_mode = false;
+    bool flags_set = false;
+    bool proxy_thread_active = false;
     int filetype = FILE_WAV;
     int sample_rate = 44100;
     int channels = 2;
-    const int audio_device_base = 0x2;/* spkr device*/
     aac_format_type_t format_type = AAC_LC;
     log_file = stdout;
     audio_devices_t output_device = AUDIO_DEVICE_OUT_SPEAKER;
+    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+    proxy_params.acp.file_name = "/data/pcm_dump.wav";
 
     struct option long_options[] = {
         /* These options set a flag. */
+        {"file-path",     required_argument,    0, 'f'},
         {"device",        required_argument,    0, 'd'},
         {"sample-rate",   required_argument,    0, 'r'},
         {"channels",      required_argument,    0, 'c'},
         {"volume",        required_argument,    0, 'v'},
         {"log-file",      required_argument,    0, 'l'},
+        {"dump-file",     required_argument,    0, 'D'},
         {"file-type",     required_argument,    0, 't'},
         {"aac-type",      required_argument,    0, 'a'},
         {"kvpairs",       required_argument,    0, 'k'},
+        {"flags",         required_argument,    0, 'F'},
+        {"kpi-mode",      no_argument,          0, 'K'},
         {"help",          no_argument,          0, 'h'},
         {0, 0, 0, 0}
     };
 
     int opt = 0;
     int option_index = 0;
+    proxy_params.hdr.riff_id = ID_RIFF;
+    proxy_params.hdr.riff_sz = 0;
+    proxy_params.hdr.riff_fmt = ID_WAVE;
+    proxy_params.hdr.fmt_id = ID_FMT;
+    proxy_params.hdr.fmt_sz = 16;
+    proxy_params.hdr.audio_format = FORMAT_PCM;
+    proxy_params.hdr.num_channels = channels;
+    proxy_params.hdr.sample_rate = sample_rate;
+    proxy_params.hdr.byte_rate = proxy_params.hdr.sample_rate * proxy_params.hdr.num_channels * 2;
+    proxy_params.hdr.block_align = proxy_params.hdr.num_channels * 2;
+    proxy_params.hdr.bits_per_sample = 16;
+    proxy_params.hdr.data_id = ID_DATA;
+    proxy_params.hdr.data_sz = 0;
     while ((opt = getopt_long(argc,
                               argv,
-                              "-r:c:d:v:l::t:a:k:h",
+                              "-f:r:c:d:v:l:t:a:k:D:KF:h",
                               long_options,
                               &option_index)) != -1) {
             switch (opt) {
+            case 'f':
+                filename = optarg;
+                break;
             case 'r':
                 sample_rate = atoi(optarg);
                 break;
@@ -421,21 +653,18 @@
                 channels = atoi(optarg);
                 break;
             case 'd':
-                output_device = atoi(optarg);
+                output_device = atoll(optarg);
                 break;
             case 'v':
                 vol_level = atof(optarg);
                 break;
             case 'l':
-                /*
-                 * Fix Me: unable to log to a given file.
-                 */
                 log_filename = optarg;
-                if((log_file = fopen(log_filename,"wb"))== NULL) {
+                if (strcasecmp(log_filename, "stdout") &&
+                    strcasecmp(log_filename, "1") &&
+                    (log_file = fopen(log_filename,"wb")) == NULL) {
                     fprintf(stderr, "Cannot open log file %s\n", log_filename);
-                    /*
-                     * continue to log to std out.
-                     */
+                    /* continue to log to std out. */
                     log_file = stdout;
                 }
 
@@ -449,6 +678,16 @@
             case 'k':
                 kvpair_values = optarg;
                 break;
+            case 'D':
+                proxy_params.acp.file_name = optarg;
+                break;
+            case 'K':
+                kpi_mode = true;
+                break;
+            case 'F':
+                flags = atoll(optarg);
+                flags_set = true;
+                break;
             case 'h':
                 usage();
                 return 0;
@@ -456,21 +695,20 @@
          }
     }
 
-    filename = argv[1];
-    if((file_stream = fopen(filename, "r"))== NULL) {
-        fprintf(stderr, "Cannot Open Audio File %s\n", filename);
-        goto EXIT;
+    if (!kpi_mode) {
+        if (filename == nullptr) {
+            fprintf(stderr, "File name is must for non kpi-mode\n");
+            usage();
+            goto EXIT;
+        }
+        if ((file_stream = fopen(filename, "r"))== NULL) {
+            fprintf(stderr, "Cannot Open Audio File %s\n", filename);
+            goto EXIT;
+        }
+        fprintf(stdout, "Playing:%s\n", filename);
+        fprintf(stdout, "File Type:%d\n", filetype);
     }
 
-    /*
-     * Set to a high number so it doesn't interfere with existing stream handles
-     */
-
-    audio_io_handle_t handle = 0x999;
-    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
-
-    fprintf(stdout, "Playing:%s\n", filename);
-    fprintf(stdout, "File Type:%d\n", filetype);
     fprintf(stdout, "Sample Rate:%d\n", sample_rate);
     fprintf(stdout, "Channels:%d\n", channels);
     fprintf(stdout, "Log file:%s\n", log_filename);
@@ -478,6 +716,15 @@
     fprintf(stdout, "Output Device:%d\n", output_device);
     fprintf(stdout, "Format Type:%d\n", format_type);
     fprintf(stdout, "kvpair values:%s\n", kvpair_values);
+    fprintf(stdout, "Output Flags:%d\n", flags);
+
+    /*
+     * Set to a high number so it doesn't interfere with existing stream handles
+     */
+    audio_io_handle_t handle = 0x999;
+
+    if (output_device & AUDIO_DEVICE_OUT_ALL_A2DP)
+        fprintf(stdout, "Saving pcm data to file: %s\n", proxy_params.acp.file_name);
 
     fprintf(stdout, "Starting audio hal tests.\n");
 
@@ -486,87 +733,69 @@
     audio_config_t config;
     memset(&config, 0, sizeof(audio_config_t));
 
-    switch (filetype) {
-    case FILE_WAV:
-        /*
-         * Read the wave header
-         */
-        rc = fread (header, 44 , 1, file_stream);
-        if (rc != 1) {
-           fprintf(stdout, "Error .Fread failed\n");
-           exit(0);
-        }
-        if (strncmp (header, "RIFF", 4) && strncmp (header+8, "WAVE", 4)) {
-           fprintf(stdout, "Not a wave format\n");
-           exit (1);
-        }
-        memcpy (&channels, &header[22], 2);
-        memcpy (&sample_rate, &header[24], 4);
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.offload_info.sample_rate = sample_rate;
+    if (kpi_mode) {
         config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
-        break;
+    } else {
 
-    case FILE_MP3:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        config.offload_info.format = AUDIO_FORMAT_MP3;
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
+        if (!flags_set)
+            flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING;
 
-    case FILE_AAC:
-    case FILE_AAC_ADTS:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        if (!is_valid_aac_format_type(format_type)) {
-            fprintf(log_file, "Invalid format type for AAC %d\n", format_type);
-            goto EXIT;
+        switch (filetype) {
+        case FILE_WAV:
+            /*
+             * Read the wave header
+             */
+            rc = fread (header, 44 , 1, file_stream);
+            if (rc != 1) {
+               fprintf(stdout, "Error .Fread failed\n");
+               exit(0);
+            }
+            if (strncmp (header, "RIFF", 4) && strncmp (header+8, "WAVE", 4)) {
+               fprintf(stdout, "Not a wave format\n");
+               exit (1);
+            }
+            memcpy (&channels, &header[22], 2);
+            memcpy (&sample_rate, &header[24], 4);
+            config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
+            if (!flags_set)
+                flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+            break;
+
+        case FILE_MP3:
+            config.offload_info.format = AUDIO_FORMAT_MP3;
+            break;
+
+        case FILE_AAC:
+        case FILE_AAC_ADTS:
+            if (!is_valid_aac_format_type(format_type)) {
+                fprintf(log_file, "Invalid format type for AAC %d\n", format_type);
+                goto EXIT;
+            }
+            config.offload_info.format = get_aac_format(filetype, format_type);
+            break;
+        case FILE_FLAC:
+            config.offload_info.format = AUDIO_FORMAT_FLAC;
+            break;
+        case FILE_ALAC:
+            config.offload_info.format = AUDIO_FORMAT_ALAC;
+            break;
+        case FILE_VORBIS:
+            config.offload_info.format = AUDIO_FORMAT_VORBIS;
+            break;
+        case FILE_WMA:
+            config.offload_info.format = AUDIO_FORMAT_WMA;
+            break;
+        default:
+           fprintf(stderr, "Does not support given filetype\n");
+           usage();
+           return 0;
         }
-        config.offload_info.format = get_aac_format(filetype, format_type);
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
-    case FILE_FLAC:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        config.offload_info.format = AUDIO_FORMAT_FLAC;
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
-    case FILE_ALAC:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        config.offload_info.format = AUDIO_FORMAT_ALAC;
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
-    case FILE_VORBIS:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        config.offload_info.format = AUDIO_FORMAT_VORBIS;
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
-    case FILE_WMA:
-        config.channel_mask = audio_channel_out_mask_from_count(channels);
-        config.offload_info.channel_mask = config.channel_mask;
-        config.sample_rate = sample_rate;
-        config.offload_info.sample_rate = sample_rate;
-        config.offload_info.format = AUDIO_FORMAT_WMA;
-        flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-        break;
-    default:
-       fprintf(stderr, "Does not support given filetype\n");
-       usage();
-       return 0;
     }
+    config.channel_mask = audio_channel_out_mask_from_count(channels);
+    config.sample_rate = sample_rate;
+    config.format = config.offload_info.format;
+    config.offload_info.channel_mask = config.channel_mask;
+    config.offload_info.sample_rate = sample_rate;
     config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT;
     config.offload_info.size = sizeof(audio_offload_info_t);
 
@@ -574,6 +803,9 @@
         , output_device, config.offload_info.sample_rate);
     const char* stream_name = "output_stream";
 
+    if (output_device & AUDIO_DEVICE_OUT_ALL_A2DP) {
+        output_device = AUDIO_DEVICE_OUT_PROXY;
+    }
     fprintf(log_file, "calling open_out_put_stream:\n");
     rc = qahw_open_output_stream(qahw_mod_handle,
                                  handle,
@@ -589,6 +821,11 @@
         goto EXIT;
     }
 
+    if (kpi_mode) {
+        measure_kpi_values(out_handle, (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
+        goto EXIT;
+    }
+
     switch(filetype) {
     case FILE_WMA:
     case FILE_VORBIS:
@@ -606,12 +843,42 @@
         break;
     }
 
+    if (output_device & AUDIO_DEVICE_OUT_PROXY) {
+        proxy_params.acp.qahw_mod_handle = qahw_mod_handle;
+        proxy_params.acp.handle = 0x998;
+        proxy_params.acp.input_device = AUDIO_DEVICE_IN_PROXY;
+        proxy_params.acp.flags = AUDIO_INPUT_FLAG_NONE;
+        proxy_params.acp.config.channel_mask = audio_channel_in_mask_from_count(AFE_PROXY_CHANNEL_COUNT);
+        proxy_params.acp.config.sample_rate = AFE_PROXY_SAMPLING_RATE;
+        proxy_params.acp.config.format = AUDIO_FORMAT_PCM_16_BIT;
+        proxy_params.acp.kStreamName = "input_stream";
+        proxy_params.acp.kInputSource = AUDIO_SOURCE_UNPROCESSED;
+        proxy_params.acp.thread_exit = false;
+        fprintf(log_file, "create thread to read data from proxy \n");
+        rc = pthread_create(&proxy_thread, NULL, proxy_read, (void *)&proxy_params);
+        if (!rc)
+            proxy_thread_active = true;
+    }
     play_file(out_handle,
               file_stream,
              (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
 
+    if (proxy_thread_active) {
+        /*
+         * DSP gives drain ack for last buffer which will close proxy thread before
+         * app reads last buffer. So add sleep before exiting proxy thread to read
+         * last buffer of data. This is not a calculated value.
+         */
+        usleep(500000);
+        proxy_params.acp.thread_exit = true;
+        fprintf(log_file, "wait for proxy thread exit\n");
+    }
+
 EXIT:
 
+    if (proxy_thread_active)
+        pthread_join(proxy_thread, NULL);
+
     if (out_handle != nullptr) {
         rc = qahw_out_standby(out_handle);
         if (rc) {