/*
 * Copyright (C) 2012  The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "audio_hw_generic"
#define LOG_NDEBUG 0

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cutils/log.h>
#include <cutils/str_parms.h>

#include <hardware/audio.h>
#include <hardware/hardware.h>
#include <system/audio.h>

#include "anbox/audio/client_info.h"

#define PCM_CARD 0
#define PCM_DEVICE 0

#define OUT_PERIOD_MS 15
#define OUT_PERIOD_COUNT 4

#define IN_PERIOD_MS 10
#define IN_PERIOD_COUNT 4


#define AUDIO_DEVICE_NAME "/dev/anbox_audio"
#define OUT_SAMPLING_RATE 44100
#define OUT_BUFFER_SIZE 4096
#define OUT_LATENCY_MS 20
#define IN_SAMPLING_RATE 8000
#define IN_BUFFER_SIZE 320

#if defined(__cplusplus)
extern "C" {
#endif



/* Bit formats */
enum pcm_format {
    PCM_FORMAT_INVALID = -1,
    PCM_FORMAT_S16_LE = 0,  /* 16-bit signed */
    PCM_FORMAT_S32_LE,      /* 32-bit signed */
    PCM_FORMAT_S8,          /* 8-bit signed */
    PCM_FORMAT_S24_LE,      /* 24-bits in 4-bytes */
    PCM_FORMAT_S24_3LE,     /* 24-bits in 3-bytes */

    PCM_FORMAT_MAX,
};

/* Bitmask has 256 bits (32 bytes) in asound.h */
struct pcm_mask {
    unsigned int bits[32 / sizeof(unsigned int)];
};



/* Configuration for a stream */
struct pcm_config {
    unsigned int channels;
    unsigned int rate;
    unsigned int period_size;
    unsigned int period_count;
    enum pcm_format format;

    /* Values to use for the ALSA start, stop and silence thresholds, and
     * silence size.  Setting any one of these values to 0 will cause the
     * default tinyalsa values to be used instead.
     * Tinyalsa defaults are as follows.
     *
     * start_threshold   : period_count * period_size
     * stop_threshold    : period_count * period_size
     * silence_threshold : 0
     * silence_size      : 0
     */
    unsigned int start_threshold;
    unsigned int stop_threshold;
    unsigned int silence_threshold;
    unsigned int silence_size;

    /* Minimum number of frames available before pcm_mmap_write() will actually
     * write into the kernel buffer. Only used if the stream is opened in mmap mode
     * (pcm_open() called with PCM_MMAP flag set).   Use 0 for default.
     */
    int avail_min;
};

/* PCM parameters */
enum pcm_param {
    /* mask parameters */
    PCM_PARAM_ACCESS,
    PCM_PARAM_FORMAT,
    PCM_PARAM_SUBFORMAT,
    /* interval parameters */
    PCM_PARAM_SAMPLE_BITS,
    PCM_PARAM_FRAME_BITS,
    PCM_PARAM_CHANNELS,
    PCM_PARAM_RATE,
    PCM_PARAM_PERIOD_TIME,
    PCM_PARAM_PERIOD_SIZE,
    PCM_PARAM_PERIOD_BYTES,
    PCM_PARAM_PERIODS,
    PCM_PARAM_BUFFER_TIME,
    PCM_PARAM_BUFFER_SIZE,
    PCM_PARAM_BUFFER_BYTES,
    PCM_PARAM_TICK_TIME,
};


struct generic_audio_device {
    struct audio_hw_device device;
    pthread_mutex_t lock;
    bool mic_mute;
    struct mixer* mixer;           // Proteced by this->lock
};

static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
static int adev_get_microphones(const audio_hw_device_t *dev,
                                struct audio_microphone_characteristic_t *mic_array,
                                size_t *mic_count);
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;


unsigned int pcm_format_to_bits(enum pcm_format format)
{
    switch (format) {
        case PCM_FORMAT_S32_LE:
        case PCM_FORMAT_S24_LE:
            return 32;
        case PCM_FORMAT_S16_LE:
            return 16;
        default:
            return 0;
    }
}

int pcm_frames_to_bytes(struct pcm_config *config, unsigned int frames)
{
    return frames * config->channels *
        (pcm_format_to_bits(config->format) >> 3);
}



static 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 = (uint8_t*)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;
}

static 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;
}

