Snap for 6838321 from ea8b73ca844ab32b24a53e3f474bafcb43023f32 to mainline-release

Change-Id: I22f75c461e7bd7d47439e459001c8d0303cc1fb0
diff --git a/aosp_trout_arm64.mk b/aosp_trout_arm64.mk
index a24db1d..65688d9 100644
--- a/aosp_trout_arm64.mk
+++ b/aosp_trout_arm64.mk
@@ -16,6 +16,12 @@
 
 $(call inherit-product, device/google/cuttlefish/vsoc_arm64/auto/aosp_cf.mk)
 
+# Audio Control HAL
+# TODO (chenhaosjtuacm, egranata): move them to kernel command line
+LOCAL_AUDIOCONTROL_PROPERTIES ?= \
+    ro.vendor.audiocontrol.server.cid=1000 \
+    ro.vendor.audiocontrol.server.port=9410 \
+
 include device/google/trout/aosp_trout_common.mk
 
 DEVICE_MANIFEST_FILE += device/google/trout/manifest.xml
@@ -29,15 +35,10 @@
 
 PRODUCT_COPY_FILES += \
     device/google/trout/product_files/odm/ueventd.rc:$(TARGET_COPY_OUT_ODM)/ueventd.rc \
-
-PRODUCT_COPY_FILES += \
-    device/google/trout/product_files/vendor/etc/input-port-associations.xml:$(TARGET_COPY_OUT_VENDOR)/etc/input-port-associations.xml \
+    device/google/trout/product_files/odm/usr/idc/Vendor_0fff_Product_0fff.idc:$(TARGET_COPY_OUT_ODM)/usr/idc/Vendor_0fff_Product_0fff.idc \
 
 PRODUCT_COPY_FILES += device/google/trout/product_files/vendor/etc/sensors/hals.conf:$(TARGET_COPY_OUT_VENDOR)/etc/sensors/hals.conf
 
