/*
 * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2015 The Android Open Source Project *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* 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 <signal.h>
#include <errno.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;
volatile bool stop_record = false;

#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_input_flags_t flags;
    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;

int sourcetrack_done = 0;
static pthread_mutex_t sourcetrack_lock;
struct qahw_sound_focus_param sound_focus_data;

static bool request_wake_lock(bool wakelock_acquired, bool enable)
{
   int system_ret;

   if (enable) {
       if (!wakelock_acquired) {
           system_ret = system("echo audio_services > /sys/power/wake_lock");
           if (system_ret < 0) {
               fprintf(stderr, "%s.Failed to acquire audio_service lock\n", __func__);
               fprintf(log_file, "%s.Failed to acquire audio_service lock\n", __func__);
           } else {
               wakelock_acquired = true;
               fprintf(log_file, "%s.Success to acquire audio_service lock\n", __func__);
           }
       } else
            fprintf(log_file, "%s.Lock is already acquired\n", __func__);
   }

   if (!enable) {
       if (wakelock_acquired) {
           system_ret = system("echo audio_services > /sys/power/wake_unlock");
           if (system_ret < 0) {
               fprintf(stderr, "%s.Failed to release audio_service lock\n", __func__);
               fprintf(log_file, "%s.Failed to release audio_service lock\n", __func__);
           } else {
               wakelock_acquired = false;
               fprintf(log_file, "%s.Success to release audio_service lock\n", __func__);
           }
       } else
            fprintf(log_file, "%s.No Lock is acquired to release\n", __func__);
   }
   return wakelock_acquired;
}

void stop_signal_handler(int signal __unused)
{
   stop_record = true;
}


void read_soundfocus_param(void)
{
    uint16_t start_angle[4] = {0};
    uint8_t enable_sector[4] = {0};
    uint16_t gain_step;

    printf("\nEnter soundfocusparams startangle :::::");
    scanf("%hd %hd %hd %hd",&start_angle[0], &start_angle[1],
                            &start_angle[2], &start_angle[3]);
    memcpy(&sound_focus_data.start_angle, start_angle, sizeof(start_angle));

    printf("\nEnter soundfocusparams enablesector :::::");
    scanf("%hhd %hhd %hhd %hhd",&enable_sector[0], &enable_sector[1],
                                &enable_sector[2], &enable_sector[3]);
    memcpy(&sound_focus_data.enable, enable_sector, sizeof(enable_sector));

    printf("\nEnter soundfocusparams gainstep:::::");
    scanf("%hd",&gain_step);
    memcpy(&sound_focus_data.gain_step, &gain_step, sizeof(gain_step));
}

void sourcetrack_signal_handler(int signal __unused)
{
/* Function to read keyboard interupt to enable user to set parameters
   for sourcetracking usecase Dynamically */

    pthread_mutex_lock(&sourcetrack_lock);
    read_soundfocus_param();
    pthread_mutex_unlock(&sourcetrack_lock);
}