static 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;
}

#define MIN(a, b) (((a)<(b)) ? (a) : (b))
static 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 {
            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;
}

static 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;
}


struct generic_stream_out {
    struct audio_stream_out stream;
    pthread_mutex_t lock;
    struct generic_audio_device *dev;
    audio_devices_t device;
    struct audio_config  req_config;
    struct pcm_config pcm_config;
    audio_vbuffer_t buffer;

    bool standby;
    uint64_t underrun_position;
    struct timespec underrun_time;
    uint64_t last_write_time_us;
    uint64_t frames_total_buffered;
    uint64_t frames_written;
    uint64_t frames_rendered;

    pthread_t worker_thread;
    pthread_cond_t worker_wake;
    bool worker_standby;
    bool worker_exit;
};

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

    // 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
};

static struct pcm_config pcm_config_out = {
    .channels = 2,
    .rate = 0,
    .period_size = 0,
    .period_count = OUT_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
};

static struct pcm_config pcm_config_in = {
    .channels = 1,
    .rate = 0,
    .period_size = 0,
    .period_count = IN_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
    .start_threshold = 0,
    .stop_threshold = INT_MAX,
};

static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned int audio_device_ref_count = 0;

static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    return out->req_config.sample_rate;
}

static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
    return -ENOSYS;
}

static size_t out_get_buffer_size(const struct audio_stream *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    int size = out->pcm_config.period_size *
                audio_stream_out_frame_size(&out->stream);
    return size;
}

static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    return out->req_config.channel_mask;
}

static audio_format_t out_get_format(const struct audio_stream *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    return out->req_config.format;
}

static int out_set_format(struct audio_stream *stream, audio_format_t format)
{
    return -ENOSYS;
}

static int stop_audio_recording(int fd)
{
    anbox::audio::RecordCommand recordCmd = anbox::audio::RecordCommand::StopRecord;
    int errorCode = -1;
    if (::write(fd, &recordCmd, sizeof(recordCmd)) < 0) {
        ALOGE("stop_audio_recording, write msg failed");
        close(fd);
        return -EIO;
    }

    auto bytes_read = ::read(fd, &errorCode, sizeof(int));
    if (bytes_read < 0) {
        ALOGE("stop_audio_recording,anbox responed error");
        close(fd);
        return -EIO;
    }

    if (errorCode == 0) {
        ALOGD("stop audio record sucess");
    } else {
        ALOGE("stop audio record failed  errorCode is %d", errorCode);
        close(fd);
        return -EIO;
    }

    return 0;
}

static int start_audio_record(int fd)
{
    anbox::audio::RecordCommand recordCmd = anbox::audio::RecordCommand::StartRecord;
    if (::write(fd, &recordCmd, sizeof(recordCmd)) < 0) {
        ALOGE("start audio record,failed");
        close(fd);
        return -EIO;
    }
    return 0;
}

static int connect_audio_server(const anbox::audio::ClientInfo::Type &type)
{
    int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (fd < 0) {
        return -errno;
    }

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, AUDIO_DEVICE_NAME, sizeof(addr.sun_path));

    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(fd);
        return -errno;
    }

    // We will send out client type information to the server and the
    // server will either deny the request by closing the connection
    // or by sending us the approved client details back.
    anbox::audio::ClientInfo client_info{type};
    if (::write(fd, &client_info, sizeof(client_info)) < 0) {
        close(fd);
        return -EIO;
    }

    auto bytes_read = ::read(fd, &client_info, sizeof(client_info));
    if (bytes_read < 0) {
        close(fd);
        return -EIO;
    }

    ALOGE("Successfully connected Anbox audio server");

    return fd;
}


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\tsample rate: %u\n"
            "\t\tbuffer size: %lu\n"
            "\t\tchannel mask: %08x\n"
            "\t\tformat: %d\n"
            "\t\tdevice: %08x\n"
            "\t\taudio dev: %p\n\n",
            out_get_sample_rate(stream),
            out_get_buffer_size(stream),
            out_get_channels(stream),
            out_get_format(stream),
            out->device,
            out->dev);
    pthread_mutex_unlock(&out->lock);
    return 0;
}

