Migrate goldfish audio HAL to HIDL

Bug: 157158109
Test: atest VtsHalAudioV6_0TargetTest
Test: atest VtsHalAudioEffectV6_0TargetTest
Change-Id: I5f6749c533fed40a99b10e915112c68f9dc0519d
Merged-In: I5f6749c533fed40a99b10e915112c68f9dc0519d
Signed-off-by: Roman Kiryanov <rkir@google.com>
diff --git a/audio/Android.bp b/audio/Android.bp
index 28c563a..4b80fd7 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -14,24 +14,38 @@
 // limitations under the License.
 
 cc_library_shared {
-    name: "audio.primary.goldfish",
+    name: "android.hardware.audio@6.0-impl.ranchu",
     vendor: true,
+    vintf_fragments: ["android.hardware.audio@6.0-impl.ranchu.xml"],
     relative_install_path: "hw",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "entry.cpp",
+        "device_factory.cpp",
+        "primary_device.cpp",
+        "stream_common.cpp",
+        "stream_in.cpp",
+        "stream_out.cpp",
+        "io_thread.cpp",
+        "talsa.cpp",
+        "util.cpp",
+    ],
     shared_libs: [
+        "android.hardware.audio@6.0",
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+        "libbase",
         "libcutils",
+        "libhidlbase",
         "liblog",
         "libtinyalsa",
-    ],
-    srcs: [
-        "audio_hw.c",
-        "audio_vbuffer.c",
-        "ext_pcm.c",
+        "libutils",
+        "libfmq",
     ],
     header_libs: [
-        "libhardware_headers",
-        "libcutils_headers",
+        "libaudio_system_headers",
     ],
     cflags: [
-        "-Wno-unused-parameter"
+        "-DLOG_TAG=\"android.hardware.audio@6.0-impl.ranchu\"",
     ],
 }
