merge in KQS81M
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index c2997c6..c5e44aa 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1220,7 +1220,7 @@
             }
         }
 
-        if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
+        if ((adev->mode == AUDIO_MODE_NORMAL) && adev->in_call &&
                 (out == adev->primary_output)) {
             stop_voice_call(adev);
         }
@@ -1422,19 +1422,37 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
     int ret = -1;
+    unsigned long dsp_frames;
 
     pthread_mutex_lock(&out->lock);
 
-    if (out->pcm) {
-        size_t avail;
-        if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
-            size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
-            // FIXME This calculation is incorrect if there is buffering after app processor
-            int64_t signed_frames = out->written - kernel_buffer_size + avail;
-            // It would be unusual for this value to be negative, but check just in case ...
-            if (signed_frames >= 0) {
-                *frames = signed_frames;
-                ret = 0;
+    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+        if (out->compr != NULL) {
+            compress_get_tstamp(out->compr, &dsp_frames,
+                    &out->sample_rate);
+            ALOGVV("%s rendered frames %ld sample_rate %d",
+                   __func__, dsp_frames, out->sample_rate);
+            *frames = dsp_frames;
+            ret = 0;
+            /* this is the best we can do */
+            clock_gettime(CLOCK_MONOTONIC, timestamp);
+        }
+    } else {
+        if (out->pcm) {
+            size_t avail;
+            if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
+                size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
+                int64_t signed_frames = out->written - kernel_buffer_size + avail;
+                // This adjustment accounts for buffering after app processor.
+                // It is based on estimated DSP latency per use case, rather than exact.
+                signed_frames -=
+                    (platform_render_latency(out->usecase) * out->sample_rate / 1000000LL);
+
+                // It would be unusual for this value to be negative, but check just in case ...
+                if (signed_frames >= 0) {
+                    *frames = signed_frames;
+                    ret = 0;
+                }
             }
         }
     }
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index e533f33..b200e27 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -200,6 +200,9 @@
     [SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
 };
 
+#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
+#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
+
 static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
 static bool is_tmus = false;
 
@@ -880,3 +883,16 @@
 
     return max_channels;
 }
+
+/* Delay in Us */
+int64_t platform_render_latency(audio_usecase_t usecase)
+{
+    switch (usecase) {
+        case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+            return DEEP_BUFFER_PLATFORM_DELAY;
+        case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
+            return LOW_LATENCY_PLATFORM_DELAY;
+        default:
+            return 0;
+    }
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index cd7150c..b5d568f 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -185,6 +185,9 @@
     [SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
 };
 
+#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
+#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
+
 static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
 static bool is_tmus = false;
 
@@ -840,3 +843,16 @@
 
     return max_channels;
 }
+
+/* Delay in Us */
+int64_t platform_render_latency(audio_usecase_t usecase)
+{
+    switch (usecase) {
+        case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+            return DEEP_BUFFER_PLATFORM_DELAY;
+        case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
+            return LOW_LATENCY_PLATFORM_DELAY;
+        default:
+            return 0;
+    }
+}
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 2362a5b..afd2ee4 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -36,4 +36,7 @@
 int platform_set_hdmi_channels(void *platform, int channel_count);
 int platform_edid_get_max_channels(void *platform);
 
+/* returns the latency for a usecase in Us */
+int64_t platform_render_latency(audio_usecase_t usecase);
+
 #endif // QCOM_AUDIO_PLATFORM_API_H
diff --git a/visualizer/offload_visualizer.c b/visualizer/offload_visualizer.c
index 75ce4a5..eb43558 100644
--- a/visualizer/offload_visualizer.c
+++ b/visualizer/offload_visualizer.c
@@ -17,6 +17,7 @@
 #define LOG_TAG "offload_visualizer"
 /*#define LOG_NDEBUG 0*/
 #include <assert.h>
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -80,6 +81,16 @@
 
 #define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
 
+#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
+
+/* maximum number of buffers for which we keep track of the measurements */
+#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
+
+typedef struct buffer_stats_s {
+    bool is_valid;
+    uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
+    float rms_squared; /* the average square of the samples in a buffer */
+} buffer_stats_t;
 
 typedef struct visualizer_context_s {
     effect_context_t common;
@@ -91,6 +102,12 @@
     uint32_t latency;
     struct timespec buffer_update_time;
     uint8_t capture_buf[CAPTURE_BUF_SIZE];
+    /* for measurements */
+    uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
+    uint32_t meas_mode;
+    uint8_t meas_wndw_size_in_buffers;
+    uint8_t meas_buffer_idx;
+    buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
 } visualizer_context_t;
 
 
@@ -507,6 +524,23 @@
  * Visualizer operations
  */
 
+uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) {
+    uint32_t delta_ms = 0;
+    if (visu_ctxt->buffer_update_time.tv_sec != 0) {
+        struct timespec ts;
+        if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+            time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
+            long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
+            if (nsec < 0) {
+                --secs;
+                nsec += 1000000000;
+            }
+            delta_ms = secs * 1000 + nsec / 1000000;
+        }
+    }
+    return delta_ms;
+}
+
 int visualizer_reset(effect_context_t *context)
 {
     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
@@ -521,6 +555,8 @@
 
 int visualizer_init(effect_context_t *context)
 {
+    int32_t i;
+
     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
 
     context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
@@ -543,6 +579,17 @@
     visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX;
     visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
 
+    // measurement initialization
+    visu_ctxt->channel_count = popcount(context->config.inputCfg.channels);
+    visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
+    visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
+    visu_ctxt->meas_buffer_idx = 0;
+    for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+        visu_ctxt->past_meas[i].is_valid = false;
+        visu_ctxt->past_meas[i].peak_u16 = 0;
+        visu_ctxt->past_meas[i].rms_squared = 0;
+    }
+
     set_config(context, &context->config);
 
     return 0;
@@ -571,6 +618,12 @@
         p->vsize = sizeof(uint32_t);
         *size += sizeof(uint32_t);
         break;
+    case VISUALIZER_PARAM_MEASUREMENT_MODE:
+        ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode);
+        *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode;
+        p->vsize = sizeof(uint32_t);
+        *size += sizeof(uint32_t);
+        break;
     default:
         p->status = -EINVAL;
     }
@@ -598,6 +651,10 @@
          * visu_ctxt->latency = *((uint32_t *)p->data + 1); */
         ALOGV("%s set latency = %d", __func__, visu_ctxt->latency);
         break;
+    case VISUALIZER_PARAM_MEASUREMENT_MODE:
+        visu_ctxt->meas_mode = *((uint32_t *)p->data + 1);
+        ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode);
+        break;
     default:
         return -EINVAL;
     }
@@ -621,6 +678,30 @@
         return -EINVAL;
     }
 
+    // perform measurements if needed
+    if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) {
+        // find the peak and RMS squared for the new buffer
+        uint32_t inIdx;
+        int16_t max_sample = 0;
+        float rms_squared_acc = 0;
+        for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) {
+            if (inBuffer->s16[inIdx] > max_sample) {
+                max_sample = inBuffer->s16[inIdx];
+            } else if (-inBuffer->s16[inIdx] > max_sample) {
+                max_sample = -inBuffer->s16[inIdx];
+            }
+            rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
+        }
+        // store the measurement
+        visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample;
+        visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared =
+                rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count);
+        visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true;
+        if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) {
+            visu_ctxt->meas_buffer_idx = 0;
+        }
+    }
+
     /* all code below assumes stereo 16 bit PCM output and input */
     int32_t shift;
 
@@ -700,23 +781,12 @@
 
         if (context->state == EFFECT_STATE_ACTIVE) {
             int32_t latency_ms = visu_ctxt->latency;
-            uint32_t delta_ms = 0;
-            if (visu_ctxt->buffer_update_time.tv_sec != 0) {
-                struct timespec ts;
-                if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
-                    time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
-                    long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
-                    if (nsec < 0) {
-                        --secs;
-                        nsec += 1000000000;
-                    }
-                    delta_ms = secs * 1000 + nsec / 1000000;
-                    latency_ms -= delta_ms;
-                    if (latency_ms < 0)
-                        latency_ms = 0;
-                }
+            const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
+            latency_ms -= delta_ms;
+            if (latency_ms < 0) {
+                latency_ms = 0;
             }
-            uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
+            const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
 
             int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
             int32_t capture_size = visu_ctxt->capture_size;
@@ -753,6 +823,56 @@
         }
         break;
 
+    case VISUALIZER_CMD_MEASURE: {
+        uint16_t peak_u16 = 0;
+        float sum_rms_squared = 0.0f;
+        uint8_t nb_valid_meas = 0;
+        /* reset measurements if last measurement was too long ago (which implies stored
+         * measurements aren't relevant anymore and shouldn't bias the new one) */
+        const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
+        if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) {
+            uint32_t i;
+            ALOGV("Discarding measurements, last measurement is %dms old", delay_ms);
+            for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+                visu_ctxt->past_meas[i].is_valid = false;
+                visu_ctxt->past_meas[i].peak_u16 = 0;
+                visu_ctxt->past_meas[i].rms_squared = 0;
+            }
+            visu_ctxt->meas_buffer_idx = 0;
+        } else {
+            /* only use actual measurements, otherwise the first RMS measure happening before
+             * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
+             * low */
+            uint32_t i;
+            for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+                if (visu_ctxt->past_meas[i].is_valid) {
+                    if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) {
+                        peak_u16 = visu_ctxt->past_meas[i].peak_u16;
+                    }
+                    sum_rms_squared += visu_ctxt->past_meas[i].rms_squared;
+                    nb_valid_meas++;
+                }
+            }
+        }
+        float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas);
+        int32_t* p_int_reply_data = (int32_t*)pReplyData;
+        /* convert from I16 sample values to mB and write results */
+        if (rms < 0.000016f) {
+            p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB
+        } else {
+            p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
+        }
+        if (peak_u16 == 0) {
+            p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
+        } else {
+            p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f));
+        }
+        ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
+                peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK],
+                rms, p_int_reply_data[MEASUREMENT_IDX_RMS]);
+        }
+        break;
+
     default:
         ALOGW("%s invalid command %d", __func__, cmdCode);
         return -EINVAL;