static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    struct str_parms *parms;
    char value[32];
    int ret = -ENOSYS;
    int success;
    long val;
    char *end;

    if (kvpairs == NULL || kvpairs[0] == 0) {
        return 0;
    }
    pthread_mutex_lock(&out->lock);
    if (out->standby) {
        parms = str_parms_create_str(kvpairs);
        success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
                                    value, sizeof(value));
        if (success >= 0) {
            errno = 0;
            val = strtol(value, &end, 10);
            if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) {
                out->device = (int)val;
                ret = 0;
            }
        }

        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
                                    value, sizeof(value));
        if (success >= 0) {
            ret = 0;
        }

        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
                                    value, sizeof(value));
        if (success >= 0) {
            ret = 0;
        }
        if (ret != 0) {
            ALOGD("%s Unsupported parameter %s", __FUNCTION__, kvpairs);
        }

        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 = NULL;
    char value[256];
    struct str_parms *reply = str_parms_create();
    int ret;
    bool get = false;

    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);
        get = true;
    }

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
        value[0] = 0;
        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
        get = true;
    }

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
        value[0] = 0;
        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
        get = true;
    }
    if (get) {
        str = strdup(str_parms_to_str(reply));
    } else {
        ALOGD("%s Unsupported parameter:%s", __FUNCTION__, keys);
    }

    str_parms_destroy(query);
    str_parms_destroy(reply);
    return str;
}

static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    return (out->pcm_config.period_size  * 1000) / out->pcm_config.rate;
}

static int out_set_volume(struct audio_stream_out *stream, float left, float right)
{
    return -ENOSYS;
}

static void *out_write_worker(void * args)
{
    struct generic_stream_out *out = (struct generic_stream_out *)args;
    struct pcm *pcm = NULL;
    uint8_t *buffer = NULL;
    int buffer_frames;
    int buffer_size;
    bool restart = false;
    bool shutdown = false;
    int ret = 0;
    int fd = -1;
    while (true) {
        pthread_mutex_lock(&out->lock);
        while (out->worker_standby || restart) {
            restart = false;
            if (fd >= 0) {
                close(fd);
                fd = -1;
                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 not in standby before exiting");
            }
            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 (fd < 0) {
            fd = connect_audio_server(anbox::audio::ClientInfo::Type::Playback);
            if (fd < 0) {
                ret = fd;
                ALOGE("Failed to connect with Anbox audio servers (err %d)", ret);
                pthread_mutex_unlock(&out->lock);
                break;
            }

            buffer_frames = out->pcm_config.period_size;
            // two channel  16BIT format
            buffer_size = pcm_frames_to_bytes(&out->pcm_config, buffer_frames);
            buffer = (uint8_t *)malloc(buffer_size);
            if (!buffer) {
                ALOGE("could not allocate write buffer");
                pthread_mutex_unlock(&out->lock);
                break;
            }
        }
        int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames);
        pthread_mutex_unlock(&out->lock);
        int written = write(fd, buffer, pcm_frames_to_bytes(&out->pcm_config, frames));
        if (written == 0) {
            ALOGE("write failed");
            restart = true;
        }

    }
    if (buffer) {
        free(buffer);
    }

    return NULL;
}

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;

    if (*position > out->frames_written) {
        ALOGW("Not supplying enough data to HAL, expected position %lu, only wrote %lu",
                *position, out->frames_written);
        *position = out->frames_written;
        out->underrun_position = *position;
        out->underrun_time = curtime;
        out->frames_total_buffered = 0;
    }
}

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;
    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 = audio_vbuffer_write(&out->buffer, buffer, frames);
    pthread_cond_signal(&out->worker_wake);

    out->frames_written += frames;
    out->frames_rendered += frames;
    out->frames_total_buffered += frames;

    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);

    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;

    struct generic_audio_device *adev = out->dev;

    pthread_mutex_unlock(&out->lock);

    if (sleep_time_us > 0) {
        usleep(sleep_time_us);
    }

    if (frames_written < frames) {
        ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames", frames_written, frames);
    }

    return bytes;
}

static int out_get_presentation_position(const struct audio_stream_out *stream,
                                         uint64_t *frames, struct timespec *timestamp)
{
    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)
{
    return 0;
}

static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
    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 int check_input_parameters(uint32_t sample_rate, audio_format_t format,
                                  audio_channel_mask_t channel_mask)
{
    return refine_input_parameters(&sample_rate, &format, &channel_mask);
}