diff --git a/audio/android.hardware.audio@6.0-impl.ranchu.xml b/audio/android.hardware.audio@6.0-impl.ranchu.xml
new file mode 100644
index 0000000..b0a2aaa
--- /dev/null
+++ b/audio/android.hardware.audio@6.0-impl.ranchu.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.audio</name>
+        <transport>hwbinder</transport>
+        <version>6.0</version>
+        <interface>
+            <name>IDevicesFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
deleted file mode 100644
index 28991b6..0000000
--- a/audio/audio_hw.c
+++ /dev/null
@@ -1,1450 +0,0 @@
-/*
- * Copyright (C) 2012 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_hw_generic"
-
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <cutils/properties.h>
-#include <cutils/str_parms.h>
-
-#include <hardware/hardware.h>
-#include <system/audio.h>
-
-#include "audio_hw.h"
-#include "ext_pcm.h"
-
-#define PCM_CARD 0
-#define PCM_DEVICE 0
-
-#define OUT_PERIOD_MS 15
-#define OUT_PERIOD_COUNT 4
-
-#define IN_PERIOD_MS 15
-#define IN_PERIOD_COUNT 4
-
-#define _bool_str(x) ((x)?"true":"false")
-
-#define PROP_KEY_SIMULATE_MULTI_ZONE_AUDIO "ro.aae.simulateMultiZoneAudio"
-
-static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
-
-static struct pcm_config pcm_config_out = {
-    .channels = 2,
-    .rate = 0,
-    .period_size = 0,
-    .period_count = OUT_PERIOD_COUNT,
-    .format = PCM_FORMAT_S16_LE,
-    .start_threshold = 0,
-};
-
-static struct pcm_config pcm_config_in = {
-    .channels = 2,
-    .rate = 0,
-    .period_size = 0,
-    .period_count = IN_PERIOD_COUNT,
-    .format = PCM_FORMAT_S16_LE,
-    .start_threshold = 0,
-    .stop_threshold = INT_MAX,
-};
-
-static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
-static unsigned int audio_device_ref_count = 0;
-
-static uint32_t out_get_sample_rate(const struct audio_stream *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    return out->req_config.sample_rate;
-}
-
-static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) {
-    return -ENOSYS;
-}
-
-static size_t out_get_buffer_size(const struct audio_stream *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    int size = out->pcm_config.period_size *
-                audio_stream_out_frame_size(&out->stream);
-
-    return size;
-}
-
-static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    return out->req_config.channel_mask;
-}
-
-static audio_format_t out_get_format(const struct audio_stream *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    return out->req_config.format;
-}
-
-static int out_set_format(struct audio_stream *stream, audio_format_t format) {
-    return -ENOSYS;
-}
-
-static int out_dump(const struct audio_stream *stream, int fd) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    pthread_mutex_lock(&out->lock);
-    dprintf(fd, "\tout_dump:\n"
-                "\t\taddress: %s\n"
-                "\t\tsample rate: %u\n"
-                "\t\tbuffer size: %zu\n"
-                "\t\tchannel mask: %08x\n"
-                "\t\tformat: %d\n"
-                "\t\tdevice: %08x\n"
-                "\t\tamplitude ratio: %f\n"
-                "\t\tenabled channels: %d\n"
-                "\t\taudio dev: %p\n\n",
-                out->bus_address,
-                out_get_sample_rate(stream),
-                out_get_buffer_size(stream),
-                out_get_channels(stream),
-                out_get_format(stream),
-                out->device,
-                out->amplitude_ratio,
-                out->enabled_channels,
-                out->dev);
-    pthread_mutex_unlock(&out->lock);
-    return 0;
-}
-
-static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    struct str_parms *parms;
-    char value[32];
-    int ret;
-    long val;
-    char *end;
-
-    pthread_mutex_lock(&out->lock);
-    if (!out->standby) {
-        //Do not support changing params while stream running
-        ret = -ENOSYS;
-    } else {
-        parms = str_parms_create_str(kvpairs);
-        ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
-                                value, sizeof(value));
-        if (ret >= 0) {
-            errno = 0;
-            val = strtol(value, &end, 10);
-            if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) {
-                out->device = (int)val;
-                ret = 0;
-            } else {
-                ret = -EINVAL;
-            }
-        }
-        str_parms_destroy(parms);
-    }
-    pthread_mutex_unlock(&out->lock);
-    return ret;
-}
-
-static char *out_get_parameters(const struct audio_stream *stream, const char *keys) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    struct str_parms *query = str_parms_create_str(keys);
-    char *str;
-    char value[256];
-    struct str_parms *reply = str_parms_create();
-    int ret;
-
-    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
-    if (ret >= 0) {
-        pthread_mutex_lock(&out->lock);
-        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device);
-        pthread_mutex_unlock(&out->lock);
-        str = strdup(str_parms_to_str(reply));
-    } else {
-        str = strdup(keys);
-    }
-
-    str_parms_destroy(query);
-    str_parms_destroy(reply);
-    return str;
-}
-
-static uint32_t out_get_latency(const struct audio_stream_out *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    return (out->pcm_config.period_size * 1000) / out->pcm_config.rate;
-}
-
-static int out_set_volume(struct audio_stream_out *stream,
-        float left, float right) {
-    return -ENOSYS;
-}
-
-static void *out_write_worker(void *args) {
-    struct generic_stream_out *out = (struct generic_stream_out *)args;
-    struct ext_pcm *ext_pcm = NULL;
-    uint8_t *buffer = NULL;
-    int buffer_frames;
-    int buffer_size;
-    bool restart = false;
-    bool shutdown = false;
-    while (true) {
-        pthread_mutex_lock(&out->lock);
-        while (out->worker_standby || restart) {
-            restart = false;
-            if (ext_pcm) {
-                ext_pcm_close(ext_pcm); // Frees pcm
-                ext_pcm = NULL;
-                free(buffer);
-                buffer=NULL;
-            }
-            if (out->worker_exit) {
-                break;
-            }
-            pthread_cond_wait(&out->worker_wake, &out->lock);
-        }
-
-        if (out->worker_exit) {
-            if (!out->worker_standby) {
-                ALOGE("Out worker:%s not in standby before exiting", out->bus_address);
-            }
-            shutdown = true;
-        }
-
-        while (!shutdown && audio_vbuffer_live(&out->buffer) == 0) {
-            pthread_cond_wait(&out->worker_wake, &out->lock);
-        }
-
-        if (shutdown) {
-            pthread_mutex_unlock(&out->lock);
-            break;
-        }
-
-        if (!ext_pcm) {
-            ext_pcm = ext_pcm_open(PCM_CARD, PCM_DEVICE,
-                    PCM_OUT | PCM_MONOTONIC, &out->pcm_config);
-            if (!ext_pcm_is_ready(ext_pcm)) {
-                ALOGE("pcm_open(out) failed: %s: address %s channels %d format %d rate %d",
-                        ext_pcm_get_error(ext_pcm),
-                        out->bus_address,
-                        out->pcm_config.channels,
-                        out->pcm_config.format,
-                        out->pcm_config.rate);
-                pthread_mutex_unlock(&out->lock);
-                break;
-            }
-            buffer_frames = out->pcm_config.period_size;
-            buffer_size = ext_pcm_frames_to_bytes(ext_pcm, buffer_frames);
-            buffer = malloc(buffer_size);
-            if (!buffer) {
-                ALOGE("could not allocate write buffer");
-                pthread_mutex_unlock(&out->lock);
-                break;
-            }
-        }
-        int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames);
-        pthread_mutex_unlock(&out->lock);
-        int write_error = ext_pcm_write(ext_pcm, out->bus_address,
-                buffer, ext_pcm_frames_to_bytes(ext_pcm, frames));
-        if (write_error) {
-            ALOGE("pcm_write failed %s address %s", ext_pcm_get_error(ext_pcm), out->bus_address);
-            restart = true;
-        } else {
-            ALOGV("pcm_write succeed address %s", out->bus_address);
-        }
-    }
-    if (buffer) {
-        free(buffer);
-    }
-
-    return NULL;
-}
-
-// Call with in->lock held
-static void get_current_output_position(struct generic_stream_out *out,
-        uint64_t *position, struct timespec * timestamp) {
-    struct timespec curtime = { .tv_sec = 0, .tv_nsec = 0 };
-    clock_gettime(CLOCK_MONOTONIC, &curtime);
-    const int64_t now_us = (curtime.tv_sec * 1000000000LL + curtime.tv_nsec) / 1000;
-    if (timestamp) {
-        *timestamp = curtime;
-    }
-    int64_t position_since_underrun;
-    if (out->standby) {
-        position_since_underrun = 0;
-    } else {
-        const int64_t first_us = (out->underrun_time.tv_sec * 1000000000LL +
-                                  out->underrun_time.tv_nsec) / 1000;
-        position_since_underrun = (now_us - first_us) *
-                out_get_sample_rate(&out->stream.common) /
-                1000000;
-        if (position_since_underrun < 0) {
-            position_since_underrun = 0;
-        }
-    }
-    *position = out->underrun_position + position_since_underrun;
-
-    // The device will reuse the same output stream leading to periods of
-    // underrun.
-    if (*position > out->frames_written) {
-        ALOGW("Not supplying enough data to HAL, expected position %" PRIu64 " , only wrote "
-              "%" PRIu64,
-              *position, out->frames_written);
-
-        *position = out->frames_written;
-        out->underrun_position = *position;
-        out->underrun_time = curtime;
-        out->frames_total_buffered = 0;
-    }
-}
-
-// Applies gain naively, assumes AUDIO_FORMAT_PCM_16_BIT and stereo output
-static void out_apply_gain(struct generic_stream_out *out, const void *buffer, size_t bytes) {
-    int16_t *int16_buffer = (int16_t *)buffer;
-    size_t int16_size = bytes / sizeof(int16_t);
-    for (int i = 0; i < int16_size; i++) {
-        if ((i % 2) && !(out->enabled_channels & RIGHT_CHANNEL)) {
-            int16_buffer[i] = 0;
-        } else if (!(i % 2) && !(out->enabled_channels & LEFT_CHANNEL)) {
-            int16_buffer[i] = 0;
-        } else {
-            float multiplied = int16_buffer[i] * out->amplitude_ratio;
-            if (multiplied > INT16_MAX) int16_buffer[i] = INT16_MAX;
-            else if (multiplied < INT16_MIN) int16_buffer[i] = INT16_MIN;
-            else int16_buffer[i] = (int16_t)multiplied;
-        }
-    }
-}
-
-static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    ALOGV("%s: to device %s", __func__, out->bus_address);
-    const size_t frames =  bytes / audio_stream_out_frame_size(stream);
-
-    pthread_mutex_lock(&out->lock);
-
-    if (out->worker_standby) {
-        out->worker_standby = false;
-    }
-
-    uint64_t current_position;
-    struct timespec current_time;
-
-    get_current_output_position(out, &current_position, &current_time);
-    const uint64_t now_us = (current_time.tv_sec * 1000000000LL +
-                             current_time.tv_nsec) / 1000;
-    if (out->standby) {
-        out->standby = false;
-        out->underrun_time = current_time;
-        out->frames_rendered = 0;
-        out->frames_total_buffered = 0;
-    }
-
-    size_t frames_written = frames;
-    if (out->dev->master_mute) {
-        ALOGV("%s: ignored due to master mute", __func__);
-    } else {
-        out_apply_gain(out, buffer, bytes);
-        frames_written = audio_vbuffer_write(&out->buffer, buffer, frames);
-        pthread_cond_signal(&out->worker_wake);
-    }
-
-    /* Implementation just consumes bytes if we start getting backed up */
-    out->frames_written += frames;
-    out->frames_rendered += frames;
-    out->frames_total_buffered += frames;
-
-    // We simulate the audio device blocking when it's write buffers become
-    // full.
-
-    // At the beginning or after an underrun, try to fill up the vbuffer.
-    // This will be throttled by the PlaybackThread
-    int frames_sleep = out->frames_total_buffered < out->buffer.frame_count ? 0 : frames;
-
-    uint64_t sleep_time_us = frames_sleep * 1000000LL /
-                            out_get_sample_rate(&stream->common);
-
-    // If the write calls are delayed, subtract time off of the sleep to
-    // compensate
-    uint64_t time_since_last_write_us = now_us - out->last_write_time_us;
-    if (time_since_last_write_us < sleep_time_us) {
-        sleep_time_us -= time_since_last_write_us;
-    } else {
-        sleep_time_us = 0;
-    }
-    out->last_write_time_us = now_us + sleep_time_us;
-
-    pthread_mutex_unlock(&out->lock);
-
-    if (sleep_time_us > 0) {
-        usleep(sleep_time_us);
-    }
-
-    if (frames_written < frames) {
-        ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames",
-                frames_written, frames);
-    }
-
-    /* Always consume all bytes */
-    return bytes;
-}
-
-static int out_get_presentation_position(const struct audio_stream_out *stream,
-        uint64_t *frames, struct timespec *timestamp) {
-    int ret = -EINVAL;
-    if (stream == NULL || frames == NULL || timestamp == NULL) {
-        return -EINVAL;
-    }
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-
-    pthread_mutex_lock(&out->lock);
-    get_current_output_position(out, frames, timestamp);
-    pthread_mutex_unlock(&out->lock);
-
-    return 0;
-}
-
-static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames) {
-    if (stream == NULL || dsp_frames == NULL) {
-        return -EINVAL;
-    }
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    pthread_mutex_lock(&out->lock);
-    *dsp_frames = out->frames_rendered;
-    pthread_mutex_unlock(&out->lock);
-    return 0;
-}
-
-// Must be called with out->lock held
-static void do_out_standby(struct generic_stream_out *out) {
-    int frames_sleep = 0;
-    uint64_t sleep_time_us = 0;
-    if (out->standby) {
-        return;
-    }
-    while (true) {
-        get_current_output_position(out, &out->underrun_position, NULL);
-        frames_sleep = out->frames_written - out->underrun_position;
-
-        if (frames_sleep == 0) {
-            break;
-        }
-
-        sleep_time_us = frames_sleep * 1000000LL /
-                        out_get_sample_rate(&out->stream.common);
-
-        pthread_mutex_unlock(&out->lock);
-        usleep(sleep_time_us);
-        pthread_mutex_lock(&out->lock);
-    }
-    out->worker_standby = true;
-    out->standby = true;
-}
-
-static int out_standby(struct audio_stream *stream) {
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    pthread_mutex_lock(&out->lock);
-    do_out_standby(out);
-    pthread_mutex_unlock(&out->lock);
-    return 0;
-}
-
-static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) {
-    // out_add_audio_effect is a no op
-    return 0;
-}
-
-static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) {
-    // out_remove_audio_effect is a no op
-    return 0;
-}
-
-static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
-        int64_t *timestamp) {
-    return -ENOSYS;
-}
-
-static uint32_t in_get_sample_rate(const struct audio_stream *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    return in->req_config.sample_rate;
-}
-
-static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) {
-    return -ENOSYS;
-}
-
-static int refine_output_parameters(uint32_t *sample_rate, audio_format_t *format,
-        audio_channel_mask_t *channel_mask) {
-    static const uint32_t sample_rates [] = {
-        8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
-    };
-    static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
-    bool inval = false;
-    if (*format != AUDIO_FORMAT_PCM_16_BIT) {
-        *format = AUDIO_FORMAT_PCM_16_BIT;
-        inval = true;
-    }
-
-    int channel_count = popcount(*channel_mask);
-    if (channel_count != 1 && channel_count != 2) {
-        *channel_mask = AUDIO_CHANNEL_IN_STEREO;
-        inval = true;
-    }
-
-    int i;
-    for (i = 0; i < sample_rates_count; i++) {
-        if (*sample_rate < sample_rates[i]) {
-            *sample_rate = sample_rates[i];
-            inval=true;
-            break;
-        }
-        else if (*sample_rate == sample_rates[i]) {
-            break;
-        }
-        else if (i == sample_rates_count-1) {
-            // Cap it to the highest rate we support
-            *sample_rate = sample_rates[i];
-            inval=true;
-        }
-    }
-
-    if (inval) {
-        return -EINVAL;
-    }
-    return 0;
-}
-
-static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format,
-        audio_channel_mask_t *channel_mask) {
-    static const uint32_t sample_rates [] = {
-        8000, 11025, 16000, 22050, 44100, 48000
-    };
-    static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
-    bool inval = false;
-    // Only PCM_16_bit is supported. If this is changed, stereo to mono drop
-    // must be fixed in in_read
-    if (*format != AUDIO_FORMAT_PCM_16_BIT) {
-        *format = AUDIO_FORMAT_PCM_16_BIT;
-        inval = true;
-    }
-
-    int channel_count = popcount(*channel_mask);
-    if (channel_count != 1 && channel_count != 2) {
-        *channel_mask = AUDIO_CHANNEL_IN_STEREO;
-        inval = true;
-    }
-
-    int i;
-    for (i = 0; i < sample_rates_count; i++) {
-        if (*sample_rate < sample_rates[i]) {
-            *sample_rate = sample_rates[i];
-            inval=true;
-            break;
-        }
-        else if (*sample_rate == sample_rates[i]) {
-            break;
-        }
-        else if (i == sample_rates_count-1) {
-            // Cap it to the highest rate we support
-            *sample_rate = sample_rates[i];
-            inval=true;
-        }
-    }
-
-    if (inval) {
-        return -EINVAL;
-    }
-    return 0;
-}
-
-static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format,
-        audio_channel_mask_t channel_mask) {
-    size_t size;
-    size_t device_rate;
-    int channel_count = popcount(channel_mask);
-    if (refine_input_parameters(&sample_rate, &format, &channel_mask) != 0)
-        return 0;
-
-    size = sample_rate*IN_PERIOD_MS/1000;
-    // Audioflinger expects audio buffers to be multiple of 16 frames
-    size = ((size + 15) / 16) * 16;
-    size *= sizeof(short) * channel_count;
-
-    return size;
-}
-
-static size_t in_get_buffer_size(const struct audio_stream *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    int size = get_input_buffer_size(in->req_config.sample_rate,
-                                 in->req_config.format,
-                                 in->req_config.channel_mask);
-
-    return size;
-}
-
-static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    return in->req_config.channel_mask;
-}
-
-static audio_format_t in_get_format(const struct audio_stream *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    return in->req_config.format;
-}
-
-static int in_set_format(struct audio_stream *stream, audio_format_t format) {
-    return -ENOSYS;
-}
-
-static int in_dump(const struct audio_stream *stream, int fd) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-
-    pthread_mutex_lock(&in->lock);
-    dprintf(fd, "\tin_dump:\n"
-                "\t\tsample rate: %u\n"
-                "\t\tbuffer size: %zu\n"
-                "\t\tchannel mask: %08x\n"
-                "\t\tformat: %d\n"
-                "\t\tdevice: %08x\n"
-                "\t\taudio dev: %p\n\n",
-                in_get_sample_rate(stream),
-                in_get_buffer_size(stream),
-                in_get_channels(stream),
-                in_get_format(stream),
-                in->device,
-                in->dev);
-    pthread_mutex_unlock(&in->lock);
-    return 0;
-}
-
-static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    struct str_parms *parms;
-    char value[32];
-    int ret;
-    long val;
-    char *end;
-
-    pthread_mutex_lock(&in->lock);
-    if (!in->standby) {
-        ret = -ENOSYS;
-    } else {
-        parms = str_parms_create_str(kvpairs);
-
-        ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
-                                value, sizeof(value));
-        if (ret >= 0) {
-            errno = 0;
-            val = strtol(value, &end, 10);
-            if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
-                in->device = (int)val;
-                ret = 0;
-            } else {
-                ret = -EINVAL;
-            }
-        }
-
-        str_parms_destroy(parms);
-    }
-    pthread_mutex_unlock(&in->lock);
-    return ret;
-}
-
-static char *in_get_parameters(const struct audio_stream *stream, const char *keys) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    struct str_parms *query = str_parms_create_str(keys);
-    char *str;
-    char value[256];
-    struct str_parms *reply = str_parms_create();
-    int ret;
-
-    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
-    if (ret >= 0) {
-        str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device);
-        str = strdup(str_parms_to_str(reply));
-    } else {
-        str = strdup(keys);
-    }
-
-    str_parms_destroy(query);
-    str_parms_destroy(reply);
-    return str;
-}
-
-static int in_set_gain(struct audio_stream_in *stream, float gain) {
-    // TODO(hwwang): support adjusting input gain
-    return 0;
-}
-
-// Call with in->lock held
-static void get_current_input_position(struct generic_stream_in *in,
-        int64_t * position, struct timespec * timestamp) {
-    struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
-    clock_gettime(CLOCK_MONOTONIC, &t);
-    const int64_t now_us = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
-    if (timestamp) {
-        *timestamp = t;
-    }
-    int64_t position_since_standby;
-    if (in->standby) {
-        position_since_standby = 0;
-    } else {
-        const int64_t first_us = (in->standby_exit_time.tv_sec * 1000000000LL +
-                                  in->standby_exit_time.tv_nsec) / 1000;
-        position_since_standby = (now_us - first_us) *
-                in_get_sample_rate(&in->stream.common) /
-                1000000;
-        if (position_since_standby < 0) {
-            position_since_standby = 0;
-        }
-    }
-    *position = in->standby_position + position_since_standby;
-}
-
-// Must be called with in->lock held
-static void do_in_standby(struct generic_stream_in *in) {
-    if (in->standby) {
-        return;
-    }
-    in->worker_standby = true;
-    get_current_input_position(in, &in->standby_position, NULL);
-    in->standby = true;
-}
-
-static int in_standby(struct audio_stream *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    pthread_mutex_lock(&in->lock);
-    do_in_standby(in);
-    pthread_mutex_unlock(&in->lock);
-    return 0;
-}
-
-#define STEP (3.14159265 / 180)
-// Generates pure tone for FM_TUNER
-static int pseudo_pcm_read(void *data, unsigned int count) {
-    unsigned int length = count / sizeof(short);
-    short *sdata = (short *)data;
-    for (int index = 0; index < length; index++) {
-        sdata[index] = (short)(sin(index * STEP) * 4096);
-    }
-    return count;
-}
-
-static void *in_read_worker(void *args) {
-    struct generic_stream_in *in = (struct generic_stream_in *)args;
-    struct pcm *pcm = NULL;
-    uint8_t *buffer = NULL;
-    size_t buffer_frames;
-    int buffer_size;
-
-    bool restart = false;
-    bool shutdown = false;
-    while (true) {
-        pthread_mutex_lock(&in->lock);
-        while (in->worker_standby || restart) {
-            restart = false;
-            if (pcm) {
-                pcm_close(pcm); // Frees pcm
-                pcm = NULL;
-                free(buffer);
-                buffer=NULL;
-            }
-            if (in->worker_exit) {
-                break;
-            }
-            pthread_cond_wait(&in->worker_wake, &in->lock);
-        }
-
-        if (in->worker_exit) {
-            if (!in->worker_standby) {
-                ALOGE("In worker not in standby before exiting");
-            }
-            shutdown = true;
-        }
-        if (shutdown) {
-            pthread_mutex_unlock(&in->lock);
-            break;
-        }
-        if (!pcm) {
-            pcm = pcm_open(PCM_CARD, PCM_DEVICE,
-                    PCM_IN | PCM_MONOTONIC, &in->pcm_config);
-            if (!pcm_is_ready(pcm)) {
-                ALOGE("pcm_open(in) failed: %s: channels %d format %d rate %d",
-                        pcm_get_error(pcm),
-                        in->pcm_config.channels,
-                        in->pcm_config.format,
-                        in->pcm_config.rate);
-                pthread_mutex_unlock(&in->lock);
-                break;
-            }
-            buffer_frames = in->pcm_config.period_size;
-            buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
-            buffer = malloc(buffer_size);
-            if (!buffer) {
-                ALOGE("could not allocate worker read buffer");
-                pthread_mutex_unlock(&in->lock);
-                break;
-            }
-        }
-        pthread_mutex_unlock(&in->lock);
-        int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames));
-        if (ret != 0) {
-            ALOGW("pcm_read failed %s", pcm_get_error(pcm));
-            restart = true;
-        }
-
-        pthread_mutex_lock(&in->lock);
-        size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames);
-        pthread_mutex_unlock(&in->lock);
-
-        if (frames_written != buffer_frames) {
-            ALOGW("in_read_worker only could write %zu / %zu frames",
-                    frames_written, buffer_frames);
-        }
-    }
-    if (buffer) {
-        free(buffer);
-    }
-    return NULL;
-}
-
-static ssize_t in_read(struct audio_stream_in *stream, void *buffer, size_t bytes) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    struct generic_audio_device *adev = in->dev;
-    const size_t frames =  bytes / audio_stream_in_frame_size(stream);
-    int ret = 0;
-    bool mic_mute = false;
-    size_t read_bytes = 0;
-
-    adev_get_mic_mute(&adev->device, &mic_mute);
-    pthread_mutex_lock(&in->lock);
-
-    if (in->worker_standby) {
-        in->worker_standby = false;
-    }
-    // FM_TUNER fills the buffer via pseudo_pcm_read directly
-    if (in->device != AUDIO_DEVICE_IN_FM_TUNER) {
-        pthread_cond_signal(&in->worker_wake);
-    }
-
-    int64_t current_position;
-    struct timespec current_time;
-
-    get_current_input_position(in, &current_position, &current_time);
-    if (in->standby) {
-        in->standby = false;
-        in->standby_exit_time = current_time;
-        in->standby_frames_read = 0;
-    }
-
-    const int64_t frames_available =
-        current_position - in->standby_position - in->standby_frames_read;
-    assert(frames_available >= 0);
-
-    const size_t frames_wait =
-        ((uint64_t)frames_available > frames) ? 0 : frames - frames_available;
-
-    int64_t sleep_time_us  = frames_wait * 1000000LL / in_get_sample_rate(&stream->common);
-
-    pthread_mutex_unlock(&in->lock);
-
-    if (sleep_time_us > 0) {
-        usleep(sleep_time_us);
-    }
-
-    pthread_mutex_lock(&in->lock);
-    int read_frames = 0;
-    if (in->standby) {
-        ALOGW("Input put to sleep while read in progress");
-        goto exit;
-    }
-    in->standby_frames_read += frames;
-
-    if (in->device == AUDIO_DEVICE_IN_FM_TUNER) {
-        int read_bytes = pseudo_pcm_read(buffer, bytes);
-        read_frames = read_bytes / audio_stream_in_frame_size(stream);
-    } else if (popcount(in->req_config.channel_mask) == 1 &&
-        in->pcm_config.channels == 2) {
-        // Need to resample to mono
-        if (in->stereo_to_mono_buf_size < bytes*2) {
-            in->stereo_to_mono_buf = realloc(in->stereo_to_mono_buf, bytes*2);
-            if (!in->stereo_to_mono_buf) {
-                ALOGE("Failed to allocate stereo_to_mono_buff");
-                goto exit;
-            }
-        }
-
-        read_frames = audio_vbuffer_read(&in->buffer, in->stereo_to_mono_buf, frames);
-
-        // Currently only pcm 16 is supported.
-        uint16_t *src = (uint16_t *)in->stereo_to_mono_buf;
-        uint16_t *dst = (uint16_t *)buffer;
-        size_t i;
-        // Resample stereo 16 to mono 16 by dropping one channel.
-        // The stereo stream is interleaved L-R-L-R
-        for (i = 0; i < frames; i++) {
-            *dst = *src;
-            src += 2;
-            dst += 1;
-        }
-    } else {
-        read_frames = audio_vbuffer_read(&in->buffer, buffer, frames);
-    }
-
-exit:
-    read_bytes = read_frames*audio_stream_in_frame_size(stream);
-
-    if (mic_mute) {
-        read_bytes = 0;
-    }
-
-    if (read_bytes < bytes) {
-        memset (&((uint8_t *)buffer)[read_bytes], 0, bytes-read_bytes);
-    }
-
-    pthread_mutex_unlock(&in->lock);
-
-    return bytes;
-}
-
-static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) {
-    return 0;
-}
-
-static int in_get_capture_position(const struct audio_stream_in *stream,
-        int64_t *frames, int64_t *time) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    pthread_mutex_lock(&in->lock);
-    struct timespec current_time;
-    get_current_input_position(in, frames, &current_time);
-    *time = (current_time.tv_sec * 1000000000LL + current_time.tv_nsec);
-    pthread_mutex_unlock(&in->lock);
-    return 0;
-}
-
-static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) {
-    // in_add_audio_effect is a no op
-    return 0;
-}
-
-static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) {
-    // in_add_audio_effect is a no op
-    return 0;
-}
-
-static int adev_open_output_stream(struct audio_hw_device *dev,
-        audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags,
-        struct audio_config *config, struct audio_stream_out **stream_out, const char *address) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    struct generic_stream_out *out;
-    int ret = 0;
-
-    if (refine_output_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
-        ALOGE("Error opening output stream format %d, channel_mask %04x, sample_rate %u",
-              config->format, config->channel_mask, config->sample_rate);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out));
-
-    if (!out)
-        return -ENOMEM;
-
-    out->stream.common.get_sample_rate = out_get_sample_rate;
-    out->stream.common.set_sample_rate = out_set_sample_rate;
-    out->stream.common.get_buffer_size = out_get_buffer_size;
-    out->stream.common.get_channels = out_get_channels;
-    out->stream.common.get_format = out_get_format;
-    out->stream.common.set_format = out_set_format;
-    out->stream.common.standby = out_standby;
-    out->stream.common.dump = out_dump;
-    out->stream.common.set_parameters = out_set_parameters;
-    out->stream.common.get_parameters = out_get_parameters;
-    out->stream.common.add_audio_effect = out_add_audio_effect;
-    out->stream.common.remove_audio_effect = out_remove_audio_effect;
-    out->stream.get_latency = out_get_latency;
-    out->stream.set_volume = out_set_volume;
-    out->stream.write = out_write;
-    out->stream.get_render_position = out_get_render_position;
-    out->stream.get_presentation_position = out_get_presentation_position;
-    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
-
-    pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
-    out->dev = adev;
-    out->device = devices;
-    memcpy(&out->req_config, config, sizeof(struct audio_config));
-    memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config));
-    out->pcm_config.rate = config->sample_rate;
-    out->pcm_config.period_size = out->pcm_config.rate*OUT_PERIOD_MS/1000;
-
-    out->standby = true;
-    out->underrun_position = 0;
-    out->underrun_time.tv_sec = 0;
-    out->underrun_time.tv_nsec = 0;
-    out->last_write_time_us = 0;
-    out->frames_total_buffered = 0;
-    out->frames_written = 0;
-    out->frames_rendered = 0;
-
-    ret = audio_vbuffer_init(&out->buffer,
-            out->pcm_config.period_size*out->pcm_config.period_count,
-            out->pcm_config.channels *
-            pcm_format_to_bits(out->pcm_config.format) >> 3);
-    if (ret == 0) {
-        pthread_cond_init(&out->worker_wake, NULL);
-        out->worker_standby = true;
-        out->worker_exit = false;
-        pthread_create(&out->worker_thread, NULL, out_write_worker, out);
-    }
-
-    out->enabled_channels = BOTH_CHANNELS;
-    if (address) {
-        out->bus_address = calloc(strlen(address) + 1, sizeof(char));
-        strncpy(out->bus_address, address, strlen(address));
-        hashmapPut(adev->out_bus_stream_map, out->bus_address, out);
-        /* TODO: read struct audio_gain from audio_policy_configuration */
-        out->gain_stage = (struct audio_gain) {
-            .min_value = -3200,
-            .max_value = 600,
-            .step_value = 100,
-        };
-        out->amplitude_ratio = 1.0;
-        if (property_get_bool(PROP_KEY_SIMULATE_MULTI_ZONE_AUDIO, false)) {
-            out->enabled_channels = strstr(out->bus_address, "rear")
-                ? RIGHT_CHANNEL: LEFT_CHANNEL;
-        }
-    }
-    *stream_out = &out->stream;
-    ALOGD("%s bus:%s", __func__, out->bus_address);
-
-error:
-    return ret;
-}
-
-static void adev_close_output_stream(struct audio_hw_device *dev,
-        struct audio_stream_out *stream) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    struct generic_stream_out *out = (struct generic_stream_out *)stream;
-    ALOGD("%s bus:%s", __func__, out->bus_address);
-    pthread_mutex_lock(&out->lock);
-    do_out_standby(out);
-
-    out->worker_exit = true;
-    pthread_cond_signal(&out->worker_wake);
-    pthread_mutex_unlock(&out->lock);
-
-    pthread_join(out->worker_thread, NULL);
-    pthread_mutex_destroy(&out->lock);
-    audio_vbuffer_destroy(&out->buffer);
-
-    if (out->bus_address) {
-        hashmapRemove(adev->out_bus_stream_map, out->bus_address);
-        free(out->bus_address);
-    }
-    free(stream);
-}
-
-static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) {
-    return 0;
-}
-
-static char *adev_get_parameters(const struct audio_hw_device *dev, const char *keys) {
-    return NULL;
-}
-
-static int adev_init_check(const struct audio_hw_device *dev) {
-    return 0;
-}
-
-static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) {
-    // adev_set_voice_volume is a no op (simulates phones)
-    return 0;
-}
-
-static int adev_set_master_volume(struct audio_hw_device *dev, float volume) {
-    return -ENOSYS;
-}
-
-static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) {
-    return -ENOSYS;
-}
-
-static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) {
-    ALOGD("%s: %s", __func__, _bool_str(muted));
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    pthread_mutex_lock(&adev->lock);
-    adev->master_mute = muted;
-    pthread_mutex_unlock(&adev->lock);
-    return 0;
-}
-
-static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    pthread_mutex_lock(&adev->lock);
-    *muted = adev->master_mute;
-    pthread_mutex_unlock(&adev->lock);
-    ALOGD("%s: %s", __func__, _bool_str(*muted));
-    return 0;
-}
-
-static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) {
-    // adev_set_mode is a no op (simulates phones)
-    return 0;
-}
-
-static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    pthread_mutex_lock(&adev->lock);
-    adev->mic_mute = state;
-    pthread_mutex_unlock(&adev->lock);
-    return 0;
-}
-
-static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    pthread_mutex_lock(&adev->lock);
-    *state = adev->mic_mute;
-    pthread_mutex_unlock(&adev->lock);
-    return 0;
-}
-
-static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
-        const struct audio_config *config) {
-    return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask);
-}
-
-static void adev_close_input_stream(struct audio_hw_device *dev,
-        struct audio_stream_in *stream) {
-    struct generic_stream_in *in = (struct generic_stream_in *)stream;
-    pthread_mutex_lock(&in->lock);
-    do_in_standby(in);
-
-    in->worker_exit = true;
-    pthread_cond_signal(&in->worker_wake);
-    pthread_mutex_unlock(&in->lock);
-    pthread_join(in->worker_thread, NULL);
-
-    if (in->stereo_to_mono_buf != NULL) {
-        free(in->stereo_to_mono_buf);
-        in->stereo_to_mono_buf_size = 0;
-    }
-
-    if (in->bus_address) {
-        free(in->bus_address);
-    }
-
-    pthread_mutex_destroy(&in->lock);
-    audio_vbuffer_destroy(&in->buffer);
-    free(stream);
-}
-
-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,
-        audio_source_t source) {
-    ALOGV("%s: audio_source_t: %d", __func__, source);
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    struct generic_stream_in *in;
-    int ret = 0;
-    if (refine_input_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
-        ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u",
-              config->format, config->channel_mask, config->sample_rate);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in));
-    if (!in) {
-        ret = -ENOMEM;
-        goto error;
-    }
-
-    in->stream.common.get_sample_rate = in_get_sample_rate;
-    in->stream.common.set_sample_rate = in_set_sample_rate;         // no op
-    in->stream.common.get_buffer_size = in_get_buffer_size;
-    in->stream.common.get_channels = in_get_channels;
-    in->stream.common.get_format = in_get_format;
-    in->stream.common.set_format = in_set_format;                   // no op
-    in->stream.common.standby = in_standby;
-    in->stream.common.dump = in_dump;
-    in->stream.common.set_parameters = in_set_parameters;
-    in->stream.common.get_parameters = in_get_parameters;
-    in->stream.common.add_audio_effect = in_add_audio_effect;       // no op
-    in->stream.common.remove_audio_effect = in_remove_audio_effect; // no op
-    in->stream.set_gain = in_set_gain;                              // no op
-    in->stream.read = in_read;
-    in->stream.get_input_frames_lost = in_get_input_frames_lost;    // no op
-    in->stream.get_capture_position = in_get_capture_position;
-
-    pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
-    in->dev = adev;
-    in->device = devices;
-    memcpy(&in->req_config, config, sizeof(struct audio_config));
-    memcpy(&in->pcm_config, &pcm_config_in, sizeof(struct pcm_config));
-    in->pcm_config.rate = config->sample_rate;
-    in->pcm_config.period_size = in->pcm_config.rate*IN_PERIOD_MS/1000;
-
-    in->stereo_to_mono_buf = NULL;
-    in->stereo_to_mono_buf_size = 0;
-
-    in->standby = true;
-    in->standby_position = 0;
-    in->standby_exit_time.tv_sec = 0;
-    in->standby_exit_time.tv_nsec = 0;
-    in->standby_frames_read = 0;
-
-    ret = audio_vbuffer_init(&in->buffer,
-            in->pcm_config.period_size*in->pcm_config.period_count,
-            in->pcm_config.channels *
-            pcm_format_to_bits(in->pcm_config.format) >> 3);
-    if (ret == 0) {
-        pthread_cond_init(&in->worker_wake, NULL);
-        in->worker_standby = true;
-        in->worker_exit = false;
-        pthread_create(&in->worker_thread, NULL, in_read_worker, in);
-    }
-
-    if (address) {
-        in->bus_address = calloc(strlen(address) + 1, sizeof(char));
-        strncpy(in->bus_address, address, strlen(address));
-    }
-
-    *stream_in = &in->stream;
-
-error:
-    return ret;
-}
-
-static int adev_dump(const audio_hw_device_t *dev, int fd) {
-    return 0;
-}
-
-static int adev_set_audio_port_config(struct audio_hw_device *dev,
-        const struct audio_port_config *config) {
-    int ret = 0;
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    const char *bus_address = config->ext.device.address;
-    struct generic_stream_out *out = hashmapGet(adev->out_bus_stream_map, bus_address);
-    if (out) {
-        pthread_mutex_lock(&out->lock);
-        int gainIndex = (config->gain.values[0] - out->gain_stage.min_value) /
-            out->gain_stage.step_value;
-        int totalSteps = (out->gain_stage.max_value - out->gain_stage.min_value) /
-            out->gain_stage.step_value;
-        int minDb = out->gain_stage.min_value / 100;
-        int maxDb = out->gain_stage.max_value / 100;
-        // curve: 10^((minDb + (maxDb - minDb) * gainIndex / totalSteps) / 20)
-        out->amplitude_ratio = pow(10,
-                (minDb + (maxDb - minDb) * (gainIndex / (float)totalSteps)) / 20);
-        pthread_mutex_unlock(&out->lock);
-        ALOGD("%s: set audio gain: %f on %s",
-                __func__, out->amplitude_ratio, bus_address);
-    } else {
-        ALOGE("%s: can not find output stream by bus_address:%s", __func__, bus_address);
-        ret = -EINVAL;
-    }
-    return ret;
-}
-
-static int adev_create_audio_patch(struct audio_hw_device *dev,
-        unsigned int num_sources,
-        const struct audio_port_config *sources,
-        unsigned int num_sinks,
-        const struct audio_port_config *sinks,
-        audio_patch_handle_t *handle) {
-    struct generic_audio_device *audio_dev = (struct generic_audio_device *)dev;
-    for (int i = 0; i < num_sources; i++) {
-        ALOGD("%s: source[%d] type=%d address=%s", __func__, i, sources[i].type,
-                sources[i].type == AUDIO_PORT_TYPE_DEVICE
-                ? sources[i].ext.device.address
-                : "");
-    }
-    for (int i = 0; i < num_sinks; i++) {
-        ALOGD("%s: sink[%d] type=%d address=%s", __func__, i, sinks[i].type,
-                sinks[i].type == AUDIO_PORT_TYPE_DEVICE ? sinks[i].ext.device.address
-                : "N/A");
-    }
-    if (num_sources == 1 && num_sinks == 1 &&
-            sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
-            sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
-        pthread_mutex_lock(&audio_dev->lock);
-        audio_dev->last_patch_id += 1;
-        pthread_mutex_unlock(&audio_dev->lock);
-        *handle = audio_dev->last_patch_id;
-        ALOGD("%s: handle: %d", __func__, *handle);
-    }
-    return 0;
-}
-
-static int adev_release_audio_patch(struct audio_hw_device *dev,
-        audio_patch_handle_t handle) {
-    ALOGD("%s: handle: %d", __func__, handle);
-    return 0;
-}
-
-static int adev_close(hw_device_t *dev) {
-    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
-    int ret = 0;
-    if (!adev)
-        return 0;
-
-    pthread_mutex_lock(&adev_init_lock);
-
-    if (audio_device_ref_count == 0) {
-        ALOGE("adev_close called when ref_count 0");
-        ret = -EINVAL;
-        goto error;
-    }
-
-    if ((--audio_device_ref_count) == 0) {
-        if (adev->mixer) {
-            mixer_close(adev->mixer);
-        }
-        if (adev->out_bus_stream_map) {
-            hashmapFree(adev->out_bus_stream_map);
-        }
-        free(adev);
-    }
-
-error:
-    pthread_mutex_unlock(&adev_init_lock);
-    return ret;
-}
-
-/* copied from libcutils/str_parms.c */
-static bool str_eq(void *key_a, void *key_b) {
-    return !strcmp((const char *)key_a, (const char *)key_b);
-}
-
-/**
- * use djb hash unless we find it inadequate.
- * copied from libcutils/str_parms.c
- */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static int str_hash_fn(void *str) {
-    uint32_t hash = 5381;
-    char *p;
-    for (p = str; p && *p; p++) {
-        hash = ((hash << 5) + hash) + *p;
-    }
-    return (int)hash;
-}
-
-static int adev_open(const hw_module_t *module,
-        const char *name, hw_device_t **device) {
-    static struct generic_audio_device *adev;
-
-    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
-        return -EINVAL;
-
-    pthread_mutex_lock(&adev_init_lock);
-    if (audio_device_ref_count != 0) {
-        *device = &adev->device.common;
-        audio_device_ref_count++;
-        ALOGV("%s: returning existing instance of adev", __func__);
-        ALOGV("%s: exit", __func__);
-        goto unlock;
-    }
-    adev = calloc(1, sizeof(struct generic_audio_device));
-
-    pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
-
-    adev->device.common.tag = HARDWARE_DEVICE_TAG;
-    adev->device.common.version = AUDIO_DEVICE_API_VERSION_3_0;
-    adev->device.common.module = (struct hw_module_t *) module;
-    adev->device.common.close = adev_close;
-
-    adev->device.init_check = adev_init_check;               // no op
-    adev->device.set_voice_volume = adev_set_voice_volume;   // no op
-    adev->device.set_master_volume = adev_set_master_volume; // no op
-    adev->device.get_master_volume = adev_get_master_volume; // no op
-    adev->device.set_master_mute = adev_set_master_mute;
-    adev->device.get_master_mute = adev_get_master_mute;
-    adev->device.set_mode = adev_set_mode;                   // no op
-    adev->device.set_mic_mute = adev_set_mic_mute;
-    adev->device.get_mic_mute = adev_get_mic_mute;
-    adev->device.set_parameters = adev_set_parameters;       // no op
-    adev->device.get_parameters = adev_get_parameters;       // no op
-    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
-    adev->device.open_output_stream = adev_open_output_stream;
-    adev->device.close_output_stream = adev_close_output_stream;
-    adev->device.open_input_stream = adev_open_input_stream;
-    adev->device.close_input_stream = adev_close_input_stream;
-    adev->device.dump = adev_dump;
-
-    // New in AUDIO_DEVICE_API_VERSION_3_0
-    adev->device.set_audio_port_config = adev_set_audio_port_config;
-    adev->device.create_audio_patch = adev_create_audio_patch;
-    adev->device.release_audio_patch = adev_release_audio_patch;
-
-    *device = &adev->device.common;
-
-    adev->mixer = mixer_open(PCM_CARD);
-    struct mixer_ctl *ctl;
-
-    // Set default mixer ctls
-    // Enable channels and set volume
-    for (int i = 0; i < (int)mixer_get_num_ctls(adev->mixer); i++) {
-        ctl = mixer_get_ctl(adev->mixer, i);
-        ALOGD("mixer %d name %s", i, mixer_ctl_get_name(ctl));
-        if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Volume") ||
-            !strcmp(mixer_ctl_get_name(ctl), "Capture Volume")) {
-            for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
-                ALOGD("set ctl %d to %d", z, 100);
-                mixer_ctl_set_percent(ctl, z, 100);
-            }
-            continue;
-        }
-        if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Switch") ||
-            !strcmp(mixer_ctl_get_name(ctl), "Capture Switch")) {
-            for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
-                ALOGD("set ctl %d to %d", z, 1);
-                mixer_ctl_set_value(ctl, z, 1);
-            }
-            continue;
-        }
-    }
-
-    // Initialize the bus address to output stream map
-    adev->out_bus_stream_map = hashmapCreate(5, str_hash_fn, str_eq);
-
-    audio_device_ref_count++;
-
-unlock:
-    pthread_mutex_unlock(&adev_init_lock);
-    return 0;
-}
-
-static struct hw_module_methods_t hal_module_methods = {
-    .open = adev_open,
-};
-
-struct audio_module HAL_MODULE_INFO_SYM = {
-    .common = {
-        .tag = HARDWARE_MODULE_TAG,
-        .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
-        .hal_api_version = HARDWARE_HAL_API_VERSION,
-        .id = AUDIO_HARDWARE_MODULE_ID,
-        .name = "Generic audio HW HAL",
-        .author = "The Android Open Source Project",
-        .methods = &hal_module_methods,
-    },
-};
diff --git a/audio/audio_hw.h b/audio/audio_hw.h
deleted file mode 100644
index 903a9b8..0000000
--- a/audio/audio_hw.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2018 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_HW_H
-#define AUDIO_HW_H
-
-#include <pthread.h>
-
-#include <cutils/hashmap.h>
-#include <hardware/audio.h>
-#include <tinyalsa/asoundlib.h>
-
-#include "audio_vbuffer.h"
-
-struct generic_audio_device {
-  struct audio_hw_device device;  // Constant after init
-  pthread_mutex_t lock;
-  unsigned int last_patch_id;   // Protected by this->lock
-  bool master_mute;             // Protected by this->lock
-  bool mic_mute;                // Protected by this->lock
-  struct mixer *mixer;          // Protected by this->lock
-  Hashmap *out_bus_stream_map;  // Extended field. Constant after init
-};
-
-enum output_channel_enable {
-  LEFT_CHANNEL = 1,
-  RIGHT_CHANNEL = 1 << 1,
-  BOTH_CHANNELS = LEFT_CHANNEL | RIGHT_CHANNEL
-};
-
-struct generic_stream_out {
-  struct audio_stream_out stream;  // Constant after init
-  pthread_mutex_t lock;
-  struct generic_audio_device *dev;  // Constant after init
-  audio_devices_t device;            // Protected by this->lock
-  struct audio_config req_config;    // Constant after init
-  struct pcm_config pcm_config;      // Constant after init
-  audio_vbuffer_t buffer;            // Constant after init
-  const char *bus_address;           // Extended field. Constant after init
-  struct audio_gain gain_stage;      // Constant after init
-  float amplitude_ratio;             // Protected by this->lock
-  enum output_channel_enable enabled_channels;  // Constant after init
-
-  // Time & Position Keeping
-  bool standby;                    // Protected by this->lock
-  uint64_t underrun_position;      // Protected by this->lock
-  struct timespec underrun_time;   // Protected by this->lock
-  uint64_t last_write_time_us;     // Protected by this->lock
-  uint64_t frames_total_buffered;  // Protected by this->lock
-  uint64_t frames_written;         // Protected by this->lock
-  uint64_t frames_rendered;        // Protected by this->lock
-
-  // Worker
-  pthread_t worker_thread;     // Constant after init
-  pthread_cond_t worker_wake;  // Protected by this->lock
-  bool worker_standby;         // Protected by this->lock
-  bool worker_exit;            // Protected by this->lock
-};
-
-struct generic_stream_in {
-  struct audio_stream_in stream;  // Constant after init
-  pthread_mutex_t lock;
-  struct generic_audio_device *dev;  // Constant after init
-  audio_devices_t device;            // Protected by this->lock
-  struct audio_config req_config;    // Constant after init
-  struct pcm *pcm;                   // Protected by this->lock
-  struct pcm_config pcm_config;      // Constant after init
-  int16_t *stereo_to_mono_buf;       // Protected by this->lock
-  size_t stereo_to_mono_buf_size;    // Protected by this->lock
-  audio_vbuffer_t buffer;            // Protected by this->lock
-  const char *bus_address;           // Extended field. Constant after init
-
-  // Time & Position Keeping
-  bool standby;                       // Protected by this->lock
-  int64_t standby_position;           // Protected by this->lock
-  struct timespec standby_exit_time;  // Protected by this->lock
-  int64_t standby_frames_read;        // Protected by this->lock
-
-  // Worker
-  pthread_t worker_thread;     // Constant after init
-  pthread_cond_t worker_wake;  // Protected by this->lock
-  bool worker_standby;         // Protected by this->lock
-  bool worker_exit;            // Protected by this->lock
-};
-
-#endif  // AUDIO_HW_H
diff --git a/audio/audio_vbuffer.c b/audio/audio_vbuffer.c
deleted file mode 100644
index 79be545..0000000
--- a/audio/audio_vbuffer.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 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_hw_generic"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <log/log.h>
-
-#include "audio_vbuffer.h"
-
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
-                       size_t frame_size) {
-  if (!audio_vbuffer) {
-    return -EINVAL;
-  }
-  audio_vbuffer->frame_size = frame_size;
-  audio_vbuffer->frame_count = frame_count;
-  size_t bytes = frame_count * frame_size;
-  audio_vbuffer->data = calloc(bytes, 1);
-  if (!audio_vbuffer->data) {
-    return -ENOMEM;
-  }
-  audio_vbuffer->head = 0;
-  audio_vbuffer->tail = 0;
-  audio_vbuffer->live = 0;
-  pthread_mutex_init(&audio_vbuffer->lock, (const pthread_mutexattr_t *)NULL);
-  return 0;
-}
-
-int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer) {
-  if (!audio_vbuffer) {
-    return -EINVAL;
-  }
-  free(audio_vbuffer->data);
-  pthread_mutex_destroy(&audio_vbuffer->lock);
-  return 0;
-}
-
-int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer) {
-  if (!audio_vbuffer) {
-    return -EINVAL;
-  }
-  pthread_mutex_lock(&audio_vbuffer->lock);
-  int live = audio_vbuffer->live;
-  pthread_mutex_unlock(&audio_vbuffer->lock);
-  return live;
-}
-
-int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer) {
-  if (!audio_vbuffer) {
-    return -EINVAL;
-  }
-  pthread_mutex_lock(&audio_vbuffer->lock);
-  int dead = audio_vbuffer->frame_count - audio_vbuffer->live;
-  pthread_mutex_unlock(&audio_vbuffer->lock);
-  return dead;
-}
-
-size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
-                           size_t frame_count) {
-  size_t frames_written = 0;
-  pthread_mutex_lock(&audio_vbuffer->lock);
-
-  while (frame_count != 0) {
-    int frames = 0;
-    if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) {
-      frames =
-          MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head);
-    } else if (audio_vbuffer->head < audio_vbuffer->tail) {
-      frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head));
-    } else {
-      ALOGD("%s audio_vbuffer is full", __func__);
-      break;
-    }
-    memcpy(
-        &audio_vbuffer->data[audio_vbuffer->head * audio_vbuffer->frame_size],
-        &((uint8_t *)buffer)[frames_written * audio_vbuffer->frame_size],
-        frames * audio_vbuffer->frame_size);
-    audio_vbuffer->live += frames;
-    frames_written += frames;
-    frame_count -= frames;
-    audio_vbuffer->head =
-        (audio_vbuffer->head + frames) % audio_vbuffer->frame_count;
-  }
-
-  pthread_mutex_unlock(&audio_vbuffer->lock);
-  return frames_written;
-}
-
-size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
-                          size_t frame_count) {
-  size_t frames_read = 0;
-  pthread_mutex_lock(&audio_vbuffer->lock);
-
-  while (frame_count != 0) {
-    int frames = 0;
-    if (audio_vbuffer->live == audio_vbuffer->frame_count ||
-        audio_vbuffer->tail > audio_vbuffer->head) {
-      frames =
-          MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail);
-    } else if (audio_vbuffer->tail < audio_vbuffer->head) {
-      frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail);
-    } else {
-      break;
-    }
-    memcpy(
-        &((uint8_t *)buffer)[frames_read * audio_vbuffer->frame_size],
-        &audio_vbuffer->data[audio_vbuffer->tail * audio_vbuffer->frame_size],
-        frames * audio_vbuffer->frame_size);
-    audio_vbuffer->live -= frames;
-    frames_read += frames;
-    frame_count -= frames;
-    audio_vbuffer->tail =
-        (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count;
-  }
-
-  pthread_mutex_unlock(&audio_vbuffer->lock);
-  return frames_read;
-}
diff --git a/audio/audio_vbuffer.h b/audio/audio_vbuffer.h
deleted file mode 100644
index 24bf986..0000000
--- a/audio/audio_vbuffer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 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_VBUFFER_H
-#define AUDIO_VBUFFER_H
-
-#include <pthread.h>
-
-typedef struct audio_vbuffer {
-  pthread_mutex_t lock;
-  uint8_t *data;
-  size_t frame_size;
-  size_t frame_count;
-  size_t head;
-  size_t tail;
-  size_t live;
-} audio_vbuffer_t;
-
-int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
-                       size_t frame_size);
-
-int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer);
-
-int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer);
-
-int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer);
-
-size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
-                           size_t frame_count);
-
-size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
-                          size_t frame_count);
-
-#endif  // AUDIO_VBUFFER_H
diff --git a/audio/deleters.h b/audio/deleters.h
new file mode 100644
index 0000000..882308d
--- /dev/null
+++ b/audio/deleters.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <fmq/EventFlag.h>
+#include <log/log.h>
+
+namespace deleters {
+
+struct forEventFlag {
+    typedef ::android::hardware::EventFlag EventFlag;
+
+    void operator()(EventFlag *x) const {
+        LOG_ALWAYS_FATAL_IF(EventFlag::deleteEventFlag(&x) != ::android::OK);
+    };
+};
+
+}  // namespace deleters
diff --git a/audio/device_factory.cpp b/audio/device_factory.cpp
new file mode 100644
index 0000000..e0bbf1f
--- /dev/null
+++ b/audio/device_factory.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "device_factory.h"
+#include "primary_device.h"
+#include <system/audio.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+Return<void> DevicesFactory::openDevice(const hidl_string& device,
+                                        openDevice_cb _hidl_cb) {
+    Result result = Result::OK;
+    std::unique_ptr<IDevice> dev;
+
+    if (device == AUDIO_HARDWARE_MODULE_ID_PRIMARY) {
+        dev = std::make_unique<PrimaryDevice>();
+    } else {
+        result = Result::INVALID_ARGUMENTS;
+    }
+
+    if (!dev) {
+        ALOGE("DevicesFactory::%s:%d: failed, device='%s' result='%s'",
+              __func__, __LINE__, device.c_str(), toString(result).c_str());
+    }
+
+    _hidl_cb(result, dev.release());
+    return Void();
+}
+
+Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) {
+    _hidl_cb(Result::OK, new PrimaryDevice);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/device_factory.h b/audio/device_factory.h
new file mode 100644
index 0000000..ac653d0
--- /dev/null
+++ b/audio/device_factory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IDevicesFactory.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::V6_0;
+
+struct DevicesFactory : public IDevicesFactory {
+    Return<void> openDevice(const hidl_string& device, openDevice_cb _hidl_cb) override;
+    Return<void> openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/entry.cpp b/audio/entry.cpp
new file mode 100644
index 0000000..1d48f43
--- /dev/null
+++ b/audio/entry.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "device_factory.h"
+
+using android::hardware::audio::V6_0::IDevicesFactory;
+using android::hardware::audio::V6_0::implementation::DevicesFactory;
+
+extern "C" IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) {
+    (void)name;
+    return new DevicesFactory();
+}
diff --git a/audio/ext_pcm.c b/audio/ext_pcm.c
deleted file mode 100644
index de2b0c4..0000000
--- a/audio/ext_pcm.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2018 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_hw_generic"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <cutils/str_parms.h>
-
-#include "ext_pcm.h"
-
-static pthread_mutex_t ext_pcm_init_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct ext_pcm *shared_ext_pcm = NULL;
-
-// Sleep 10ms between each mixing, this interval value is arbitrary chosen
-#define MIXER_INTERVAL_MS 10
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-/* copied from libcutils/str_parms.c */
-static bool str_eq(void *key_a, void *key_b) {
-  return !strcmp((const char *)key_a, (const char *)key_b);
-}
-
-/**
- * use djb hash unless we find it inadequate.
- * copied from libcutils/str_parms.c
- */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static int str_hash_fn(void *str) {
-  uint32_t hash = 5381;
-  char *p;
-  for (p = str; p && *p; p++) {
-    hash = ((hash << 5) + hash) + *p;
-  }
-  return (int)hash;
-}
-
-static bool mixer_thread_mix(__unused void *key, void *value, void *context) {
-  struct ext_mixer_pipeline *pipeline_out = (struct ext_mixer_pipeline *)context;
-  struct ext_mixer_pipeline *pipeline_in = (struct ext_mixer_pipeline *)value;
-
-  const unsigned in_pos = pipeline_in->position;
-  const unsigned out_pos = pipeline_out->position;
-  const unsigned mix_pos = MIN(out_pos, in_pos);
-  const int16_t* in = pipeline_in->buffer;
-  int16_t* out = pipeline_out->buffer;
-  int16_t* out_end = out + mix_pos;
-
-  switch (mix_pos % 8) {
-  case 7: goto rem7;
-  case 6: goto rem6;
-  case 5: goto rem5;
-  case 4: goto rem4;
-  case 3: goto rem3;
-  case 2: goto rem2;
-  case 1: goto rem1;
-  case 0: break;
-  }
-
-#define ONE_ITER                                    \
-    mixed = *in + *out;                             \
-    clamper =                                       \
-        (mixed > INT16_MAX) * (mixed - INT16_MAX) + \
-        (mixed < INT16_MIN) * (mixed - INT16_MIN);  \
-    *out = mixed - clamper;                         \
-    ++in;                                           \
-    ++out
-
-  while (out < out_end) {
-    int32_t mixed;
-    int32_t clamper;
-
-          ONE_ITER;
-    rem7: ONE_ITER;
-    rem6: ONE_ITER;
-    rem5: ONE_ITER;
-    rem4: ONE_ITER;
-    rem3: ONE_ITER;
-    rem2: ONE_ITER;
-    rem1: ONE_ITER;
-  }
-
-#undef ONE_ITER
-
-  if (in_pos > out_pos) {
-    memcpy(&pipeline_out->buffer[out_pos],
-           &pipeline_in->buffer[out_pos],
-           sizeof(pipeline_in->buffer[0]) * (in_pos - out_pos));
-    pipeline_out->position = in_pos;
-  }
-
-  pipeline_in->position = 0;
-  return true;
-}
-
-static void *mixer_thread_loop(void *context) {
-  ALOGD("%s: __enter__", __func__);
-  struct ext_pcm *ext_pcm = (struct ext_pcm *)context;
-  do {
-    pthread_mutex_lock(&ext_pcm->mixer_lock);
-    ext_pcm->mixer_pipeline.position = 0;
-    // Combine the output from every pipeline into one output buffer
-    hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_thread_mix,
-        &ext_pcm->mixer_pipeline);
-    if (ext_pcm->mixer_pipeline.position > 0) {
-      pcm_write(ext_pcm->pcm, (void *)ext_pcm->mixer_pipeline.buffer,
-          ext_pcm->mixer_pipeline.position * sizeof(int16_t));
-    }
-    ext_pcm->mixer_pipeline.position = 0;
-    pthread_mutex_unlock(&ext_pcm->mixer_lock);
-    usleep(MIXER_INTERVAL_MS * 1000);
-  } while (1);
-}
-
-static int mixer_pipeline_write(struct ext_pcm *ext_pcm, const char *bus_address,
-                                const void *data, unsigned int count) {
-  pthread_mutex_lock(&ext_pcm->mixer_lock);
-  struct ext_mixer_pipeline *pipeline = hashmapGet(
-      ext_pcm->mixer_pipeline_map, bus_address);
-  if (!pipeline) {
-    pipeline = calloc(1, sizeof(struct ext_mixer_pipeline));
-    hashmapPut(ext_pcm->mixer_pipeline_map, bus_address, pipeline);
-  }
-  unsigned int byteCount = MIN(count,
-      (MIXER_BUFFER_SIZE - pipeline->position) * sizeof(int16_t));
-  unsigned int int16Count = byteCount / sizeof(int16_t);
-  if (int16Count > 0) {
-    memcpy(&pipeline->buffer[pipeline->position], data, byteCount);
-    pipeline->position += int16Count;
-  }
-  pthread_mutex_unlock(&ext_pcm->mixer_lock);
-  return 0;
-}
-
-struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device,
-                             unsigned int flags, struct pcm_config *config) {
-  pthread_mutex_lock(&ext_pcm_init_lock);
-  if (shared_ext_pcm == NULL) {
-    shared_ext_pcm = calloc(1, sizeof(struct ext_pcm));
-    pthread_mutex_init(&shared_ext_pcm->lock, (const pthread_mutexattr_t *) NULL);
-    shared_ext_pcm->pcm = pcm_open(card, device, flags, config);
-    pthread_mutex_init(&shared_ext_pcm->mixer_lock, (const pthread_mutexattr_t *)NULL);
-    pthread_create(&shared_ext_pcm->mixer_thread, (const pthread_attr_t *)NULL,
-            mixer_thread_loop, shared_ext_pcm);
-    shared_ext_pcm->mixer_pipeline_map = hashmapCreate(8, str_hash_fn, str_eq);
-  }
-  pthread_mutex_unlock(&ext_pcm_init_lock);
-
-  pthread_mutex_lock(&shared_ext_pcm->lock);
-  shared_ext_pcm->ref_count += 1;
-  pthread_mutex_unlock(&shared_ext_pcm->lock);
-
-  return shared_ext_pcm;
-}
-
-static bool mixer_free_pipeline(__unused void *key, void *value, void *context) {
-  struct ext_mixer_pipeline *pipeline = (struct ext_mixer_pipeline *)value;
-  free(pipeline);
-  return true;
-}
-
-int ext_pcm_close(struct ext_pcm *ext_pcm) {
-  if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
-    return -EINVAL;
-  }
-
-  pthread_mutex_lock(&ext_pcm->lock);
-  ext_pcm->ref_count -= 1;
-  pthread_mutex_unlock(&ext_pcm->lock);
-
-  pthread_mutex_lock(&ext_pcm_init_lock);
-  if (ext_pcm->ref_count <= 0) {
-    pthread_mutex_destroy(&ext_pcm->lock);
-    pcm_close(ext_pcm->pcm);
-    pthread_mutex_destroy(&ext_pcm->mixer_lock);
-    hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_free_pipeline,
-        (void *)NULL);
-    hashmapFree(ext_pcm->mixer_pipeline_map);
-    pthread_kill(ext_pcm->mixer_thread, SIGINT);
-    free(ext_pcm);
-    shared_ext_pcm = NULL;
-  }
-  pthread_mutex_unlock(&ext_pcm_init_lock);
-  return 0;
-}
-
-int ext_pcm_is_ready(struct ext_pcm *ext_pcm) {
-  if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
-    return 0;
-  }
-
-  return pcm_is_ready(ext_pcm->pcm);
-}
-
-int ext_pcm_write(struct ext_pcm *ext_pcm, const char *address,
-                  const void *data, unsigned int count) {
-  if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
-    return -EINVAL;
-  }
-
-  return mixer_pipeline_write(ext_pcm, address, data, count);
-}
-
-const char *ext_pcm_get_error(struct ext_pcm *ext_pcm) {
-  if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
-    return NULL;
-  }
-
-  return pcm_get_error(ext_pcm->pcm);
-}
-
-unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
-                                     unsigned int frames) {
-  if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
-    return -EINVAL;
-  }
-
-  return pcm_frames_to_bytes(ext_pcm->pcm, frames);
-}
diff --git a/audio/ext_pcm.h b/audio/ext_pcm.h
deleted file mode 100644
index 0bc8df5..0000000
--- a/audio/ext_pcm.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 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 EXT_PCM_H
-#define EXT_PCM_H
-
-#include <pthread.h>
-
-#include <cutils/hashmap.h>
-#include <tinyalsa/asoundlib.h>
-
-// Holds up to 4KB buffer for each mixer pipeline, this value is arbitrary chosen
-#define MIXER_BUFFER_SIZE (1024 * 4)
-
-struct ext_mixer_pipeline {
-  int16_t buffer[MIXER_BUFFER_SIZE];
-  unsigned int position;
-};
-
-struct ext_pcm {
-  struct pcm *pcm;
-  pthread_mutex_t lock;
-  unsigned int ref_count;
-  pthread_mutex_t mixer_lock;
-  struct ext_mixer_pipeline mixer_pipeline;
-  pthread_t mixer_thread;
-  Hashmap *mixer_pipeline_map;
-};
-
-struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device,
-                             unsigned int flags, struct pcm_config *config);
-int ext_pcm_close(struct ext_pcm *ext_pcm);
-int ext_pcm_is_ready(struct ext_pcm *ext_pcm);
-int ext_pcm_write(struct ext_pcm *ext_pcm, const char *bus_address,
-                  const void *data, unsigned int count);
-const char *ext_pcm_get_error(struct ext_pcm *ext_pcm);
-unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
-                                     unsigned int frames);
-
-#endif  // EXT_PCM_H
diff --git a/audio/io_thread.cpp b/audio/io_thread.cpp
new file mode 100644
index 0000000..e26299a
--- /dev/null
+++ b/audio/io_thread.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+bool IOThread::notify(const uint32_t mask) {
+    EventFlag *evf = getEventFlag();
+    return evf ? evf->wake(mask) : false;
+}
+
+bool IOThread::standby() {
+    return notify(STAND_BY_REQUEST);
+}
+
+void IOThread::requestExit() {
+    Thread::requestExit();
+    notify(EXIT_REQUEST);
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/io_thread.h b/audio/io_thread.h
new file mode 100644
index 0000000..355a29c
--- /dev/null
+++ b/audio/io_thread.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <utils/Thread.h>
+#include <fmq/EventFlag.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+struct IOThread : public ::android::Thread {
+    static constexpr uint32_t STAND_BY_REQUEST = 1 << 20;
+    static constexpr uint32_t EXIT_REQUEST = 1 << 21;
+
+    IOThread() : Thread(false /*canCallJava*/) {}
+    virtual EventFlag *getEventFlag() = 0;
+    virtual bool notify(uint32_t mask);
+    virtual bool standby();
+    void requestExit() override;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/policy/audio_policy_configuration.xml b/audio/policy/audio_policy_configuration.xml
new file mode 100644
index 0000000..f631bb0
--- /dev/null
+++ b/audio/policy/audio_policy_configuration.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
+
+    <!-- Global configuration Decalaration -->
+    <globalConfiguration speaker_drc_enabled="false"/>
+
+    <modules>
+        <!-- Primary Audio HAL -->
+        <xi:include href="primary_audio_policy_configuration.xml"/>
+
+    </modules>
+    <!-- End of Modules section -->
+
+    <!-- Volume section:
+        IMPORTANT NOTE: Volume tables have been moved to engine configuration.
+                        Keep it here for legacy.
+                        Engine will fallback on these files if none are provided by engine.
+     -->
+
+    <xi:include href="audio_policy_volumes.xml"/>
+    <xi:include href="default_volume_tables.xml"/>
+
+    <!-- End of Volume section -->
+
+</audioPolicyConfiguration>
diff --git a/audio/policy/primary_audio_policy_configuration.xml b/audio/policy/primary_audio_policy_configuration.xml
new file mode 100644
index 0000000..eedc96b
--- /dev/null
+++ b/audio/policy/primary_audio_policy_configuration.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Default Primary Audio HAL Module Audio Policy Configuration include file -->
+<module name="primary" halVersion="2.0">
+    <attachedDevices>
+        <item>Speaker</item>
+        <item>Built-In Mic</item>
+    </attachedDevices>
+    <defaultOutputDevice>Speaker</defaultOutputDevice>
+    <mixPorts>
+        <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+        </mixPort>
+        <mixPort name="primary input" role="sink">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+        </mixPort>
+   </mixPorts>
+   <devicePorts>
+        <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
+        </devicePort>
+
+        <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+        </devicePort>
+    </devicePorts>
+    <routes>
+        <route type="mix" sink="Speaker"
+               sources="primary output"/>
+        <route type="mix" sink="primary input"
+               sources="Built-In Mic"/>
+    </routes>
+</module>
diff --git a/audio/primary_device.cpp b/audio/primary_device.cpp
new file mode 100644
index 0000000..03b2791
--- /dev/null
+++ b/audio/primary_device.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <log/log.h>
+#include <system/audio.h>
+#include "primary_device.h"
+#include "stream_in.h"
+#include "stream_out.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+constexpr size_t kInBufferDurationMs = 15;
+constexpr size_t kOutBufferDurationMs = 15;
+
+using ::android::hardware::Void;
+
+PrimaryDevice::PrimaryDevice()
+        : mMixer(talsa::mixerOpen(talsa::kPcmDevice)) {
+    if (mMixer) {
+        mMixerMasterVolumeCtl = mixer_get_ctl_by_name(mMixer.get(), "Master Playback Volume");
+        mMixerCaptureVolumeCtl = mixer_get_ctl_by_name(mMixer.get(), "Capture Volume");
+        mMixerMasterPaybackSwitchCtl = mixer_get_ctl_by_name(mMixer.get(), "Master Playback Switch");
+        mMixerCaptureSwitchCtl = mixer_get_ctl_by_name(mMixer.get(), "Capture Switch");
+
+        talsa::mixerSetPercentAll(mMixerMasterVolumeCtl, 100);
+        talsa::mixerSetPercentAll(mMixerCaptureVolumeCtl, 100);
+        talsa::mixerSetValueAll(mMixerMasterPaybackSwitchCtl, 1);
+        talsa::mixerSetValueAll(mMixerCaptureSwitchCtl, 1);
+    }
+}
+
+Return<Result> PrimaryDevice::initCheck() {
+    return mMixer ? Result::OK : Result::NOT_INITIALIZED;
+}
+
+Return<Result> PrimaryDevice::setMasterVolume(float volume) {
+    if (volume < 0 || volume > 1.0) {
+        return Result::INVALID_ARGUMENTS;
+    }
+
+    if (!mMixerMasterVolumeCtl) {
+        return Result::INVALID_STATE;
+    }
+
+    talsa::mixerSetPercentAll(mMixerMasterVolumeCtl, int(100 * volume));
+    return Result::OK;
+}
+
+Return<void> PrimaryDevice::getMasterVolume(getMasterVolume_cb _hidl_cb) {
+    if (mMixerMasterVolumeCtl) {
+        _hidl_cb(Result::OK,
+                 mixer_ctl_get_percent(mMixerMasterVolumeCtl, 0) / 100.0);
+    } else {
+        _hidl_cb(Result::INVALID_STATE, 0);
+    }
+
+    return Void();
+}
+
+Return<Result> PrimaryDevice::PrimaryDevice::setMicMute(bool mute) {
+    if (mMixerCaptureSwitchCtl) {
+        talsa::mixerSetValueAll(mMixerCaptureSwitchCtl, mute ? 0 : 1);
+        return Result::OK;
+    } else {
+        return Result::INVALID_STATE;
+    }
+}
+
+Return<void> PrimaryDevice::getMicMute(getMicMute_cb _hidl_cb) {
+    if (mMixerCaptureSwitchCtl) {
+        const int value = mixer_ctl_get_value(mMixerCaptureSwitchCtl, 0);
+        _hidl_cb(Result::OK, value == 0);
+    } else {
+        _hidl_cb(Result::INVALID_STATE, 0);
+    }
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setMasterMute(bool mute) {
+    if (mMixerMasterPaybackSwitchCtl) {
+        talsa::mixerSetValueAll(mMixerMasterPaybackSwitchCtl, mute ? 0 : 1);
+        return Result::OK;
+    } else {
+        return Result::INVALID_STATE;
+    }
+}
+
+Return<void> PrimaryDevice::getMasterMute(getMasterMute_cb _hidl_cb) {
+    if (mMixerMasterPaybackSwitchCtl) {
+        const int value = mixer_ctl_get_value(mMixerMasterPaybackSwitchCtl, 0);
+        _hidl_cb(Result::OK, value == 0);
+    } else {
+        _hidl_cb(Result::NOT_SUPPORTED, 0);
+    }
+
+    return Void();
+}
+
+Return<void> PrimaryDevice::getInputBufferSize(const AudioConfig& config,
+                                               getInputBufferSize_cb _hidl_cb) {
+    AudioConfig suggestedConfig;
+    if (util::checkAudioConfig(false, kInBufferDurationMs, config, suggestedConfig)) {
+        const size_t sz =
+            suggestedConfig.frameCount
+            * util::countChannels(suggestedConfig.channelMask)
+            * util::getBytesPerSample(suggestedConfig.format);
+
+        _hidl_cb(Result::OK, sz);
+    } else {
+        ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+        _hidl_cb(Result::INVALID_ARGUMENTS, 0);
+    }
+
+    return Void();
+}
+
+Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle,
+                                             const DeviceAddress& device,
+                                             const AudioConfig& config,
+                                             hidl_bitfield<AudioOutputFlag> flags,
+                                             const SourceMetadata& sourceMetadata,
+                                             openOutputStream_cb _hidl_cb) {
+    AudioConfig suggestedConfig;
+    if (util::checkAudioConfig(true, kOutBufferDurationMs, config, suggestedConfig)) {
+        ++mNStreams;
+        _hidl_cb(Result::OK,
+                 new StreamOut(this, &unrefDevice,
+                               ioHandle, device, suggestedConfig, flags, sourceMetadata),
+                 config);
+    } else {
+        ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, suggestedConfig);
+    }
+
+    return Void();
+}
+
+Return<void> PrimaryDevice::openInputStream(int32_t ioHandle,
+                                            const DeviceAddress& device,
+                                            const AudioConfig& config,
+                                            hidl_bitfield<AudioInputFlag> flags,
+                                            const SinkMetadata& sinkMetadata,
+                                            openInputStream_cb _hidl_cb) {
+    AudioConfig suggestedConfig;
+    if (util::checkAudioConfig(false, kInBufferDurationMs, config, suggestedConfig)) {
+        ++mNStreams;
+        _hidl_cb(Result::OK,
+                 new StreamIn(this, &unrefDevice,
+                              ioHandle, device, suggestedConfig, flags, sinkMetadata),
+                 config);
+    } else {
+        ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, suggestedConfig);
+    }
+
+    return Void();
+}
+
+Return<bool> PrimaryDevice::supportsAudioPatches() {
+    return false;
+}
+
+Return<void> PrimaryDevice::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
+                                             const hidl_vec<AudioPortConfig>& sinks,
+                                             createAudioPatch_cb _hidl_cb) {
+    (void)sources;
+    (void)sinks;
+    _hidl_cb(Result::NOT_SUPPORTED, 0);
+    return Void();
+}
+
+Return<void> PrimaryDevice::updateAudioPatch(int32_t previousPatch,
+                                             const hidl_vec<AudioPortConfig>& sources,
+                                             const hidl_vec<AudioPortConfig>& sinks,
+                                             updateAudioPatch_cb _hidl_cb) {
+    (void)previousPatch;
+    (void)sources;
+    (void)sinks;
+    _hidl_cb(Result::NOT_SUPPORTED, 0);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::releaseAudioPatch(int32_t patch) {
+    (void)patch;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, port);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setAudioPortConfig(const AudioPortConfig& config) {
+    (void)config;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setScreenState(bool turnedOn) {
+    (void)turnedOn;
+    return Result::OK;
+}
+
+Return<void> PrimaryDevice::getHwAvSync(getHwAvSync_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<void> PrimaryDevice::getParameters(const hidl_vec<ParameterValue>& context,
+                                          const hidl_vec<hidl_string>& keys,
+                                          getParameters_cb _hidl_cb) {
+    (void)context;
+    if (keys.size() == 0) {
+        _hidl_cb(Result::OK, {});
+    } else {
+        _hidl_cb(Result::NOT_SUPPORTED, {});
+    }
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setParameters(const hidl_vec<ParameterValue>& context,
+                                            const hidl_vec<ParameterValue>& parameters) {
+    (void)context;
+    if (parameters.size() == 0) {
+        return Result::OK;
+    } else {
+        return Result::NOT_SUPPORTED;
+    }
+}
+
+Return<void> PrimaryDevice::getMicrophones(getMicrophones_cb _hidl_cb) {
+    _hidl_cb(Result::OK, {util::getMicrophoneInfo()});
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setConnectedState(const DeviceAddress& dev_addr, bool connected) {
+    (void)dev_addr;
+    (void)connected;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::close() {
+    if (mNStreams > 0) {
+        return Result::INVALID_STATE;
+    } else if (mMixer) {
+        mMixerMasterVolumeCtl = nullptr;
+        mMixerCaptureVolumeCtl = nullptr;
+        mMixerMasterPaybackSwitchCtl = nullptr;
+        mMixerCaptureSwitchCtl = nullptr;
+        mMixer.reset();
+        return Result::OK;
+    } else {
+        return Result::INVALID_STATE;
+    }
+}
+
+Return<Result> PrimaryDevice::addDeviceEffect(AudioPortHandle device, uint64_t effectId) {
+    (void)device;
+    (void)effectId;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::removeDeviceEffect(AudioPortHandle device, uint64_t effectId) {
+    (void)device;
+    (void)effectId;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setVoiceVolume(float volume) {
+    return (volume >= 0 && volume <= 1.0) ? Result::OK : Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> PrimaryDevice::setMode(AudioMode mode) {
+    switch (mode) {
+    case AudioMode::NORMAL:
+    case AudioMode::RINGTONE:
+    case AudioMode::IN_CALL:
+    case AudioMode::IN_COMMUNICATION:
+        return Result::OK;
+
+    default:
+        return Result::INVALID_ARGUMENTS;
+    }
+}
+
+Return<Result> PrimaryDevice::setBtScoHeadsetDebugName(const hidl_string& name) {
+    (void)name;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setBtScoNrecEnabled(bool enabled) {
+    (void)enabled;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setBtScoWidebandEnabled(bool enabled) {
+    (void)enabled;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getTtyMode(getTtyMode_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, TtyMode::OFF);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setTtyMode(IPrimaryDevice::TtyMode mode) {
+    (void)mode;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getHacEnabled(getHacEnabled_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setHacEnabled(bool enabled) {
+    (void)enabled;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return Void();
+}
+
+Return<Result> PrimaryDevice::setBtHfpEnabled(bool enabled) {
+    (void)enabled;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setBtHfpSampleRate(uint32_t sampleRateHz) {
+    (void)sampleRateHz;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setBtHfpVolume(float volume) {
+    (void)volume;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::updateRotation(IPrimaryDevice::Rotation rotation) {
+    (void)rotation;
+    return Result::NOT_SUPPORTED;
+}
+
+void PrimaryDevice::unrefDevice(IDevice *dev) {
+    static_cast<PrimaryDevice *>(dev)->unrefDeviceImpl();
+}
+
+void PrimaryDevice::unrefDeviceImpl() {
+    LOG_ALWAYS_FATAL_IF(--mNStreams < 0);
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/primary_device.h b/audio/primary_device.h
new file mode 100644
index 0000000..72b5c75
--- /dev/null
+++ b/audio/primary_device.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IPrimaryDevice.h>
+#include <atomic>
+#include "talsa.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct PrimaryDevice : public IPrimaryDevice {
+    PrimaryDevice();
+
+    Return<Result> initCheck() override;
+    Return<Result> setMasterVolume(float volume) override;
+    Return<void> getMasterVolume(getMasterVolume_cb _hidl_cb) override;
+    Return<Result> setMicMute(bool mute) override;
+    Return<void> getMicMute(getMicMute_cb _hidl_cb) override;
+    Return<Result> setMasterMute(bool mute) override;
+    Return<void> getMasterMute(getMasterMute_cb _hidl_cb) override;
+    Return<void> getInputBufferSize(const AudioConfig& config,
+                                    getInputBufferSize_cb _hidl_cb) override;
+    Return<void> openOutputStream(int32_t ioHandle,
+                                  const DeviceAddress& device,
+                                  const AudioConfig& config,
+                                  hidl_bitfield<AudioOutputFlag> flags,
+                                  const SourceMetadata& sourceMetadata,
+                                  openOutputStream_cb _hidl_cb) override;
+    Return<void> openInputStream(int32_t ioHandle,
+                                 const DeviceAddress& device,
+                                 const AudioConfig& config,
+                                 hidl_bitfield<AudioInputFlag> flags,
+                                 const SinkMetadata& sinkMetadata,
+                                 openInputStream_cb _hidl_cb) override;
+    Return<bool> supportsAudioPatches() override;
+    Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
+                                  const hidl_vec<AudioPortConfig>& sinks,
+                                  createAudioPatch_cb _hidl_cb) override;
+    Return<void> updateAudioPatch(int32_t previousPatch,
+                                  const hidl_vec<AudioPortConfig>& sources,
+                                  const hidl_vec<AudioPortConfig>& sinks,
+                                  updateAudioPatch_cb _hidl_cb) override;
+    Return<Result> releaseAudioPatch(int32_t patch) override;
+    Return<void> getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) override;
+    Return<Result> setAudioPortConfig(const AudioPortConfig& config) override;
+    Return<Result> setScreenState(bool turnedOn) override;
+    Return<void> getHwAvSync(getHwAvSync_cb _hidl_cb) override;
+    Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+                               const hidl_vec<hidl_string>& keys,
+                               getParameters_cb _hidl_cb) override;
+    Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+                                 const hidl_vec<ParameterValue>& parameters) override;
+    Return<void> getMicrophones(getMicrophones_cb _hidl_cb) override;
+    Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
+    Return<Result> close() override;
+    Return<Result> addDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
+    Return<Result> removeDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
+    Return<Result> setVoiceVolume(float volume) override;
+    Return<Result> setMode(AudioMode mode) override;
+    Return<Result> setBtScoHeadsetDebugName(const hidl_string& name) override;
+    Return<void> getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) override;
+    Return<Result> setBtScoNrecEnabled(bool enabled) override;
+    Return<void> getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) override;
+    Return<Result> setBtScoWidebandEnabled(bool enabled) override;
+    Return<void> getTtyMode(getTtyMode_cb _hidl_cb) override;
+    Return<Result> setTtyMode(IPrimaryDevice::TtyMode mode) override;
+    Return<void> getHacEnabled(getHacEnabled_cb _hidl_cb) override;
+    Return<Result> setHacEnabled(bool enabled) override;
+    Return<void> getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) override;
+    Return<Result> setBtHfpEnabled(bool enabled) override;
+    Return<Result> setBtHfpSampleRate(uint32_t sampleRateHz) override;
+    Return<Result> setBtHfpVolume(float volume) override;
+    Return<Result> updateRotation(IPrimaryDevice::Rotation rotation) override;
+
+private:
+    static void unrefDevice(IDevice*);
+    void unrefDeviceImpl();
+
+    talsa::MixerPtr     mMixer;
+    talsa::mixer_ctl_t  *mMixerMasterVolumeCtl = nullptr;
+    talsa::mixer_ctl_t  *mMixerCaptureVolumeCtl = nullptr;
+    talsa::mixer_ctl_t  *mMixerMasterPaybackSwitchCtl = nullptr;
+    talsa::mixer_ctl_t  *mMixerCaptureSwitchCtl = nullptr;
+    std::atomic<int>    mNStreams = 0;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_common.cpp b/audio/stream_common.cpp
new file mode 100644
index 0000000..3ac4da8
--- /dev/null
+++ b/audio/stream_common.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include "stream_common.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+StreamCommon::StreamCommon(int32_t ioHandle,
+                           const DeviceAddress& device,
+                           const AudioConfig& config,
+                           hidl_bitfield<AudioInputFlag> flags)
+        : m_ioHandle(ioHandle)
+        , m_device(device)
+        , m_config(config)
+        , m_flags(flags)
+{}
+
+uint64_t StreamCommon::getFrameSize() const {
+    return util::countChannels(m_config.channelMask)
+           * util::getBytesPerSample(m_config.format);
+}
+
+uint64_t StreamCommon::getFrameCount() const {
+    return m_config.frameCount;
+}
+
+uint64_t StreamCommon::getBufferSize() const {
+    return getFrameSize() * getFrameCount();
+}
+
+uint32_t StreamCommon::getSampleRate() const {
+    return m_config.sampleRateHz;
+}
+
+void StreamCommon::getSupportedSampleRates(AudioFormat format,
+                                           IStream::getSupportedSampleRates_cb _hidl_cb) const {
+    if (m_config.format == format) {
+        _hidl_cb(Result::OK, {m_config.sampleRateHz});
+    } else {
+        _hidl_cb(Result::OK, {});
+    }
+}
+
+Result StreamCommon::setSampleRate(uint32_t sampleRateHz) const {
+    (void)sampleRateHz;
+    return Result::NOT_SUPPORTED;
+}
+
+hidl_bitfield<AudioChannelMask> StreamCommon::getChannelMask() const {
+    return m_config.channelMask;
+}
+
+void StreamCommon::getSupportedChannelMasks(AudioFormat format,
+                                            IStream::getSupportedChannelMasks_cb _hidl_cb) const {
+    if (m_config.format == format) {
+        _hidl_cb(Result::OK, {m_config.channelMask});
+    } else {
+        _hidl_cb(Result::OK, {});
+    }
+}
+
+Result StreamCommon::setChannelMask(hidl_bitfield<AudioChannelMask> mask) const {
+    (void)mask;
+    return Result::NOT_SUPPORTED;
+}
+
+AudioFormat StreamCommon::getFormat() const {
+    return m_config.format;
+}
+
+void StreamCommon::getSupportedFormats(IStream::getSupportedFormats_cb _hidl_cb) const {
+    _hidl_cb(Result::OK, {m_config.format});
+}
+
+Result StreamCommon::setFormat(AudioFormat format) const {
+    (void)format;
+    return Result::NOT_SUPPORTED;
+}
+
+void StreamCommon::getAudioProperties(IStream::getAudioProperties_cb _hidl_cb) const {
+    _hidl_cb(m_config.sampleRateHz, m_config.channelMask, m_config.format);
+}
+
+void StreamCommon::getDevices(IStream::getDevices_cb _hidl_cb) const {
+    _hidl_cb(Result::OK, {m_device});
+}
+
+Result StreamCommon::setDevices(const hidl_vec<DeviceAddress>& devices) const {
+    (void)devices;
+    return Result::NOT_SUPPORTED;
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_common.h b/audio/stream_common.h
new file mode 100644
index 0000000..ecacee1
--- /dev/null
+++ b/audio/stream_common.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStream.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamCommon {
+    StreamCommon(int32_t ioHandle,
+                 const DeviceAddress& device,
+                 const AudioConfig& config,
+                 hidl_bitfield<AudioInputFlag> flags);
+
+    uint64_t getFrameSize() const;
+    uint64_t getFrameCount() const;
+    uint64_t getBufferSize() const;
+    uint32_t getSampleRate() const;
+    void getSupportedSampleRates(AudioFormat format,
+                                 IStream::getSupportedSampleRates_cb _hidl_cb) const;
+    Result setSampleRate(uint32_t sampleRateHz) const;
+    hidl_bitfield<AudioChannelMask> getChannelMask() const;
+    void getSupportedChannelMasks(AudioFormat format,
+                                  IStream::getSupportedChannelMasks_cb _hidl_cb) const;
+    Result setChannelMask(hidl_bitfield<AudioChannelMask> mask) const;
+    AudioFormat getFormat() const;
+    void getSupportedFormats(IStream::getSupportedFormats_cb _hidl_cb) const;
+    Result setFormat(AudioFormat format) const;
+    void getAudioProperties(IStream::getAudioProperties_cb _hidl_cb) const;
+    void getDevices(IStream::getDevices_cb _hidl_cb) const;
+    Result setDevices(const hidl_vec<DeviceAddress>& devices) const;
+
+    const int32_t m_ioHandle;
+    const DeviceAddress m_device;
+    const AudioConfig m_config;
+    const hidl_bitfield<AudioOutputFlag> m_flags;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_in.cpp b/audio/stream_in.cpp
new file mode 100644
index 0000000..0f87a4d
--- /dev/null
+++ b/audio/stream_in.cpp
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "stream_in.h"
+#include "deleters.h"
+#include "talsa.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+namespace {
+
+struct ReadThread : public IOThread {
+    typedef MessageQueue<IStreamIn::ReadParameters, kSynchronizedReadWrite> CommandMQ;
+    typedef MessageQueue<IStreamIn::ReadStatus, kSynchronizedReadWrite> StatusMQ;
+    typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
+
+    ReadThread(StreamIn *stream,
+               const unsigned nChannels,
+               const size_t sampleRateHz,
+               const size_t frameCount,
+               const size_t bufferSize)
+            : mStream(stream)
+            , mNChannels(nChannels)
+            , mSampleRateHz(sampleRateHz)
+            , mFrameCount(frameCount)
+            , mCommandMQ(1)
+            , mStatusMQ(1)
+            , mDataMQ(bufferSize, true /* EventFlag */) {
+        if (!mCommandMQ.isValid()) {
+            ALOGE("ReadThread::%s:%d: mCommandMQ is invalid", __func__, __LINE__);
+            return;
+        }
+        if (!mDataMQ.isValid()) {
+            ALOGE("ReadThread::%s:%d: mDataMQ is invalid", __func__, __LINE__);
+            return;
+        }
+        if (!mStatusMQ.isValid()) {
+            ALOGE("ReadThread::%s:%d: mStatusMQ is invalid", __func__, __LINE__);
+            return;
+        }
+
+        status_t status;
+
+        EventFlag* rawEfGroup = nullptr;
+        status = EventFlag::createEventFlag(mDataMQ.getEventFlagWord(), &rawEfGroup);
+        if (status != OK || !rawEfGroup) {
+            ALOGE("ReadThread::%s:%d: rawEfGroup is invalid", __func__, __LINE__);
+            return;
+        } else {
+            mEfGroup.reset(rawEfGroup);
+        }
+
+        status = run("reader", PRIORITY_URGENT_AUDIO);
+        if (status != OK) {
+            ALOGE("ReadThread::%s:%d: failed to start the thread: %s",
+                  __func__, __LINE__, strerror(-status));
+        }
+    }
+
+    ~ReadThread() {
+        requestExit();
+        LOG_ALWAYS_FATAL_IF(join() != OK);
+    }
+
+    EventFlag *getEventFlag() override {
+        return mEfGroup.get();
+    }
+
+    bool threadLoop() override {
+        while (!exitPending()) {
+            uint32_t efState = 0;
+            mEfGroup->wait(MessageQueueFlagBits::NOT_EMPTY | 0, &efState);
+            if (efState & EXIT_REQUEST) {
+                return false;
+            }
+
+            if (efState & STAND_BY_REQUEST) {
+                mPcm.reset();
+                mBuffer.reset();
+            }
+
+            if (efState & (MessageQueueFlagBits::NOT_EMPTY | 0)) {
+                if (!mPcm) {
+                    mBuffer.reset(new uint8_t[mDataMQ.getQuantumCount()]);
+                    LOG_ALWAYS_FATAL_IF(!mBuffer);
+
+                    mPcm = talsa::pcmOpen(
+                        talsa::kPcmCard, talsa::kPcmDevice,
+                        mNChannels, mSampleRateHz, mFrameCount,
+                        false /* isOut */);
+                    LOG_ALWAYS_FATAL_IF(!mPcm);
+
+                    mPos.reset();
+                }
+
+                processCommand();
+            }
+        }
+
+        return false;   // do not restart threadLoop
+    }
+
+    void processCommand() {
+        IStreamIn::ReadParameters rParameters;
+
+        if (!mCommandMQ.read(&rParameters)) {
+            return;  // Nothing to do.
+        }
+
+        IStreamIn::ReadStatus rStatus;
+        switch (rParameters.command) {
+            case IStreamIn::ReadCommand::READ:
+                rStatus = doRead(rParameters);
+                break;
+
+            case IStreamIn::ReadCommand::GET_CAPTURE_POSITION:
+                rStatus = doGetCapturePosition();
+                break;
+
+            default:
+                ALOGE("ReadThread::%s:%d: Unknown read thread command code %d",
+                      __func__, __LINE__, rParameters.command);
+                rStatus.retval = Result::NOT_SUPPORTED;
+                break;
+        }
+
+        rStatus.replyTo = rParameters.command;
+
+        if (!mStatusMQ.write(&rStatus)) {
+            ALOGE("ReadThread::%s:%d: status message queue write failed", __func__, __LINE__);
+        }
+
+        mEfGroup->wake(MessageQueueFlagBits::NOT_FULL | 0);
+    }
+
+    IStreamIn::ReadStatus doRead(const IStreamIn::ReadParameters &rParameters) {
+        const size_t bytesToRead = std::min(mDataMQ.availableToWrite(),
+                                            static_cast<size_t>(rParameters.params.read));
+
+        IStreamIn::ReadStatus status;
+        size_t read = 0;
+        status.retval = doReadImpl(&mBuffer[0], bytesToRead, read);
+        if (status.retval == Result::OK) {
+            if (mDataMQ.write(&mBuffer[0], read)) {
+                ALOGE("ReadThread::%s:%d: mDataMQ.write failed", __func__, __LINE__);
+            }
+
+            mPos.addFrames(pcm_bytes_to_frames(mPcm.get(), read));
+            status.reply.read = read;
+        }
+
+        return status;
+    }
+
+    Result doReadImpl(uint8_t *const data, const size_t toRead, size_t &read) {
+        const int res = pcm_read(mPcm.get(), data, toRead);
+        if (res < 0) {
+            memset(data, 0, toRead);
+            read = toRead;
+
+            ALOGE("ReadThread::%s:%d pcm_read failed with %s",
+                  __func__, __LINE__, strerror(-res));
+        } else if (res == 0) {
+            read = toRead;
+        } else {
+            read = res;
+        }
+
+        return Result::OK;
+    }
+
+    IStreamIn::ReadStatus doGetCapturePosition() {
+        IStreamIn::ReadStatus status;
+
+        status.retval = Result::OK;
+        nsecs_t t = 0;
+        mPos.now(mSampleRateHz, status.reply.capturePosition.frames, t);
+        status.reply.capturePosition.time = t;
+
+        return status;
+    }
+
+    StreamIn *const mStream;
+    const unsigned mNChannels;
+    const size_t mSampleRateHz;
+    const size_t mFrameCount;
+    CommandMQ mCommandMQ;
+    StatusMQ mStatusMQ;
+    DataMQ mDataMQ;
+    std::unique_ptr<EventFlag, deleters::forEventFlag> mEfGroup;
+    std::unique_ptr<uint8_t[]> mBuffer;
+    talsa::PcmPtr mPcm;
+    util::StreamPosition mPos;
+};
+
+} // namespace
+
+StreamIn::StreamIn(sp<IDevice> dev,
+                   void (*unrefDevice)(IDevice*),
+                   int32_t ioHandle,
+                   const DeviceAddress& device,
+                   const AudioConfig& config,
+                   hidl_bitfield<AudioInputFlag> flags,
+                   const SinkMetadata& sinkMetadata)
+        : mDev(std::move(dev))
+        , mUnrefDevice(unrefDevice)
+        , mCommon(ioHandle, device, config, flags)
+        , mSinkMetadata(sinkMetadata) {
+}
+
+StreamIn::~StreamIn() {
+    close();
+}
+
+Return<uint64_t> StreamIn::getFrameSize() {
+    return mCommon.getFrameSize();
+}
+
+Return<uint64_t> StreamIn::getFrameCount() {
+    return mCommon.getFrameCount();
+}
+
+Return<uint64_t> StreamIn::getBufferSize() {
+    return mCommon.getBufferSize();
+}
+
+Return<uint32_t> StreamIn::getSampleRate() {
+    return mCommon.getSampleRate();
+}
+
+Return<void> StreamIn::getSupportedSampleRates(AudioFormat format,
+                                               getSupportedSampleRates_cb _hidl_cb) {
+    mCommon.getSupportedSampleRates(format, _hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamIn::setSampleRate(uint32_t sampleRateHz) {
+    return mCommon.setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamIn::getChannelMask() {
+    return mCommon.getChannelMask();
+}
+
+Return<void> StreamIn::getSupportedChannelMasks(AudioFormat format,
+                                                IStream::getSupportedChannelMasks_cb _hidl_cb) {
+    mCommon.getSupportedChannelMasks(format, _hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamIn::setChannelMask(hidl_bitfield<AudioChannelMask> mask) {
+    return mCommon.setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamIn::getFormat() {
+    return mCommon.getFormat();
+}
+
+Return<void> StreamIn::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
+    mCommon.getSupportedFormats(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamIn::setFormat(AudioFormat format) {
+    return mCommon.setFormat(format);
+}
+
+Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+    mCommon.getAudioProperties(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamIn::addEffect(uint64_t effectId) {
+    (void)effectId;
+    return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamIn::removeEffect(uint64_t effectId) {
+    (void)effectId;
+    return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamIn::standby() {
+    if (mReadThread) {
+        LOG_ALWAYS_FATAL_IF(!mReadThread->standby());
+    }
+
+    return Result::OK;
+}
+
+Return<void> StreamIn::getDevices(getDevices_cb _hidl_cb) {
+    mCommon.getDevices(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamIn::setDevices(const hidl_vec<DeviceAddress>& devices) {
+    return mCommon.setDevices(devices);
+}
+
+Return<void> StreamIn::getParameters(const hidl_vec<ParameterValue>& context,
+                                     const hidl_vec<hidl_string>& keys,
+                                     getParameters_cb _hidl_cb) {
+    (void)context;
+    _hidl_cb((keys.size() > 0) ? Result::NOT_SUPPORTED : Result::OK, {});
+    return Void();
+}
+
+Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& context,
+                                       const hidl_vec<ParameterValue>& parameters) {
+    (void)context;
+    return (parameters.size() > 0) ? Result::NOT_SUPPORTED : Result::OK;
+}
+
+Return<Result> StreamIn::setHwAvSync(uint32_t hwAvSync) {
+    (void)hwAvSync;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::start() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::stop() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamIn::createMmapBuffer(int32_t minSizeFrames,
+                                        createMmapBuffer_cb _hidl_cb) {
+    (void)minSizeFrames;
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<void> StreamIn::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<Result> StreamIn::close() {
+    if (mDev) {
+        mReadThread.reset();
+        mUnrefDevice(mDev.get());
+        mDev = nullptr;
+        return Result::OK;
+    } else {
+        return Result::INVALID_STATE;
+    }
+}
+
+Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<Result> StreamIn::setGain(float gain) {
+    (void)gain;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+    (void)sinkMetadata;
+    return Void();
+}
+
+Return<void> StreamIn::prepareForReading(uint32_t frameSize,
+                                         uint32_t framesCount,
+                                         prepareForReading_cb _hidl_cb) {
+    if (!frameSize || !framesCount || frameSize > 256 || framesCount > (1u << 20)) {
+        _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+        return Void();
+    }
+
+    if (mReadThread) {  // INVALID_STATE if the method was already called.
+        _hidl_cb(Result::INVALID_STATE, {}, {}, {}, {});
+        return Void();
+    }
+
+    auto t = std::make_unique<ReadThread>(this,
+                                          util::countChannels(mCommon.getChannelMask()),
+                                          mCommon.getSampleRate(),
+                                          mCommon.getFrameCount(),
+                                          frameSize * framesCount);
+
+    if (t->isRunning()) {
+        _hidl_cb(Result::OK,
+                 *(t->mCommandMQ.getDesc()),
+                 *(t->mDataMQ.getDesc()),
+                 *(t->mStatusMQ.getDesc()),
+                 {.pid = getpid(), .tid = t->getTid()});
+
+        mReadThread = std::move(t);
+    } else {
+        _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+    }
+
+    return Void();
+}
+
+Return<uint32_t> StreamIn::getInputFramesLost() {
+    return 0;
+}
+
+Return<void> StreamIn::getCapturePosition(getCapturePosition_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, 0, 0);  // see ReadThread::doGetCapturePosition
+    return Void();
+}
+
+Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
+    _hidl_cb(Result::OK, {util::getMicrophoneInfo()});
+    return Void();
+}
+
+Return<Result> StreamIn::setMicrophoneDirection(MicrophoneDirection direction) {
+    (void)direction;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::setMicrophoneFieldDimension(float zoom) {
+    (void)zoom;
+    return Result::NOT_SUPPORTED;
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_in.h b/audio/stream_in.h
new file mode 100644
index 0000000..13b28d8
--- /dev/null
+++ b/audio/stream_in.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStreamIn.h>
+#include <android/hardware/audio/6.0/IDevice.h>
+#include "stream_common.h"
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamIn : public IStreamIn {
+    StreamIn(sp<IDevice> dev,
+             void (*unrefDevice)(IDevice*),
+             int32_t ioHandle,
+             const DeviceAddress& device,
+             const AudioConfig& config,
+             hidl_bitfield<AudioInputFlag> flags,
+             const SinkMetadata& sinkMetadata);
+    ~StreamIn();
+
+    // IStream
+    Return<uint64_t> getFrameSize() override;
+    Return<uint64_t> getFrameCount() override;
+    Return<uint64_t> getBufferSize() override;
+    Return<uint32_t> getSampleRate() override;
+    Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+    Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+    Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+    Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+    Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+    Return<AudioFormat> getFormat() override;
+    Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+    Return<Result> setFormat(AudioFormat format) override;
+    Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+    Return<Result> addEffect(uint64_t effectId) override;
+    Return<Result> removeEffect(uint64_t effectId) override;
+    Return<Result> standby() override;
+    Return<void> getDevices(getDevices_cb _hidl_cb) override;
+    Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+    Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+    Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+                               const hidl_vec<hidl_string>& keys,
+                               getParameters_cb _hidl_cb) override;
+    Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+                                 const hidl_vec<ParameterValue>& parameters) override;
+    Return<Result> start() override;
+    Return<Result> stop() override;
+    Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
+    Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+    Return<Result> close() override;
+
+    // IStreamIn
+    Return<void> getAudioSource(getAudioSource_cb _hidl_cb) override;
+    Return<Result> setGain(float gain) override;
+    Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
+    Return<void> prepareForReading(uint32_t frameSize, uint32_t framesCount,
+                                   prepareForReading_cb _hidl_cb) override;
+    Return<uint32_t> getInputFramesLost() override;
+    Return<void> getCapturePosition(getCapturePosition_cb _hidl_cb) override;
+    Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+    Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
+    Return<Result> setMicrophoneFieldDimension(float zoom) override;
+
+private:
+    sp<IDevice> mDev;
+    void (* const mUnrefDevice)(IDevice*);
+    const StreamCommon mCommon;
+    const SinkMetadata mSinkMetadata;
+    std::unique_ptr<IOThread> mReadThread;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_out.cpp b/audio/stream_out.cpp
new file mode 100644
index 0000000..3e7cc80
--- /dev/null
+++ b/audio/stream_out.cpp
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <math.h>
+#include "stream_out.h"
+#include "talsa.h"
+#include "deleters.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+namespace {
+
+struct WriteThread : public IOThread {
+    typedef MessageQueue<IStreamOut::WriteCommand, kSynchronizedReadWrite> CommandMQ;
+    typedef MessageQueue<IStreamOut::WriteStatus, kSynchronizedReadWrite> StatusMQ;
+    typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
+
+    WriteThread(StreamOut *stream,
+                const unsigned nChannels,
+                const size_t sampleRateHz,
+                const size_t frameCount,
+                const size_t mqBufferSize)
+            : mStream(stream)
+            , mNChannels(nChannels)
+            , mSampleRateHz(sampleRateHz)
+            , mFrameCount(frameCount)
+            , mCommandMQ(1)
+            , mStatusMQ(1)
+            , mDataMQ(mqBufferSize, true /* EventFlag */) {
+        if (!mCommandMQ.isValid()) {
+            ALOGE("WriteThread::%s:%d: mCommandMQ is invalid", __func__, __LINE__);
+            return;
+        }
+        if (!mDataMQ.isValid()) {
+            ALOGE("WriteThread::%s:%d: mDataMQ is invalid", __func__, __LINE__);
+            return;
+        }
+        if (!mStatusMQ.isValid()) {
+            ALOGE("WriteThread::%s:%d: mStatusMQ is invalid", __func__, __LINE__);
+            return;
+        }
+
+        status_t status;
+
+        EventFlag* rawEfGroup = nullptr;
+        status = EventFlag::createEventFlag(mDataMQ.getEventFlagWord(), &rawEfGroup);
+        if (status != OK || !rawEfGroup) {
+            ALOGE("WriteThread::%s:%d: rawEfGroup is invalid", __func__, __LINE__);
+            return;
+        } else {
+            mEfGroup.reset(rawEfGroup);
+        }
+
+        status = run("writer", PRIORITY_URGENT_AUDIO);
+        if (status != OK) {
+            ALOGE("WriteThread::%s:%d: failed to start the thread: %s",
+                  __func__, __LINE__, strerror(-status));
+        }
+    }
+
+    ~WriteThread() {
+        requestExit();
+        LOG_ALWAYS_FATAL_IF(join() != OK);
+    }
+
+    EventFlag *getEventFlag() override {
+        return mEfGroup.get();
+    }
+
+    bool threadLoop() override {
+        while (!exitPending()) {
+            uint32_t efState = 0;
+            mEfGroup->wait(MessageQueueFlagBits::NOT_EMPTY | STAND_BY_REQUEST | EXIT_REQUEST,
+                           &efState);
+            if (efState & EXIT_REQUEST) {
+                return false;
+            }
+
+            if (efState & STAND_BY_REQUEST) {
+                mPcm.reset();
+                mBuffer.reset();
+            }
+
+            if (efState & (MessageQueueFlagBits::NOT_EMPTY | 0)) {
+                if (!mPcm) {
+                    mBuffer.reset(new uint8_t[mDataMQ.getQuantumCount()]);
+                    LOG_ALWAYS_FATAL_IF(!mBuffer);
+
+                    mPcm = talsa::pcmOpen(
+                        talsa::kPcmCard, talsa::kPcmDevice,
+                        mNChannels, mSampleRateHz, mFrameCount,
+                        true /* isOut */);
+                    LOG_ALWAYS_FATAL_IF(!mPcm);
+
+                    mPos.reset();
+                }
+
+                processCommand();
+            }
+        }
+
+        return false;   // do not restart threadLoop
+    }
+
+    void processCommand() {
+        IStreamOut::WriteCommand wCommand;
+
+        if (!mCommandMQ.read(&wCommand)) {
+            return;  // Nothing to do.
+        }
+
+        IStreamOut::WriteStatus wStatus;
+        switch (wCommand) {
+            case IStreamOut::WriteCommand::WRITE:
+                wStatus = doWrite();
+                break;
+
+            case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
+                wStatus = doGetPresentationPosition();
+                break;
+
+            case IStreamOut::WriteCommand::GET_LATENCY:
+                wStatus = doGetLatency();
+                break;
+
+            default:
+                ALOGE("WriteThread::%s:%d: Unknown write thread command code %d",
+                      __func__, __LINE__, wCommand);
+                wStatus.retval = Result::NOT_SUPPORTED;
+                break;
+        }
+
+        wStatus.replyTo = wCommand;
+
+        if (!mStatusMQ.write(&wStatus)) {
+            ALOGE("status message queue write failed");
+        }
+
+        mEfGroup->wake(MessageQueueFlagBits::NOT_FULL | 0);
+    }
+
+    IStreamOut::WriteStatus doWrite() {
+        IStreamOut::WriteStatus status;
+
+        const size_t availToRead = mDataMQ.availableToRead();
+        size_t written = 0;
+        if (mDataMQ.read(&mBuffer[0], availToRead)) {
+            status.retval = doWriteImpl(&mBuffer[0], availToRead, written);
+
+            mPos.addFrames(pcm_bytes_to_frames(mPcm.get(), written));
+            status.reply.written = written;
+        } else {
+            ALOGE("WriteThread::%s:%d: mDataMQ.read failed", __func__, __LINE__);
+            status.retval = Result::OK;
+        }
+
+        return status;
+    }
+
+    IStreamOut::WriteStatus doGetPresentationPosition() {
+        IStreamOut::WriteStatus status;
+
+        status.retval = doGetPresentationPositionImpl(
+            status.reply.presentationPosition.frames,
+            status.reply.presentationPosition.timeStamp);
+
+        return status;
+    }
+
+    IStreamOut::WriteStatus doGetLatency() {
+        IStreamOut::WriteStatus status;
+
+        status.retval = Result::OK;
+        status.reply.latencyMs = mStream->getLatency();
+
+        return status;
+    }
+
+    Result doWriteImpl(const uint8_t *const data,
+                       const size_t toWrite,
+                       size_t &written) {
+        const int res = pcm_write(mPcm.get(), data, toWrite);
+        if (res) {
+            ALOGE("WriteThread::%s:%d: pcm_write failed with %s",
+                  __func__, __LINE__, strerror(-res));
+        }
+
+        written = toWrite;
+        return Result::OK;
+    }
+
+    Result doGetPresentationPositionImpl(uint64_t &frames, TimeSpec &ts) {
+        nsecs_t t = 0;
+        mPos.now(mSampleRateHz, frames, t);
+        ts.tvSec = ns2s(t);
+        ts.tvNSec = t - s2ns(ts.tvSec);
+        return Result::OK;
+    }
+
+    StreamOut *const mStream;
+    const unsigned mNChannels;
+    const size_t mSampleRateHz;
+    const size_t mFrameCount;
+    CommandMQ mCommandMQ;
+    StatusMQ mStatusMQ;
+    DataMQ mDataMQ;
+    std::unique_ptr<EventFlag, deleters::forEventFlag> mEfGroup;
+    std::unique_ptr<uint8_t[]> mBuffer;
+    talsa::PcmPtr mPcm;
+    util::StreamPosition mPos;
+};
+
+} // namespace
+
+StreamOut::StreamOut(sp<IDevice> dev,
+                     void (*unrefDevice)(IDevice*),
+                     int32_t ioHandle,
+                     const DeviceAddress& device,
+                     const AudioConfig& config,
+                     hidl_bitfield<AudioOutputFlag> flags,
+                     const SourceMetadata& sourceMetadata)
+        : mDev(std::move(dev))
+        , mUnrefDevice(unrefDevice)
+        , mCommon(ioHandle, device, config, flags)
+        , mSourceMetadata(sourceMetadata) {
+}
+
+StreamOut::~StreamOut() {
+    close();
+}
+
+Return<uint64_t> StreamOut::getFrameSize() {
+    return mCommon.getFrameSize();
+}
+
+Return<uint64_t> StreamOut::getFrameCount() {
+    return mCommon.getFrameCount();
+}
+
+Return<uint64_t> StreamOut::getBufferSize() {
+    return mCommon.getBufferSize();
+}
+
+Return<uint32_t> StreamOut::getSampleRate() {
+    return mCommon.getSampleRate();
+}
+
+Return<void> StreamOut::getSupportedSampleRates(AudioFormat format,
+                                                getSupportedSampleRates_cb _hidl_cb) {
+    mCommon.getSupportedSampleRates(format, _hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) {
+    return mCommon.setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamOut::getChannelMask() {
+    return mCommon.getChannelMask();
+}
+
+Return<void> StreamOut::getSupportedChannelMasks(AudioFormat format,
+                                                 IStream::getSupportedChannelMasks_cb _hidl_cb) {
+    mCommon.getSupportedChannelMasks(format, _hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamOut::setChannelMask(hidl_bitfield<AudioChannelMask> mask) {
+    return mCommon.setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamOut::getFormat() {
+    return mCommon.getFormat();
+}
+
+Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
+    mCommon.getSupportedFormats(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamOut::setFormat(AudioFormat format) {
+    return mCommon.setFormat(format);
+}
+
+Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+    mCommon.getAudioProperties(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamOut::addEffect(uint64_t effectId) {
+    (void)effectId;
+    return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamOut::removeEffect(uint64_t effectId) {
+    (void)effectId;
+    return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamOut::standby() {
+    if (mWriteThread) {
+        LOG_ALWAYS_FATAL_IF(!mWriteThread->standby());
+    }
+
+    return Result::OK;
+}
+
+Return<void> StreamOut::getDevices(getDevices_cb _hidl_cb) {
+    mCommon.getDevices(_hidl_cb);
+    return Void();
+}
+
+Return<Result> StreamOut::setDevices(const hidl_vec<DeviceAddress>& devices) {
+    return mCommon.setDevices(devices);
+}
+
+Return<void> StreamOut::getParameters(const hidl_vec<ParameterValue>& context,
+                                      const hidl_vec<hidl_string>& keys,
+                                      getParameters_cb _hidl_cb) {
+    (void)context;
+    _hidl_cb((keys.size() > 0) ? Result::NOT_SUPPORTED : Result::OK, {});
+    return Void();
+}
+
+Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& context,
+                                        const hidl_vec<ParameterValue>& parameters) {
+    (void)context;
+    return (parameters.size() > 0) ? Result::NOT_SUPPORTED : Result::OK;
+}
+
+Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
+    (void)hwAvSync;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::close() {
+    if (mDev) {
+        mWriteThread.reset();
+        mUnrefDevice(mDev.get());
+        mDev = nullptr;
+        return Result::OK;
+    } else {
+        return Result::INVALID_STATE;
+    }
+}
+
+Return<Result> StreamOut::start() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::stop() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames,
+                                         createMmapBuffer_cb _hidl_cb) {
+    (void)minSizeFrames;
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<uint32_t> StreamOut::getLatency() {
+    return mCommon.getFrameCount() * 1000 / mCommon.getSampleRate();
+}
+
+Return<Result> StreamOut::setVolume(float left, float right) {
+    (void)left;
+    (void)right;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+    (void)sourceMetadata;
+    return Void();
+}
+
+Return<void> StreamOut::prepareForWriting(uint32_t frameSize,
+                                          uint32_t framesCount,
+                                          prepareForWriting_cb _hidl_cb) {
+    if (!frameSize || !framesCount || frameSize > 256 || framesCount > (1u << 20)) {
+        _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+        return Void();
+    }
+
+    if (mWriteThread) {  // INVALID_STATE if the method was already called.
+        _hidl_cb(Result::INVALID_STATE, {}, {}, {}, {});
+        return Void();
+    }
+
+    auto t = std::make_unique<WriteThread>(this,
+                                           util::countChannels(mCommon.getChannelMask()),
+                                           mCommon.getSampleRate(),
+                                           mCommon.getFrameCount(),
+                                           frameSize * framesCount);
+
+    if (t->isRunning()) {
+        _hidl_cb(Result::OK,
+                 *(t->mCommandMQ.getDesc()),
+                 *(t->mDataMQ.getDesc()),
+                 *(t->mStatusMQ.getDesc()),
+                 {.pid = getpid(), .tid = t->getTid()});
+
+        mWriteThread = std::move(t);
+    } else {
+        _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+    }
+
+    return Void();
+}
+
+Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, 0);
+    return Void();
+}
+
+Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, 0);
+    return Void();
+}
+
+Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
+    (void)callback;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::clearCallback() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::setEventCallback(const sp<IStreamOutEventCallback>& callback) {
+    (void)callback;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
+    _hidl_cb(false, false);
+    return Void();
+}
+
+Return<Result> StreamOut::pause() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::resume() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<bool> StreamOut::supportsDrain() {
+    return false;
+}
+
+Return<Result> StreamOut::drain(AudioDrain type) {
+    (void)type;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::flush() {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {}, {});    // see WriteThread::doGetPresentationPosition
+    return Void();
+}
+
+Return<Result> StreamOut::selectPresentation(int32_t presentationId,
+                                             int32_t programId) {
+    (void)presentationId;
+    (void)programId;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<Result> StreamOut::setDualMonoMode(DualMonoMode mode) {
+    (void)mode;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, 0);
+    return Void();
+}
+
+Return<Result> StreamOut::setAudioDescriptionMixLevel(float leveldB) {
+    (void)leveldB;
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, {});
+    return Void();
+}
+
+Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate &playbackRate) {
+    (void)playbackRate;
+    return Result::NOT_SUPPORTED;
+}
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/stream_out.h b/audio/stream_out.h
new file mode 100644
index 0000000..c55ad8c
--- /dev/null
+++ b/audio/stream_out.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStreamOut.h>
+#include <android/hardware/audio/6.0/IDevice.h>
+#include "stream_common.h"
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamOut : public IStreamOut {
+    StreamOut(sp<IDevice> dev,
+              void (*unrefDevice)(IDevice*),
+              int32_t ioHandle,
+              const DeviceAddress& device,
+              const AudioConfig& config,
+              hidl_bitfield<AudioOutputFlag> flags,
+              const SourceMetadata& sourceMetadata);
+    ~StreamOut();
+
+    // IStream
+    Return<uint64_t> getFrameSize() override;
+    Return<uint64_t> getFrameCount() override;
+    Return<uint64_t> getBufferSize() override;
+    Return<uint32_t> getSampleRate() override;
+    Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+    Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+    Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+    Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+    Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+    Return<AudioFormat> getFormat() override;
+    Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+    Return<Result> setFormat(AudioFormat format) override;
+    Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+    Return<Result> addEffect(uint64_t effectId) override;
+    Return<Result> removeEffect(uint64_t effectId) override;
+    Return<Result> standby() override;
+    Return<void> getDevices(getDevices_cb _hidl_cb) override;
+    Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+    Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+    Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+                               const hidl_vec<hidl_string>& keys,
+                               getParameters_cb _hidl_cb) override;
+    Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+                                 const hidl_vec<ParameterValue>& parameters) override;
+    Return<Result> start() override;
+    Return<Result> stop() override;
+    Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
+    Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+    Return<Result> close() override;
+
+    // IStreamOut
+    Return<uint32_t> getLatency() override;
+    Return<Result> setVolume(float left, float right) override;
+    Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
+    Return<void> prepareForWriting(uint32_t frameSize, uint32_t framesCount,
+                                   prepareForWriting_cb _hidl_cb) override;
+    Return<void> getRenderPosition(getRenderPosition_cb _hidl_cb) override;
+    Return<void> getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) override;
+    Return<Result> setCallback(const sp<IStreamOutCallback>& callback) override;
+    Return<Result> clearCallback() override;
+    Return<Result> setEventCallback(const sp<IStreamOutEventCallback>& callback) override;
+    Return<void> supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) override;
+    Return<Result> pause() override;
+    Return<Result> resume() override;
+    Return<bool> supportsDrain() override;
+    Return<Result> drain(AudioDrain type) override;
+    Return<Result> flush() override;
+    Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) override;
+    Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
+    Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
+    Return<Result> setDualMonoMode(DualMonoMode mode) override;
+    Return<void> getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) override;
+    Return<Result> setAudioDescriptionMixLevel(float leveldB) override;
+    Return<void> getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) override;
+    Return<Result> setPlaybackRateParameters(const PlaybackRate &playbackRate) override;
+
+private:
+    sp<IDevice> mDev;
+    void (* const mUnrefDevice)(IDevice*);
+    const StreamCommon mCommon;
+    const SourceMetadata mSourceMetadata;
+    std::unique_ptr<IOThread> mWriteThread;
+};
+
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/talsa.cpp b/audio/talsa.cpp
new file mode 100644
index 0000000..9fb7099
--- /dev/null
+++ b/audio/talsa.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "talsa.h"
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace talsa {
+
+void PcmDeleter::operator()(pcm_t *x) const {
+    LOG_ALWAYS_FATAL_IF(pcm_close(x) != 0);
+};
+
+void MixerDeleter::operator()(struct mixer *x) const {
+    mixer_close(x);
+}
+
+std::unique_ptr<pcm_t, PcmDeleter> pcmOpen(const unsigned int dev,
+                                           const unsigned int card,
+                                           const unsigned int nChannels,
+                                           const size_t sampleRateHz,
+                                           const size_t frameCount,
+                                           const bool isOut) {
+    struct pcm_config pcm_config;
+    memset(&pcm_config, 0, sizeof(pcm_config));
+
+    pcm_config.channels = nChannels;
+    pcm_config.rate = sampleRateHz;
+    pcm_config.period_size = frameCount;     // Approx frames between interrupts
+    pcm_config.period_count = 4;             // Approx interrupts per buffer
+    pcm_config.format = PCM_FORMAT_S16_LE;
+    pcm_config.start_threshold = 0;
+    pcm_config.stop_threshold = isOut ? 0 : INT_MAX;
+
+    PcmPtr pcm =
+        PcmPtr(::pcm_open(dev, card,
+                          (isOut ? PCM_OUT : PCM_IN) | PCM_MONOTONIC,
+                           &pcm_config));
+    if (::pcm_is_ready(pcm.get())) {
+        return pcm;
+    } else {
+        ALOGE("%s:%d pcm_open failed for sampleRateHz=%zu "
+              "frameCount=%zu with %s", __func__, __LINE__,
+              sampleRateHz, frameCount,
+              pcm_get_error(pcm.get()));
+        return nullptr;
+    }
+}
+
+MixerPtr mixerOpen(unsigned int card) {
+    return MixerPtr(::mixer_open(card));
+}
+
+void mixerSetValueAll(mixer_ctl_t *ctl, int value) {
+    const unsigned int n = mixer_ctl_get_num_values(ctl);
+    for (unsigned int i = 0; i < n; i++) {
+        mixer_ctl_set_value(ctl, i, value);
+    }
+}
+
+void mixerSetPercentAll(mixer_ctl_t *ctl, int percent) {
+    const unsigned int n = mixer_ctl_get_num_values(ctl);
+    for (unsigned int i = 0; i < n; i++) {
+        mixer_ctl_set_percent(ctl, i, percent);
+    }
+}
+
+
+}  // namespace talsa
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/talsa.h b/audio/talsa.h
new file mode 100644
index 0000000..0b05bfc
--- /dev/null
+++ b/audio/talsa.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <memory>
+#include <tinyalsa/asoundlib.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace talsa {
+
+constexpr unsigned int kPcmDevice = 0;
+constexpr unsigned int kPcmCard = 0;
+
+typedef struct pcm pcm_t;
+struct PcmDeleter { void operator()(pcm_t *x) const; };
+typedef std::unique_ptr<pcm_t, PcmDeleter> PcmPtr;
+PcmPtr pcmOpen(unsigned int dev, unsigned int card, unsigned int nChannels, size_t sampleRateHz, size_t frameCount, bool isOut);
+
+typedef struct mixer mixer_t;
+typedef struct mixer_ctl mixer_ctl_t;
+struct MixerDeleter { void operator()(struct mixer *m) const; };
+typedef std::unique_ptr<mixer_t, MixerDeleter> MixerPtr;
+MixerPtr mixerOpen(unsigned int card);
+void mixerSetValueAll(mixer_ctl_t *ctl, int value);
+void mixerSetPercentAll(mixer_ctl_t *ctl, int percent);
+
+}  // namespace talsa
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/util.cpp b/audio/util.cpp
new file mode 100644
index 0000000..746e37d
--- /dev/null
+++ b/audio/util.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include <cutils/bitops.h>
+#include <system/audio.h>
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace util {
+
+namespace {
+
+const std::array<uint32_t, 8> kSupportedRatesHz = {
+    8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+const std::array<hidl_bitfield<AudioChannelMask>, 4> kSupportedInChannelMask = {
+    AudioChannelMask::IN_LEFT | 0,
+    AudioChannelMask::IN_RIGHT | 0,
+    AudioChannelMask::IN_FRONT | 0,
+    AudioChannelMask::IN_STEREO | 0,
+};
+
+const std::array<hidl_bitfield<AudioChannelMask>, 4> kSupportedOutChannelMask = {
+    AudioChannelMask::OUT_FRONT_LEFT | 0,
+    AudioChannelMask::OUT_FRONT_RIGHT | 0,
+    AudioChannelMask::OUT_FRONT_CENTER | 0,
+    AudioChannelMask::OUT_STEREO | 0,
+};
+
+const std::array<AudioFormat, 1> kSupportedAudioFormats = {
+    AudioFormat::PCM_16_BIT,
+};
+
+bool checkSampleRateHz(uint32_t value, uint32_t &suggest) {
+    for (const uint32_t supported : kSupportedRatesHz) {
+        if (value <= supported) {
+            suggest = supported;
+            return (value == supported);
+        }
+    }
+
+    suggest = kSupportedRatesHz.back();
+    return false;
+}
+
+size_t getBufferSizeFrames(size_t duration_ms, uint32_t sample_rate) {
+    return sample_rate * duration_ms / 1000;
+}
+
+}  // namespace
+
+MicrophoneInfo getMicrophoneInfo() {
+    MicrophoneInfo mic;
+
+    mic.deviceId = "mic_goldfish";
+    mic.group = 0;
+    mic.indexInTheGroup = 0;
+    mic.sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+    mic.maxSpl = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+    mic.minSpl = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+    mic.directionality = AudioMicrophoneDirectionality::UNKNOWN;
+    mic.position.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic.position.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic.position.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic.orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic.orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    mic.orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+
+    return mic;
+}
+
+size_t countChannels(hidl_bitfield<AudioChannelMask> mask) {
+    return popcount(mask);
+}
+
+size_t getBytesPerSample(AudioFormat format) {
+    return audio_bytes_per_sample(static_cast<audio_format_t>(format));
+}
+
+bool checkAudioConfig(bool isOut,
+                      size_t duration_ms,
+                      const AudioConfig &cfg,
+                      AudioConfig &suggested) {
+    bool valid = checkSampleRateHz(cfg.sampleRateHz, suggested.sampleRateHz);
+
+    if (isOut) {
+        if (std::find(kSupportedOutChannelMask.begin(),
+                      kSupportedOutChannelMask.end(),
+                      cfg.channelMask) == kSupportedOutChannelMask.end()) {
+            suggested.channelMask = AudioChannelMask::OUT_STEREO | 0;
+            valid = false;
+        } else {
+            suggested.channelMask = cfg.channelMask;
+        }
+    } else {
+        if (std::find(kSupportedInChannelMask.begin(),
+                      kSupportedInChannelMask.end(),
+                      cfg.channelMask) == kSupportedInChannelMask.end()) {
+            suggested.channelMask = AudioChannelMask::IN_STEREO | 0;
+            valid = false;
+        } else {
+            suggested.channelMask = cfg.channelMask;
+        }
+    }
+
+    if (std::find(kSupportedAudioFormats.begin(),
+                  kSupportedAudioFormats.end(),
+                  cfg.format) == kSupportedAudioFormats.end()) {
+        suggested.format = AudioFormat::PCM_16_BIT;
+        valid = false;
+    } else {
+        suggested.format = cfg.format;
+    }
+
+    suggested.offloadInfo = cfg.offloadInfo;    // don't care
+
+    suggested.frameCount = (cfg.frameCount == 0)
+        ? getBufferSizeFrames(duration_ms, suggested.sampleRateHz)
+        : cfg.frameCount;
+
+    return valid;
+}
+
+StreamPosition::StreamPosition() : mTimestamp(systemTime(SYSTEM_TIME_MONOTONIC)) {}
+
+void StreamPosition::addFrames(uint64_t n) {
+    mTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+    mFrames += n;
+}
+
+void StreamPosition::now(const size_t sampleRateHz,
+                         uint64_t &frames,
+                         nsecs_t &timestamp) const {
+    const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    const uint64_t deltaUs = ns2us(now - mTimestamp);
+
+    frames = mFrames + sampleRateHz * deltaUs / 1000000;
+    timestamp = now;
+}
+
+void StreamPosition::reset() {
+    *this = StreamPosition();
+}
+
+}  // namespace util
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/util.h b/audio/util.h
new file mode 100644
index 0000000..bfac69a
--- /dev/null
+++ b/audio/util.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <array>
+#include <android/hardware/audio/common/6.0/types.h>
+#include <android/hardware/audio/6.0/types.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace util {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::audio::common::V6_0::AudioFormat;
+using ::android::hardware::audio::common::V6_0::AudioChannelMask;
+using ::android::hardware::audio::common::V6_0::AudioConfig;
+using ::android::hardware::audio::V6_0::MicrophoneInfo;
+
+MicrophoneInfo getMicrophoneInfo();
+
+size_t countChannels(hidl_bitfield<AudioChannelMask>);
+size_t getBytesPerSample(AudioFormat);
+
+bool checkAudioConfig(bool isOut,
+                      size_t duration_ms,
+                      const AudioConfig &cfg,
+                      AudioConfig &suggested);
+
+struct StreamPosition {
+    StreamPosition();
+    void addFrames(uint64_t n);
+    void now(size_t sampleRateHz, uint64_t &frames, nsecs_t &timestamp) const;
+    void reset();
+
+    uint64_t mFrames = 0;
+    nsecs_t mTimestamp;
+};
+
+}  // namespace util
+}  // namespace implementation
+}  // namespace V6_0
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio_policy.conf b/audio_policy.conf
deleted file mode 100644
index 0945c25..0000000
--- a/audio_policy.conf
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Audio policy configuration for generic device builds (goldfish audio HAL - emulator)
-#
-
-# Global configuration section: lists input and output devices always present on the device
-# as well as the output device selected by default.
-# Devices are designated by a string that corresponds to the enum in audio.h
-
-global_configuration {
-  attached_output_devices AUDIO_DEVICE_OUT_SPEAKER
-  default_output_device AUDIO_DEVICE_OUT_SPEAKER
-  attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX
-}
-
-# audio hardware module section: contains descriptors for all audio hw modules present on the
-# device. Each hw module node is named after the corresponding hw module library base name.
-# For instance, "primary" corresponds to audio.primary.<device>.so.
-# The "primary" module is mandatory and must include at least one output with
-# AUDIO_OUTPUT_FLAG_PRIMARY flag.
-# Each module descriptor contains one or more output profile descriptors and zero or more
-# input profile descriptors. Each profile lists all the parameters supported by a given output
-# or input stream category.
-# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding
-# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n".
-
-audio_hw_modules {
-  primary {
-    outputs {
-      primary {
-        sampling_rates 8000|11025|16000|22050|24000|44100|48000
-        channel_masks AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO
-        formats AUDIO_FORMAT_PCM_16_BIT
-        devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_WIRED_HEADSET
-        flags AUDIO_OUTPUT_FLAG_PRIMARY
-      }
-    }
-    inputs {
-      primary {
-        sampling_rates 8000|11025|16000|22050|44100|48000
-        channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO
-        formats AUDIO_FORMAT_PCM_16_BIT
-        devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_WIRED_HEADSET
-      }
-    }
-  }
-  r_submix {
-    outputs {
-      submix {
-        sampling_rates 48000
-        channel_masks AUDIO_CHANNEL_OUT_STEREO
-        formats AUDIO_FORMAT_PCM_16_BIT
-        devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX
-      }
-    }
-    inputs {
-      submix {
-        sampling_rates 48000
-        channel_masks AUDIO_CHANNEL_IN_STEREO
-        formats AUDIO_FORMAT_PCM_16_BIT
-        devices AUDIO_DEVICE_IN_REMOTE_SUBMIX
-      }
-    }
-  }
-}
diff --git a/manifest.xml b/manifest.xml
index b54e735..8d8d572 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -60,15 +60,6 @@
         </interface>
     </hal>
     <hal format="hidl">
-        <name>android.hardware.audio</name>
-        <transport>hwbinder</transport>
-        <version>6.0</version>
-        <interface>
-            <name>IDevicesFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl">
         <name>android.hardware.graphics.allocator</name>
         <transport>hwbinder</transport>
         <version>3.0</version>
diff --git a/vendor.mk b/vendor.mk
index 0a7dc2f..9a67b78 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -40,7 +40,6 @@
     libgoldfish-rild \
     libril-goldfish-fork \
     qemu-props \
-    audio.primary.goldfish \
     stagefright \
     fingerprint.ranchu \
     android.hardware.graphics.composer@2.3-impl \
@@ -75,7 +74,7 @@
 PRODUCT_PACKAGES += \
     audio.r_submix.default \
     android.hardware.audio.service \
-    android.hardware.audio@6.0-impl:32 \
+    android.hardware.audio@6.0-impl.ranchu \
     android.hardware.audio.effect@6.0-impl:32
 
 PRODUCT_PACKAGES += \
@@ -286,13 +285,10 @@
     frameworks/native/data/etc/android.software.autofill.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.autofill.xml \
     frameworks/native/data/etc/android.software.verified_boot.xml:${TARGET_COPY_OUT_PRODUCT}/etc/permissions/android.software.verified_boot.xml \
     frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
-    device/generic/goldfish/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf \
-    frameworks/av/services/audiopolicy/config/audio_policy_configuration_generic.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
-    frameworks/av/services/audiopolicy/config/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \
-    frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \
+    device/generic/goldfish/audio/policy/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+    device/generic/goldfish/audio/policy/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \
     frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \
     frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
-    frameworks/av/services/audiopolicy/config/surround_sound_configuration_5_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/surround_sound_configuration_5_0.xml \
     device/generic/goldfish/data/etc/permissions/privapp-permissions-goldfish.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/permissions/privapp-permissions-goldfish.xml \
     hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_back.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_back.json \
     hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_front.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_front.json \