yukawa: Integrate Google AEC into HAL

Use google_aec library on mic capture path in HAL.

Bug: 139423645
Test: Manual

Change-Id: If72c201bc30ce88dea6cb50a4ef436644bcf289c
diff --git a/audio/Android.mk b/audio/Android.mk
index 929fb9e..e8c2129 100644
--- a/audio/Android.mk
+++ b/audio/Android.mk
@@ -37,5 +37,12 @@
         system/media/audio_utils/include \
         system/media/audio_effects/include
 
-include $(BUILD_SHARED_LIBRARY)
+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_CFLAGS += -DAEC_HAL
+endif
 
+include $(BUILD_SHARED_LIBRARY)
diff --git a/audio/audio_aec.c b/audio/audio_aec.c
new file mode 100644
index 0000000..6e39fdd
--- /dev/null
+++ b/audio/audio_aec.c
@@ -0,0 +1,628 @@
+/*
+ * 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.
+ */
+
+/*
+ * Typical AEC signal flow:
+ *
+ *                          Microphone Audio
+ *                          Timestamps
+ *                        +--------------------------------------+
+ *                        |                                      |       +---------------+
+ *                        |    Microphone +---------------+      |       |               |
+ *             O|======   |    Audio      | Sample Rate   |      +------->               |
+ *    (from         .  +--+    Samples    | +             |              |               |
+ *     mic          .  +==================> Format        |==============>               |
+ *     codec)       .                     | Conversion    |              |               |   Cleaned
+ *             O|======                   | (if required) |              |   Acoustic    |   Audio
+ *                                        +---------------+              |   Echo        |   Samples
+ *                                                                       |   Canceller   |===================>
+ *                                                                       |   (AEC)       |
+ *                            Reference   +---------------+              |               |
+ *                            Audio       | Sample Rate   |              |               |
+ *                            Samples     | +             |              |               |
+ *                          +=============> Format        |==============>               |
+ *                          |             | Conversion    |              |               |
+ *                          |             | (if required) |      +------->               |
+ *                          |             +---------------+      |       |               |
+ *                          |                                    |       +---------------+
+ *                          |    +-------------------------------+
+ *                          |    |  Reference Audio
+ *                          |    |  Timestamps
+ *                          |    |
+ *                       +--+----+---------+                                                       AUDIO CAPTURE
+ *                       | Speaker         |
+ *          +------------+ Audio/Timestamp +---------------------------------------------------------------------------+
+ *                       | Buffer          |
+ *                       +--^----^---------+                                                       AUDIO PLAYBACK
+ *                          |    |
+ *                          |    |
+ *                          |    |
+ *                          |    |
+ *                |\        |    |
+ *                | +-+     |    |
+ *      (to       | | +-----C----+
+ *       speaker  | | |     |                                                                  Playback
+ *       codec)   | | <=====+================================================================+ Audio
+ *                | +-+                                                                        Samples
+ *                |/
+ *
+ */
+
+#define LOG_TAG "audio_hw_aec"
+// #define LOG_NDEBUG 0
+
+#include <audio_utils/primitives.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <tinyalsa/asoundlib.h>
+#include <log/log.h>
+#include "audio_aec.h"
+#include "audio_aec_process.h"
+
+#define DEBUG_AEC 0
+#define MAX_TIMESTAMP_DIFF_USEC 200000
+
+uint64_t timespec_to_usec(struct timespec ts) {
+    return (ts.tv_sec * 1e6L + ts.tv_nsec/1000);
+}
+
+void timestamp_adjust(struct timespec *ts, size_t frames, uint32_t sampling_rate) {
+    /* This function assumes the adjustment (in nsec) is less than the max value of long,
+     * which for 32-bit long this is 2^31 * 1e-9 seconds, slightly over 2 seconds.
+     * For 64-bit long it is  9e+9 seconds. */
+    long adj_nsec = (frames / (float) sampling_rate) * 1E9L;
+    ts->tv_nsec -= adj_nsec;
+    if (ts->tv_nsec < 0) {
+        ts->tv_sec--;
+        ts->tv_nsec += 1E9L;
+    }
+}
+
+void get_reference_audio_in_place(struct aec_t *aec, size_t frames) {
+    if (aec->num_reference_channels == aec->spk_num_channels) {
+        /* Reference count equals speaker channels, nothing to do here. */
+        return;
+    } else if (aec->num_reference_channels != 1) {
+        /* We don't have  a rule for non-mono references, show error on log */
+        ALOGE("Invalid reference count - must be 1 or match number of playback channels!");
+        return;
+    }
+    int16_t *src_Nch = &aec->spk_buf_playback_format[0];
+    int16_t *dst_1ch = &aec->spk_buf_playback_format[0];
+    int32_t num_channels = (int32_t)aec->spk_num_channels;
+    size_t frame, ch;
+    for (frame = 0; frame < frames; frame++) {
+        int32_t acc = 0;
+        for (ch = 0; ch < aec->spk_num_channels; ch++) {
+            acc += src_Nch[ch];
+        }
+        *dst_1ch++ = clamp16(acc/num_channels);
+        src_Nch += aec->spk_num_channels;
+    }
+}
+
+void print_queue_status_to_log(struct aec_t *aec, bool write_side) {
+    ssize_t q1 = fifo_available_to_read(aec->spk_fifo);
+    ssize_t q2 = fifo_available_to_read(aec->ts_fifo);
+
+    if (write_side) {
+        ALOGV("Queue available (POST-WRITE): Spk %zd (count %zd) TS %zd (count %zd)",
+        q1, q1/aec->spk_frame_size_bytes/PLAYBACK_PERIOD_SIZE, q2, q2/sizeof(struct ts_fifo_payload));
+    } else {
+        ALOGV("Queue available (PRE-READ): Spk %zd (count %zd) TS %zd (count %zd)",
+        q1, q1/aec->spk_frame_size_bytes/PLAYBACK_PERIOD_SIZE, q2, q2/sizeof(struct ts_fifo_payload));
+    }
+}
+
+void flush_aec_fifos(struct aec_t *aec) {
+    if (aec == NULL) {
+        return;
+    }
+    if (aec->spk_fifo != NULL) {
+        ALOGV("Flushing AEC Spk FIFO...");
+        fifo_flush(aec->spk_fifo);
+    }
+    if (aec->ts_fifo != NULL) {
+        ALOGV("Flushing AEC Timestamp FIFO...");
+        fifo_flush(aec->ts_fifo);
+    }
+    /* Reset FIFO read-write offset tracker */
+    aec->read_write_diff_bytes = 0;
+}
+
+struct aec_t *init_aec_interface() {
+    ALOGV("%s enter", __func__);
+    struct aec_t *aec = (struct aec_t *)calloc(1, sizeof(struct aec_t));
+    if (aec == NULL) {
+        ALOGE("Failed to allocate memory for AEC interface!");
+    } else {
+        pthread_mutex_init(&aec->lock, NULL);
+    }
+
+    ALOGV("%s exit", __func__);
+    return aec;
+}
+
+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);
+    pthread_mutex_unlock(&aec->lock);
+    free(aec);
+    ALOGV("%s exit", __func__);
+}
+
+int init_aec(int sampling_rate, int num_reference_channels,
+                int num_microphone_channels, struct aec_t **aec_ptr) {
+    ALOGV("%s enter", __func__);
+    int ret = 0;
+    int aec_ret = aec_spk_mic_init(
+                    sampling_rate,
+                    num_reference_channels,
+                    num_microphone_channels);
+    if (aec_ret) {
+        ALOGE("AEC object failed to initialize!");
+        ret = -EINVAL;
+    }
+    struct aec_t *aec = init_aec_interface();
+    if (!ret) {
+        aec->num_reference_channels = num_reference_channels;
+        /* Set defaults, will be overridden by settings in init_aec_(mic|referece_config) */
+        /* Capture uses 2-ch, 32-bit frames */
+        aec->mic_sampling_rate = CAPTURE_CODEC_SAMPLING_RATE;
+        aec->mic_frame_size_bytes = CHANNEL_STEREO * sizeof(int32_t);
+        aec->mic_num_channels = CHANNEL_STEREO;
+
+        /* Playback uses 2-ch, 16-bit frames */
+        aec->spk_sampling_rate = PLAYBACK_CODEC_SAMPLING_RATE;
+        aec->spk_frame_size_bytes = CHANNEL_STEREO * sizeof(int16_t);
+        aec->spk_num_channels = CHANNEL_STEREO;
+    }
+
+    (*aec_ptr) = aec;
+    ALOGV("%s exit", __func__);
+    return ret;
+}
+
+void release_aec(struct aec_t *aec) {
+    ALOGV("%s enter", __func__);
+    if (aec == NULL) {
+        return;
+    }
+    release_aec_interface(aec);
+    aec_spk_mic_release();
+    ALOGV("%s exit", __func__);
+}
+
+int init_aec_reference_config(struct aec_t *aec, struct alsa_stream_out *out) {
+    ALOGV("%s enter", __func__);
+    if (!aec) {
+        ALOGE("AEC: No valid interface found!");
+        return -EINVAL;
+    }
+
+    int ret = 0;
+    pthread_mutex_lock(&aec->lock);
+    aec->spk_fifo = fifo_init(
+            out->config.period_count * out->config.period_size *
+                audio_stream_out_frame_size(&out->stream),
+            false /* reader_throttles_writer */);
+    if (aec->spk_fifo == NULL) {
+        ALOGE("AEC: Speaker loopback FIFO Init failed!");
+        ret = -EINVAL;
+        goto exit;
+    }
+    aec->ts_fifo = fifo_init(
+            out->config.period_count * sizeof(struct ts_fifo_payload),
+            false /* reader_throttles_writer */);
+    if (aec->ts_fifo == NULL) {
+        ALOGE("AEC: Speaker timestamp FIFO Init failed!");
+        ret = -EINVAL;
+        fifo_release(aec->spk_fifo);
+        goto exit;
+    }
+
+    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;
+exit:
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+    return ret;
+}
+
+int init_aec_mic_config(struct aec_t *aec, struct alsa_stream_in *in) {
+    ALOGV("%s enter", __func__);
+#if DEBUG_AEC
+    remove("/data/local/traces/aec_in.pcm");
+    remove("/data/local/traces/aec_out.pcm");
+    remove("/data/local/traces/aec_ref.pcm");
+    remove("/data/local/traces/aec_timestamps.txt");
+#endif /* #if DEBUG_AEC */
+
+    if (!aec) {
+        ALOGE("AEC: No valid interface found!");
+        return -EINVAL;
+    }
+
+    int ret = 0;
+    pthread_mutex_lock(&aec->lock);
+    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;
+
+    aec->mic_buf_size_bytes = in->config.period_size * audio_stream_in_frame_size(&in->stream);
+    aec->mic_buf = (int32_t *)malloc(aec->mic_buf_size_bytes);
+    if (aec->mic_buf == NULL) {
+        ret = -ENOMEM;
+        goto exit;
+    }
+    memset(aec->mic_buf, 0, aec->mic_buf_size_bytes);
+    /* Reference buffer is the same number of frames as mic,
+     * only with a different number of channels in the frame. */
+    aec->spk_buf_size_bytes = in->config.period_size * aec->spk_frame_size_bytes;
+    aec->spk_buf = (int32_t *)malloc(aec->spk_buf_size_bytes);
+    if (aec->spk_buf == NULL) {
+        ret = -ENOMEM;
+        goto exit_1;
+    }
+    memset(aec->spk_buf, 0, aec->spk_buf_size_bytes);
+
+    /* Pre-resampler buffer */
+    size_t spk_frame_out_format_bytes = aec->spk_sampling_rate / aec->mic_sampling_rate *
+                                            aec->spk_buf_size_bytes;
+    aec->spk_buf_playback_format = (int16_t *)malloc(spk_frame_out_format_bytes);
+    if (aec->spk_buf_playback_format == NULL) {
+        ret = -ENOMEM;
+        goto exit_2;
+    }
+    /* Resampler is 16-bit */
+    aec->spk_buf_resampler_out = (int16_t *)malloc(aec->spk_buf_size_bytes);
+    if (aec->spk_buf_resampler_out == NULL) {
+        ret = -ENOMEM;
+        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;
+    }
+
+    flush_aec_fifos(aec);
+    aec_spk_mic_reset();
+
+exit:
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+    return ret;
+
+exit_4:
+    free(aec->spk_buf_resampler_out);
+exit_3:
+    free(aec->spk_buf_playback_format);
+exit_2:
+    free(aec->spk_buf);
+exit_1:
+    free(aec->mic_buf);
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+    return ret;
+}
+
+void aec_set_spk_running(struct aec_t *aec, bool state) {
+    ALOGV("%s enter", __func__);
+    pthread_mutex_lock(&aec->lock);
+    aec->spk_running = state;
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+}
+
+bool aec_get_spk_running(struct aec_t *aec) {
+    ALOGV("%s enter", __func__);
+    pthread_mutex_lock(&aec->lock);
+    bool state = aec->spk_running;
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+    return state;
+}
+
+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);
+    aec_set_spk_running(aec, false);
+    fifo_release(aec->spk_fifo);
+    fifo_release(aec->ts_fifo);
+    memset(&aec->last_spk_ts, 0, sizeof(struct ts_fifo_payload));
+    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_ts, 0, sizeof(struct ts_fifo_payload));
+    pthread_mutex_unlock(&aec->lock);
+    ALOGV("%s exit", __func__);
+}
+
+int write_to_reference_fifo(struct aec_t *aec, struct alsa_stream_out *out,
+                                void *buffer, size_t bytes) {
+    ALOGV("%s enter", __func__);
+    int ret = 0;
+
+    /* 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;
+    }
+
+    /* Get current timestamp and write to FIFO */
+    struct ts_fifo_payload spk_ts;
+    pcm_get_htimestamp(out->pcm, &spk_ts.available, &spk_ts.timestamp);
+    /* We need the timestamp of the first frame, adjust htimestamp */
+    timestamp_adjust(
+        &spk_ts.timestamp,
+        pcm_get_buffer_size(out->pcm) - spk_ts.available,
+        aec->spk_sampling_rate);
+    spk_ts.bytes = written_bytes;
+    ALOGV("Speaker timestamp: %ld s, %ld nsec", spk_ts.timestamp.tv_sec, spk_ts.timestamp.tv_nsec);
+    ssize_t ts_bytes = fifo_write(aec->ts_fifo, &spk_ts, sizeof(struct ts_fifo_payload));
+    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_ts.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_ts,
+                                        sizeof(struct ts_fifo_payload));
+        ALOGV("Read TS bytes: %zd, expected %zu", ts_bytes, sizeof(struct ts_fifo_payload));
+        aec->read_write_diff_bytes -= aec->last_spk_ts.bytes;
+    }
+
+    *spk_time = timespec_to_usec(aec->last_spk_ts.timestamp) + spk_time_offset;
+
+    aec->read_write_diff_bytes += read_bytes;
+    struct ts_fifo_payload spk_ts = aec->last_spk_ts;
+    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_ts, sizeof(struct ts_fifo_payload));
+        ALOGV("Fast-forwarded timestamp by %zd bytes, remaining bytes: %zd,"
+                " new timestamp (usec) %"PRIu64,
+                spk_ts.bytes, aec->read_write_diff_bytes, timespec_to_usec(spk_ts.timestamp));
+        aec->read_write_diff_bytes -= spk_ts.bytes;
+    }
+    aec->last_spk_ts = spk_ts;
+}
+
+int process_aec(struct aec_t *aec, struct alsa_stream_in *in, void* buffer, size_t bytes) {
+    ALOGV("%s enter", __func__);
+    int ret = 0;
+
+    if (aec == NULL) {
+        ALOGE("AEC: Interface uninitialized! Cannot process.");
+        return -EINVAL;
+    }
+
+    size_t frame_size = aec->mic_frame_size_bytes;
+    size_t in_frames = bytes / frame_size;
+
+    /* Copy raw mic samples to AEC input buffer */
+    memcpy(aec->mic_buf, buffer, bytes);
+
+    pcm_get_htimestamp(in->pcm, &aec->last_mic_ts.available, &aec->last_mic_ts.timestamp);
+    /* We need the timestamp of the first frame, adjust htimestamp */
+    timestamp_adjust(
+        &aec->last_mic_ts.timestamp,
+        pcm_get_buffer_size(in->pcm) - aec->last_mic_ts.available,
+        aec->mic_sampling_rate);
+    uint64_t mic_time = timespec_to_usec(aec->last_mic_ts.timestamp);
+    uint64_t spk_time = 0;
+
+    /*
+     * Only run AEC if there is speaker playback.
+     * The first time speaker state changes to running, flush FIFOs, so we're not stuck
+     * processing stale reference input.
+     */
+    bool spk_running = aec_get_spk_running(aec);
+
+    if (!spk_running) {
+        /* No new playback samples, so don't run AEC.
+         * 'buffer' already contains input samples. */
+        ALOGV("Speaker not running, skipping AEC..");
+        goto exit;
+    }
+
+    if (!aec->prev_spk_running) {
+        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....");
+        goto exit;
+    }
+
+    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);
+
+    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;
+        }
+    }
+
+    /* 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");
+        flush_aec_fifos(aec);
+        aec_spk_mic_reset();
+        goto exit;
+    }
+
+    ALOGV("Mic time: %"PRIu64", spk time: %"PRIu64, mic_time, spk_time);
+
+    /*
+     * AEC processing call - output stored at 'buffer'
+     */
+    int32_t aec_status = aec_spk_mic_process(
+        aec->spk_buf, spk_time,
+        aec->mic_buf, mic_time,
+        in_frames,
+        buffer);
+
+    if (!aec_status) {
+        ALOGE("AEC processing failed!");
+        ret = -EINVAL;
+    }
+
+exit:
+    aec->prev_spk_running = spk_running;
+    ALOGV("Mic time: %"PRIu64", spk time: %"PRIu64, mic_time, spk_time);
+    if (ret) {
+        /* Best we can do is copy over the raw mic signal */
+        memcpy(buffer, aec->mic_buf, bytes);
+        flush_aec_fifos(aec);
+        aec_spk_mic_reset();
+    }
+
+#if DEBUG_AEC
+    /* ref data is 32-bit at this point */
+    size_t ref_bytes = in_frames*aec->num_reference_channels*sizeof(int32_t);
+
+    FILE *fp_in = fopen("/data/local/traces/aec_in.pcm", "a+");
+    if (fp_in) {
+        fwrite((char *)aec->mic_buf, 1, bytes, fp_in);
+        fclose(fp_in);
+    } else {
+        ALOGE("AEC debug: Could not open file aec_in.pcm!");
+    }
+    FILE *fp_out = fopen("/data/local/traces/aec_out.pcm", "a+");
+    if (fp_out) {
+        fwrite((char *)buffer, 1, bytes, fp_out);
+        fclose(fp_out);
+    } else {
+        ALOGE("AEC debug: Could not open file aec_out.pcm!");
+    }
+    FILE *fp_ref = fopen("/data/local/traces/aec_ref.pcm", "a+");
+    if (fp_ref) {
+        fwrite((char *)aec->spk_buf, 1, ref_bytes, fp_ref);
+        fclose(fp_ref);
+    } else {
+        ALOGE("AEC debug: Could not open file aec_ref.pcm!");
+    }
+    FILE *fp_ts = fopen("/data/local/traces/aec_timestamps.txt", "a+");
+    if (fp_ts) {
+        fprintf(fp_ts, "%"PRIu64",%"PRIu64"\n", mic_time, spk_time);
+        fclose(fp_ts);
+    } else {
+        ALOGE("AEC debug: Could not open file aec_timestamps.txt!");
+    }
+#endif /* #if DEBUG_AEC */
+    ALOGV("%s exit", __func__);
+    return ret;
+}
diff --git a/audio/audio_aec.h b/audio/audio_aec.h
new file mode 100644
index 0000000..24bcf7a
--- /dev/null
+++ b/audio/audio_aec.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+/*
+ * Definitions and interface related to HAL implementations of Acoustic Echo Canceller (AEC).
+ *
+ * AEC cleans the microphone signal by removing from it audio data corresponding to loudspeaker
+ * playback. Note that this process can be nonlinear.
+ *
+ */
+
+#ifndef _AUDIO_AEC_H_
+#define _AUDIO_AEC_H_
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <hardware/audio.h>
+#include <audio_utils/resampler.h>
+#include "audio_hw.h"
+#include "fifo_wrapper.h"
+
+/* 'bytes' are the number of bytes written to audio FIFO, for which 'timestamp' is valid.
+ * 'available' is the number of frames available to read (for input) or yet to be played
+ * (for output) frames in the PCM buffer.
+ * timestamp and available are updated by pcm_get_htimestamp(), so they use the same
+ * datatypes as the corresponding arguments to that function. */
+struct ts_fifo_payload {
+    struct timespec timestamp;
+    unsigned int available;
+    size_t bytes;
+};
+
+struct aec_t {
+    pthread_mutex_t lock;
+    size_t num_reference_channels;
+    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 ts_fifo_payload last_mic_ts;
+    int32_t *spk_buf;
+    size_t spk_num_channels;
+    size_t spk_buf_size_bytes;
+    size_t spk_frame_size_bytes;
+    uint32_t spk_sampling_rate;
+    struct ts_fifo_payload last_spk_ts;
+    int16_t *spk_buf_playback_format;
+    int16_t *spk_buf_resampler_out;
+    void *spk_fifo;
+    void *ts_fifo;
+    ssize_t read_write_diff_bytes;
+    struct resampler_itfe *spk_resampler;
+    bool spk_running;
+    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, struct alsa_stream_out *out,
+                                void *buffer, size_t bytes);
+
+/* 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, struct alsa_stream_in *in, void* buffer, size_t bytes);
+
+/* Initialize AEC object.
+ * This must be called when the audio device is opened.
+ * ALSA device mutex must be held before calling this API.*/
+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);
+
+/* Initialize reference configuration for AEC.
+ * Must be called when a new output stream is opened. */
+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);
+
+/* Clear microphone configuration for AEC.
+ * Must be called when the input stream is closed. */
+void destroy_aec_mic_config (struct aec_t *aec);
+
+/* Used to communicate playback state (running or not) to AEC interface.
+ * 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);
+
+#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 */
+
+#endif /* _AUDIO_AEC_H_ */
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index 9db0cff..49b00d5 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -44,80 +44,8 @@
 
 #include <sys/ioctl.h>
 
