BT Audio support using primary HAL

- Added BT extension to support audio playback via primary output.
  The BT extension exposes APIs to commicate with the BT device for
  audio playback
  The BT extension uses the BT IPC interfaces in order to communicate
  with the BT devices.

Change-Id: I53e642a778dbe74e3e0030a81a19c001b7560d16
diff --git a/hal/Android.mk b/hal/Android.mk
index 4b41346..1e210cc 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -268,6 +268,11 @@
     LOCAL_SRC_FILES += audio_hw_extn_api.c
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_BT_HAL)),true)
+    LOCAL_CFLAGS += -DAUDIO_EXTN_BT_HAL_ENABLED
+    LOCAL_SRC_FILES += audio_extn/bt_hal.c
+endif
+
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
 	libcutils \
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 6526f4f..8bf165a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -665,6 +665,26 @@
 #define audio_extn_qaf_set_parameters(adev, parms)                      (0)
 #endif
 
+#ifdef AUDIO_EXTN_BT_HAL_ENABLED
+int audio_extn_bt_hal_load(void **handle);
+int audio_extn_bt_hal_open_output_stream(void *handle, int in_rate, audio_channel_mask_t channel_mask, int bit_width);
+int audio_extn_bt_hal_unload(void *handle);
+int audio_extn_bt_hal_close_output_stream(void *handle);
+int audio_extn_bt_hal_out_write(void *handle, void *buf, int size);
+struct audio_stream_out *audio_extn_bt_hal_get_output_stream(void *handle);
+void *audio_extn_bt_hal_get_device(void *handle);
+int audio_extn_bt_hal_get_latency(void *handle);
+#else
+#define audio_extn_bt_hal_load(...)                   (-EINVAL)
+#define audio_extn_bt_hal_unload(...)                 (-EINVAL)
+#define audio_extn_bt_hal_open_output_stream(...)     (-EINVAL)
+#define audio_extn_bt_hal_close_output_stream(...)    (-EINVAL)
+#define audio_extn_bt_hal_out_write(...)              (-EINVAL)
+#define audio_extn_bt_hal_get_latency(...)            (-EINVAL)
+#define audio_extn_bt_hal_get_output_stream(...)      NULL
+#define audio_extn_bt_hal_get_device(...)             NULL
+#endif
+
 #ifndef KEEP_ALIVE_ENABLED
 #define audio_extn_keep_alive_init(a) do {} while(0)
 #define audio_extn_keep_alive_start() do {} while(0)
