diff options
Diffstat (limited to 'services/audioflinger/A2dpAudioInterface.cpp')
| -rw-r--r-- | services/audioflinger/A2dpAudioInterface.cpp | 466 | 
1 files changed, 466 insertions, 0 deletions
| diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp new file mode 100644 index 000000000000..995e31ca0797 --- /dev/null +++ b/services/audioflinger/A2dpAudioInterface.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2008 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. + */ + +#include <math.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface() +//{ +//    AudioHardwareInterface* hw = 0; +// +//    hw = AudioHardwareInterface::create(); +//    LOGD("new A2dpAudioInterface(hw: %p)", hw); +//    hw = new A2dpAudioInterface(hw); +//    return hw; +//} + +A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) : +    mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ +    closeOutputStream((AudioStreamOut *)mOutput); +    delete mHardwareInterface; +} + +status_t A2dpAudioInterface::initCheck() +{ +    if (mHardwareInterface == 0) return NO_INIT; +    return mHardwareInterface->initCheck(); +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( +        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ +    if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { +        LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); +        return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); +    } + +    status_t err = 0; + +    // only one output stream allowed +    if (mOutput) { +        if (status) +            *status = -1; +        return NULL; +    } + +    // create new output stream +    A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); +    if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { +        mOutput = out; +        mOutput->setBluetoothEnabled(mBluetoothEnabled); +        mOutput->setSuspended(mSuspended); +    } else { +        delete out; +    } + +    if (status) +        *status = err; +    return mOutput; +} + +void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) { +    if (mOutput == 0 || mOutput != out) { +        mHardwareInterface->closeOutputStream(out); +    } +    else { +        delete mOutput; +        mOutput = 0; +    } +} + + +AudioStreamIn* A2dpAudioInterface::openInputStream( +        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, +        AudioSystem::audio_in_acoustics acoustics) +{ +    return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); +} + +void A2dpAudioInterface::closeInputStream(AudioStreamIn* in) +{ +    return mHardwareInterface->closeInputStream(in); +} + +status_t A2dpAudioInterface::setMode(int mode) +{ +    return mHardwareInterface->setMode(mode); +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ +    return mHardwareInterface->setMicMute(state); +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ +    return mHardwareInterface->getMicMute(state); +} + +status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) +{ +    AudioParameter param = AudioParameter(keyValuePairs); +    String8 value; +    String8 key; +    status_t status = NO_ERROR; + +    LOGV("setParameters() %s", keyValuePairs.string()); + +    key = "bluetooth_enabled"; +    if (param.get(key, value) == NO_ERROR) { +        mBluetoothEnabled = (value == "true"); +        if (mOutput) { +            mOutput->setBluetoothEnabled(mBluetoothEnabled); +        } +        param.remove(key); +    } +    key = String8("A2dpSuspended"); +    if (param.get(key, value) == NO_ERROR) { +        mSuspended = (value == "true"); +        if (mOutput) { +            mOutput->setSuspended(mSuspended); +        } +        param.remove(key); +    } + +    if (param.size()) { +        status_t hwStatus = mHardwareInterface->setParameters(param.toString()); +        if (status == NO_ERROR) { +            status = hwStatus; +        } +    } + +    return status; +} + +String8 A2dpAudioInterface::getParameters(const String8& keys) +{ +    AudioParameter param = AudioParameter(keys); +    AudioParameter a2dpParam = AudioParameter(); +    String8 value; +    String8 key; + +    key = "bluetooth_enabled"; +    if (param.get(key, value) == NO_ERROR) { +        value = mBluetoothEnabled ? "true" : "false"; +        a2dpParam.add(key, value); +        param.remove(key); +    } +    key = "A2dpSuspended"; +    if (param.get(key, value) == NO_ERROR) { +        value = mSuspended ? "true" : "false"; +        a2dpParam.add(key, value); +        param.remove(key); +    } + +    String8 keyValuePairs  = a2dpParam.toString(); + +    if (param.size()) { +        if (keyValuePairs != "") { +            keyValuePairs += ";"; +        } +        keyValuePairs += mHardwareInterface->getParameters(param.toString()); +    } + +    LOGV("getParameters() %s", keyValuePairs.string()); +    return keyValuePairs; +} + +size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ +    return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ +    return mHardwareInterface->setVoiceVolume(v); +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ +    return mHardwareInterface->setMasterVolume(v); +} + +status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) +{ +    return mHardwareInterface->dumpState(fd, args); +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : +    mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL), +    // assume BT enabled to start, this is safe because its only the +    // enabled->disabled transition we are worried about +    mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false) +{ +    // use any address by default +    strcpy(mA2dpAddress, "00:00:00:00:00:00"); +    init(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( +        uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ +    int lFormat = pFormat ? *pFormat : 0; +    uint32_t lChannels = pChannels ? *pChannels : 0; +    uint32_t lRate = pRate ? *pRate : 0; + +    LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate); + +    // fix up defaults +    if (lFormat == 0) lFormat = format(); +    if (lChannels == 0) lChannels = channels(); +    if (lRate == 0) lRate = sampleRate(); + +    // check values +    if ((lFormat != format()) || +            (lChannels != channels()) || +            (lRate != sampleRate())){ +        if (pFormat) *pFormat = format(); +        if (pChannels) *pChannels = channels(); +        if (pRate) *pRate = sampleRate(); +        return BAD_VALUE; +    } + +    if (pFormat) *pFormat = lFormat; +    if (pChannels) *pChannels = lChannels; +    if (pRate) *pRate = lRate; + +    mDevice = device; +    return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ +    LOGV("A2dpAudioStreamOut destructor"); +    standby(); +    close(); +    LOGV("A2dpAudioStreamOut destructor returning from close()"); +} + +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; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::init() +{ +    if (!mData) { +        status_t status = a2dp_init(44100, 2, &mData); +        if (status < 0) { +            LOGE("a2dp_init failed err: %d\n", status); +            mData = NULL; +            return status; +        } +        a2dp_set_sink(mData, mA2dpAddress); +    } + +    return 0; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() +{ +    int result = 0; + +    if (mClosing) { +        LOGV("Ignore standby, closing"); +        return result; +    } + +    Mutex::Autolock lock(mLock); + +    if (!mStandby) { +        result = a2dp_stop(mData); +        if (result == 0) +            mStandby = true; +    } + +    return result; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs) +{ +    AudioParameter param = AudioParameter(keyValuePairs); +    String8 value; +    String8 key = String8("a2dp_sink_address"); +    status_t status = NO_ERROR; +    int device; +    LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string()); + +    if (param.get(key, value) == NO_ERROR) { +        if (value.length() != strlen("00:00:00:00:00:00")) { +            status = BAD_VALUE; +        } else { +            setAddress(value.string()); +        } +        param.remove(key); +    } +    key = String8("closing"); +    if (param.get(key, value) == NO_ERROR) { +        mClosing = (value == "true"); +        param.remove(key); +    } +    key = AudioParameter::keyRouting; +    if (param.getInt(key, device) == NO_ERROR) { +        if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) { +            mDevice = device; +            status = NO_ERROR; +        } else { +            status = BAD_VALUE; +        } +        param.remove(key); +    } + +    if (param.size()) { +        status = BAD_VALUE; +    } +    return status; +} + +String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys) +{ +    AudioParameter param = AudioParameter(keys); +    String8 value; +    String8 key = String8("a2dp_sink_address"); + +    if (param.get(key, value) == NO_ERROR) { +        value = mA2dpAddress; +        param.add(key, value); +    } +    key = AudioParameter::keyRouting; +    if (param.get(key, value) == NO_ERROR) { +        param.addInt(key, (int)mDevice); +    } + +    LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string()); +    return param.toString(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) +{ +    Mutex::Autolock lock(mLock); + +    if (strlen(address) != strlen("00:00:00:00:00:00")) +        return -EINVAL; + +    strcpy(mA2dpAddress, address); +    if (mData) +        a2dp_set_sink(mData, mA2dpAddress); + +    return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled) +{ +    LOGD("setBluetoothEnabled %d", enabled); + +    Mutex::Autolock lock(mLock); + +    mBluetoothEnabled = enabled; +    if (!enabled) { +        return close_l(); +    } +    return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff) +{ +    LOGV("setSuspended %d", onOff); +    mSuspended = onOff; +    standby(); +    return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ +    Mutex::Autolock lock(mLock); +    LOGV("A2dpAudioStreamOut::close() calling close_l()"); +    return close_l(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l() +{ +    if (mData) { +        LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)"); +        a2dp_cleanup(mData); +        mData = NULL; +    } +    return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args) +{ +    return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames) +{ +    //TODO: enable when supported by driver +    return INVALID_OPERATION; +} + +}; // namespace android | 