static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format,
                                    audio_channel_mask_t channel_mask)
{
    size_t size;
    int channel_count = popcount(channel_mask);
    if (check_input_parameters(sample_rate, format, channel_mask) != 0) {
        return 0;
    }

    size = (sample_rate * IN_PERIOD_MS) / 1000;
    // Audioflinger expects audio buffers to be multiple of 16 frames
    size = ((size + 15) / 16) * 16;
    size *= sizeof(short) * channel_count;

    return size;
}


static size_t in_get_buffer_size(const struct audio_stream *stream)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;
    int size = get_input_buffer_size(in->req_config.sample_rate,
                                     in->req_config.format,
                                     in->req_config.channel_mask);
    return size;
}

static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;
    return in->req_config.channel_mask;
}

static audio_format_t in_get_format(const struct audio_stream *stream)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;
    return in->req_config.format;
}

static int in_set_format(struct audio_stream *stream, audio_format_t format)
{
    return -ENOSYS;
}

static int in_dump(const struct audio_stream *stream, int fd)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;

    pthread_mutex_lock(&in->lock);
    dprintf(fd, "\tin_dump:\n"
                "\t\tsample rate: %u\n"
                "\t\tbuffer size: %zu\n"
                "\t\tchannel mask: %08x\n"
                "\t\tformat: %d\n"
                "\t\tdevice: %08x\n"
                "\t\taudio dev: %p\n\n",
            in_get_sample_rate(stream),
            in_get_buffer_size(stream),
            in_get_channels(stream),
            in_get_format(stream),
            in->device,
            in->dev);
    pthread_mutex_unlock(&in->lock);
    return 0;
}

static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;
    struct str_parms *parms;
    char value[32];
    int ret = -ENOSYS;
    int success;
    long val;
    char *end;

    if (kvpairs == NULL || kvpairs[0] == 0) {
        return 0;
    }
    pthread_mutex_lock(&in->lock);
    if (in->standby) {
        parms = str_parms_create_str(kvpairs);

        success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
                                    value, sizeof(value));
        if (success >= 0) {
            errno = 0;
            val = strtol(value, &end, 10);
            if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
                in->device = (int)val;
                ret = 0;
            }
        }
        // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT
        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
                                    value, sizeof(value));
        if (success >= 0) {
            ret = 0;
        }
        success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
                                    value, sizeof(value));
        if (success >= 0) {
            ret = 0;
        }

        if (ret != 0) {
            ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs);
        }

        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 = NULL;
    char value[256];
    struct str_parms *reply = str_parms_create();
    int ret;
    bool get = false;

    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);
        get = true;
    }

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
        value[0] = 0;
        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
        get = true;
    }

    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
        value[0] = 0;
        strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
        get = true;
    }

    if (get) {
        str = strdup(str_parms_to_str(reply));
    } else {
        ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys);
    }

    str_parms_destroy(query);
    str_parms_destroy(reply);
    return str;
}

static int in_set_gain(struct audio_stream_in *stream, float 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;
}

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;
    int ret = 0;
    int fd = -1;
    while (true) {
        pthread_mutex_lock(&in->lock);
        while (in->worker_standby || restart) {
            restart = false;
            if (fd >= 0) {
                if (stop_audio_recording(fd) == 0) {
                    ALOGD("stop_audio_recording success fd is %d", fd);
                    close(fd);
                }
                fd = -1;
                free(buffer);
                buffer = NULL;
            }
            if (in->worker_exit) {
                break;
            }
            ALOGE("In worker pthread_cond_wait");
            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) {
            ALOGE("In worker shutdown break");
            pthread_mutex_unlock(&in->lock);
            break;
        }
        if (fd < 0) {
            fd = connect_audio_server(anbox::audio::ClientInfo::Type::Recording);
            if (fd < 0) {
                ret = fd;
                ALOGE("Failed to connect with Anbox audio servers (err %d)", ret);
                pthread_mutex_unlock(&in->lock);
                break;
            }
            buffer_frames = in->pcm_config.period_size;
            buffer_size = pcm_frames_to_bytes(&in->pcm_config, buffer_frames);
            buffer = (uint8_t*)malloc(buffer_size);
            if (!buffer) {
                ALOGE("could not allocate worker read buffer");
                pthread_mutex_unlock(&in->lock);
                break;
            }
        }
        pthread_mutex_unlock(&in->lock);
        if (0 != start_audio_record(fd)) {
            ALOGE("audio start record failed");
            restart = true;
            continue;
        }
        int read_bytes = read(fd, buffer, pcm_frames_to_bytes(&in->pcm_config, buffer_frames));
        if (read_bytes <= 10) {
            ALOGE("audio read failed");
            restart = true;
            continue;
        }
        pthread_mutex_lock(&in->lock);
        size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames);
        pthread_mutex_unlock(&in->lock);

        if (frames_written != buffer_frames) {
            ALOGW("in_read_worker only could write %zu / %zu frames", frames_written, buffer_frames);
        }
    }
    if (buffer) {
        free(buffer);
    }
    return NULL;
}

