yukawa: Add HAL support for ECHO_REFERENCE audio source.

Bug: b/143616861
Test: Manual, plus tests found at go/ag/9507428.

Change-Id: Iac114c0cdc706da88e88075dbe7225183b8e0a81
diff --git a/audio/Android.mk b/audio/Android.mk
index e8c2129..357f12b 100644
--- a/audio/Android.mk
+++ b/audio/Android.mk
@@ -27,8 +27,10 @@
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_VENDOR_MODULE := true
 
-LOCAL_SRC_FILES := audio_hw.c
-LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioroute
+LOCAL_SRC_FILES := audio_hw.c \
+    audio_aec.c \
+    fifo_wrapper.cpp
+LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioroute libaudioutils
 LOCAL_CFLAGS := -Wno-unused-parameter
 LOCAL_C_INCLUDES += \
         external/tinyalsa/include \
@@ -38,10 +40,7 @@
         system/media/audio_effects/include
 
 ifneq ($(findstring google_aec, $(call all-makefiles-under,$(TOPDIR)vendor/amlogic/yukawa)),)
-    LOCAL_SRC_FILES += \
-        audio_aec.c \
-        fifo_wrapper.cpp
-    LOCAL_SHARED_LIBRARIES += google_aec libaudioutils
+    LOCAL_SHARED_LIBRARIES += google_aec
     LOCAL_CFLAGS += -DAEC_HAL
 endif
 
diff --git a/audio/audio_aec.c b/audio/audio_aec.c
index 2728be8..ab99c93 100644
--- a/audio/audio_aec.c
+++ b/audio/audio_aec.c
@@ -73,13 +73,23 @@
 #include <malloc.h>
 #include <sys/time.h>
 #include <tinyalsa/asoundlib.h>
+#include <unistd.h>
 #include <log/log.h>
 #include "audio_aec.h"
-#include "audio_aec_process.h"
 
-#define DEBUG_AEC 0
+#ifdef AEC_HAL
+#include "audio_aec_process.h"
+#else
+#define aec_spk_mic_init(...) ((int)0)
+#define aec_spk_mic_reset(...) ((void)0)
+#define aec_spk_mic_process(...) ((int32_t)0)
+#define aec_spk_mic_release(...) ((void)0)
+#endif
+
 #define MAX_TIMESTAMP_DIFF_USEC 200000
 
+#define MAX_READ_WAIT_TIME_MSEC 80
+
 uint64_t timespec_to_usec(struct timespec ts) {
     return (ts.tv_sec * 1e6L + ts.tv_nsec/1000);
 }
@@ -133,6 +143,38 @@
     aec->read_write_diff_bytes = 0;
 }
 