-#define CARD_OUT 0
-#define PORT_HDMI 0
-#define CARD_IN 0
-#define PORT_BUILTIN_MIC 3
-
-#define MIXER_XML_PATH "/vendor/etc/mixer_paths.xml"
-/* Minimum granularity - Arbitrary but small value */
-#define CODEC_BASE_FRAME_COUNT 32
-
-#define CHANNEL_STEREO 2
-
-#define PCM_OPEN_RETRIES 100
-#define PCM_OPEN_WAIT_TIME_MS 20
-
-/* Capture codec parameters */
-/* Set up a capture period of 20 ms:
- * CAPTURE_PERIOD = PERIOD_SIZE / SAMPLE_RATE, so (20e-3) = PERIOD_SIZE / (16e3)
- * => PERIOD_SIZE = 320 frames, where each "frame" consists of 1 sample of every channel (here, 2ch) */
-#define CAPTURE_PERIOD_MULTIPLIER 10
-#define CAPTURE_PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * CAPTURE_PERIOD_MULTIPLIER)
-#define CAPTURE_PERIOD_COUNT 4
-#define CAPTURE_PERIOD_START_THRESHOLD 0
-#define CAPTURE_CODEC_SAMPLING_RATE 16000
-
-/* Playback codec parameters */
-/* number of base blocks in a short period (low latency) */
-#define PLAYBACK_PERIOD_MULTIPLIER 32  /* 21 ms */
-/* number of frames per short period (low latency) */
-#define PLAYBACK_PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * PLAYBACK_PERIOD_MULTIPLIER)
-/* number of pseudo periods for low latency playback */
-#define PLAYBACK_PERIOD_COUNT 4
-#define PLAYBACK_PERIOD_START_THRESHOLD 2
-#define PLAYBACK_CODEC_SAMPLING_RATE 48000
-#define MIN_WRITE_SLEEP_US      5000
-
-
-struct alsa_audio_device {
-    struct audio_hw_device hw_device;
-
-    pthread_mutex_t lock;   /* see note below on mutex acquisition order */
-    int devices;
-    struct alsa_stream_in *active_input;
-    struct alsa_stream_out *active_output;
-    struct audio_route *audio_route;
-    struct mixer *mixer;
-
-    bool mic_mute;
-};
-
-struct alsa_stream_in {
-    struct audio_stream_in stream;
-
-    pthread_mutex_t lock;   /* see note below on mutex acquisition order */
-    struct pcm_config config;
-    struct pcm *pcm;
-    bool unavailable;
-    bool standby;
-    struct alsa_audio_device *dev;
-    int read_threshold;
-    unsigned int read;
-};
-
-struct alsa_stream_out {
-    struct audio_stream_out stream;
-
-    pthread_mutex_t lock;   /* see note below on mutex acquisition order */
-    struct pcm_config config;
-    struct pcm *pcm;
-    bool unavailable;
-    int standby;
-    struct alsa_audio_device *dev;
-    int write_threshold;
-    unsigned int written;
-};
+#include "audio_hw.h"
+#include "audio_aec.h"
 
 /* must be called with hw device and output stream mutexes locked */
 static int start_output_stream(struct alsa_stream_out *out)