-BOARD_VENDOR_KERNEL_MODULES += \
-    $(wildcard device/google/trout-kernel/5.4-arm64/*.ko) \
-
 PRODUCT_NAME := aosp_trout_arm64
 PRODUCT_DEVICE := vsoc_arm64
 PRODUCT_MODEL := arm64 trout
diff --git a/aosp_trout_common.mk b/aosp_trout_common.mk
index c4e5b50..ae7ec53 100644
--- a/aosp_trout_common.mk
+++ b/aosp_trout_common.mk
@@ -14,6 +14,16 @@
 # limitations under the License.
 #
 
+# Audio HAL
+LOCAL_AUDIO_PRODUCT_PACKAGE ?= \
+    audio.primary.trout \
+    audio.r_submix.default \
+    android.hardware.audio@6.0-impl:32 \
+    android.hardware.audio.effect@6.0-impl:32 \
+    android.hardware.audio.service \
+    android.hardware.soundtrigger@2.3-impl
+LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS ?= device/google/trout/hal/audio/6.0/overlay
+
 # Audio Control HAL
 LOCAL_AUDIOCONTROL_HAL_PRODUCT_PACKAGE ?= android.hardware.audiocontrol@2.0-service.trout
 
@@ -34,8 +44,17 @@
 
 PRODUCT_PROPERTY_OVERRIDES += \
     ro.hardware.type=automotive \
+    ${LOCAL_AUDIOCONTROL_PROPERTIES} \
     ${LOCAL_DUMPSTATE_PROPERTIES}
 
 TARGET_BOARD_INFO_FILE ?= device/google/trout/board-info.txt
 
-include packages/services/Car/computepipe/products/computepipe.mk
+LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \
+    device/google/trout/hal/audio/6.0/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+    device/google/trout/hal/audio/6.0/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml \
+    frameworks/native/data/etc/android.hardware.broadcastradio.xml:system/etc/permissions/android.hardware.broadcastradio.xml \
+    frameworks/av/services/audiopolicy/config/a2dp_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_audio_policy_configuration.xml \
+    frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/usb_audio_policy_configuration.xml
+
+# TODO(b/162901005): Include computepipe once this project points to main.
+# include packages/services/Car/cpp/computepipe/products/computepipe.mk
diff --git a/aosp_trout_x86.mk b/aosp_trout_x86.mk
index 59fb9d9..28bf246 100644
--- a/aosp_trout_x86.mk
+++ b/aosp_trout_x86.mk
@@ -16,14 +16,21 @@
 
 $(call inherit-product, device/google/cuttlefish/vsoc_x86/auto/device.mk)
 
+# Audio Control HAL
+# TODO (chenhaosjtuacm, egranata): move them to kernel command line
+LOCAL_AUDIOCONTROL_PROPERTIES ?= \
+    ro.vendor.audiocontrol.server.cid=3 \
+    ro.vendor.audiocontrol.server.port=9410 \
+
 include device/google/trout/aosp_trout_common.mk
 
 DEVICE_MANIFEST_FILE += device/google/trout/manifest_x86.xml
 DEVICE_MATRIX_FILE += device/google/trout/compatibility_matrix.xml
 DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE := device/google/trout/framework_compatibility_matrix.xml
 
-PRODUCT_COPY_FILES += \
-    packages/services/Car/computepipe/products/init.computepipe.rc:$(TARGET_COPY_OUT_SYSTEM)/etc/init/computepipe.rc
+# TODO(b/162901005): Include computepipe once this project points to main.
+# PRODUCT_COPY_FILES += \
+#    packages/services/Car/cpp/computepipe/products/init.computepipe.rc:$(TARGET_COPY_OUT_SYSTEM)/etc/init/computepipe.rc
 
 PRODUCT_NAME := aosp_trout_x86
 PRODUCT_DEVICE := vsoc_x86
diff --git a/hal/audio/6.0/Android.bp b/hal/audio/6.0/Android.bp
new file mode 100644
index 0000000..1847f6f
--- /dev/null
+++ b/hal/audio/6.0/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2017 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.
+
+// Derived from device/generic/goldfish/audio/Android.mk
+
+cc_library_shared {
+
+    vendor: true,
+    // vintf_fragments: ["android.hardware.audio@6.0-trout-impl.xml"],
+    name: "audio.primary.trout",
+    relative_install_path: "hw",
+
+    srcs: [
+        "audio_hw.c",
+        "audio_vbuffer.c",
+        "ext_pcm.c",
+    ],
+
+    include_dirs: ["external/tinyalsa/include"],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libdl",
+        "libtinyalsa",
+    ],
+
+    cflags: ["-Wno-unused-parameter"],
+    header_libs: [
+        "libhardware_headers",
+        "libcutils_headers",
+    ],
+
+}
diff --git a/hal/audio/6.0/android.hardware.audio@6.0-trout-impl.xml b/hal/audio/6.0/android.hardware.audio@6.0-trout-impl.xml
new file mode 100644
index 0000000..2814739
--- /dev/null
+++ b/hal/audio/6.0/android.hardware.audio@6.0-trout-impl.xml
@@ -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.
+-->
+<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>
\ No newline at end of file
diff --git a/hal/audio/6.0/audio_hw.c b/hal/audio/6.0/audio_hw.c
new file mode 100644
index 0000000..bc27e90
--- /dev/null
+++ b/hal/audio/6.0/audio_hw.c
@@ -0,0 +1,1581 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Derived from goldfish/audio/audio_hw.c
+ * Changes made to adding support of AUDIO_DEVICE_OUT_BUS
+ */
+
+#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 <dlfcn.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 PI 3.14159265
+#define TWO_PI  (2*PI)
+
+// 150 Hz
+#define DEFAULT_FREQUENCY 150
+// Increase in changes to tone frequency
+#define TONE_FREQUENCY_INCREASE 20
+// Max tone frequency to auto assign, don't want to generate too high of a pitch
+#define MAX_TONE_FREQUENCY 500
+
+#define _bool_str(x) ((x)?"true":"false")
+
+static const char * const PROP_KEY_SIMULATE_MULTI_ZONE_AUDIO = "ro.aae.simulateMultiZoneAudio";
+static const char * const AAE_PARAMETER_KEY_FOR_SELECTED_ZONE = "com.android.car.emulator.selected_zone";
+#define PRIMARY_ZONE_ID 0
+#define INVALID_ZONE_ID -1
+// Note the primary zone goes to left speaker so route other zone to right speaker
+#define DEFAULT_ZONE_TO_LEFT_SPEAKER (PRIMARY_ZONE_ID + 1)
+
+static const char * const TONE_ADDRESS_KEYWORD = "_tone_";
+static const char * const AUDIO_ZONE_KEYWORD = "_audio_zone_";
+
+#define SIZE_OF_PARSE_BUFFER 32
+
+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 int get_int_value(const struct str_parms *str_parms, const char *key, int *return_value) {
+    char value[SIZE_OF_PARSE_BUFFER];
+    int results = str_parms_get_str(str_parms, key, value, SIZE_OF_PARSE_BUFFER);
+    if (results >= 0) {
+        char *end = NULL;
+        errno = 0;
+        long val = strtol(value, &end, 10);
+        if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int) val == val)) {
+            *return_value = val;
+        } else {
+            results = -EINVAL;
+        }
+    }
+    return results;
+}
+
+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 bool is_zone_selected_to_play(struct audio_hw_device *dev, int zone_id) {
+    // play if current zone is enable or zone equal to primary zone
+    bool is_selected_zone = true;
+    if (zone_id != PRIMARY_ZONE_ID) {
+        struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+        pthread_mutex_lock(&adev->lock);
+        is_selected_zone = adev->last_zone_selected_to_play == zone_id;
+        pthread_mutex_unlock(&adev->lock);
+    }
+    return is_selected_zone;
+}
+
+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;
+    int ret = 0;
+
+    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);
+        int val = 0;
+        ret = get_int_value(parms, AUDIO_PARAMETER_STREAM_ROUTING, &val);
+        if (ret >= 0) {
+            out->device = (int)val;
+            ret = 0;
+        }
+        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 int get_zone_id_from_address(const char *address) {
+    int zone_id = INVALID_ZONE_ID;
+    char *zone_start = strstr(address, AUDIO_ZONE_KEYWORD);
+    if (zone_start) {
+        char *end = NULL;
+        zone_id = strtol(zone_start + strlen(AUDIO_ZONE_KEYWORD), &end, 10);
+        if (end == NULL || zone_id < 0) {
+            return INVALID_ZONE_ID;
+        }
+    }
+    return zone_id;
+}
+
+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;
+    int zone_id = PRIMARY_ZONE_ID;
+    // If it is a audio zone keyword bus address then get zone id
+    if (strstr(out->bus_address, AUDIO_ZONE_KEYWORD)) {
+        zone_id = get_zone_id_from_address(out->bus_address);
+        if (zone_id == INVALID_ZONE_ID) {
+            ALOGE("%s Found invalid zone id, defaulting device %s to zone %d", __func__,
+                out->bus_address, DEFAULT_ZONE_TO_LEFT_SPEAKER);
+            zone_id = DEFAULT_ZONE_TO_LEFT_SPEAKER;
+        }
+    }
+    ALOGD("Out worker:%s zone id %d", out->bus_address, zone_id);
+
+    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);
+
+        if (is_zone_selected_to_play(out->dev, zone_id)) {
+            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("%s Hardware backing HAL too slow, could only write %zu of %zu frames",
+            __func__, 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;
+    int ret = 0;
+
+    pthread_mutex_lock(&in->lock);
+    if (!in->standby) {
+        ret = -ENOSYS;
+    } else {
+        parms = str_parms_create_str(kvpairs);
+        int val = 0;
+        ret = get_int_value(parms, AUDIO_PARAMETER_STREAM_ROUTING, &val);
+        if (ret >= 0) {
+            in->device = (int)val;
+            ret = 0;
+        }
+
+        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;
+}
+
+// Generates pure tone for FM_TUNER and bus_device
+static int pseudo_pcm_read(void *data, unsigned int count, struct oscillator *oscillator) {
+    unsigned int length = count / sizeof(int16_t);
+    int16_t *sdata = (int16_t *)data;
+    for (int index = 0; index < length; index++) {
+        sdata[index] = (int16_t)(sin(oscillator->phase) * 4096);
+        oscillator->phase += oscillator->phase_increment;
+        oscillator->phase = oscillator->phase > TWO_PI ?
+            oscillator->phase - TWO_PI : oscillator->phase;
+    }
+
+    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 bool address_has_tone_keyword(char * address) {
+    return strstr(address, TONE_ADDRESS_KEYWORD) != NULL;
+}
+
+static bool is_tone_generator_device(struct generic_stream_in *in) {
+    return in->device == AUDIO_DEVICE_IN_FM_TUNER || ((in->device == AUDIO_DEVICE_IN_BUS) &&
+        address_has_tone_keyword(in->bus_address));
+}
+
+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;
+    }
+
+    // Tone generators fill the buffer via pseudo_pcm_read directly
+    if (!is_tone_generator_device(in)) {
+        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 (is_tone_generator_device(in)) {
+        int read_bytes = pseudo_pcm_read(buffer, bytes, &in->oscillator);
+        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, AUDIO_ZONE_KEYWORD)
+                ? RIGHT_CHANNEL: LEFT_CHANNEL;
+            ALOGD("%s Routing %s to %s channel", __func__,
+             out->bus_address, out->enabled_channels == RIGHT_CHANNEL ? "Right" : "Left");
+        }
+    }
+    *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) {
+    struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+    pthread_mutex_lock(&adev->lock);
+    struct str_parms *parms = str_parms_create_str(kvpairs);
+    int value = 0;
+    int results = get_int_value(parms, AAE_PARAMETER_KEY_FOR_SELECTED_ZONE, &value);
+    if (results >= 0) {
+        adev->last_zone_selected_to_play = value;
+        results = 0;
+        ALOGD("%s Changed play zone id to %d", __func__, adev->last_zone_selected_to_play);
+    }
+    str_parms_destroy(parms);
+    pthread_mutex_unlock(&adev->lock);
+    return results;
+}
+
+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 void increase_next_tone_frequency(struct generic_audio_device *adev) {
+    adev->next_tone_frequency_to_assign += TONE_FREQUENCY_INCREASE;
+    if (adev->next_tone_frequency_to_assign > MAX_TONE_FREQUENCY) {
+        adev->next_tone_frequency_to_assign = DEFAULT_FREQUENCY;
+    }
+}
+
+static int create_or_fetch_tone_frequency(struct generic_audio_device *adev,
+        char *address, int update_frequency) {
+    int *frequency = hashmapGet(adev->in_bus_tone_frequency_map, address);
+    if (frequency == NULL) {
+        frequency = calloc(1, sizeof(int));
+        *frequency = update_frequency;
+        hashmapPut(adev->in_bus_tone_frequency_map, strdup(address), frequency);
+        ALOGD("%s assigned frequency %d to %s", __func__, *frequency, address);
+    }
+    return *frequency;
+}
+
+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 = strdup(address);
+        if (is_tone_generator_device(in)) {
+            int update_frequency = adev->next_tone_frequency_to_assign;
+            int frequency = create_or_fetch_tone_frequency(adev, address, update_frequency);
+            if (update_frequency == frequency) {
+                increase_next_tone_frequency(adev);
+            }
+            in->oscillator.phase = 0.0f;
+            in->oscillator.phase_increment = (TWO_PI*(frequency))
+                / ((float) in_get_sample_rate(&in->stream.common));
+        }
+    }
+
+    *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);
+        }
+        if (adev->in_bus_tone_frequency_map) {
+            hashmapFree(adev->in_bus_tone_frequency_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);
+
+    ALOGD("%s Mixer name %s", __func__, mixer_get_name(adev->mixer));
+    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);
+
+    // Initialize the bus address to input stream map
+    adev->in_bus_tone_frequency_map = hashmapCreate(5, str_hash_fn, str_eq);
+
+    adev->next_tone_frequency_to_assign = DEFAULT_FREQUENCY;
+
+    adev->last_zone_selected_to_play = DEFAULT_ZONE_TO_LEFT_SPEAKER;
+
+    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 car audio HW HAL",
+        .author = "The Android Open Source Project",
+        .methods = &hal_module_methods,
+    },
+};
diff --git a/hal/audio/6.0/audio_hw.h b/hal/audio/6.0/audio_hw.h
new file mode 100644
index 0000000..d195a53
--- /dev/null
+++ b/hal/audio/6.0/audio_hw.h
@@ -0,0 +1,111 @@
+/*
+ * 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
+  Hashmap *in_bus_tone_frequency_map;  // Extended field. Constant after init
+  int next_tone_frequency_to_assign; // Protected by this->lock
+  // Play on Speaker zone selection
+  int last_zone_selected_to_play; // Protected by this->lock
+};
+
+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 oscillator {
+    float phase;
+    float phase_increment;
+};
+
+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
+
+   // Tone Oscillator
+   struct oscillator oscillator; // Protected by this->lock
+};
+
+#endif  // AUDIO_HW_H
diff --git a/hal/audio/6.0/audio_policy_configuration.xml b/hal/audio/6.0/audio_policy_configuration.xml
new file mode 100644
index 0000000..ed0f4a6
--- /dev/null
+++ b/hal/audio/6.0/audio_policy_configuration.xml
@@ -0,0 +1,330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<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="true"/>
+
+    <!-- Modules section:
+        There is one section per audio HW module present on the platform.
+        Each module section will contains two mandatory tags for audio HAL “halVersion” and “name”.
+        The module names are the same as in current .conf file:
+                “primary”, “A2DP”, “remote_submix”, “USB”
+        Each module will contain the following sections:
+        “devicePorts”: a list of device descriptors for all input and output devices accessible via this
+        module.
+        This contains both permanently attached devices and removable devices.
+            "gain": constraints applied to the millibel values:
+                - maxValueMB >= minValueMB
+                - defaultValueMB >= minValueMB && defaultValueMB <= maxValueMB
+                - (maxValueMB - minValueMB) % stepValueMB == 0
+                - (defaultValueMB - minValueMB) % stepValueMB == 0
+        “mixPorts”: listing all output and input streams exposed by the audio HAL
+        “routes”: list of possible connections between input and output devices or between stream and
+        devices.
+            "route": is defined by an attribute:
+                -"type": <mux|mix> means all sources are mutual exclusive (mux) or can be mixed (mix)
+                -"sink": the sink involved in this route
+                -"sources": all the sources than can be connected to the sink via vis route
+        “attachedDevices”: permanently attached devices.
+        The attachedDevices section is a list of devices names. The names correspond to device names
+        defined in <devicePorts> section.
+        “defaultOutputDevice”: device to be used by default when no policy rule applies
+    -->
+    <modules>
+        <!-- Primary Audio HAL -->
+        <module name="primary" halVersion="3.0">
+            <attachedDevices>
+                <!-- One bus per context -->
+                <item>bus0_media_out</item>
+                <item>bus1_navigation_out</item>
+                <item>bus2_voice_command_out</item>
+                <item>bus3_call_ring_out</item>
+                <item>bus4_call_out</item>
+                <item>bus5_alarm_out</item>
+                <item>bus6_notification_out</item>
+                <item>bus7_system_sound_out</item>
+                <!-- names with _audio_zone_# are used for defined an emulator rear seat audio zone
+                    where each number # is the zone id number -->
+                <item>bus100_audio_zone_1</item>
+                <item>bus200_audio_zone_2</item>
+                <item>Built-In Mic</item>
+                <item>Built-In Back Mic</item>
+                <item>Echo-Reference Mic</item>
+                <item>FM Tuner</item>
+                <item>Tone Generator 0</item>
+                <item>Tone Generator 1</item>
+            </attachedDevices>
+            <defaultOutputDevice>bus0_media_out</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="mixport_bus0_media_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus1_navigation_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus2_voice_command_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus3_call_ring_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus4_call_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus5_alarm_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus6_notification_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus7_system_sound_out" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus100_audio_zone_1" role="source"
+                        flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_bus200_audio_zone_2" role="source"
+                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </mixPort>
+                <mixPort name="mixport_tuner0" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_input_bus_tone_zone_0" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+                <mixPort name="mixport_input_bus_tone_zone_1" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000"
+                             channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus0_media_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus1_navigation_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus1_navigation_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus2_voice_command_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus2_voice_command_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus3_call_ring_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus3_call_ring_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus4_call_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus4_call_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus5_alarm_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus5_alarm_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus6_notification_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus6_notification_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus7_system_sound_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus7_system_sound_out">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus100_audio_zone_1" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                        address="bus100_audio_zone_1">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="bus200_audio_zone_2" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+                            address="bus200_audio_zone_2">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                            channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+                <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                            channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+                <devicePort tagName="Echo-Reference Mic" type="AUDIO_DEVICE_IN_ECHO_REFERENCE" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                            channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+                <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source"
+                        address="tuner0">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                            samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                                minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Tone Generator 0" type="AUDIO_DEVICE_IN_BUS" role="source"
+                            address="input_bus_tone_zone_0">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Tone Generator 1" type="AUDIO_DEVICE_IN_BUS" role="source"
+                            address="input_bus_tone_zone_1">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+                    <gains>
+                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+            </devicePorts>
+            <!-- route declaration, i.e. list all available sources for a given sink -->
+            <routes>
+                <route type="mix" sink="bus0_media_out" sources="mixport_bus0_media_out"/>
+                <route type="mix" sink="bus1_navigation_out" sources="mixport_bus1_navigation_out"/>
+                <route type="mix" sink="bus2_voice_command_out" sources="mixport_bus2_voice_command_out"/>
+                <route type="mix" sink="bus3_call_ring_out" sources="mixport_bus3_call_ring_out"/>
+                <route type="mix" sink="bus4_call_out" sources="mixport_bus4_call_out"/>
+                <route type="mix" sink="bus5_alarm_out" sources="mixport_bus5_alarm_out"/>
+                <route type="mix" sink="bus6_notification_out" sources="mixport_bus6_notification_out"/>
+                <route type="mix" sink="bus7_system_sound_out" sources="mixport_bus7_system_sound_out"/>
+                <route type="mix" sink="bus100_audio_zone_1" sources="mixport_bus100_audio_zone_1"/>
+                <route type="mix" sink="bus200_audio_zone_2" sources="mixport_bus200_audio_zone_2"/>
+                <route type="mix" sink="primary input" sources="Built-In Mic,Built-In Back Mic,Echo-Reference Mic"/>
+                <route type="mix" sink="mixport_tuner0" sources="FM Tuner"/>
+                <route type="mix" sink="mixport_input_bus_tone_zone_0" sources="Tone Generator 0"/>
+                <route type="mix" sink="mixport_input_bus_tone_zone_1" sources="Tone Generator 1"/>
+            </routes>
+
+        </module>
+
+        <!-- A2dp Audio HAL -->
+        <xi:include href="a2dp_audio_policy_configuration.xml"/>
+
+        <!-- Usb Audio HAL -->
+        <xi:include href="usb_audio_policy_configuration.xml"/>
+
+        <!-- Remote Submix Audio HAL -->
+        <xi:include href="r_submix_audio_policy_configuration.xml"/>
+
+    </modules>
+    <!-- End of Modules section -->
+
+    <!-- Volume section -->
+
+    <xi:include href="audio_policy_volumes.xml"/>
+    <xi:include href="default_volume_tables.xml"/>
+
+    <!-- End of Volume section -->
+    <!-- End of Modules section -->
+
+</audioPolicyConfiguration>
diff --git a/hal/audio/6.0/audio_vbuffer.c b/hal/audio/6.0/audio_vbuffer.c
new file mode 100644
index 0000000..79be545
--- /dev/null
+++ b/hal/audio/6.0/audio_vbuffer.c
@@ -0,0 +1,137 @@
+/*
+ * 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/hal/audio/6.0/audio_vbuffer.h b/hal/audio/6.0/audio_vbuffer.h
new file mode 100644
index 0000000..24bf986
--- /dev/null
+++ b/hal/audio/6.0/audio_vbuffer.h
@@ -0,0 +1,47 @@
+/*
+ * 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/hal/audio/6.0/car_audio_configuration.xml b/hal/audio/6.0/car_audio_configuration.xml
new file mode 100644
index 0000000..4a8f372
--- /dev/null
+++ b/hal/audio/6.0/car_audio_configuration.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+  Defines the audio configuration in a car, including
+    - Audio zones
+    - Context to audio bus mappings
+    - Volume groups
+  in the car environment.
+-->
+
+<!-- TODO (chenhaosjtuacm):
+    Copied from device/generic/car/emulator/audio/car_audio_configuration.xml
+    Needs to be updated.
+-->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" occupantZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                    <device address="bus6_notification_out">
+                        <context context="notification"/>
+                    </device>
+                    <device address="bus7_system_sound_out">
+                        <context context="system_sound"/>
+                        <context context="emergency"/>
+                        <context context="safety"/>
+                        <context context="vehicle_status"/>
+                        <context context="announcement"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                    <device address="bus2_voice_command_out">
+                        <context context="voice_command"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus4_call_out">
+                        <context context="call"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus5_alarm_out">
+                        <context context="alarm"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone 1" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_audio_zone_1">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                        <context context="emergency"/>
+                        <context context="safety"/>
+                        <context context="vehicle_status"/>
+                        <context context="announcement"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone 2"  audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus200_audio_zone_2">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                        <context context="emergency"/>
+                        <context context="safety"/>
+                        <context context="vehicle_status"/>
+                        <context context="announcement"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/hal/audio/6.0/ext_pcm.c b/hal/audio/6.0/ext_pcm.c
new file mode 100644
index 0000000..b1519f5
--- /dev/null
+++ b/hal/audio/6.0/ext_pcm.c
@@ -0,0 +1,195 @@
+/*
+ * 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;
+  pipeline_out->position = MAX(pipeline_out->position, pipeline_in->position);
+  for (int i = 0; i < pipeline_out->position; i++) {
+    float mixed = pipeline_out->buffer[i] + pipeline_in->buffer[i];
+    if (mixed > INT16_MAX) pipeline_out->buffer[i] = INT16_MAX;
+    else if (mixed < INT16_MIN) pipeline_out->buffer[i] = INT16_MIN;
+    else pipeline_out->buffer[i] = (int16_t)mixed;
+  }
+  memset(pipeline_in, 0, sizeof(struct ext_mixer_pipeline));
+  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));
+    }
+    memset(&ext_pcm->mixer_pipeline, 0, sizeof(struct ext_mixer_pipeline));
+    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/hal/audio/6.0/ext_pcm.h b/hal/audio/6.0/ext_pcm.h
new file mode 100644
index 0000000..0bc8df5
--- /dev/null
+++ b/hal/audio/6.0/ext_pcm.h
@@ -0,0 +1,53 @@
+/*
+ * 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/hal/audio/6.0/overlay/frameworks/base/core/res/res/values/config.xml b/hal/audio/6.0/overlay/frameworks/base/core/res/res/values/config.xml
new file mode 100644
index 0000000..b256c8b
--- /dev/null
+++ b/hal/audio/6.0/overlay/frameworks/base/core/res/res/values/config.xml
@@ -0,0 +1,8 @@
+<resources>
+    <!-- Car uses hardware amplifier for volume. -->
+    <bool name="config_useFixedVolume">true</bool>
+    <!--
+      Handle volume keys directly in CarAudioService without passing them to the foreground app
+    -->
+    <bool name="config_handleVolumeKeysInWindowManager">true</bool>
+</resources>
diff --git a/hal/audio/6.0/overlay/packages/services/Car/service/res/values/config.xml b/hal/audio/6.0/overlay/packages/services/Car/service/res/values/config.xml
new file mode 100644
index 0000000..28857b3
--- /dev/null
+++ b/hal/audio/6.0/overlay/packages/services/Car/service/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!--
+  Overlay resources to configure car service based on each OEM's preference.
+  See also packages/services/Car/service/res/values/config.xml
+-->
+<resources>
+    <bool name="audioUseDynamicRouting">true</bool>
+</resources>
diff --git a/hal/dumpstate/1.1/DumpstateDevice.cpp b/hal/dumpstate/1.1/DumpstateDevice.cpp
index 4eebc6a..5e419fe 100644
--- a/hal/dumpstate/1.1/DumpstateDevice.cpp
+++ b/hal/dumpstate/1.1/DumpstateDevice.cpp
@@ -119,14 +119,14 @@
     return true;
 }
 
-void DumpstateDevice::dumpHelperSystem(int textFd, int binFd) {
+bool DumpstateDevice::dumpHelperSystem(int textFd, int binFd) {
     std::string helperSystemLogDir =
             android::base::GetProperty(VENDOR_HELPER_SYSTEM_LOG_LOC_PROPERTY, "");
 
     if (helperSystemLogDir.empty()) {
         LOG(ERROR) << "Helper system log location '" << VENDOR_HELPER_SYSTEM_LOG_LOC_PROPERTY
                    << "' not set";
-        return;
+        return false;
     }
 
     std::error_code error;
@@ -135,14 +135,22 @@
     if (!fs::create_directories(helperSysLogPath, error)) {
         LOG(ERROR) << "Failed to create the dumping log directory " << helperSystemLogDir << ": "
                    << error;
-        return;
+        return false;
     }
 
     if (!fs::is_directory(helperSysLogPath)) {
         LOG(ERROR) << helperSystemLogDir << " is not a directory";
-        return;
+        return false;
     }
 
+    if (!isHealthy()) {
+        LOG(ERROR) << "Failed to connect to the dumpstate server";
+        return false;
+    }
+
+    // When start dumping, we always return success to keep dumped logs
+    // even if some of them are failed
+
     {
         // Dumping system logs
         ::grpc::ClientContext context;
@@ -170,6 +178,7 @@
         LOG(ERROR) << "Failed to clear the dumping log directory " << helperSystemLogDir << ": "
                    << error;
     }
+    return true;
 }
 
 bool DumpstateDevice::isHealthy() {
@@ -222,7 +231,9 @@
     const int textFd = handle->data[0];
     const int binFd = handle->numFds >= 2 ? handle->data[1] : -1;
 
-    dumpHelperSystem(textFd, binFd);
+    if (!dumpHelperSystem(textFd, binFd)) {
+        return DumpstateStatus::DEVICE_LOGGING_NOT_ENABLED;
+    }
 
     return DumpstateStatus::OK;
 }
@@ -236,6 +247,26 @@
     return android::base::GetBoolProperty(VENDOR_VERBOSE_LOGGING_ENABLED_PROPERTY, false);
 }
 
+Return<void> DumpstateDevice::debug(const hidl_handle& h, const hidl_vec<hidl_string>& options) {
+    if (h.getNativeHandle() == nullptr || h->numFds == 0) {
+        LOG(ERROR) << "Invalid FD passed to debug() function";
+        return Void();
+    }
+
+    const int fd = h->data[0];
+    auto pf = [fd](std::string s) -> void { dprintf(fd, "%s\n", s.c_str()); };
+    debugDumpServices(pf);
+
+    return Void();
+}
+
+void DumpstateDevice::debugDumpServices(std::function<void(std::string)> f) {
+    f("Available services for Dumpstate:");
+    for (const auto& svc : getAvailableServices()) {
+        f("  " + svc);
+    }
+}
+
 sp<DumpstateDevice> makeVirtualizationDumpstateDevice(const std::string& addr) {
     return new DumpstateDevice(addr);
 }
diff --git a/hal/dumpstate/1.1/DumpstateDevice.h b/hal/dumpstate/1.1/DumpstateDevice.h
index c199f9c..0b40a11 100644
--- a/hal/dumpstate/1.1/DumpstateDevice.h
+++ b/hal/dumpstate/1.1/DumpstateDevice.h
@@ -18,6 +18,7 @@
 #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
 
 #include <automotive/filesystem>
+#include <functional>
 
 #include <grpc++/grpc++.h>
 
@@ -43,11 +44,15 @@
 
     bool isHealthy();
 
+    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options);
+
   private:
     bool dumpRemoteLogs(::grpc::ClientReaderInterface<dumpstate_proto::DumpstateBuffer>* reader,
                         const fs::path& dumpPath);
 
-    void dumpHelperSystem(int textFd, int binFd);
+    bool dumpHelperSystem(int textFd, int binFd);
+
+    void debugDumpServices(std::function<void(std::string)> f);
 
     std::vector<std::string> getAvailableServices();
 
diff --git a/hal/dumpstate/1.1/service.cpp b/hal/dumpstate/1.1/service.cpp
index 09ead42..5c0f08a 100644
--- a/hal/dumpstate/1.1/service.cpp
+++ b/hal/dumpstate/1.1/service.cpp
@@ -60,7 +60,7 @@
 
     auto dumpstate = makeVirtualizationDumpstateDevice(si->str());
     // This method MUST be called before interacting with any HIDL interfaces.
-    configureRpcThreadpool(1, true);
+    configureRpcThreadpool(2, true);
     if (dumpstate->registerAsService() != OK) {
         ALOGE("Could not register service.");
         return 1;
diff --git a/hal/sensors/2.0/Sensor.cpp b/hal/sensors/2.0/Sensor.cpp
index a525754..b5288e2 100644
--- a/hal/sensors/2.0/Sensor.cpp
+++ b/hal/sensors/2.0/Sensor.cpp
@@ -122,30 +122,25 @@
 
 void HWSensorBase::processScanData(uint8_t* data, Event* evt) {
     float channelData[NUM_OF_CHANNEL_SUPPORTED - 1];
-    int64_t ts;
     unsigned int chanIdx;
     evt->sensorHandle = mSensorInfo.sensorHandle;
     evt->sensorType = mSensorInfo.type;
     for (auto i = 0u; i < miio_data.channelInfo.size(); i++) {
         chanIdx = miio_data.channelInfo[i].index;
-        if (miio_data.channelInfo[i].sign) {
-            int64_t val = *reinterpret_cast<int64_t*>(
-                    data + chanIdx * miio_data.channelInfo[i].storage_bytes);
-            if (chanIdx == (miio_data.channelInfo.size() - 1)) {
-                ts = val;
-            } else {
-                channelData[chanIdx] = (static_cast<float>(val) * miio_data.resolution);
-            }
+        const int64_t val = *reinterpret_cast<int64_t*>(
+                data + chanIdx * miio_data.channelInfo[i].storage_bytes);
+
+        // If the channel index is the last, it is timestamp
+        // else it is sensor data
+        if (chanIdx == miio_data.channelInfo.size() - 1) {
+            evt->timestamp = val;
         } else {
-            uint64_t val = *reinterpret_cast<uint64_t*>(
-                    data + chanIdx * miio_data.channelInfo[i].storage_bytes);
             channelData[chanIdx] = (static_cast<float>(val) * miio_data.resolution);
         }
     }
     evt->u.vec3.x = channelData[0];
     evt->u.vec3.y = channelData[1];
     evt->u.vec3.z = channelData[2];
-    evt->timestamp = ts;
     evt->u.vec3.status = SensorStatus::ACCURACY_HIGH;
 }
 
diff --git a/product_files/odm/usr/idc/Vendor_0fff_Product_0fff.idc b/product_files/odm/usr/idc/Vendor_0fff_Product_0fff.idc
new file mode 100644
index 0000000..8e9d4d6
--- /dev/null
+++ b/product_files/odm/usr/idc/Vendor_0fff_Product_0fff.idc
@@ -0,0 +1 @@
+device.internal = 1
diff --git a/product_files/vendor/etc/input-port-associations.xml b/product_files/vendor/etc/input-port-associations.xml
deleted file mode 100644
index ed849a1..0000000
--- a/product_files/vendor/etc/input-port-associations.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<ports>
-    <port display="0" input="virtio0/input0"/>
-    <port display="0" input="virtio1/input0"/>
-</ports>
diff --git a/tests/hal/dumpstate/1.1/Android.bp b/tests/hal/dumpstate/1.1/Android.bp
new file mode 100644
index 0000000..7142fb2
--- /dev/null
+++ b/tests/hal/dumpstate/1.1/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+android_test {
+    name: "TroutDumpstateTest",
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+    static_libs: [
+        "android.hardware.dumpstate-V1.1-java",
+        "android-support-test",
+        "apache-commons-compress",
+        "hwbinder",
+    ],
+    certificate: "platform",
+    test_suites: ["device-tests"],
+}
diff --git a/tests/hal/dumpstate/1.1/AndroidManifest.xml b/tests/hal/dumpstate/1.1/AndroidManifest.xml
new file mode 100644
index 0000000..5f97988
--- /dev/null
+++ b/tests/hal/dumpstate/1.1/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.trout.dumpstate.tests" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.trout.dumpstate.tests" />
+</manifest>
diff --git a/tests/hal/dumpstate/1.1/src/com/android/trout/dumpstate/tests/DumpstateE2eTests.java b/tests/hal/dumpstate/1.1/src/com/android/trout/dumpstate/tests/DumpstateE2eTests.java
new file mode 100644
index 0000000..bbe2f9c
--- /dev/null
+++ b/tests/hal/dumpstate/1.1/src/com/android/trout/dumpstate/tests/DumpstateE2eTests.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+package com.android.trout.dumpstate.tests;
+
+import android.hardware.dumpstate.V1_1.DumpstateMode;
+import android.hardware.dumpstate.V1_1.DumpstateStatus;
+import android.hardware.dumpstate.V1_1.IDumpstateDevice;
+import android.os.NativeHandle;
+import android.support.test.filters.MediumTest;
+import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests used to validate Trout dumpstate E2E functionality.
+ */
+@RunWith(JUnit4.class)
+public class DumpstateE2eTests {
+    private static final String TAG = DumpstateE2eTests.class.getSimpleName();
+    private IDumpstateDevice mDevice;
+
+    @Rule public TemporaryFolder mTempFolder = new TemporaryFolder();
+
+    @Before
+    public void setUp() throws Exception {
+        mDevice = android.hardware.dumpstate.V1_1.IDumpstateDevice.getService(true /* retry */);
+    }
+
+    @Test
+    @MediumTest
+    public void testDumpstateBoard() throws Exception {
+        File dumpstate_board_txt_file = mTempFolder.newFile();
+        File dumpstate_board_bin_file = mTempFolder.newFile();
+
+        try (FileOutputStream dumpstate_board_txt_ostream =
+                        new FileOutputStream(dumpstate_board_txt_file.getPath());
+                FileOutputStream dumpstate_board_bin_ostream =
+                        new FileOutputStream(dumpstate_board_bin_file.getPath());) {
+            NativeHandle handle = new NativeHandle(
+                    new FileDescriptor[] {
+                            dumpstate_board_txt_ostream.getFD(),
+                            dumpstate_board_bin_ostream.getFD(),
+                    },
+                    new int[0], false);
+            int dumping_status = mDevice.dumpstateBoard_1_1(
+                    handle, DumpstateMode.DEFAULT, 10 * 1000 /* milliseconds */);
+            Assert.assertEquals(DumpstateStatus.OK, dumping_status);
+        }
+
+        Assert.assertNotEquals(0, dumpstate_board_txt_file.length());
+        Assert.assertNotEquals(0, dumpstate_board_bin_file.length());
+
+        try (FileInputStream dumpstate_board_tar_istream =
+                        new FileInputStream(dumpstate_board_bin_file.getPath());
+                TarArchiveInputStream tar_stream =
+                        (TarArchiveInputStream) new ArchiveStreamFactory().createArchiveInputStream(
+                                "tar", dumpstate_board_tar_istream);) {
+            // Traversing the tar package to make sure it is valid
+            TarArchiveEntry tar_entry = null;
+            while ((tar_entry = (TarArchiveEntry) tar_stream.getNextEntry()) != null) {
+                Log.d(TAG, tar_entry.getName());
+            }
+        }
+    }
+}