ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
{
    Mutex::Autolock lock(mLock);

    size_t remaining = bytes;
    status_t status = -1;

    if (!mBluetoothEnabled || mClosing || mSuspended) {
        LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
               mBluetoothEnabled %d, mClosing %d, mSuspended %d",
                mBluetoothEnabled, mClosing, mSuspended);
        goto Error;
    }

    status = init();
    if (status < 0)
        goto Error;

    while (remaining > 0) {
        status = a2dp_write(mData, buffer, remaining);
        if (status <= 0) {
            LOGE("a2dp_write failed err: %d\n", status);
            goto Error;
        }
        remaining -= status;
        buffer = ((char *)buffer) + status;
    }

    mStandby = false;

    return bytes;

Error:
    // Simulate audio output timing in case of error
    usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);

    return status;
}
static void *_out_buf_thread_func(void *context)
{
    struct astream_out *out = (struct astream_out *)context;

    pthread_mutex_lock(&out->buf_lock);

    while(!out->buf_thread_exit) {
        size_t frames;

        frames = _out_frames_ready_locked(out);
        while (frames && !out->buf_thread_exit) {
            int retries = MAX_WRITE_RETRIES;
            uint64_t now;
            uint32_t elapsed_us;

            while (frames > 0 && !out->buf_thread_exit) {
                int ret;
                uint32_t buffer_duration_us;
                /* PCM format is always 16bit stereo */
                size_t bytes = frames * sizeof(uint32_t);
                if (bytes > out->buffer_size) {
                    bytes = out->buffer_size;
                }

                pthread_mutex_lock(&out->lock);
                if (out->standby) {
                    /* abort and clear all pending frames if standby requested */
                    pthread_mutex_unlock(&out->lock);
                    frames = _out_frames_ready_locked(out);
                    _out_inc_rd_idx_locked(out, frames);
                    goto wait;
                }
                /* indicate to out_standby_stream_locked() that a2dp_write() is active */
                out->write_busy = true;
                pthread_mutex_unlock(&out->lock);
                pthread_mutex_unlock(&out->buf_lock);

                ret = a2dp_write(out->data, out->buf + out->buf_rd_idx, bytes);

                /* clear write_busy condition */
                pthread_mutex_lock(&out->buf_lock);
                pthread_mutex_lock(&out->lock);
                out->write_busy = false;
                pthread_cond_signal(&out->write_cond);
                pthread_mutex_unlock(&out->lock);

                if (ret < 0) {
                    LOGE("%s: a2dp_write failed (%d)\n", __func__, ret);
                    /* skip pending frames in case of write error */
                    _out_inc_rd_idx_locked(out, frames);
                    break;
                } else if (ret == 0) {
                    if (retries-- == 0) {
                        /* skip pending frames in case of multiple time out */
                        _out_inc_rd_idx_locked(out, frames);
                        break;
                    }
                    continue;
                }
                ret /= sizeof(uint32_t);
                _out_inc_rd_idx_locked(out, ret);
                frames -= 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;
                buffer_duration_us = ((ret * 1000) / out->sample_rate) * 1000;

                if (elapsed_us < (buffer_duration_us / 4)) {
                    LOGV("A2DP sink runs too fast");
                    usleep(buffer_duration_us - elapsed_us);
                }
                out->last_write_time = now;

            }
            frames = _out_frames_ready_locked(out);
        }
wait:
        if (!out->buf_thread_exit) {
            pthread_cond_wait(&out->buf_cond, &out->buf_lock);
        }
    }
    pthread_mutex_unlock(&out->buf_lock);
    return NULL;
}