replicant-frameworks_native/libs/audioflinger/A2dpAudioInterface.cpp
Eric Laurent 2115412564 Fix issue 2174002: After rejecting Call when device ringtone is mute and playing music, audio is not transfered to BT device.
Added a workarouond to request the A2DP output standby directly to audio hardware when the sink is suspended as it seems that the suspend request often fails.

Also take into account resume requests received while a suspend request is pending.
2009-10-08 12:03:51 -07:00

459 lines
12 KiB
C++

/*
* 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()) {
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 * 1000000 / frameSize() / sampleRate());
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;
}
}; // namespace android