void *read_sourcetrack_data(void* data)
{
    int idx =0, status = 0,count = 0, sect = 0;
    qahw_param_id param;
    qahw_param_payload payload;
    qahw_module_handle_t *qawh_module_handle = NULL;

    qawh_module_handle = (qahw_module_handle_t *)data;

    while (1) {
        sleep(1);
        if (sourcetrack_done == 1)
            return NULL;

        pthread_mutex_lock(&sourcetrack_lock);
        payload = (qahw_param_payload)sound_focus_data;
        param = QAHW_PARAM_SOUND_FOCUS;
        status = qahw_set_param_data(qawh_module_handle, param, &payload);
        if (status != 0)
            fprintf(log_file, "Error.Failed Set SoundFocus Params\n");

        fprintf(log_file, "\nGet SoundFocus Params from app");
        status = qahw_get_param_data(qawh_module_handle, param, &payload);
        if (status < 0) {
                fprintf(log_file, "Failed to get sound focus params\n");
        } else {
           memcpy (&sound_focus_data, &payload.sf_params,
                   sizeof(struct qahw_sound_focus_param));
           for (idx = 0; idx < 4; idx++){
                fprintf(log_file, "\nstart_angle[%d]=%d",idx,
                        payload.sf_params.start_angle[idx]);
                fprintf(log_file, " enable[%d]=%d",idx,
                        payload.sf_params.enable[idx]);
                fprintf(log_file, " gain=%d\n",payload.sf_params.gain_step);
           }
        }
        param = QAHW_PARAM_SOURCE_TRACK;
        status = qahw_get_param_data(qawh_module_handle, param, &payload);
        if (status < 0) {
            fprintf (log_file, "Failed to get source tracking params\n");
        } else {
            for (idx = 0; idx < 4; idx++){
                fprintf(log_file, "vad[%d]=%d ",idx, payload.st_params.vad[idx]);
                if (idx < 3)
                    fprintf(log_file, "doa_noise[%d]=%d \n",
                            idx, payload.st_params.doa_noise[idx]);
            }
            fprintf(log_file, "doa_speech=%d\n",payload.st_params.doa_speech);
            fprintf(log_file, "polar_activity:");
            for (sect = 0; sect < 4; sect++ ){
                fprintf(log_file, "\nSector No-%d:\n",sect + 1);
                idx = sound_focus_data.start_angle[sect];
                count = sound_focus_data.start_angle[(sect + 1)%4] -
                        sound_focus_data.start_angle[sect];
                if (count <0)
                    count = count + 360;
                do {
                    fprintf(log_file, "%d,",payload.st_params.polar_activity[idx%360]);
                    count--;
                    idx++;
                } while (count);
                fprintf(log_file, "\n");
            }
        }
        pthread_mutex_unlock(&sourcetrack_lock);
    }
}

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, 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;

  /* 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);
  }

  /* Turn BT_SCO on if bt_sco recording */
  if(audio_is_bluetooth_sco_device(params->input_device)) {
      int ret = -1;
      const char * bt_sco_on = "BT_SCO=on";
      ret = qahw_set_parameters(qahw_mod_handle, bt_sco_on);
      fprintf(log_file, " param %s set to hal with return value %d\n", bt_sco_on, ret);
  }

  /* 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, "input_stream",
                              params->source);
  if (rc) {
      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 = qahw_in_get_buffer_size(in_handle);
  char *buffer = (char *)calloc(1, buffer_size);
  size_t written_size;
  if (buffer == NULL) {
      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);
  }

  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);

  /* Caution: Below ADL log shouldnt be altered without notifying automation APT since it used for
   * automation testing
   */
  fprintf(log_file, "\n ADL: Please speak into the microphone for %lf seconds, handle(%d)\n", params->record_length, params->handle);
  if (log_file != stdout)
      fprintf(stdout, "\n ADL: Please speak into the microphone for %lf seconds, handle(%d)\n", params->record_length, params->handle);

  snprintf(file_name + name_len, sizeof(file_name) - name_len, "%d.wav", (0x99A - params->handle));
  FILE *fd = fopen(file_name,"w");
  if (fd == NULL) {
      fprintf(log_file, "File open failed \n");
      if (log_file != stdout)
          fprintf(stdout, "File open failed \n");
      test_end();
      pthread_exit(0);
  }
  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 && !stop_record) {
      if(time_elapsed < params->record_delay) {
          usleep(1000000*(params->record_delay - time_elapsed));
          time_elapsed = difftime(time(0), start_time);
          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);

      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);
      written_size = fwrite(in_buf.buffer, 1, buffer_size, fd);
      if (written_size < buffer_size) {
         printf("Error in fwrite(%d)=%s\n",ferror(fd), strerror(ferror(fd)));
         break;
      }
      data_sz += buffer_size;
  }
  /*Stopping sourcetracking thread*/
  sourcetrack_done = 1;

  /* 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);

  /* 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 = ((uint64_t)tsCont.tv_sec)*1000 - tsec*1000 + ((uint64_t)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);
  printf("closing input");

  /* Close input stream and device. */
  rc = qahw_in_standby(in_handle);
  if (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) {
      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.
   * Caution: Below ADL log shouldnt be altered without notifying automation APT since it used for
   * automation testing
   */
  fprintf(log_file, "\n\n ADL: 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);
  if (log_file != stdout)
      fprintf(stdout, "\n\n ADL: 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);

  test_end();
  return NULL;
}

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", &thread_param->input_device);
    thread_param->input_device |= AUDIO_DEVICE_BIT_IN;

    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 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", &thread_param->config.sample_rate);

    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);

    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->record_length);

    printf("\n Enter the start delay for the session in seconds ::::  ");
    scanf(" %lf", &thread_param->record_delay);

    return 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 -> 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.\n\n");
    printf(" hal_rec_test -S -c 1 -r 48000 -t 30 -> Enable Sourcetracking\n");
    printf("                                      For mono channel 48kHz rate for 30seconds\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";
    bool wakelock_acquired = false;

    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;
         }
    }

    wakelock_acquired = request_wake_lock(wakelock_acquired, true);
    qahw_mod_handle = qahw_load_module(mod_name);
    if(qahw_mod_handle == NULL) {
        fprintf(log_file, " qahw_load_module failed");
        return -1;
    }
    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:
            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:
            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:
            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:
            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:
            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 (source_tracking && max_recordings_requested) {
        if (pthread_mutex_init(&sourcetrack_lock, NULL) != 0) {
               printf("\n mutex init failed\n");
               source_tracking = 0;
               goto sourcetrack_error;
        }
        if (signal(SIGQUIT, sourcetrack_signal_handler) == SIG_ERR)
            fprintf(log_file, "Failed to register SIGQUIT:%d\n",errno);
        else
            printf("NOTE::::To set sourcetracking params at runtime press 'Ctrl+\\' from keyboard\n");
        read_soundfocus_param();
        printf("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_tracking = 0;
            pthread_mutex_destroy(&sourcetrack_lock);
            goto sourcetrack_error;
        }
    }

    /* Register the SIGINT to close the App properly */
    if (signal(SIGINT, stop_signal_handler) == SIG_ERR)
        fprintf(log_file, "Failed to register SIGINT:%d\n",errno);

    if (thread_active[0] == 1) {
        fprintf(log_file, "\n Create first record thread \n");
        ret = pthread_create(&tid[0], NULL, start_input, (void *)&params[0]);
        if (ret) {
            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) {
        fprintf(log_file, "Create second record thread \n");
        ret = pthread_create(&tid[1], NULL, start_input, (void *)&params[1]);
        if (ret) {
            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) {
        fprintf(log_file, "Create third record thread \n");
        ret = pthread_create(&tid[2], NULL, start_input, (void *)&params[2]);
        if (ret) {
            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) {
        fprintf(log_file, "Create fourth record thread \n");
        ret = pthread_create(&tid[3], NULL, start_input, (void *)&params[3]);
        if (ret) {
            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;
        }
    }
    fprintf(log_file, " All threads started \n");
    if (log_file != stdout)
        fprintf(stdout, " All threads started \n");

    /* 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);
            }
        }
        usleep(10000);
    }

    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);
        fprintf(log_file, " after first record thread exit \n");
    }
    if (thread_active[1] == 1) {
        pthread_join(tid[1], NULL);
        fprintf(log_file, " after second record thread exit \n");
    }
    if (thread_active[2] == 1) {
        pthread_join(tid[2], NULL);
        fprintf(log_file, " after third record thread exit \n");
    }
    if (thread_active[3] == 1) {
        pthread_join(tid[3], NULL);
        fprintf(log_file, " after fourth record thread exit \n");
    }

sourcetrack_error:
    if (source_tracking) {
        pthread_join(sourcetrack_thread,NULL);
        pthread_mutex_destroy(&sourcetrack_lock);
        printf("after source tracking thread exit \n");
    }
    ret = qahw_unload_module(qahw_mod_handle);
    if (ret) {
        fprintf(log_file, "could not unload hal %d \n",ret);
    }

    /* Caution: Below ADL log shouldnt be altered without notifying automation APT since it used
     * for automation testing
     */
    fprintf(log_file, "\n ADL: Done with hal record test \n");
    if (log_file != stdout) {
        fprintf(stdout, "\n ADL: Done with hal record test \n");
        fclose(log_file);
    }
    wakelock_acquired = request_wake_lock(wakelock_acquired, false);
    return 0;
}
