bluez/audio: add native android audio HAL wrapper

Change-Id: Ia01ea6f9caf51756d9d45a6068a8e2dc4bf7d49e
Signed-off-by: Dima Zavin <dima@android.com>
diff --git a/audio/Android.mk b/audio/Android.mk
index ae9da30..7251ae6 100755
--- a/audio/Android.mk
+++ b/audio/Android.mk
@@ -58,6 +58,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+	android_audio_hw.c \
 	liba2dp.c \
 	ipc.c \
 	../sbc/sbc_primitives.c \
@@ -82,8 +83,11 @@
 	system/bluetooth/bluez-clean-headers
 
 LOCAL_SHARED_LIBRARIES := \
+	libpower \
 	libcutils
 
-LOCAL_MODULE := liba2dp
+LOCAL_MODULE := audio.a2dp.default
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/audio/android_audio_hw.c b/audio/android_audio_hw.c
new file mode 100644
index 0000000..90bb72e
--- /dev/null
+++ b/audio/android_audio_hw.c
@@ -0,0 +1,739 @@
+/*
+ *  Copyright (C) 2008-2011 The Android Open Source Project
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define LOG_TAG "a2dp_audio_hw"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+
+#include <hardware/hardware.h>
+#include <hardware/audio.h>
+#include <hardware/audio_hal.h>
+
+#include <hardware_legacy/power.h>
+
+#include "liba2dp.h"
+
+#define A2DP_WAKE_LOCK_NAME            "A2dpOutputStream"
+#define MAX_WRITE_RETRIES              5
+
+#define A2DP_SUSPENDED_PARM            "A2dpSuspended"
+#define BLUETOOOTH_ENABLED_PARM        "bluetooth_enabled"
+
+#define OUT_SINK_ADDR_PARM             "a2dp_sink_address"
+
+/* NOTE: If you need to hold the adev_a2dp->lock AND the astream_out->lock,
+   you MUST take adev_a2dp lock first!!
+ */
+
+struct astream_out;
+struct adev_a2dp {
+    struct audio_hw_device  device;
+
+    audio_mode_t            mode;
+    bool                    bt_enabled;
+    bool                    suspended;
+
+    pthread_mutex_t         lock;
+
+    struct astream_out      *output;
+};
+
+struct astream_out {
+    struct audio_stream_out stream;
+
+    uint32_t                sample_rate;
+    size_t                  buffer_size;
+    uint32_t                channels;
+    int                     format;
+
+    int                     fd;
+    bool                    standby;
+    int                     start_count;
+    int                     retry_count;
+    void*                   data;
+
+    pthread_mutex_t         lock;
+
+    audio_devices_t         device;
+    uint64_t                 last_write_time;
+    uint32_t                buffer_duration_us;
+
+    bool                    bt_enabled;
+    bool                    suspended;
+    char                    a2dp_addr[20];
+};
+
+static uint64_t system_time(void)
+{
+    struct timespec t;
+
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+
+    return t.tv_sec*1000000000LL + t.tv_nsec;
+}
+
+/** audio_stream_out implementation **/
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+    return out->sample_rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+
+    LOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__);
+    return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+    return out->buffer_size;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+    return out->channels;
+}
+
+static int out_get_format(const struct audio_stream *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+    return out->format;
+}
+
+static int out_set_format(struct audio_stream *stream, int format)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+    LOGE("(%s:%d) %s: Implement me!", __FILE__, __LINE__, __func__);
+    return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+    return 0;
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+
+    return (out->buffer_duration_us / 1000) + 200;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+                          float right)
+{
+    return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+                                   uint32_t *dsp_frames)
+{
+    return -ENOSYS;
+}
+
+static int _out_init_locked(struct astream_out *out, const char *addr)
+{
+    int ret;
+
+    if (out->data)
+        return 0;
+
+    /* XXX: shouldn't this use the sample_rate/channel_count from 'out'? */
+    ret = a2dp_init(44100, 2, &out->data);
+    if (ret < 0) {
+        LOGE("a2dp_init failed err: %d\n", ret);
+        out->data = NULL;
+        return ret;
+    }
+
+    /* XXX: is this even necessary? */
+    if (addr)
+        strlcpy(out->a2dp_addr, addr, sizeof(out->a2dp_addr));
+    a2dp_set_sink(out->data, out->a2dp_addr);
+
+    return 0;
+}
+
+static bool _out_validate_parms(struct astream_out *out, int format,
+                                uint32_t chans, uint32_t rate)
+{
+    if ((format && (format != out->format)) ||
+        (chans && (chans != out->channels)) ||
+        (rate && (rate != out->sample_rate)))
+        return false;
+    return true;
+}
+
+static int out_standby_stream_locked(struct astream_out *out)
+{
+    int ret = 0;
+
+    if (out->standby || !out->data)
+        return 0;
+
+    LOGV_IF(!out->bt_enabled, "Standby skip stop: enabled %d", out->bt_enabled);
+    if (out->bt_enabled)
+        ret = a2dp_stop(out->data);
+    release_wake_lock(A2DP_WAKE_LOCK_NAME);
+    out->standby = true;
+
+    return ret;
+}
+
+static int out_close_stream_locked(struct astream_out *out)
+{
+    out_standby_stream_locked(out);
+
+    if (out->data) {
+        LOGV("%s: calling a2dp_cleanup()", __func__);
+        a2dp_cleanup(out->data);
+        out->data = NULL;
+    }
+
+    return 0;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+
+    pthread_mutex_lock(&out->lock);
+    out_standby_stream_locked(out);
+    pthread_mutex_unlock(&out->lock);
+
+    return 0;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+    struct str_parms *parms;
+    char *str;
+    char value[32];
+    int ret;
+
+    parms = str_parms_create_str(kvpairs);
+
+    pthread_mutex_lock(&out->lock);
+
+    ret = str_parms_get_str(parms, OUT_SINK_ADDR_PARM, value, sizeof(value));
+    if (ret >= 0) {
+        /* strlen(00:00:00:00:00:00) == 17 */
+        if (strlen(value) == 17) {
+            strlcpy(out->a2dp_addr, value, sizeof(out->a2dp_addr));
+            if (out->data)
+                a2dp_set_sink(out->data, out->a2dp_addr);
+        } else
+            ret = -EINVAL;
+    }
+
+    pthread_mutex_unlock(&out->lock);
+    str_parms_destroy(parms);
+    return ret;
+}
+
+static audio_devices_t out_get_device(const struct audio_stream *stream)
+{
+    const struct astream_out *out = (const struct astream_out *)stream;
+    return out->device;
+}
+
+
+static int out_set_device(struct audio_stream *stream, audio_devices_t device)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+
+    if (!audio_is_a2dp_device(device))
+        return -EINVAL;
+
+    /* XXX: if out->device ever starts getting used for anything, need to
+     * grab the out->lock */
+    out->device = device;
+    return 0;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream,
+                                 const char *keys)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+    struct str_parms *parms;
+    struct str_parms *out_parms;
+    char *str;
+    char value[20];
+    int ret;
+
+    parms = str_parms_create_str(keys);
+    out_parms = str_parms_create();
+
+    pthread_mutex_lock(&out->lock);
+
+    ret = str_parms_get_str(parms, OUT_SINK_ADDR_PARM, value, sizeof(value));
+    if (ret >= 0)
+        str_parms_add_str(out_parms, OUT_SINK_ADDR_PARM, out->a2dp_addr);
+
+    pthread_mutex_unlock(&out->lock);
+
+    str = str_parms_to_str(out_parms);
+    str_parms_destroy(out_parms);
+    str_parms_destroy(parms);
+
+    return str;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    struct astream_out *out = (struct astream_out *)stream;
+    int ret;
+    int cnt = bytes;
+    int retries = MAX_WRITE_RETRIES;
+    uint64_t now;
+    uint32_t elapsed_us;
+    const uint8_t *buf = buffer;
+
+    pthread_mutex_lock(&out->lock);
+    if (!out->bt_enabled || out->suspended) {
+        LOGV("a2dp %s: bluetooth disabled bt_en %d, suspended %d",
+             out->bt_enabled, out->suspended);
+        ret = -1;
+        goto err_bt_disabled;
+    }
+
+    if (out->standby) {
+        acquire_wake_lock(PARTIAL_WAKE_LOCK, A2DP_WAKE_LOCK_NAME);
+        out->standby = false;
+        out->last_write_time = system_time();
+    }
+
+    ret = _out_init_locked(out, NULL);
+    if (ret < 0)
+        goto err_init;
+
+    while (cnt > 0 && retries > 0) {
+        ret = a2dp_write(out->data, buf, cnt);
+        if (ret < 0) {
+            LOGE("%s: a2dp_write failed (%d)\n", __func__, ret);
+            goto err_write;
+        } else if (ret == 0) {
+            retries--;
+            continue;
+        }
+
+        cnt -= ret;
+        buf += ret;
+    }
+
+    /* XXX: PLEASE FIX ME!!!! */
+
+    /* if A2DP sink runs abnormally fast, sleep a little so that
+     * audioflinger mixer thread does no spin and starve other threads. */
+    /* NOTE: It is likely that the A2DP headset is being disconnected */
+    now = system_time();
+    elapsed_us = (now - out->last_write_time) / 1000UL;
+    if (elapsed_us < (out->buffer_duration_us / 4)) {
+        LOGV("A2DP sink runs too fast");
+        usleep(out->buffer_duration_us - elapsed_us);
+    }
+    out->last_write_time = now;
+
+    pthread_mutex_unlock(&out->lock);
+
+    return bytes;
+
+err_write:
+err_init:
+err_bt_disabled:
+    out_standby_stream_locked(out);
+    pthread_mutex_unlock(&out->lock);
+
+    /* XXX: simulate audio output timing in case of error?!?! */
+    usleep(out->buffer_duration_us);
+    return ret;
+}
+
+static int _out_bt_enable(struct astream_out *out, bool enable)
+{
+    int ret = 0;
+
+    pthread_mutex_lock(&out->lock);
+    out->bt_enabled = enable;
+    if (!enable)
+        ret = out_close_stream_locked(out);
+    pthread_mutex_unlock(&out->lock);
+
+    return ret;
+}
+
+static int _out_a2dp_suspend(struct astream_out *out, bool suspend)
+{
+    pthread_mutex_lock(&out->lock);
+    out->suspended = suspend;
+    out_standby_stream_locked(out);
+    pthread_mutex_unlock(&out->lock);
+
+    return 0;
+}
+
+static int adev_open_output_stream(struct audio_hw_device *dev,
+                                   uint32_t devices, int *format,
+                                   uint32_t *channels, uint32_t *sample_rate,
+                                   struct audio_stream_out **stream_out)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)dev;
+    struct astream_out *out;
+    int ret;
+
+    pthread_mutex_lock(&adev->lock);
+
+    /* one output stream at a time */
+    if (adev->output) {
+        LOGV("output exists");
+        ret = -EBUSY;
+        goto err_output_exists;
+    }
+
+    out = calloc(1, sizeof(struct astream_out));
+    if (!out) {
+        ret = -ENOMEM;
+        goto err_alloc;
+    }
+
+    pthread_mutex_init(&out->lock, NULL);
+
+    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.set_device = out_set_device;
+    out->stream.common.get_device = out_get_device;
+    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->sample_rate = 44100;
+    out->buffer_size = 512 * 20;
+    out->channels = AUDIO_CHANNEL_OUT_STEREO;
+    out->format = AUDIO_FORMAT_PCM_16_BIT;
+
+    out->fd = -1;
+    out->device = devices;
+    out->bt_enabled = adev->bt_enabled;
+    out->suspended = adev->suspended;
+
+    /* for now, buffer_duration_us is precalculated and never changed.
+     * if the sample rate or the format ever changes on the fly, we'd have
+     * to recalculate this */
+    out->buffer_duration_us = ((out->buffer_size * 1000 ) /
+                               audio_stream_frame_size(&out->stream.common) /
+                               out->sample_rate) * 1000;
+    if (!_out_validate_parms(out, format ? *format : 0,
+                             channels ? *channels : 0,
+                             sample_rate ? *sample_rate : 0)) {
+        LOGV("invalid parameters");
+        ret = -EINVAL;
+        goto err_validate_parms;
+    }
+
+    /* XXX: check return code? */
+    if (adev->bt_enabled)
+        _out_init_locked(out, "00:00:00:00:00:00");
+
+    adev->output = out;
+
+    if (format)
+        *format = out->format;
+    if (channels)
+        *channels = out->channels;
+    if (sample_rate)
+        *sample_rate = out->sample_rate;
+
+    pthread_mutex_unlock(&adev->lock);
+
+    *stream_out = &out->stream;
+
+    return 0;
+
+err_validate_parms:
+    free(out);
+err_alloc:
+err_output_exists:
+    pthread_mutex_unlock(&adev->lock);
+    *stream_out = NULL;
+    return ret;
+}
+
+/* needs the adev->lock held */
+static void adev_close_output_stream_locked(struct adev_a2dp *dev,
+                                            struct astream_out *stream)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)dev;
+    struct astream_out *out = (struct astream_out *)stream;
+
+    /* invalid stream? */
+    if (!adev->output || adev->output != out) {
+        LOGE("%s: unknown stream %p (ours is %p)", __func__, out, adev->output);
+        return;
+    }
+
+    pthread_mutex_lock(&out->lock);
+    out_close_stream_locked(out);
+    pthread_mutex_unlock(&out->lock);
+
+    adev->output = NULL;
+    free(out);
+}
+
+static void adev_close_output_stream(struct audio_hw_device *dev,
+                                     struct audio_stream_out *stream)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)dev;
+    struct astream_out *out = (struct astream_out *)stream;
+
+    pthread_mutex_lock(&adev->lock);
+    adev_close_output_stream_locked(adev, out);
+    pthread_mutex_unlock(&adev->lock);
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)dev;
+    struct str_parms *parms;
+    char *str;
+    char value[8];
+    int ret;
+
+    parms = str_parms_create_str(kvpairs);
+
+    pthread_mutex_lock(&adev->lock);
+
+    ret = str_parms_get_str(parms, BLUETOOOTH_ENABLED_PARM, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        adev->bt_enabled = !strcmp(value, "true");
+        _out_bt_enable(adev->output, adev->bt_enabled);
+    }
+
+    ret = str_parms_get_str(parms, A2DP_SUSPENDED_PARM, value, sizeof(value));
+    if (ret >= 0) {
+        adev->suspended = !strcmp(value, "true");
+        _out_a2dp_suspend(adev->output, adev->suspended);
+    }
+
+    pthread_mutex_unlock(&adev->lock);
+
+    str_parms_destroy(parms);
+
+    return ret;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev,
+                                  const char *keys)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)dev;
+    struct str_parms *parms;
+    struct str_parms *out_parms;
+    char *str;
+    char value[8];
+    int ret;
+
+    parms = str_parms_create_str(keys);
+    out_parms = str_parms_create();
+
+    pthread_mutex_lock(&adev->lock);
+
+    ret = str_parms_get_str(parms, BLUETOOOTH_ENABLED_PARM, value,
+                            sizeof(value));
+    if (ret >= 0)
+        str_parms_add_str(out_parms, BLUETOOOTH_ENABLED_PARM,
+                          adev->bt_enabled ? "true" : "false");
+
+    ret = str_parms_get_str(parms, A2DP_SUSPENDED_PARM, value, sizeof(value));
+    if (ret >= 0)
+        str_parms_add_str(out_parms, A2DP_SUSPENDED_PARM,
+                          adev->suspended ? "true" : "false");
+
+    pthread_mutex_unlock(&adev->lock);
+
+    str = str_parms_to_str(out_parms);
+    str_parms_destroy(out_parms);
+    str_parms_destroy(parms);
+
+    return str;
+}
+
+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 -ENOSYS;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, int mode)
+{
+    /* TODO: do we care for the mode? */
+    return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+    return -ENOSYS;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+    return -ENOSYS;
+}
+
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+                                         uint32_t sample_rate, int format,
+                                         int channel_count)
+{
+    /* no input */
+    return 0;
+}
+
+static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices,
+                                  int *format, uint32_t *channels,
+                                  uint32_t *sample_rate,
+                                  audio_in_acoustics_t acoustics,
+                                  struct audio_stream_in **stream_in)
+{
+    return -ENOSYS;
+}
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+                                   struct audio_stream_in *in)
+{
+    return;
+}
+
+static int adev_dump(const audio_hw_device_t *device, int fd)
+{
+    return 0;
+}
+
+static int adev_close(hw_device_t *device)
+{
+    struct adev_a2dp *adev = (struct adev_a2dp *)device;
+
+    pthread_mutex_lock(&adev->lock);
+    if (adev->output)
+        adev_close_output_stream_locked(adev, adev->output);
+    pthread_mutex_unlock(&adev->lock);
+    free(adev);
+
+    return 0;
+}
+
+static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
+{
+    return AUDIO_DEVICE_OUT_ALL_A2DP;
+}
+
+static int adev_open(const hw_module_t* module, const char* name,
+                     hw_device_t** device)
+{
+    struct adev_a2dp *adev;
+    int ret;
+
+    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
+        return -EINVAL;
+
+    adev = calloc(1, sizeof(struct adev_a2dp));
+    if (!adev)
+        return -ENOMEM;
+
+    adev->bt_enabled = true;
+    pthread_mutex_init(&adev->lock, NULL);
+
+    adev->device.common.tag = HARDWARE_DEVICE_TAG;
+    adev->device.common.version = 0;
+    adev->device.common.module = (struct hw_module_t *) module;
+    adev->device.common.close = adev_close;
+
+    adev->device.get_supported_devices = adev_get_supported_devices;
+    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.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;
+
+    return 0;
+
+err_str_parms_create:
+    free(adev);
+    return ret;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+    .open = adev_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+    .common = {
+        .tag = HARDWARE_MODULE_TAG,
+        .version_major = 1,
+        .version_minor = 0,
+        .id = AUDIO_HARDWARE_MODULE_ID,
+        .name = "A2DP Audio HW HAL",
+        .author = "The Android Open Source Project",
+        .methods = &hal_module_methods,
+    },
+};
+
+