diff --git a/hal/audio_extn/bt_hal.c b/hal/audio_extn/bt_hal.c
new file mode 100644
index 0000000..21baa9c
--- /dev/null
+++ b/hal/audio_extn/bt_hal.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "BT_HAL_EXTN"
+/*#define LOG_NDEBUG 0*/
+
+#include <inttypes.h>
+#include <cutils/log.h>
+#include <audio_hw.h>
+#include <audio_extn.h>
+#include <platform_api.h>
+#include <audio_utils/resampler.h>
+#include <audio_utils/format.h>
+
+#include <../../../../system/bt/audio_a2dp_hw/bthost_ipc.h>
+#include <dlfcn.h>
+
+#define DEFAULT_BUF_SIZE 6144
+
+#define UNUSED(x) (void)(x)
+#define ASSERTC(cond, msg, val) if (!(cond)) {ALOGE("### ASSERT : %s line %d %s (%d) ###", __FILE__, __LINE__, msg, val);}
+
+static void *lib_handle = NULL;
+bt_host_ipc_interface_t *ipc_if = NULL;
+
+struct a2dp_stream_out {
+    struct audio_stream_out stream;
+    struct a2dp_stream_common common;
+};
+
+
+typedef struct bt_hal_module {
+    struct audio_hw_device *a2dp_device;
+    struct a2dp_stream_out *a2dp_output;
+    struct audio_config config;
+    audio_channel_mask_t in_channel_mask;
+    int bit_width;
+    struct resampler_itfe *resampler;
+    void *data;
+    void *reformatted_buf;
+} bt_hal_module_t;
+
+static int bt_create_resampler(uint32_t in_rate,
+                        struct bt_hal_module *bt)
+{
+    int err = 0;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (bt->resampler == NULL) {
+        err = create_resampler(in_rate,
+                               bt->config.sample_rate,
+                               popcount(bt->config.channel_mask),
+                               RESAMPLER_QUALITY_DEFAULT,
+                               NULL,
+                               &bt->resampler);
+
+        if (err) {
+            ALOGE("%s: Failed to create resampler", __FUNCTION__);
+            free(bt->resampler);
+            bt->resampler = NULL;
+            return -EINVAL;
+        }
+
+        ALOGV("%s: Created resampler[%p] with in_sample_rate[%d], out_sample_rate[%d]", __FUNCTION__, bt->resampler, in_rate, bt->config.sample_rate);
+    }
+    return 0;
+}
+
+static void bt_destroy_resampler(struct bt_hal_module *bt)
+{
+    ALOGV("%s", __FUNCTION__);
+
+    if (bt->resampler != NULL) {
+        release_resampler(bt->resampler);
+        bt->resampler = NULL;
+    }
+
+    if (bt->data != NULL) {
+        free(bt->data);
+        bt->data = NULL;
+    }
+}
+
+static uint32_t out_get_latency(struct a2dp_stream_out * out)
+{
+    int latency_us;
+
+    ALOGV("%s",__FUNCTION__);
+
+    latency_us = ((out->common.buffer_sz * 1000 ) /
+                    audio_stream_out_frame_size(&out->stream) /
+                    out->common.cfg.rate) * 1000;
+
+
+    return (latency_us / 1000) + 200;
+}
+
+static int calc_audiotime(struct a2dp_config cfg, int bytes)
+{
+    int chan_count = popcount(cfg.channel_flags);
+    int bytes_per_sample = 4;
+
+    ASSERTC(cfg.format == AUDIO_FORMAT_PCM_8_24_BIT,
+            "unsupported sample sz", cfg.format);
+
+    return (int)(((int64_t)bytes * (1000000 / (chan_count * bytes_per_sample))) / cfg.rate);
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
+    int sent;
+    int us_delay;
+
+    ALOGV("write %zu bytes (fd %d)", bytes, out->common.audio_fd);
+
+    pthread_mutex_lock(&out->common.lock);
+    if (out->common.state == AUDIO_A2DP_STATE_SUSPENDED ||
+            out->common.state == AUDIO_A2DP_STATE_STOPPING) {
+        ALOGV("stream suspended or closing");
+        goto error;
+    }
+
+    /* only allow autostarting if we are in stopped or standby */
+    if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) ||
+        (out->common.state == AUDIO_A2DP_STATE_STANDBY))
+    {
+        if (ipc_if->start_audio_datapath(&out->common) < 0)
+        {
+            goto error;
+        }
+    }
+    else if (out->common.state != AUDIO_A2DP_STATE_STARTED)
+    {
+        ALOGE("%s: stream not in stopped or standby", __FUNCTION__);
+        goto error;
+    }
+
+    pthread_mutex_unlock(&out->common.lock);
+
+    sent = ipc_if->skt_write(out->common.audio_fd, buffer,  bytes);
+    pthread_mutex_lock(&out->common.lock);
+
+    if (sent == -1)
+    {
+        ipc_if->skt_disconnect(out->common.audio_fd);
+        out->common.audio_fd = AUDIO_SKT_DISCONNECTED;
+        if ((out->common.state != AUDIO_A2DP_STATE_SUSPENDED) &&
+                (out->common.state != AUDIO_A2DP_STATE_STOPPING)) {
+            out->common.state = AUDIO_A2DP_STATE_STOPPED;
+        } else {
+            ALOGE("%s: write failed : stream suspended, avoid resetting state", __FUNCTION__);
+        }
+        goto error;
+    }
+
+    pthread_mutex_unlock(&out->common.lock);
+    return bytes;
+
+error:
+    pthread_mutex_unlock(&out->common.lock);
+    us_delay = calc_audiotime(out->common.cfg, bytes);
+    usleep(us_delay);
+    return bytes;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
+    ALOGV("format 0x%x", out->common.cfg.format);
+    return out->common.cfg.format;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
+
+    ALOGV("channels 0x%" PRIx32, out->common.cfg.channel_flags);
+
+    return out->common.cfg.channel_flags;
+}
+
+
+int audio_extn_bt_hal_load(void **handle)
+{
+    hw_module_t *mod;
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = malloc(sizeof(bt_hal_module_t));
+    if (bt == NULL){
+        ALOGE("%s: Memory allocation failed!", __FUNCTION__);
+        status = -ENOMEM;
+        goto EXIT;
+    }
+
+    *handle = bt;
+
+    status = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID/*_A2DP*/,
+                                    (const char*)"a2dp",
+                                    (const hw_module_t**)&mod);
+    if (status) {
+        ALOGE("%s: Could not get a2dp hardware module", __FUNCTION__);
+        goto EXIT;
+    }
+
+    bt->a2dp_device = calloc(1, sizeof(struct audio_hw_device));
+    if (!bt->a2dp_device)
+        return -ENOMEM;
+
+    bt->a2dp_device->common.module = (struct hw_module_t *) mod;
+
+    if (status) {
+        ALOGE("%s: couldn't open a2dp audio hw device", __FUNCTION__);
+        goto EXIT;
+    }
+
+EXIT:
+    return status;
+}
+
+int audio_extn_bt_hal_unload(void *handle)
+{
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    if (!bt->a2dp_device) {
+        ALOGE("%s: No active A2dp output found", __FUNCTION__);
+        goto EXIT;
+    }
+
+    if (bt->a2dp_output != NULL) {
+        audio_extn_bt_hal_close_output_stream(bt);
+        bt->a2dp_output = NULL;
+    }
+
+    if (bt->reformatted_buf != NULL) {
+        free(bt->reformatted_buf);
+        bt->reformatted_buf = NULL;
+    }
+
+    free(bt->a2dp_device);
+
+EXIT:
+    return status;
+}
+
+int audio_extn_bt_hal_open_output_stream(void *handle, int in_rate, audio_channel_mask_t channel_mask, int bit_width)
+{
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    if (bt->a2dp_device == NULL) {
+        ALOGE("%s: Invalid device instance!", __FUNCTION__);
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    if (channel_mask != AUDIO_CHANNEL_OUT_STEREO && channel_mask != AUDIO_CHANNEL_OUT_MONO) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    if (bit_width != CODEC_BACKEND_DEFAULT_BIT_WIDTH) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt->in_channel_mask = channel_mask;
+    bt->bit_width = bit_width;
+
+    bt->a2dp_output = (struct a2dp_stream_out *)calloc(1, sizeof(struct a2dp_stream_out));
+
+    if (!bt->a2dp_output) {
+        status = -ENOMEM;
+        goto EXIT;
+    }
+
+    lib_handle = dlopen("libbthost_if.so", RTLD_NOW);
+    if (!lib_handle)
+    {
+        ALOGV("Failed to load bthost-ipc library %s",dlerror());
+        status = -EINVAL;
+        goto EXIT;
+    }
+    else
+    {
+        ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE");
+        if (!ipc_if)
+        {
+            ALOGE("%s: Failed to load BT IPC library symbol", __FUNCTION__);
+            status  = -EINVAL;
+            goto EXIT;
+        }
+    }
+
+    bt->a2dp_output->stream.common.get_channels = out_get_channels;
+    bt->a2dp_output->stream.common.get_format = out_get_format;
+    bt->a2dp_output->stream.write = out_write;
+
+    /* initialize a2dp specifics */
+    ipc_if->a2dp_stream_common_init(&bt->a2dp_output->common);
+    bt->a2dp_output->common.cfg.channel_flags = AUDIO_STREAM_DEFAULT_CHANNEL_FLAG;
+    bt->a2dp_output->common.cfg.format = AUDIO_FORMAT_PCM_8_24_BIT;
+    bt->a2dp_output->common.cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+
+    /* set output bt->config values */
+
+    bt->config.format = bt->a2dp_output->common.cfg.format;
+    bt->config.sample_rate = bt->a2dp_output->common.cfg.rate;
+    bt->config.channel_mask = bt->a2dp_output->common.cfg.channel_flags;
+
+    ipc_if->a2dp_open_ctrl_path(&bt->a2dp_output->common);
+
+    if (bt->a2dp_output->common.ctrl_fd == AUDIO_SKT_DISCONNECTED)
+    {
+        ALOGE("%s: ctrl socket failed to connect (%s)", __FUNCTION__, strerror(errno));
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    if (ipc_if->a2dp_command(&bt->a2dp_output->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) {
+        ALOGI("Streaming mode set successfully");
+    }
+    /* Delay to ensure Headset is in proper state when START is initiated
+       from DUT immediately after the connection due to ongoing music playback. */
+    usleep(250000);
+    if (status) {
+        ALOGE("%s: Failed to open output stream for a2dp: status %d", __FUNCTION__, status);
+        goto EXIT;
+    } else {
+
+        if (in_rate != 44100) {
+            bt_create_resampler(in_rate, bt);
+        }
+        if (bt->config.format != AUDIO_FORMAT_PCM_16_BIT) {
+            bt->reformatted_buf = malloc(DEFAULT_BUF_SIZE * 4);
+            if (bt->reformatted_buf == NULL) {
+                ALOGE("%s: memory allocation failed!", __FUNCTION__);
+                status = -ENOMEM;
+                goto EXIT;
+            }
+        }
+    }
+
+EXIT:
+    if (status != 0 && bt->a2dp_output != NULL) {
+        free(bt->a2dp_output);
+        bt->a2dp_output = NULL;
+    }
+    return status;
+}
+
+int audio_extn_bt_hal_close_output_stream(void *handle)
+{
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    if (bt->a2dp_device == NULL) {
+        ALOGI("%s: No active A2dp output found", __FUNCTION__);
+        bt->a2dp_output = NULL;
+        goto EXIT;
+    }
+
+    if (bt->a2dp_output == NULL) {
+        ALOGE("%s: Invalid A2DP stream instance!", __FUNCTION__);
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt_destroy_resampler(bt);
+
+    pthread_mutex_lock(&bt->a2dp_output->common.lock);
+    if ((bt->a2dp_output->common.state == AUDIO_A2DP_STATE_STARTED) ||
+            (bt->a2dp_output->common.state == AUDIO_A2DP_STATE_STOPPING))
+        ipc_if->stop_audio_datapath(&bt->a2dp_output->common);
+
+    ipc_if->skt_disconnect(bt->a2dp_output->common.ctrl_fd);
+    bt->a2dp_output->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    if (lib_handle)
+        dlclose(lib_handle);
+    free(bt->a2dp_output);
+    pthread_mutex_unlock(&bt->a2dp_output->common.lock);
+    bt->a2dp_output = NULL;
+
+    if (bt->reformatted_buf != NULL) {
+        free(bt->reformatted_buf);
+        bt->reformatted_buf = NULL;
+    }
+EXIT:
+    return status;
+}
+
+int audio_extn_bt_hal_out_write(void *handle, void *buf, int size)
+{
+    int status = 0;
+    size_t in_frames = 0;
+    size_t out_frames = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    in_frames = size/(popcount(bt->in_channel_mask) * (bt->bit_width / 8));
+
+    if (bt->resampler != NULL) {
+        if (bt->data == NULL) {
+            bt->data = malloc(size);
+            if (bt->data == NULL){
+                ALOGE("%s: Memory Allocation failed!", __FUNCTION__);
+                status = -EINVAL;
+                goto EXIT;
+            }
+        }
+        out_frames = in_frames;
+        bt->resampler->resample_from_input(bt->resampler,
+                                           (int16_t *)buf,
+                                           &in_frames,
+                                           (int16_t *)bt->data,
+                                           &out_frames);
+
+        if (out_frames > 0) {
+            ALOGV("%s: writing %d bytes to BT device", __FUNCTION__, (int) (out_frames * popcount(bt->in_channel_mask) * (bt->bit_width / 8)));
+
+            if (bt->reformatted_buf != NULL) {
+                if (size > DEFAULT_BUF_SIZE) {
+                    bt->reformatted_buf = realloc(bt->reformatted_buf, size * 4);
+                    if (bt->reformatted_buf == NULL) {
+                        status = -ENOMEM;
+                        goto EXIT;
+                    }
+                }
+
+                memcpy_by_audio_format(bt->reformatted_buf, bt->config.format, bt->data, AUDIO_FORMAT_PCM_16_BIT, out_frames * popcount(bt->in_channel_mask));
+                (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->reformatted_buf, out_frames * popcount(bt->in_channel_mask) * (bt->bit_width/8) * 2);
+            } else {
+                (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->data, (out_frames * popcount(bt->in_channel_mask) * (bt->bit_width / 8)));
+            }
+        }
+    } else {
+        if (bt->reformatted_buf != NULL) {
+            if (size > DEFAULT_BUF_SIZE) {
+                bt->reformatted_buf = realloc(bt->reformatted_buf, size * 4);
+                if (bt->reformatted_buf == NULL) {
+                    status = -ENOMEM;
+                    goto EXIT;
+                }
+            }
+
+            memcpy_by_audio_format(bt->reformatted_buf, bt->config.format, buf, AUDIO_FORMAT_PCM_16_BIT, in_frames * popcount(bt->in_channel_mask));
+            (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, bt->reformatted_buf, size * 2);
+        } else {
+            (bt->a2dp_output)->stream.write(&bt->a2dp_output->stream, buf, size);
+        }
+    }
+
+EXIT:
+    return status;
+}
+
+int audio_extn_bt_hal_get_latency(void *handle) {
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+    if (bt->a2dp_output != NULL) {
+        status = out_get_latency(bt->a2dp_output);
+    } else {
+        status = -EINVAL;
+    }
+
+    return status;
+}
+
+struct audio_stream_out *audio_extn_bt_hal_get_output_stream(void *handle)
+{
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        return NULL;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    return (bt->a2dp_output == NULL)? NULL: &bt->a2dp_output->stream;
+}
+
+void *audio_extn_bt_hal_get_device(void *handle)
+{
+    int status = 0;
+    struct bt_hal_module *bt = NULL;
+
+    ALOGV("%s", __FUNCTION__);
+
+    if (handle == NULL) {
+        status = -EINVAL;
+        goto EXIT;
+    }
+
+    bt = (struct bt_hal_module *) handle;
+
+    return bt->a2dp_device;
+
+EXIT:
+    return NULL;
+}
diff --git a/hal/audio_extn/qaf.c b/hal/audio_extn/qaf.c
index d24d4d1..9739201 100644
--- a/hal/audio_extn/qaf.c
+++ b/hal/audio_extn/qaf.c
@@ -82,6 +82,12 @@
  */
 #define TRANSCODE_LATENCY(buffer_size, frame_size, encoder_output_in_bytes) ((buffer_size * frame_size) / encoder_output_in_bytes)
 
+/*
+ * QAF Latency to process buffers since out_write from primary HAL
+ */
+#define QAF_COMPRESS_OFFLOAD_PROCESSING_LATENCY 18
+#define QAF_PCM_OFFLOAD_PROCESSING_LATENCY 48
+
 #include <stdlib.h>
 #include <pthread.h>
 #include <errno.h>
@@ -135,6 +141,7 @@
     struct stream_out *qaf_compr_offload_out_mch;
     struct stream_out *qaf_compr_passthrough_out;
     struct stream_out *qaf_passthrough_out;
+    void *bt_hdl;
     bool hdmi_connect;
     int passthrough_enabled;
     int hdmi_sink_channels;
@@ -422,6 +429,8 @@
         out->platform_latency = latency + (COMPRESS_OFFLOAD_NUM_FRAGMENTS * PCM_OFFLOAD_BUFFER_SIZE \
                                        /(popcount(qaf_mod->qaf_compr_offload_out->channel_mask) * sizeof(short))) \
                                        +((platform_render_latency(qaf_mod->qaf_compr_offload_out->usecase) * qaf_mod->qaf_compr_offload_out->sample_rate) / 1000000LL);
+    } else if (audio_extn_bt_hal_get_output_stream(qaf_mod->bt_hdl) != NULL) {
+        out->platform_latency = latency + audio_extn_bt_hal_get_latency(qaf_mod->bt_hdl);
     } else if (NULL != qaf_mod->qaf_compr_passthrough_out) {
         out->platform_latency = latency + ((qaf_mod->qaf_compr_passthrough_out->format == AUDIO_FORMAT_AC3) ? TRANSCODE_LATENCY(COMPRESS_PASSTHROUGH_BUFFER_SIZE, DD_FRAME_SIZE, DD_ENCODER_OUTPUT_SIZE) : TRANSCODE_LATENCY(COMPRESS_PASSTHROUGH_BUFFER_SIZE, DD_FRAME_SIZE, DDP_ENCODER_OUTPUT_SIZE)) \
                                         + (COMPRESS_OFFLOAD_PLAYBACK_LATENCY *  qaf_mod->qaf_compr_passthrough_out->sample_rate/1000);
@@ -578,6 +587,15 @@
     } else {
         latency = PCM_OFFLOAD_PLAYBACK_LATENCY;
     }
+
+    if (audio_extn_bt_hal_get_output_stream(qaf_mod->bt_hdl) != NULL) {
+
+        if (is_offload_usecase(out->usecase)) {
+            latency = audio_extn_bt_hal_get_latency(qaf_mod->bt_hdl) + QAF_COMPRESS_OFFLOAD_PROCESSING_LATENCY;
+        } else {
+            latency = audio_extn_bt_hal_get_latency(qaf_mod->bt_hdl) + QAF_PCM_OFFLOAD_PROCESSING_LATENCY;
+        }
+    }
     ALOGV("%s: Latency %d", __func__, latency);
     return latency;
 }
@@ -598,6 +616,7 @@
     int ret;
     audio_output_flags_t flags;
     struct qaf* qaf_module = (struct qaf* ) prv_data;
+    struct audio_stream_out *bt_stream = NULL;
 
     ALOGV("%s device 0x%X, %d in event = %d", __func__, device, __LINE__, event_id);
 
@@ -709,8 +728,18 @@
             if (qaf_mod->qaf_compr_offload_out_mch)
                 ret = qaf_mod->qaf_compr_offload_out_mch->stream.write((struct audio_stream_out *) qaf_mod->qaf_compr_offload_out_mch, buf, size);
         } else {
+            bt_stream = audio_extn_bt_hal_get_output_stream(qaf_mod->bt_hdl);
+            if (bt_stream != NULL) {
+                if (qaf_mod->qaf_compr_offload_out) {
+                    adev_close_output_stream((struct audio_hw_device *) qaf_mod->adev,
+                                                 (struct audio_stream_out *) (qaf_mod->qaf_compr_offload_out));
+                    qaf_mod->qaf_compr_offload_out = NULL;
+                }
 
-            if (NULL == qaf_mod->qaf_compr_offload_out && qaf_mod->qaf_passthrough_out == NULL) {
+                audio_extn_bt_hal_out_write(qaf_mod->bt_hdl, buf, size);
+            }
+
+            if (NULL == qaf_mod->qaf_compr_offload_out && bt_stream == NULL && qaf_mod->qaf_passthrough_out == NULL) {
                 struct audio_config config;
                 audio_devices_t devices;
 
@@ -756,7 +785,7 @@
              * TODO:: Since this is mixed data,
              * need to identify to which stream the error should be sent
              */
-            if (qaf_mod->qaf_compr_offload_out)
+            if (bt_stream == NULL && qaf_mod->qaf_compr_offload_out)
                 ret = qaf_mod->qaf_compr_offload_out->stream.write((struct audio_stream_out *) qaf_mod->qaf_compr_offload_out, buf, size);
         }
 
@@ -1142,6 +1171,32 @@
         } else {
             if (!(qaf_mod->passthrough_enabled && qaf_mod->hdmi_connect))
                 qaf_mod->qaf_compr_offload_out->stream.common.set_parameters((struct audio_stream *) qaf_mod->qaf_compr_offload_out, kvpairs);
+
+            parms = str_parms_create_str(kvpairs);
+            if (!parms) {
+                ALOGE("str_parms_create_str failed!");
+            } else {
+                err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+                if (err >= 0) {
+                    val = atoi(value);
+                    if (val == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) { //BT routing
+                        if (audio_extn_bt_hal_get_output_stream(qaf_mod->bt_hdl) == NULL && audio_extn_bt_hal_get_device(qaf_mod->bt_hdl) != NULL) {
+                            ret = audio_extn_bt_hal_open_output_stream(qaf_mod->bt_hdl,
+                                    QAF_OUTPUT_SAMPLING_RATE,
+                                    AUDIO_CHANNEL_OUT_STEREO,
+                                    CODEC_BACKEND_DEFAULT_BIT_WIDTH);
+                            if (ret != 0) {
+                                ALOGE("%s: BT Output stream open failure!", __FUNCTION__);
+                            }
+                        }
+                    } else if (val != 0) {
+                        if (audio_extn_bt_hal_get_output_stream(qaf_mod->bt_hdl)!= NULL) {
+                            audio_extn_bt_hal_close_output_stream(qaf_mod->bt_hdl);
+                        }
+                    }
+                }
+                str_parms_destroy(parms);
+            }
         }
     }
 
@@ -1425,6 +1480,13 @@
                 str_parms_destroy(qaf_params);
             }
             qaf_mod->hdmi_connect = 1;
+        } else if (val & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) {
+            ALOGV("%s: Opening a2dp output...", __FUNCTION__);
+            status = audio_extn_bt_hal_load(&qaf_mod->bt_hdl);
+            if(status != 0) {
+                ALOGE("%s:Error opening BT module", __FUNCTION__);
+                return status;
+            }
         }
     }
 
@@ -1444,6 +1506,9 @@
             format_params = str_parms_to_str(qaf_params);
             qaf_mod->qaf_audio_session_set_param(qaf_mod->session_handle, format_params);
             str_parms_destroy(qaf_params);
+        } else if (val & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) {
+            ALOGV("%s: Closing a2dp output...", __FUNCTION__);
+            audio_extn_bt_hal_unload(qaf_mod->bt_hdl);
         }
     }