@@ -208,6 +136,7 @@
         adev->active_output = NULL;
         out->standby = 1;
     }
+    aec_set_spk_running(adev->aec, false);
     return 0;
 }
 
@@ -301,6 +230,7 @@
             goto exit;
         }
         out->standby = 0;
+        aec_set_spk_running(adev->aec, true);
     }
 
     pthread_mutex_unlock(&adev->lock);
@@ -309,6 +239,12 @@
     ret = pcm_write(out->pcm, buffer, out_frames * frame_size);
     if (ret == 0) {
         out->written += out_frames;
+
+        int aec_ret = write_to_reference_fifo(adev->aec, out, (void *)buffer,
+                                                out_frames * frame_size);
+        if (aec_ret) {
+            ALOGE("AEC: Write to speaker loopback FIFO failed!");
+        }
     }
 
 exit:
@@ -449,8 +385,6 @@
 
 static size_t in_get_buffer_size(const struct audio_stream *stream)
 {
-    struct alsa_stream_in *in = (struct alsa_stream_in *)stream;
-
     size_t buffer_size = get_input_buffer_size(stream->get_format(stream),
                             stream->get_channels(stream));
     ALOGV("in_get_buffer_size: %zu", buffer_size);
@@ -516,7 +450,7 @@
     size_t in_frames = bytes / frame_size;
 
     /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
-     * on the output stream mutex - e.g. executing select_mode() while holding the hw device
+     * on the input stream mutex - e.g. executing select_mode() while holding the hw device
      * mutex
      */
     pthread_mutex_lock(&in->lock);
@@ -533,7 +467,6 @@
 
     pthread_mutex_unlock(&adev->lock);
 
-
     ret = pcm_read(in->pcm, buffer, in_frames * frame_size);
     if (ret == 0) {
         in->read += in_frames;
@@ -552,6 +485,15 @@
     if (ret != 0) {
         usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) /
                 in_get_sample_rate(&stream->common));
+    } else {
+        /* Process AEC if available */
+        /* TODO move to a separate thread */
+        if (!adev->mic_mute) {
+            int aec_ret = process_aec(adev->aec, in, buffer, bytes);
+            if (aec_ret) {
+                ALOGE("process_aec returned error code %d", aec_ret);
+            }
+        }
     }
 
     return bytes;