+void aec_set_spk_running_no_lock(struct aec_t* aec, bool state) {
+    aec->spk_running = state;
+}
+
+bool aec_get_spk_running_no_lock(struct aec_t* aec) {
+    return aec->spk_running;
+}
+
+void destroy_aec_reference_config_no_lock(struct aec_t* aec) {
+    if (!aec->spk_initialized) {
+        return;
+    }
+    aec_set_spk_running_no_lock(aec, false);
+    fifo_release(aec->spk_fifo);
+    fifo_release(aec->ts_fifo);
+    memset(&aec->last_spk_info, 0, sizeof(struct aec_info));
+    aec->spk_initialized = false;
+}
+
+void destroy_aec_mic_config_no_lock(struct aec_t* aec) {
+    if (!aec->mic_initialized) {
+        return;
+    }
+    release_resampler(aec->spk_resampler);
+    free(aec->mic_buf);
+    free(aec->spk_buf);
+    free(aec->spk_buf_playback_format);
+    free(aec->spk_buf_resampler_out);
+    memset(&aec->last_mic_info, 0, sizeof(struct aec_info));
+    aec->mic_initialized = false;
+}
+
 struct aec_t *init_aec_interface() {
     ALOGV("%s enter", __func__);
     struct aec_t *aec = (struct aec_t *)calloc(1, sizeof(struct aec_t));
@@ -149,8 +191,8 @@
 void release_aec_interface(struct aec_t *aec) {
     ALOGV("%s enter", __func__);
     pthread_mutex_lock(&aec->lock);
-    destroy_aec_mic_config(aec);
-    destroy_aec_reference_config(aec);
+    destroy_aec_mic_config_no_lock(aec);
+    destroy_aec_reference_config_no_lock(aec);
     pthread_mutex_unlock(&aec->lock);
     free(aec);
     ALOGV("%s exit", __func__);
@@ -207,6 +249,10 @@
 
     int ret = 0;
     pthread_mutex_lock(&aec->lock);
+    if (aec->spk_initialized) {
+        destroy_aec_reference_config_no_lock(aec);
+    }
+
     aec->spk_fifo = fifo_init(
             out->config.period_count * out->config.period_size *
                 audio_stream_out_frame_size(&out->stream),
@@ -229,12 +275,169 @@
     aec->spk_sampling_rate = out->config.rate;
     aec->spk_frame_size_bytes = audio_stream_out_frame_size(&out->stream);
     aec->spk_num_channels = out->config.channels;
+    aec->spk_initialized = true;
 exit:
     pthread_mutex_unlock(&aec->lock);
     ALOGV("%s exit", __func__);
     return ret;
 }
 
+void destroy_aec_reference_config(struct aec_t* aec) {
+    ALOGV("%s enter", __func__);
+    if (aec == NULL) {
+        ALOGV("%s exit", __func__);
+        return;
+    }
+    pthread_mutex_lock(&aec->lock);
+    destroy_aec_reference_config_no_lock(aec);
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+}
+
+int write_to_reference_fifo(struct aec_t* aec, void* buffer, struct aec_info* info) {
+    ALOGV("%s enter", __func__);
+    int ret = 0;
+    size_t bytes = info->bytes;
+
+    /* Write audio samples to FIFO */
+    ssize_t written_bytes = fifo_write(aec->spk_fifo, buffer, bytes);
+    if (written_bytes != bytes) {
+        ALOGE("Could only write %zu of %zu bytes", written_bytes, bytes);
+        ret = -ENOMEM;
+    }
+
+    /* Write timestamp to FIFO */
+    info->bytes = written_bytes;
+    ALOGV("Speaker timestamp: %ld s, %ld nsec", info->timestamp.tv_sec, info->timestamp.tv_nsec);
+    ssize_t ts_bytes = fifo_write(aec->ts_fifo, info, sizeof(struct aec_info));
+    ALOGV("Wrote TS bytes: %zu", ts_bytes);
+    print_queue_status_to_log(aec, true);
+    ALOGV("%s exit", __func__);
+    return ret;
+}
+
+void get_spk_timestamp(struct aec_t* aec, ssize_t read_bytes, uint64_t* spk_time) {
+    *spk_time = 0;
+    uint64_t spk_time_offset = 0;
+    float usec_per_byte = 1E6 / ((float)(aec->spk_frame_size_bytes * aec->spk_sampling_rate));
+    if (aec->read_write_diff_bytes < 0) {
+        /* We're still reading a previous write packet. (We only need the first sample's timestamp,
+         * so even if we straddle packets we only care about the first one)
+         * So we just use the previous timestamp, with an appropriate offset
+         * based on the number of bytes remaining to be read from that write packet. */
+        spk_time_offset = (aec->last_spk_info.bytes + aec->read_write_diff_bytes) * usec_per_byte;
+        ALOGV("Reusing previous timestamp, calculated offset (usec) %" PRIu64, spk_time_offset);
+    } else {
+        /* If read_write_diff_bytes > 0, there are no new writes, so there won't be timestamps in
+         * the FIFO, and the check below will fail. */
+        if (!fifo_available_to_read(aec->ts_fifo)) {
+            ALOGE("Timestamp error: no new timestamps!");
+            return;
+        }
+        /* We just read valid data, so if we're here, we should have a valid timestamp to use. */
+        ssize_t ts_bytes = fifo_read(aec->ts_fifo, &aec->last_spk_info, sizeof(struct aec_info));
+        ALOGV("Read TS bytes: %zd, expected %zu", ts_bytes, sizeof(struct aec_info));
+        aec->read_write_diff_bytes -= aec->last_spk_info.bytes;
+    }
+
+    *spk_time = timespec_to_usec(aec->last_spk_info.timestamp) + spk_time_offset;
+
+    aec->read_write_diff_bytes += read_bytes;
+    struct aec_info spk_info = aec->last_spk_info;
+    while (aec->read_write_diff_bytes > 0) {
+        /* If read_write_diff_bytes > 0, it means that there are more write packet timestamps
+         * in FIFO (since there we read more valid data the size of the current timestamp's
+         * packet). Keep reading timestamps from FIFO to get to the most recent one. */
+        if (!fifo_available_to_read(aec->ts_fifo)) {
+            /* There are no more timestamps, we have the most recent one. */
+            ALOGV("At the end of timestamp FIFO, breaking...");
+            break;
+        }
+        fifo_read(aec->ts_fifo, &spk_info, sizeof(struct aec_info));
+        ALOGV("Fast-forwarded timestamp by %zd bytes, remaining bytes: %zd,"
+              " new timestamp (usec) %" PRIu64,
+              spk_info.bytes, aec->read_write_diff_bytes, timespec_to_usec(spk_info.timestamp));
+        aec->read_write_diff_bytes -= spk_info.bytes;
+    }
+    aec->last_spk_info = spk_info;
+}
+
+int get_reference_samples(struct aec_t* aec, void* buffer, struct aec_info* info) {
+    ALOGV("%s enter", __func__);
+
+    if (!aec->spk_initialized) {
+        ALOGE("%s called with no reference initialized", __func__);
+        return -EINVAL;
+    }
+
+    size_t bytes = info->bytes;
+    const size_t frames = bytes / aec->mic_frame_size_bytes;
+    const size_t sample_rate_ratio = aec->spk_sampling_rate / aec->mic_sampling_rate;
+
+    /* Read audio samples from FIFO */
+    const size_t req_bytes = frames * sample_rate_ratio * aec->spk_frame_size_bytes;
+    ssize_t available_bytes = 0;
+    unsigned int wait_count = MAX_READ_WAIT_TIME_MSEC;
+    while (true) {
+        available_bytes = fifo_available_to_read(aec->spk_fifo);
+        if (available_bytes >= req_bytes) {
+            break;
+        } else if (available_bytes < 0) {
+            ALOGE("fifo_read returned code %zu ", available_bytes);
+            return -ENOMEM;
+        }
+
+        ALOGV("Sleeping, required bytes: %zu, available bytes: %zd", req_bytes, available_bytes);
+        usleep(1000);
+        if ((wait_count--) == 0) {
+            ALOGE("Timed out waiting for read from reference FIFO");
+            return -ETIMEDOUT;
+        }
+    }
+
+    const size_t read_bytes = fifo_read(aec->spk_fifo, aec->spk_buf_playback_format, req_bytes);
+
+    /* Get timestamp*/
+    get_spk_timestamp(aec, read_bytes, &info->timestamp_usec);
+
+    /* Get reference - could be mono, downmixed from multichannel.
+     * Reference stored at spk_buf_playback_format */
+    const size_t resampler_in_frames = frames * sample_rate_ratio;
+    get_reference_audio_in_place(aec, resampler_in_frames);
+
+    int16_t* resampler_out_buf;
+    /* Resample to mic sampling rate (16-bit resampler) */
+    if (aec->spk_resampler != NULL) {
+        size_t in_frame_count = resampler_in_frames;
+        size_t out_frame_count = frames;
+        aec->spk_resampler->resample_from_input(aec->spk_resampler, aec->spk_buf_playback_format,
+                                                &in_frame_count, aec->spk_buf_resampler_out,
+                                                &out_frame_count);
+        resampler_out_buf = aec->spk_buf_resampler_out;
+    } else {
+        if (sample_rate_ratio != 1) {
+            ALOGE("Speaker sample rate %d, mic sample rate %d but no resampler defined!",
+                  aec->spk_sampling_rate, aec->mic_sampling_rate);
+        }
+        resampler_out_buf = aec->spk_buf_playback_format;
+    }
+
+    /* Convert to 32 bit */
+    int16_t* src16 = resampler_out_buf;
+    int32_t* dst32 = buffer;
+    size_t frame, ch;
+    for (frame = 0; frame < frames; frame++) {
+        for (ch = 0; ch < aec->num_reference_channels; ch++) {
+            *dst32++ = ((int32_t)*src16++) << 16;
+        }
+    }
+
+    info->bytes = bytes;
+
+    ALOGV("%s exit", __func__);
+    return 0;
+}
+
 int init_aec_mic_config(struct aec_t *aec, struct alsa_stream_in *in) {
     ALOGV("%s enter", __func__);
 #if DEBUG_AEC
@@ -251,6 +454,9 @@
 
     int ret = 0;
     pthread_mutex_lock(&aec->lock);
+    if (aec->mic_initialized) {
+        destroy_aec_mic_config_no_lock(aec);
+    }
     aec->mic_sampling_rate = in->config.rate;
     aec->mic_frame_size_bytes = audio_stream_in_frame_size(&in->stream);
     aec->mic_num_channels = in->config.channels;
@@ -287,21 +493,25 @@
         goto exit_3;
     }
 
-    int resampler_ret = create_resampler(
-                            aec->spk_sampling_rate,
-                            in->config.rate,
-                            aec->num_reference_channels,
-                            RESAMPLER_QUALITY_MAX - 1, /* MAX - 1 is the real max */
-                            NULL, /* resampler_buffer_provider */
-                            &aec->spk_resampler);
-    if (resampler_ret) {
-        ALOGE("AEC: Resampler initialization failed! Error code %d", resampler_ret);
-        ret = resampler_ret;
-        goto exit_4;
+    /* Don't use resampler if it's not required */
+    if (in->config.rate == aec->spk_sampling_rate) {
+        aec->spk_resampler = NULL;
+    } else {
+        int resampler_ret = create_resampler(
+                aec->spk_sampling_rate, in->config.rate, aec->num_reference_channels,
+                RESAMPLER_QUALITY_MAX - 1, /* MAX - 1 is the real max */
+                NULL,                      /* resampler_buffer_provider */
+                &aec->spk_resampler);
+        if (resampler_ret) {
+            ALOGE("AEC: Resampler initialization failed! Error code %d", resampler_ret);
+            ret = resampler_ret;
+            goto exit_4;
+        }
     }
 
     flush_aec_fifos(aec);
     aec_spk_mic_reset();
+    aec->mic_initialized = true;
 
 exit:
     pthread_mutex_unlock(&aec->lock);
@@ -324,7 +534,7 @@
 void aec_set_spk_running(struct aec_t *aec, bool state) {
     ALOGV("%s enter", __func__);
     pthread_mutex_lock(&aec->lock);
-    aec->spk_running = state;
+    aec_set_spk_running_no_lock(aec, state);
     pthread_mutex_unlock(&aec->lock);
     ALOGV("%s exit", __func__);
 }
@@ -332,113 +542,26 @@
 bool aec_get_spk_running(struct aec_t *aec) {
     ALOGV("%s enter", __func__);
     pthread_mutex_lock(&aec->lock);
-    bool state = aec->spk_running;
+    bool state = aec_get_spk_running_no_lock(aec);
     pthread_mutex_unlock(&aec->lock);
     ALOGV("%s exit", __func__);
     return state;
 }
 
-void destroy_aec_reference_config(struct aec_t *aec) {
+void destroy_aec_mic_config(struct aec_t* aec) {
     ALOGV("%s enter", __func__);
     if (aec == NULL) {
         ALOGV("%s exit", __func__);
         return;
     }
+
     pthread_mutex_lock(&aec->lock);
-    aec_set_spk_running(aec, false);
-    fifo_release(aec->spk_fifo);
-    fifo_release(aec->ts_fifo);
-    memset(&aec->last_spk_info, 0, sizeof(struct aec_info));
+    destroy_aec_mic_config_no_lock(aec);
     pthread_mutex_unlock(&aec->lock);
     ALOGV("%s exit", __func__);
 }
 
-void destroy_aec_mic_config(struct aec_t *aec) {
-    ALOGV("%s enter", __func__);
-    if (aec == NULL) {
-        ALOGV("%s exit", __func__);
-        return;
-    }
-    pthread_mutex_lock(&aec->lock);
-    release_resampler(aec->spk_resampler);
-    free(aec->mic_buf);
-    free(aec->spk_buf);
-    free(aec->spk_buf_playback_format);
-    free(aec->spk_buf_resampler_out);
-    memset(&aec->last_mic_info, 0, sizeof(struct aec_info));
-    pthread_mutex_unlock(&aec->lock);
-    ALOGV("%s exit", __func__);
-}
-
-int write_to_reference_fifo (struct aec_t *aec, void *buffer, struct aec_info *info) {
-    ALOGV("%s enter", __func__);
-    int ret = 0;
-    size_t bytes = info->bytes;
-
-    /* Write audio samples to FIFO */
-    ssize_t written_bytes = fifo_write(aec->spk_fifo, buffer, bytes);
-    if (written_bytes != bytes) {
-        ALOGE("Could only write %zu of %zu bytes", written_bytes, bytes);
-        ret = -ENOMEM;
-    }
-
-    /* Write timestamp to FIFO */
-    info->bytes = written_bytes;
-    ALOGV("Speaker timestamp: %ld s, %ld nsec", info->timestamp.tv_sec, info->timestamp.tv_nsec);
-    ssize_t ts_bytes = fifo_write(aec->ts_fifo, info, sizeof(struct aec_info));
-    ALOGV("Wrote TS bytes: %zu", ts_bytes);
-    print_queue_status_to_log(aec, true);
-    ALOGV("%s exit", __func__);
-    return ret;
-}
-
-void get_spk_timestamp(struct aec_t *aec, ssize_t read_bytes, uint64_t *spk_time) {
-    *spk_time = 0;
-    uint64_t spk_time_offset = 0;
-    float usec_per_byte = 1E6 / ((float)(aec->spk_frame_size_bytes * aec->spk_sampling_rate));
-    if (aec->read_write_diff_bytes < 0) {
-        /* We're still reading a previous write packet. (We only need the first sample's timestamp,
-         * so even if we straddle packets we only care about the first one)
-         * So we just use the previous timestamp, with an appropriate offset
-         * based on the number of bytes remaining to be read from that write packet. */
-        spk_time_offset = (aec->last_spk_info.bytes + aec->read_write_diff_bytes) * usec_per_byte;
-        ALOGV("Reusing previous timestamp, calculated offset (usec) %"PRIu64, spk_time_offset);
-    } else {
-        /* If read_write_diff_bytes > 0, there are no new writes, so there won't be timestamps in
-         * the FIFO, and the check below will fail. */
-        if (!fifo_available_to_read(aec->ts_fifo)) {
-            ALOGE("Timestamp error: no new timestamps!");
-            return;
-        }
-        /* We just read valid data, so if we're here, we should have a valid timestamp to use. */
-        ssize_t ts_bytes = fifo_read(aec->ts_fifo, &aec->last_spk_info,
-                                        sizeof(struct aec_info));
-        ALOGV("Read TS bytes: %zd, expected %zu", ts_bytes, sizeof(struct aec_info));
-        aec->read_write_diff_bytes -= aec->last_spk_info.bytes;
-    }
-
-    *spk_time = timespec_to_usec(aec->last_spk_info.timestamp) + spk_time_offset;
-
-    aec->read_write_diff_bytes += read_bytes;
-    struct aec_info spk_info = aec->last_spk_info;
-    while (aec->read_write_diff_bytes > 0) {
-        /* If read_write_diff_bytes > 0, it means that there are more write packet timestamps
-         * in FIFO (since there we read more valid data the size of the current timestamp's
-         * packet). Keep reading timestamps from FIFO to get to the most recent one. */
-        if (!fifo_available_to_read(aec->ts_fifo)) {
-            /* There are no more timestamps, we have the most recent one. */
-            ALOGV("At the end of timestamp FIFO, breaking...");
-            break;
-        }
-        fifo_read(aec->ts_fifo, &spk_info, sizeof(struct aec_info));
-        ALOGV("Fast-forwarded timestamp by %zd bytes, remaining bytes: %zd,"
-                " new timestamp (usec) %"PRIu64,
-                spk_info.bytes, aec->read_write_diff_bytes, timespec_to_usec(spk_info.timestamp));
-        aec->read_write_diff_bytes -= spk_info.bytes;
-    }
-    aec->last_spk_info = spk_info;
-}
-
+#ifdef AEC_HAL
 int process_aec(struct aec_t *aec, void* buffer, struct aec_info *info) {
     ALOGV("%s enter", __func__);
     int ret = 0;
@@ -448,6 +571,12 @@
         return -EINVAL;
     }
 
+    if ((!aec->mic_initialized) || (!aec->spk_initialized)) {
+        ALOGE("%s called with initialization: mic: %d, spk: %d", __func__, aec->mic_initialized,
+              aec->spk_initialized);
+        return -EINVAL;
+    }
+
     size_t bytes = info->bytes;
 
     size_t frame_size = aec->mic_frame_size_bytes;
@@ -477,11 +606,6 @@
         flush_aec_fifos(aec);
     }
 
-    size_t spk_frame_size_bytes = aec->spk_frame_size_bytes;
-    size_t sample_rate_ratio = aec->spk_sampling_rate / aec->mic_sampling_rate;
-    size_t resampler_in_frames = in_frames * sample_rate_ratio;
-    size_t req_bytes = resampler_in_frames * spk_frame_size_bytes;
-
     /* If there's no data in FIFO, exit */
     if (fifo_available_to_read(aec->spk_fifo) <= 0) {
         ALOGV("Echo reference buffer empty, zeroing reference....");
@@ -490,48 +614,18 @@
 
     print_queue_status_to_log(aec, false);
 
-    /* Read from FIFO */
-    ssize_t read_bytes = fifo_read(aec->spk_fifo, aec->spk_buf_playback_format, req_bytes);
-    get_spk_timestamp(aec, read_bytes, &spk_time);
+    /* Get reference, with format and sample rate required by AEC */
+    struct aec_info spk_info;
+    spk_info.bytes = bytes;
+    int ref_ret = get_reference_samples(aec, aec->spk_buf, &spk_info);
+    spk_time = spk_info.timestamp_usec;
 
-    if (read_bytes < req_bytes) {
-        ALOGI("Could only read %zd of %zu bytes", read_bytes, req_bytes);
-        if (read_bytes > 0) {
-            memmove(aec->spk_buf_playback_format + req_bytes - read_bytes,
-                        aec->spk_buf_playback_format, read_bytes);
-            memset(aec->spk_buf_playback_format, 0, req_bytes - read_bytes);
-        } else {
-            ALOGE("Fifo read returned code %zd ", read_bytes);
-            ret = -ENOMEM;
-            goto exit;
-        }
+    if (ref_ret) {
+        ALOGE("get_reference_samples returned code %d", ref_ret);
+        ret = -ENOMEM;
+        goto exit;
     }
 
-    /* Get reference - could be mono, downmixed from multichannel.
-     * Reference stored at spk_buf_playback_format */
-    get_reference_audio_in_place(aec, resampler_in_frames);
-
-    /* Resample to mic sampling rate (16-bit resampler) */
-    size_t in_frame_count = resampler_in_frames;
-    size_t out_frame_count = in_frames;
-    aec->spk_resampler->resample_from_input(
-                            aec->spk_resampler,
-                            aec->spk_buf_playback_format,
-                            &in_frame_count,
-                            aec->spk_buf_resampler_out,
-                            &out_frame_count);
-
-    /* Convert to 32 bit */
-    int16_t *src16 = aec->spk_buf_resampler_out;
-    int32_t *dst32 = aec->spk_buf;
-    size_t frame, ch;
-    for (frame = 0; frame < in_frames; frame++) {
-        for (ch = 0; ch < aec->num_reference_channels; ch++) {
-           *dst32++ = ((int32_t)*src16++) << 16;
-        }
-    }
-
-
     int64_t time_diff = (mic_time > spk_time) ? (mic_time - spk_time) : (spk_time - mic_time);
     if ((spk_time == 0) || (mic_time == 0) || (time_diff > MAX_TIMESTAMP_DIFF_USEC)) {
         ALOGV("Speaker-mic timestamps diverged, skipping AEC");
@@ -602,3 +696,5 @@
     ALOGV("%s exit", __func__);
     return ret;
 }
+
+#endif /*#ifdef AEC_HAL*/
diff --git a/audio/audio_aec.h b/audio/audio_aec.h
index 9cfc9aa..daa7fb2 100644
--- a/audio/audio_aec.h
+++ b/audio/audio_aec.h
@@ -36,12 +36,14 @@
 struct aec_t {
     pthread_mutex_t lock;
     size_t num_reference_channels;
+    bool mic_initialized;
     int32_t *mic_buf;
     size_t mic_num_channels;
     size_t mic_buf_size_bytes;
     size_t mic_frame_size_bytes;
     uint32_t mic_sampling_rate;
     struct aec_info last_mic_info;
+    bool spk_initialized;
     int32_t *spk_buf;
     size_t spk_num_channels;
     size_t spk_buf_size_bytes;
@@ -58,41 +60,33 @@
     bool prev_spk_running;
 };
 
-#ifdef AEC_HAL
-
-/* Write audio samples to AEC reference FIFO for use in AEC.
- * Both audio samples and timestamps are added in FIFO fashion.
- * Must be called after every write to PCM. */
-int write_to_reference_fifo (struct aec_t *aec, void *buffer, struct aec_info *info);
-
-/* Processing function call for AEC.
- * AEC output is updated at location pointed to by 'buffer'.
- * This function does not run AEC when there is no playback -
- * as communicated to this AEC interface using aec_set_spk_running().*/
-int process_aec (struct aec_t *aec, void* buffer, struct aec_info *info);
-
 /* Initialize AEC object.
  * This must be called when the audio device is opened.
- * ALSA device mutex must be held before calling this API.*/
+ * ALSA device mutex must be held before calling this API.
+ * Returns -EINVAL if AEC object fails to initialize, else returns 0. */
 int init_aec (int sampling_rate, int num_reference_channels,
                 int num_microphone_channels, struct aec_t **);
 
 /* Release AEC object.
  * This must be called when the audio device is closed. */
-void release_aec(struct aec_t *aec);
+void release_aec(struct aec_t* aec);
 
 /* Initialize reference configuration for AEC.
- * Must be called when a new output stream is opened. */
+ * Must be called when a new output stream is opened.
+ * Returns -EINVAL if any processing block fails to initialize,
+ * else returns 0. */
 int init_aec_reference_config (struct aec_t *aec, struct alsa_stream_out *out);
 
-/* Initialize microphone configuration for AEC.
- * Must be called when a new input stream is opened. */
-int init_aec_mic_config (struct aec_t *aec, struct alsa_stream_in *in);
-
 /* Clear reference configuration for AEC.
  * Must be called when the output stream is closed. */
 void destroy_aec_reference_config (struct aec_t *aec);
 
+/* Initialize microphone configuration for AEC.
+ * Must be called when a new input stream is opened.
+ * Returns -EINVAL if any processing block fails to initialize,
+ * else returns 0. */
+int init_aec_mic_config(struct aec_t* aec, struct alsa_stream_in* in);
+
 /* Clear microphone configuration for AEC.
  * Must be called when the input stream is closed. */
 void destroy_aec_mic_config (struct aec_t *aec);
@@ -101,17 +95,34 @@
  * This is used by process_aec() to determine if AEC processing is to be run. */
 void aec_set_spk_running (struct aec_t *aec, bool state);
 
+/* Write audio samples to AEC reference FIFO for use in AEC.
+ * Both audio samples and timestamps are added in FIFO fashion.
+ * Must be called after every write to PCM.
+ * Returns -ENOMEM if the write fails, else returns 0. */
+int write_to_reference_fifo(struct aec_t* aec, void* buffer, struct aec_info* info);
+
+/* Get reference audio samples + timestamp, in the format expected by AEC,
+ * i.e. same sample rate and bit rate as microphone audio.
+ * Timestamp is updated in field 'timestamp_usec', and not in 'timestamp'.
+ * Returns:
+ *  -EINVAL    if the AEC object is invalid.
+ *  -ENOMEM    if the reference FIFO overflows or is corrupted.
+ *  -ETIMEDOUT if we timed out waiting for the requested number of bytes
+ *  0          otherwise */
+int get_reference_samples(struct aec_t* aec, void* buffer, struct aec_info* info);
+
+#ifdef AEC_HAL
+
+/* Processing function call for AEC.
+ * AEC output is updated at location pointed to by 'buffer'.
+ * This function does not run AEC when there is no playback -
+ * as communicated to this AEC interface using aec_set_spk_running().
+ * Returns -EINVAL if processing fails, else returns 0. */
+int process_aec(struct aec_t* aec, void* buffer, struct aec_info* info);
+
 #else /* #ifdef AEC_HAL */
 
-#define write_to_reference_fifo(...) ((int)0)
 #define process_aec(...) ((int)0)
-#define init_aec(...) ((int)0)
-#define release_aec(...) ((void)0)
-#define init_aec_reference_config(...) ((int)0)
-#define init_aec_mic_config(...) ((int)0)
-#define destroy_aec_reference_config(...) ((void)0)
-#define destroy_aec_mic_config(...) ((void)0)
-#define aec_set_spk_running(...) ((void)0)
 
 #endif /* #ifdef AEC_HAL */
 
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index cd72172..ac886db 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -384,12 +384,10 @@
     return -ENOSYS;
 }
 
-static size_t get_input_buffer_size(audio_format_t format,
-                                    audio_channel_mask_t channel_mask)
-{
+static size_t get_input_buffer_size(size_t frames, audio_format_t format,
+                                    audio_channel_mask_t channel_mask) {
     /* return the closest majoring multiple of 16 frames, as
      * audioflinger expects audio buffers to be a multiple of 16 frames */
-    size_t frames = CAPTURE_PERIOD_SIZE;
     frames = ((frames + 15) / 16) * 16;
     size_t bytes_per_frame = audio_channel_count_from_in_mask(channel_mask) *
                             audio_bytes_per_sample(format);
@@ -418,8 +416,14 @@
 
 static size_t in_get_buffer_size(const struct audio_stream *stream)
 {
-    size_t buffer_size = get_input_buffer_size(stream->get_format(stream),
-                            stream->get_channels(stream));
+    struct alsa_stream_in* in = (struct alsa_stream_in*)stream;
+    size_t frames = CAPTURE_PERIOD_SIZE;
+    if (in->source == AUDIO_SOURCE_ECHO_REFERENCE) {
+        frames = CAPTURE_PERIOD_SIZE * PLAYBACK_CODEC_SAMPLING_RATE / CAPTURE_CODEC_SAMPLING_RATE;
+    }
+
+    size_t buffer_size =
+            get_input_buffer_size(frames, stream->get_format(stream), stream->get_channels(stream));
     ALOGV("in_get_buffer_size: %zu", buffer_size);
     return buffer_size;
 }
@@ -474,14 +478,42 @@
 static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
         size_t bytes)
 {
-    ALOGV("in_read: bytes %zu", bytes);
-
     int ret;
     struct alsa_stream_in *in = (struct alsa_stream_in *)stream;
     struct alsa_audio_device *adev = in->dev;
     size_t frame_size = audio_stream_in_frame_size(stream);
     size_t in_frames = bytes / frame_size;
 
+    ALOGV("in_read: stream: %d, bytes %zu", in->source, bytes);
+
+    /* Special handling for Echo Reference: simply get the reference from FIFO.
+     * The format and sample rate should be specified by arguments to adev_open_input_stream. */
+    if (in->source == AUDIO_SOURCE_ECHO_REFERENCE) {
+        struct aec_info info;
+        info.bytes = bytes;
+
+        if (!adev->aec->spk_running) {
+            memset(buffer, 0, bytes);
+            usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) /
+                   in_get_sample_rate(&stream->common));
+        } else {
+            get_reference_samples(adev->aec, buffer, &info);
+        }
+
+#if DEBUG_AEC
+        FILE* fp_ref = fopen("/data/local/traces/aec_ref.pcm", "a+");
+        if (fp_ref) {
+            fwrite((char*)buffer, 1, bytes, fp_ref);
+            fclose(fp_ref);
+        } else {
+            ALOGE("AEC debug: Could not open file aec_ref.pcm!");
+        }
+#endif
+        return info.bytes;
+    }
+
+    /* Microphone input stream read */
+
     /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
      * on the input stream mutex - e.g. executing select_mode() while holding the hw device
      * mutex
@@ -525,6 +557,7 @@
             struct aec_info info;
             get_pcm_timestamp(in->pcm, in->config.rate, &info);
             info.bytes = bytes;
+
             int aec_ret = process_aec(adev->aec, buffer, &info);
             if (aec_ret) {
                 ALOGE("process_aec returned error code %d", aec_ret);
@@ -532,6 +565,16 @@
         }
     }
 
+#if DEBUG_AEC && !defined(AEC_HAL)
+    FILE* fp_in = fopen("/data/local/traces/aec_in.pcm", "a+");
+    if (fp_in) {
+        fwrite((char*)buffer, 1, bytes, fp_in);
+        fclose(fp_in);
+    } else {
+        ALOGE("AEC debug: Could not open file aec_in.pcm!");
+    }
+#endif
+
     return bytes;
 }
 
@@ -740,21 +783,17 @@
 static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
         const struct audio_config *config)
 {
-    size_t buffer_size = get_input_buffer_size(config->format, config->channel_mask);
+    size_t buffer_size =
+            get_input_buffer_size(CAPTURE_PERIOD_SIZE, config->format, config->channel_mask);
     ALOGV("adev_get_input_buffer_size: %zu", buffer_size);
     return buffer_size;
 }
 
-static int adev_open_input_stream(struct audio_hw_device *dev,
-        audio_io_handle_t handle,
-        audio_devices_t devices,
-        struct audio_config *config,
-        struct audio_stream_in **stream_in,
-        audio_input_flags_t flags __unused,
-        const char *address __unused,
-        audio_source_t source __unused)
-{
-
+static int adev_open_input_stream(struct audio_hw_device* dev, audio_io_handle_t handle,
+                                  audio_devices_t devices, struct audio_config* config,
+                                  struct audio_stream_in** stream_in,
+                                  audio_input_flags_t flags __unused, const char* address __unused,
+                                  audio_source_t source) {
     ALOGV("adev_open_input_stream...");
 
     struct alsa_audio_device *ladev = (struct alsa_audio_device *)dev;
@@ -787,7 +826,11 @@
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
 
     in->config.channels = CHANNEL_STEREO;
-    in->config.rate = CAPTURE_CODEC_SAMPLING_RATE;
+    if (source == AUDIO_SOURCE_ECHO_REFERENCE) {
+        in->config.rate = PLAYBACK_CODEC_SAMPLING_RATE;
+    } else {
+        in->config.rate = CAPTURE_CODEC_SAMPLING_RATE;
+    }
     in->config.format = PCM_FORMAT_S32_LE;
     in->config.period_size = CAPTURE_PERIOD_SIZE;
     in->config.period_count = CAPTURE_PERIOD_COUNT;
@@ -798,18 +841,26 @@
         ret = -EINVAL;
     }
 
-    ALOGI("adev_open_input_stream selects channels=%d rate=%d format=%d",
-                in->config.channels, in->config.rate, in->config.format);
+    ALOGI("adev_open_input_stream selects channels=%d rate=%d format=%d source=%d",
+          in->config.channels, in->config.rate, in->config.format, source);
 
     in->dev = ladev;
     in->standby = true;
     in->unavailable = false;
+    in->source = source;
 
     config->format = in_get_format(&in->stream.common);
     config->channel_mask = in_get_channels(&in->stream.common);
     config->sample_rate = in_get_sample_rate(&in->stream.common);
 
-    if (ret == 0) {
+    /* If AEC is in the app, only configure based on ECHO_REFERENCE spec.
+     * If AEC is in the HAL, configure using the given mic stream. */
+    bool aecInput = true;
+#if !defined(AEC_HAL)
+    aecInput = (in->source == AUDIO_SOURCE_ECHO_REFERENCE);
+#endif
+
+    if ((ret == 0) && aecInput) {
         int aec_ret = init_aec_mic_config(ladev->aec, in);
         if (aec_ret) {
             ALOGE("AEC: Mic config init failed!");
@@ -823,6 +874,10 @@
         *stream_in = &in->stream;
     }
 
+#if DEBUG_AEC
+    remove("/data/local/traces/aec_ref.pcm");
+    remove("/data/local/traces/aec_in.pcm");
+#endif
     return ret;
 }
 
diff --git a/audio/audio_hw.h b/audio/audio_hw.h
index 0eb786d..5ef6584 100644
--- a/audio/audio_hw.h
+++ b/audio/audio_hw.h
@@ -30,7 +30,15 @@
 #define CODEC_BASE_FRAME_COUNT 32
 
 #define CHANNEL_STEREO 2
+
+#ifdef AEC_HAL
 #define NUM_AEC_REFERENCE_CHANNELS 1
+#else
+/* App AEC uses 2-channel reference */
+#define NUM_AEC_REFERENCE_CHANNELS 2
+#endif /* #ifdef AEC_HAL */
+
+#define DEBUG_AEC 0
 
 #define PCM_OPEN_RETRIES 100
 #define PCM_OPEN_WAIT_TIME_MS 20
@@ -80,6 +88,7 @@
     struct alsa_audio_device *dev;
     int read_threshold;
     unsigned int read;
+    audio_source_t source;
 };
 
 struct alsa_stream_out {
@@ -102,6 +111,7 @@
  * datatypes as the corresponding arguments to that function. */
 struct aec_info {
     struct timespec timestamp;
+    uint64_t timestamp_usec;
     unsigned int available;
     size_t bytes;
 };
diff --git a/audio/audio_policy_configuration.xml b/audio/audio_policy_configuration.xml
index 6c696cb..b62a329 100644
--- a/audio/audio_policy_configuration.xml
+++ b/audio/audio_policy_configuration.xml
@@ -49,6 +49,7 @@
                 <item>Speaker</item>
                 <item>Built-In Mic</item>
                 <item>Built-In Back Mic</item>
+                <item>Echo Reference</item>
             </attachedDevices>
             <defaultOutputDevice>Speaker</defaultOutputDevice>
             <mixPorts>
@@ -82,6 +83,16 @@
                              samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
                              channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
                 </mixPort>
+                <mixPort name="built-in mic" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
+                             samplingRates="16000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+                <mixPort name="echo reference" role="sink">
+                    <profile name="echo_reference" format="AUDIO_FORMAT_PCM_32_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
                 <mixPort name="voice_rx" role="sink">
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                              samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
@@ -123,9 +134,9 @@
                 </devicePort>
 
                 <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
-                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
-                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
-                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                    <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
+                             samplingRates="16000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
                 </devicePort>
                 <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
@@ -145,6 +156,11 @@
                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                              samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
                 </devicePort>
+                <devicePort tagName="Echo Reference" type="AUDIO_DEVICE_IN_ECHO_REFERENCE" role="source">
+                    <profile name="echo_reference" format="AUDIO_FORMAT_PCM_32_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </devicePort>
             </devicePorts>
             <!-- route declaration, i.e. list all available sources for a given sink -->
             <routes>
@@ -158,6 +174,10 @@
                        sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
                 <route type="mix" sink="primary input"
                        sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/>
+                <route type="mix" sink="built-in mic"
+                       sources="Built-In Mic"/>
+                <route type="mix" sink="echo reference"
+                       sources="Echo Reference"/>
                 <route type="mix" sink="Telephony Tx"
                        sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic,voice_tx"/>
                 <route type="mix" sink="voice_rx"
diff --git a/device-yukawa.mk b/device-yukawa.mk
index 263b9fc..b6fd8c4 100644
--- a/device-yukawa.mk
+++ b/device-yukawa.mk
@@ -16,3 +16,6 @@
 
 PRODUCT_COPY_FILES +=  $(LOCAL_DTB):meson-sm1-sei610.dtb
 
+# Feature permissions
+PRODUCT_COPY_FILES += \
+    device/amlogic/yukawa/permissions/yukawa.xml:/system/etc/sysconfig/yukawa.xml
diff --git a/permissions/yukawa.xml b/permissions/yukawa.xml
new file mode 100644
index 0000000..41aad35
--- /dev/null
+++ b/permissions/yukawa.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<permissions>
+    <feature name="com.google.android.feature.ECHO_REFERENCE" />
+</permissions>