static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
                       size_t bytes)
{
    struct generic_stream_in *in = (struct generic_stream_in *)stream;
    struct generic_audio_device *adev = in->dev;
    const size_t frames =  bytes / audio_stream_in_frame_size(stream);
    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;
    }

    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;
    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 (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 = (int16_t*)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_get_active_microphones(const struct audio_stream_in *stream,
                                     struct audio_microphone_characteristic_t *mic_array,
                                     size_t *mic_count)
{
    return adev_get_microphones(NULL, mic_array, mic_count);
}

static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
    return 0;
}


static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
    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 __unused)
{
    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);

    }
    *stream_out = &out->stream;

error:

    return ret;
}

static void adev_close_output_stream(struct audio_hw_device *dev,
                                     struct audio_stream_out *stream)
{
    struct generic_stream_out *out = (struct generic_stream_out *)stream;
    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);
    free(stream);
}

static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
    return 0;
}

static char *adev_get_parameters(const struct audio_hw_device *dev,
                                 const char *keys)
{
    return strdup("");
}

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)
{
    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)
{
    return -ENOSYS;
}

static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted)
{
    return -ENOSYS;
}

static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
{
    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);
    ALOGD("adev_close_input_stream enter");

    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;
    }

    pthread_mutex_destroy(&in->lock);
    audio_vbuffer_destroy(&in->buffer);
    free(stream);
}

static int adev_open_input_stream(struct audio_hw_device *dev,
                                  audio_io_handle_t handle,
                                  audio_devices_t devices,
                                  struct audio_config *config,
                                  struct audio_stream_in **stream_in,
                                  audio_input_flags_t flags,
                                  const char *address,
                                  audio_source_t source __unused)
{
    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;
    }
    ALOGD("adev_open_input_stream enter before lock");

    in->stream.common.get_sample_rate = in_get_sample_rate;
    in->stream.common.set_sample_rate = in_set_sample_rate;
    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;
    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;
    in->stream.common.remove_audio_effect = in_remove_audio_effect;
    in->stream.set_gain = in_set_gain;
    in->stream.read = in_read;
    in->stream.get_input_frames_lost = in_get_input_frames_lost;
    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;
    ALOGD("adev_open_input_stream enter");
    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);
    }

    *stream_in = &in->stream;

error:
    return ret;
}

static int adev_dump(const audio_hw_device_t *dev, int fd)
{
    return 0;
}

static int adev_get_microphones(const audio_hw_device_t *dev,
                                struct audio_microphone_characteristic_t *mic_array,
                                size_t *mic_count)
{
    if (mic_count == NULL) {
        return -ENOSYS;
    }

    if (*mic_count == 0) {
        *mic_count = 1;
        return 0;
    }

    if (mic_array == NULL) {
        return -ENOSYS;
    }
    *mic_count = 1;
    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) {
        free(adev);
    }

error:
    pthread_mutex_unlock(&adev_init_lock);
    return ret;
}


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 = (struct generic_audio_device *)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_2_0;
    adev->device.common.module = (struct hw_module_t *) module;
    adev->device.common.close = adev_close;

    adev->device.init_check = adev_init_check;
    adev->device.set_voice_volume = adev_set_voice_volume;
    adev->device.set_master_volume = adev_set_master_volume;
    adev->device.get_master_volume = adev_get_master_volume;
    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;
    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;
    adev->device.get_parameters = adev_get_parameters;
    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;
    *device = &adev->device.common;
    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 = "Anbox audio HW HAL",
        .author = "The Android Open Source Project",
        .methods = &hal_module_methods,
    },
};


#if defined(__cplusplus)
}  /* extern "C" */
#endif