@@ -645,6 +587,14 @@
     /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */
     ret = 0;
 
+    if (ret == 0) {
+        int aec_ret = init_aec_reference_config(ladev->aec, out);
+        if (aec_ret) {
+            ALOGE("AEC: Speaker config init failed!");
+            return -EINVAL;
+        }
+    }
+
     return ret;
 }
 
@@ -652,6 +602,8 @@
         struct audio_stream_out *stream)
 {
     ALOGV("adev_close_output_stream...");
+    struct alsa_audio_device *adev = (struct alsa_audio_device *)dev;
+    destroy_aec_reference_config(adev->aec);
     free(stream);
 }
 
@@ -798,6 +750,14 @@
     config->channel_mask = in_get_channels(&in->stream.common);
     config->sample_rate = in_get_sample_rate(&in->stream.common);
 
+    if (ret == 0) {
+        int aec_ret = init_aec_mic_config(ladev->aec, in);
+        if (aec_ret) {
+            ALOGE("AEC: Mic config init failed!");
+            return -EINVAL;
+        }
+    }
+
     if (ret) {
         free(in);
     } else {
@@ -808,10 +768,12 @@
 }
 
 static void adev_close_input_stream(struct audio_hw_device *dev,
-        struct audio_stream_in *in)
+        struct audio_stream_in *stream)
 {
     ALOGV("adev_close_input_stream...");
-    free(in);
+    struct alsa_audio_device *adev = (struct alsa_audio_device *)dev;
+    destroy_aec_mic_config(adev->aec);
+    free(stream);
     return;
 }
 
@@ -825,6 +787,8 @@
 {
     ALOGV("adev_close");
 
+    struct alsa_audio_device *adev = (struct alsa_audio_device *)device;
+    release_aec(adev->aec);
     free(device);
     return 0;
 }
@@ -882,6 +846,14 @@
         return -EINVAL;
     }
 
+    pthread_mutex_lock(&adev->lock);
+    if (init_aec(CAPTURE_CODEC_SAMPLING_RATE, NUM_AEC_REFERENCE_CHANNELS,
+                    CHANNEL_STEREO, &adev->aec)) {
+        pthread_mutex_unlock(&adev->lock);
+        return -EINVAL;
+    }
+    pthread_mutex_unlock(&adev->lock);
+
     return 0;
 }
 
diff --git a/audio/audio_hw.h b/audio/audio_hw.h
new file mode 100644
index 0000000..d1af55d
--- /dev/null
+++ b/audio/audio_hw.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef _YUKAWA_AUDIO_HW_H_
+#define _YUKAWA_AUDIO_HW_H_
+
+#include <hardware/audio.h>
+#include <tinyalsa/asoundlib.h>
+
+#define CARD_OUT 0
+#define PORT_HDMI 0
+#define CARD_IN 0
+#define PORT_BUILTIN_MIC 3
+
+#define MIXER_XML_PATH "/vendor/etc/mixer_paths.xml"
+/* Minimum granularity - Arbitrary but small value */
+#define CODEC_BASE_FRAME_COUNT 32
+
+#define CHANNEL_STEREO 2
+#define NUM_AEC_REFERENCE_CHANNELS 1
+
+#define PCM_OPEN_RETRIES 100
+#define PCM_OPEN_WAIT_TIME_MS 20
+
+/* Capture codec parameters */
+/* Set up a capture period of 32 ms:
+ * CAPTURE_PERIOD = PERIOD_SIZE / SAMPLE_RATE, so (32e-3) = PERIOD_SIZE / (16e3)
+ * => PERIOD_SIZE = 512 frames, where each "frame" consists of 1 sample of every channel (here, 2ch) */
+#define CAPTURE_PERIOD_MULTIPLIER 16
+#define CAPTURE_PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * CAPTURE_PERIOD_MULTIPLIER)
+#define CAPTURE_PERIOD_COUNT 4
+#define CAPTURE_PERIOD_START_THRESHOLD 0
+#define CAPTURE_CODEC_SAMPLING_RATE 16000
+
+/* Playback codec parameters */
+/* number of base blocks in a short period (low latency) */
+#define PLAYBACK_PERIOD_MULTIPLIER 32  /* 21 ms */
+/* number of frames per short period (low latency) */
+#define PLAYBACK_PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * PLAYBACK_PERIOD_MULTIPLIER)
+/* number of pseudo periods for low latency playback */
+#define PLAYBACK_PERIOD_COUNT 4
+#define PLAYBACK_PERIOD_START_THRESHOLD 2
+#define PLAYBACK_CODEC_SAMPLING_RATE 48000
+#define MIN_WRITE_SLEEP_US      5000
+
+struct alsa_audio_device {
+    struct audio_hw_device hw_device;
+
+    pthread_mutex_t lock;   /* see notes in in_read/out_write on mutex acquisition order */
+    int devices;
+    struct alsa_stream_in *active_input;
+    struct alsa_stream_out *active_output;
+    struct audio_route *audio_route;
+    struct mixer *mixer;
+    bool mic_mute;
+    struct aec_t *aec;
+};
+
+struct alsa_stream_in {
+    struct audio_stream_in stream;
+
+    pthread_mutex_t lock;   /* see note in in_read() on mutex acquisition order */
+    struct pcm_config config;
+    struct pcm *pcm;
+    bool unavailable;
+    bool standby;
+    struct alsa_audio_device *dev;
+    int read_threshold;
+    unsigned int read;
+};
+
+struct alsa_stream_out {
+    struct audio_stream_out stream;
+
+    pthread_mutex_t lock;   /* see note in out_write() on mutex acquisition order */
+    struct pcm_config config;
+    struct pcm *pcm;
+    bool unavailable;
+    int standby;
+    struct alsa_audio_device *dev;
+    int write_threshold;
+    unsigned int written;
+};
+
+#endif /* #ifndef _YUKAWA_AUDIO_HW_H_ */
diff --git a/audio/fifo_wrapper.cpp b/audio/fifo_wrapper.cpp
new file mode 100644
index 0000000..7bc9079
--- /dev/null
+++ b/audio/fifo_wrapper.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "audio_utils_fifo_wrapper"
+// #define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <errno.h>
+#include <log/log.h>
+#include <audio_utils/fifo.h>
+#include "fifo_wrapper.h"
+
+struct audio_fifo_itfe {
+    audio_utils_fifo *p_fifo;
+    audio_utils_fifo_reader *p_fifo_reader;
+    audio_utils_fifo_writer *p_fifo_writer;
+    int8_t *p_buffer;
+};
+
+void *fifo_init(uint32_t bytes, bool reader_throttles_writer) {
+    struct audio_fifo_itfe *interface = new struct audio_fifo_itfe;
+    interface->p_buffer = new int8_t[bytes];
+    if (interface->p_buffer == NULL) {
+        ALOGE("Failed to allocate fifo buffer!");
+        return NULL;
+    }
+    interface->p_fifo = new audio_utils_fifo(bytes, 1, interface->p_buffer, reader_throttles_writer);
+    interface->p_fifo_writer = new audio_utils_fifo_writer(*interface->p_fifo);
+    interface->p_fifo_reader = new audio_utils_fifo_reader(*interface->p_fifo);
+
+    return (void *)interface;
+}
+
+void fifo_release(void *fifo_itfe) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    delete interface->p_fifo_writer;
+    delete interface->p_fifo_reader;
+    delete interface->p_fifo;
+    delete[] interface->p_buffer;
+    delete interface;
+}
+
+ssize_t fifo_read(void *fifo_itfe, void *buffer, size_t bytes) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    return interface->p_fifo_reader->read(buffer, bytes);
+}
+
+ssize_t fifo_write(void *fifo_itfe, void *buffer, size_t bytes) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    return interface->p_fifo_writer->write(buffer, bytes);
+}
+
+ssize_t fifo_available_to_read(void *fifo_itfe) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    return interface->p_fifo_reader->available();
+}
+
+ssize_t fifo_available_to_write(void *fifo_itfe) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    return interface->p_fifo_writer->available();
+}
+
+ssize_t fifo_flush(void *fifo_itfe) {
+    struct audio_fifo_itfe *interface = static_cast<struct audio_fifo_itfe *>(fifo_itfe);
+    return interface->p_fifo_reader->flush();
+}
diff --git a/audio/fifo_wrapper.h b/audio/fifo_wrapper.h
new file mode 100644
index 0000000..e9469ef
--- /dev/null
+++ b/audio/fifo_wrapper.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef _AUDIO_FIFO_WRAPPER_H_
+#define _AUDIO_FIFO_WRAPPER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *fifo_init(uint32_t bytes, bool reader_throttles_writer);
+void fifo_release(void *fifo_itfe);
+ssize_t fifo_read(void *fifo_itfe, void *buffer, size_t bytes);
+ssize_t fifo_write(void *fifo_itfe, void *buffer, size_t bytes);
+ssize_t fifo_available_to_read(void *fifo_itfe);
+ssize_t fifo_available_to_write(void *fifo_itfe);
+ssize_t fifo_flush(void *fifo_itfe);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* #ifndef _AUDIO_FIFO_WRAPPER_H_ */