aosp aidl bluetooth audio hal implementation

Bug: 228804498
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Id5fb60fe53ead9f5d2e6ecbb9988a23835cf2509
(cherry picked from commit b03b5c4a06)
Merged-In: Id5fb60fe53ead9f5d2e6ecbb9988a23835cf2509
This commit is contained in:
Mikhail Naganov 2023-07-26 13:13:35 -07:00
parent 4bb14a9cdf
commit 59df2f67e4
13 changed files with 1408 additions and 2 deletions

View file

@ -82,6 +82,9 @@ cc_library {
"alsa/ModuleAlsa.cpp",
"alsa/StreamAlsa.cpp",
"alsa/Utils.cpp",
"bluetooth/DevicePortProxy.cpp",
"bluetooth/ModuleBluetooth.cpp",
"bluetooth/StreamBluetooth.cpp",
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/RemoteSubmixUtils.cpp",
"r_submix/SubmixRoute.cpp",
@ -105,7 +108,9 @@ cc_library {
"audio_policy_engine_configuration_aidl_default",
],
shared_libs: [
"android.hardware.bluetooth.audio-V3-ndk",
"libaudio_aidl_conversion_common_ndk",
"libbluetooth_audio_session_aidl",
"libmedia_helper",
"libstagefright_foundation",
],
@ -136,7 +141,9 @@ cc_binary {
"libaudioserviceexampleimpl",
],
shared_libs: [
"android.hardware.bluetooth.audio-V3-ndk",
"libaudio_aidl_conversion_common_ndk",
"libbluetooth_audio_session_aidl",
"libmedia_helper",
"libstagefright_foundation",
],

View file

@ -561,4 +561,86 @@ std::unique_ptr<Configuration> getStubConfiguration() {
return std::make_unique<Configuration>(configuration);
}
// Bluetooth configuration:
//
// Device ports:
// * "BT A2DP Out", OUT_DEVICE, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
// * "BT A2DP Headphones", OUT_HEADSET, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
// * "BT A2DP Speaker", OUT_SPEAKER, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
// * "BT Hearing Aid Out", OUT_HEARING_AID, CONNECTION_WIRELESS
// - no profiles specified
//
// Mix ports:
// * "a2dp output", 1 max open, 1 max active stream
// - no profiles specified
// * "hearing aid output", 1 max open, 1 max active stream
// - profile PCM 16-bit; STEREO; 16000, 24000
//
// Routes:
// "a2dp output" -> "BT A2DP Out"
// "a2dp output" -> "BT A2DP Headphones"
// "a2dp output" -> "BT A2DP Speaker"
// "hearing aid output" -> "BT Hearing Aid Out"
//
std::unique_ptr<Configuration> getBluetoothConfiguration() {
static const Configuration configuration = []() {
const std::vector<AudioProfile> standardPcmAudioProfiles = {
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
{44100, 48000, 88200, 96000})};
Configuration c;
// Device ports
AudioPort btOutDevice =
createPort(c.nextPortId++, "BT A2DP Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
c.ports.push_back(btOutDevice);
c.connectedProfiles[btOutDevice.id] = standardPcmAudioProfiles;
AudioPort btOutHeadphone =
createPort(c.nextPortId++, "BT A2DP Headphones", 0, false,
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
c.ports.push_back(btOutHeadphone);
c.connectedProfiles[btOutHeadphone.id] = standardPcmAudioProfiles;
AudioPort btOutSpeaker =
createPort(c.nextPortId++, "BT A2DP Speaker", 0, false,
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
c.ports.push_back(btOutSpeaker);
c.connectedProfiles[btOutSpeaker.id] = standardPcmAudioProfiles;
AudioPort btOutHearingAid =
createPort(c.nextPortId++, "BT Hearing Aid Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0,
AudioDeviceDescription::CONNECTION_WIRELESS));
c.ports.push_back(btOutHearingAid);
c.connectedProfiles[btOutHearingAid.id] = std::vector<AudioProfile>(
{createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
// Mix ports
AudioPort btInMix =
createPort(c.nextPortId++, "a2dp output", 0, true, createPortMixExt(1, 1));
c.ports.push_back(btInMix);
AudioPort btHeadsetInMix =
createPort(c.nextPortId++, "hearing aid output", 0, true, createPortMixExt(1, 1));
btHeadsetInMix.profiles.push_back(createProfile(
PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
c.ports.push_back(btHeadsetInMix);
c.routes.push_back(createRoute({btInMix}, btOutDevice));
c.routes.push_back(createRoute({btInMix}, btOutHeadphone));
c.routes.push_back(createRoute({btInMix}, btOutSpeaker));
c.routes.push_back(createRoute({btHeadsetInMix}, btOutHearingAid));
return c;
}();
return std::make_unique<Configuration>(configuration);
}
} // namespace aidl::android::hardware::audio::core::internal

View file

@ -26,6 +26,7 @@
#include <error/expected_utils.h>
#include "core-impl/Module.h"
#include "core-impl/ModuleBluetooth.h"
#include "core-impl/ModulePrimary.h"
#include "core-impl/ModuleRemoteSubmix.h"
#include "core-impl/ModuleStub.h"
@ -117,6 +118,8 @@ std::shared_ptr<Module> Module::createInstance(Type type) {
return ndk::SharedRefBase::make<ModuleStub>();
case Type::USB:
return ndk::SharedRefBase::make<ModuleUsb>();
case Type::BLUETOOTH:
return ndk::SharedRefBase::make<ModuleBluetooth>();
}
}
@ -134,6 +137,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) {
case Module::Type::USB:
os << "usb";
break;
case Module::Type::BLUETOOTH:
os << "bluetooth";
break;
}
return os;
}
@ -301,6 +307,9 @@ std::unique_ptr<internal::Configuration> Module::initializeConfig() {
case Type::USB:
config = std::move(internal::getUsbConfiguration());
break;
case Type::BLUETOOTH:
config = std::move(internal::getBluetoothConfiguration());
break;
}
return config;
}

View file

@ -19,6 +19,11 @@
<version>1</version>
<fqname>IModule/usb</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.audio.core</name>
<version>1</version>
<fqname>IModule/bluetooth</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.audio.core</name>
<version>1</version>

View file

@ -0,0 +1,562 @@
/*
* Copyright 2023 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.
*/
#define LOG_TAG "AHAL_BluetoothPortProxy"
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <audio_utils/primitives.h>
#include <inttypes.h>
#include <log/log.h>
#include "core-impl/DevicePortProxy.h"
namespace android::bluetooth::audio::aidl {
namespace {
// The maximum time to wait in std::condition_variable::wait_for()
constexpr unsigned int kMaxWaitingTimeMs = 4500;
} // namespace
using ::aidl::android::hardware::audio::common::SinkMetadata;
using ::aidl::android::hardware::audio::common::SourceMetadata;
using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
using ::aidl::android::hardware::bluetooth::audio::SessionType;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
using ::android::base::StringPrintf;
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
switch (state) {
case BluetoothStreamState::DISABLED:
return os << "DISABLED";
case BluetoothStreamState::STANDBY:
return os << "STANDBY";
case BluetoothStreamState::STARTING:
return os << "STARTING";
case BluetoothStreamState::STARTED:
return os << "STARTED";
case BluetoothStreamState::SUSPENDING:
return os << "SUSPENDING";
case BluetoothStreamState::UNKNOWN:
return os << "UNKNOWN";
default:
return os << android::base::StringPrintf("%#hhx", state);
}
}
BluetoothAudioPortAidl::BluetoothAudioPortAidl()
: mCookie(::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined),
mState(BluetoothStreamState::DISABLED),
mSessionType(SessionType::UNKNOWN) {}
BluetoothAudioPortAidl::~BluetoothAudioPortAidl() {
unregisterPort();
}
bool BluetoothAudioPortAidl::registerPort(const AudioDeviceDescription& description) {
if (inUse()) {
LOG(ERROR) << __func__ << debugMessage() << " already in use";
return false;
}
if (!initSessionType(description)) return false;
auto control_result_cb = [port = this](uint16_t cookie, bool start_resp,
const BluetoothAudioStatus& status) {
(void)start_resp;
port->controlResultHandler(cookie, status);
};
auto session_changed_cb = [port = this](uint16_t cookie) {
port->sessionChangedHandler(cookie);
};
// TODO: Add audio_config_changed_cb
PortStatusCallbacks cbacks = {
.control_result_cb_ = control_result_cb,
.session_changed_cb_ = session_changed_cb,
};
mCookie = BluetoothAudioSessionControl::RegisterControlResultCback(mSessionType, cbacks);
auto isOk = (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
if (isOk) {
std::lock_guard guard(mCvMutex);
mState = BluetoothStreamState::STANDBY;
}
LOG(DEBUG) << __func__ << debugMessage();
return isOk;
}
bool BluetoothAudioPortAidl::initSessionType(const AudioDeviceDescription& description) {
if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP &&
(description.type == AudioDeviceType::OUT_DEVICE ||
description.type == AudioDeviceType::OUT_HEADPHONE ||
description.type == AudioDeviceType::OUT_SPEAKER)) {
LOG(VERBOSE) << __func__
<< ": device=AUDIO_DEVICE_OUT_BLUETOOTH_A2DP (HEADPHONES/SPEAKER) ("
<< description.toString() << ")";
mSessionType = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
description.type == AudioDeviceType::OUT_HEARING_AID) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_HEARING_AID (MEDIA/VOICE) ("
<< description.toString() << ")";
mSessionType = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
description.type == AudioDeviceType::OUT_HEADSET) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_HEADSET (MEDIA/VOICE) ("
<< description.toString() << ")";
mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
description.type == AudioDeviceType::OUT_SPEAKER) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_SPEAKER (MEDIA) ("
<< description.toString() << ")";
mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
description.type == AudioDeviceType::IN_HEADSET) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_IN_BLE_HEADSET (VOICE) ("
<< description.toString() << ")";
mSessionType = SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
description.type == AudioDeviceType::OUT_BROADCAST) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_BROADCAST (MEDIA) ("
<< description.toString() << ")";
mSessionType = SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
} else {
LOG(ERROR) << __func__ << ": unknown device=" << description.toString();
return false;
}
if (!BluetoothAudioSessionControl::IsSessionReady(mSessionType)) {
LOG(ERROR) << __func__ << ": device=" << description.toString()
<< ", session_type=" << toString(mSessionType) << " is not ready";
return false;
}
return true;
}
void BluetoothAudioPortAidl::unregisterPort() {
if (!inUse()) {
LOG(WARNING) << __func__ << ": BluetoothAudioPortAidl is not in use";
return;
}
BluetoothAudioSessionControl::UnregisterControlResultCback(mSessionType, mCookie);
mCookie = ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined;
LOG(VERBOSE) << __func__ << debugMessage() << " port unregistered";
}
void BluetoothAudioPortAidl::controlResultHandler(uint16_t cookie,
const BluetoothAudioStatus& status) {
std::lock_guard guard(mCvMutex);
if (!inUse()) {
LOG(ERROR) << "control_result_cb: BluetoothAudioPortAidl is not in use";
return;
}
if (mCookie != cookie) {
LOG(ERROR) << "control_result_cb: proxy of device port (cookie="
<< StringPrintf("%#hx", cookie) << ") is corrupted";
return;
}
BluetoothStreamState previous_state = mState;
LOG(INFO) << "control_result_cb:" << debugMessage() << ", previous_state=" << previous_state
<< ", status=" << toString(status);
switch (previous_state) {
case BluetoothStreamState::STARTED:
/* Only Suspend signal can be send in STARTED state*/
if (status == BluetoothAudioStatus::RECONFIGURATION ||
status == BluetoothAudioStatus::SUCCESS) {
mState = BluetoothStreamState::STANDBY;
} else {
LOG(WARNING) << StringPrintf(
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
"previous_state=%#hhx",
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
previous_state);
}
break;
case BluetoothStreamState::STARTING:
if (status == BluetoothAudioStatus::SUCCESS) {
mState = BluetoothStreamState::STARTED;
} else {
// Set to standby since the stack may be busy switching between outputs
LOG(WARNING) << StringPrintf(
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
"previous_state=%#hhx",
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
previous_state);
mState = BluetoothStreamState::STANDBY;
}
break;
case BluetoothStreamState::SUSPENDING:
if (status == BluetoothAudioStatus::SUCCESS) {
mState = BluetoothStreamState::STANDBY;
} else {
// It will be failed if the headset is disconnecting, and set to disable
// to wait for re-init again
LOG(WARNING) << StringPrintf(
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
"previous_state=%#hhx",
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
previous_state);
mState = BluetoothStreamState::DISABLED;
}
break;
default:
LOG(ERROR) << "control_result_cb: unexpected previous_state="
<< StringPrintf(
"control_result_cb: status=%s failure for session_type= %s, "
"cookie=%#hx, previous_state=%#hhx",
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
previous_state);
return;
}
mInternalCv.notify_all();
}
void BluetoothAudioPortAidl::sessionChangedHandler(uint16_t cookie) {
std::lock_guard guard(mCvMutex);
if (!inUse()) {
LOG(ERROR) << "session_changed_cb: BluetoothAudioPortAidl is not in use";
return;
}
if (mCookie != cookie) {
LOG(ERROR) << "session_changed_cb: proxy of device port (cookie="
<< StringPrintf("%#hx", cookie) << ") is corrupted";
return;
}
BluetoothStreamState previous_state = mState;
LOG(VERBOSE) << "session_changed_cb:" << debugMessage()
<< ", previous_state=" << previous_state;
mState = BluetoothStreamState::DISABLED;
mInternalCv.notify_all();
}
bool BluetoothAudioPortAidl::inUse() const {
return (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
}
bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t* interval_us) const {
if (!interval_us) {
LOG(ERROR) << __func__ << ": bad input arg";
return false;
}
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
const AudioConfiguration& hal_audio_cfg =
BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
return false;
}
*interval_us = hal_audio_cfg.get<AudioConfiguration::pcmConfig>().dataIntervalUs;
return true;
}
bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration* audio_cfg) const {
if (!audio_cfg) {
LOG(ERROR) << __func__ << ": bad input arg";
return false;
}
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
const AudioConfiguration& hal_audio_cfg =
BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
return false;
}
*audio_cfg = hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
LOG(VERBOSE) << __func__ << debugMessage() << ", state*=" << getState() << ", PcmConfig=["
<< audio_cfg->toString() << "]";
if (audio_cfg->channelMode == ChannelMode::UNKNOWN) {
return false;
}
return true;
}
bool BluetoothAudioPortAidl::condWaitState(BluetoothStreamState state) {
const auto waitTime = std::chrono::milliseconds(kMaxWaitingTimeMs);
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
switch (state) {
case BluetoothStreamState::STARTING: {
LOG(VERBOSE) << __func__ << debugMessage() << " waiting for STARTED";
mInternalCv.wait_for(lock, waitTime, [this] {
base::ScopedLockAssertion lock_assertion(mCvMutex);
return mState != BluetoothStreamState::STARTING;
});
return mState == BluetoothStreamState::STARTED;
}
case BluetoothStreamState::SUSPENDING: {
LOG(VERBOSE) << __func__ << debugMessage() << " waiting for SUSPENDED";
mInternalCv.wait_for(lock, waitTime, [this] {
base::ScopedLockAssertion lock_assertion(mCvMutex);
return mState != BluetoothStreamState::SUSPENDING;
});
return mState == BluetoothStreamState::STANDBY;
}
default:
LOG(WARNING) << __func__ << debugMessage() << " waiting for KNOWN";
return false;
}
return false;
}
bool BluetoothAudioPortAidl::start() {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " request";
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
if (mState == BluetoothStreamState::STARTED) {
return true; // nop, return
} else if (mState == BluetoothStreamState::SUSPENDING ||
mState == BluetoothStreamState::STARTING) {
/* If port is in transient state, give some time to respond */
auto state_ = mState;
lock.unlock();
if (!condWaitState(state_)) {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
return false;
}
}
}
bool retval = false;
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
if (mState == BluetoothStreamState::STARTED) {
retval = true;
} else if (mState == BluetoothStreamState::STANDBY) {
mState = BluetoothStreamState::STARTING;
lock.unlock();
if (BluetoothAudioSessionControl::StartStream(mSessionType)) {
retval = condWaitState(BluetoothStreamState::STARTING);
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
<< " Hal fails";
}
}
}
if (retval) {
LOG(INFO) << __func__ << debugMessage() << ", state=" << getState()
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " done";
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
}
return retval; // false if any failure like timeout
}
bool BluetoothAudioPortAidl::suspend() {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
if (mState == BluetoothStreamState::STANDBY) {
return true; // nop, return
} else if (mState == BluetoothStreamState::SUSPENDING ||
mState == BluetoothStreamState::STARTING) {
/* If port is in transient state, give some time to respond */
auto state_ = mState;
lock.unlock();
if (!condWaitState(state_)) {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
return false;
}
}
}
bool retval = false;
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
if (mState == BluetoothStreamState::STANDBY) {
retval = true;
} else if (mState == BluetoothStreamState::STARTED) {
mState = BluetoothStreamState::SUSPENDING;
lock.unlock();
if (BluetoothAudioSessionControl::SuspendStream(mSessionType)) {
retval = condWaitState(BluetoothStreamState::SUSPENDING);
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
<< " Hal fails";
}
}
}
if (retval) {
LOG(INFO) << __func__ << debugMessage() << ", state=" << getState() << " done";
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
}
return retval; // false if any failure like timeout
}
void BluetoothAudioPortAidl::stop() {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return;
}
std::lock_guard guard(mCvMutex);
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
if (mState != BluetoothStreamState::DISABLED) {
BluetoothAudioSessionControl::StopStream(mSessionType);
mState = BluetoothStreamState::DISABLED;
}
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " done";
}
size_t BluetoothAudioPortAidlOut::writeData(const void* buffer, size_t bytes) const {
if (!buffer) {
LOG(ERROR) << __func__ << ": bad input arg";
return 0;
}
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return 0;
}
if (!mIsStereoToMono) {
return BluetoothAudioSessionControl::OutWritePcmData(mSessionType, buffer, bytes);
}
// WAR to mix the stereo into Mono (16 bits per sample)
const size_t write_frames = bytes >> 2;
if (write_frames == 0) return 0;
auto src = static_cast<const int16_t*>(buffer);
std::unique_ptr<int16_t[]> dst{new int16_t[write_frames]};
downmix_to_mono_i16_from_stereo_i16(dst.get(), src, write_frames);
// a frame is 16 bits, and the size of a mono frame is equal to half a stereo.
auto totalWrite = BluetoothAudioSessionControl::OutWritePcmData(mSessionType, dst.get(),
write_frames * 2);
return totalWrite * 2;
}
size_t BluetoothAudioPortAidlIn::readData(void* buffer, size_t bytes) const {
if (!buffer) {
LOG(ERROR) << __func__ << ": bad input arg";
return 0;
}
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return 0;
}
return BluetoothAudioSessionControl::InReadPcmData(mSessionType, buffer, bytes);
}
bool BluetoothAudioPortAidl::getPresentationPosition(
PresentationPosition& presentation_position) const {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
bool retval = BluetoothAudioSessionControl::GetPresentationPosition(mSessionType,
presentation_position);
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
<< presentation_position.toString();
return retval;
}
bool BluetoothAudioPortAidl::updateSourceMetadata(const SourceMetadata& source_metadata) const {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
<< source_metadata.tracks.size() << " track(s)";
if (source_metadata.tracks.size() == 0) return true;
return BluetoothAudioSessionControl::UpdateSourceMetadata(mSessionType, source_metadata);
}
bool BluetoothAudioPortAidl::updateSinkMetadata(const SinkMetadata& sink_metadata) const {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
<< sink_metadata.tracks.size() << " track(s)";
if (sink_metadata.tracks.size() == 0) return true;
return BluetoothAudioSessionControl::UpdateSinkMetadata(mSessionType, sink_metadata);
}
BluetoothStreamState BluetoothAudioPortAidl::getState() const {
return mState;
}
bool BluetoothAudioPortAidl::setState(BluetoothStreamState state) {
if (!inUse()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return false;
}
std::lock_guard guard(mCvMutex);
LOG(DEBUG) << __func__ << ": BluetoothAudioPortAidl old state = " << mState
<< " new state = " << state;
mState = state;
return true;
}
bool BluetoothAudioPortAidl::isA2dp() const {
return mSessionType == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
mSessionType == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}
bool BluetoothAudioPortAidl::isLeAudio() const {
return mSessionType == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
mSessionType == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
mSessionType == SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
mSessionType == SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}
std::string BluetoothAudioPortAidl::debugMessage() const {
return StringPrintf(": session_type=%s, cookie=%#hx", toString(mSessionType).c_str(), mCookie);
}
} // namespace android::bluetooth::audio::aidl

View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 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.
*/
#define LOG_TAG "AHAL_ModuleBluetooth"
#include <android-base/logging.h>
#include "core-impl/ModuleBluetooth.h"
#include "core-impl/StreamBluetooth.h"
namespace aidl::android::hardware::audio::core {
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleBluetooth::setMicMute(bool in_mute __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleBluetooth::createInputStream(
StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata,
offloadInfo);
}
ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,298 @@
/*
* Copyright (C) 2023 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.
*/
#define LOG_TAG "AHAL_StreamBluetooth"
#include <Utils.h>
#include <android-base/logging.h>
#include <audio_utils/clock.h>
#include "BluetoothAudioSessionControl.h"
#include "core-impl/StreamBluetooth.h"
namespace aidl::android::hardware::audio::core {
using ::aidl::android::hardware::audio::common::SinkMetadata;
using ::aidl::android::hardware::audio::common::SourceMetadata;
using ::aidl::android::hardware::audio::core::VendorParameter;
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
using ::aidl::android::media::audio::common::AudioChannelLayout;
using ::aidl::android::media::audio::common::AudioDevice;
using ::aidl::android::media::audio::common::AudioDeviceAddress;
using ::aidl::android::media::audio::common::AudioFormatDescription;
using ::aidl::android::media::audio::common::AudioFormatType;
using ::aidl::android::media::audio::common::AudioOffloadInfo;
using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
using ::aidl::android::media::audio::common::MicrophoneInfo;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
using ::android::bluetooth::audio::aidl::BluetoothStreamState;
constexpr int kBluetoothDefaultInputBufferMs = 20;
constexpr int kBluetoothDefaultOutputBufferMs = 10;
// constexpr int kBluetoothSpatializerOutputBufferMs = 10;
size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) {
return (durationUs * sampleRate) / 1000000;
}
// pcm configuration params are not really used by the module
StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(context, metadata),
mSampleRate(getContext().getSampleRate()),
mChannelLayout(getContext().getChannelLayout()),
mFormat(getContext().getFormat()),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)) {
mPreferredDataIntervalUs =
mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs;
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
mIsInitialized = false;
mIsReadyToClose = false;
}
::android::status_t StreamBluetooth::init() {
return ::android::OK; // defering this till we get AudioDeviceDescription
}
const StreamCommonInterface::ConnectedDevices& StreamBluetooth::getConnectedDevices() const {
std::lock_guard guard(mLock);
return StreamCommonImpl::getConnectedDevices();
}
ndk::ScopedAStatus StreamBluetooth::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
if (mIsInput && connectedDevices.size() > 1) {
LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
<< ") for input stream";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
for (const auto& connectedDevice : connectedDevices) {
if (connectedDevice.address.getTag() != AudioDeviceAddress::mac) {
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
std::lock_guard guard(mLock);
RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
mIsInitialized = false; // updated connected device list, need initialization
return ndk::ScopedAStatus::ok();
}
::android::status_t StreamBluetooth::drain(StreamDescriptor::DrainMode) {
usleep(1000);
return ::android::OK;
}
::android::status_t StreamBluetooth::flush() {
usleep(1000);
return ::android::OK;
}
::android::status_t StreamBluetooth::pause() {
return standby();
}
::android::status_t StreamBluetooth::transfer(void* buffer, size_t frameCount,
size_t* actualFrameCount, int32_t* latencyMs) {
std::lock_guard guard(mLock);
if (!mIsInitialized || mIsReadyToClose) {
// 'setConnectedDevices' has been called or stream is ready to close, so no transfers
*actualFrameCount = 0;
*latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
return ::android::OK;
}
*actualFrameCount = 0;
*latencyMs = 0;
for (auto proxy : mBtDeviceProxies) {
if (!proxy->start()) {
LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to start ";
return -EIO;
}
const size_t fc = std::min(frameCount, mPreferredFrameCount);
const size_t bytesToTransfer = fc * mFrameSizeBytes;
if (mIsInput) {
const size_t totalRead = proxy->readData(buffer, bytesToTransfer);
*actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes);
} else {
const size_t totalWrite = proxy->writeData(buffer, bytesToTransfer);
*actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes);
}
PresentationPosition presentation_position;
if (!proxy->getPresentationPosition(presentation_position)) {
LOG(ERROR) << __func__ << ": getPresentationPosition returned error ";
return ::android::UNKNOWN_ERROR;
}
*latencyMs =
std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos /
NANOS_PER_MILLISECOND));
}
return ::android::OK;
}
::android::status_t StreamBluetooth::initialize() {
if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory service not available";
return ::android::UNKNOWN_ERROR;
}
if (StreamCommonImpl::getConnectedDevices().empty()) {
LOG(ERROR) << __func__ << ", has no connected devices";
return ::android::NO_INIT;
}
// unregister older proxies (if any)
for (auto proxy : mBtDeviceProxies) {
proxy->stop();
proxy->unregisterPort();
}
mBtDeviceProxies.clear();
for (auto it = StreamCommonImpl::getConnectedDevices().begin();
it != StreamCommonImpl::getConnectedDevices().end(); ++it) {
std::shared_ptr<BluetoothAudioPortAidl> proxy =
mIsInput ? std::shared_ptr<BluetoothAudioPortAidl>(
std::make_shared<BluetoothAudioPortAidlIn>())
: std::shared_ptr<BluetoothAudioPortAidl>(
std::make_shared<BluetoothAudioPortAidlOut>());
if (proxy->registerPort(it->type)) {
LOG(ERROR) << __func__ << ": cannot init HAL";
return ::android::UNKNOWN_ERROR;
}
PcmConfiguration config;
if (!proxy->loadAudioConfig(&config)) {
LOG(ERROR) << __func__ << ": state=" << proxy->getState()
<< " failed to get audio config";
return ::android::UNKNOWN_ERROR;
}
// TODO: Ensure minimum duration for spatialized output?
// WAR to support Mono / 16 bits per sample as the Bluetooth stack required
if (!mIsInput && config.channelMode == ChannelMode::MONO && config.bitsPerSample == 16) {
proxy->forcePcmStereoToMono(true);
config.channelMode = ChannelMode::STEREO;
LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO";
}
if (!checkConfigParams(config)) {
LOG(ERROR) << __func__ << " checkConfigParams failed";
return ::android::UNKNOWN_ERROR;
}
mBtDeviceProxies.push_back(std::move(proxy));
}
mIsInitialized = true;
return ::android::OK;
}
bool StreamBluetooth::checkConfigParams(
::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config) {
if ((int)mSampleRate != config.sampleRateHz) {
LOG(ERROR) << __func__ << ": Sample Rate mismatch, stream val = " << mSampleRate
<< " hal val = " << config.sampleRateHz;
return false;
}
auto channelCount = aidl::android::hardware::audio::common::getChannelCount(mChannelLayout);
if ((config.channelMode == ChannelMode::MONO && channelCount != 1) ||
(config.channelMode == ChannelMode::STEREO && channelCount != 2)) {
LOG(ERROR) << __func__ << ": Channel count mismatch, stream val = " << channelCount
<< " hal val = " << toString(config.channelMode);
return false;
}
if (mFormat.type != AudioFormatType::PCM) {
LOG(ERROR) << __func__ << ": unexpected format type "
<< aidl::android::media::audio::common::toString(mFormat.type);
return false;
}
int8_t bps = aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(mFormat.pcm) * 8;
if (bps != config.bitsPerSample) {
LOG(ERROR) << __func__ << ": bits per sample mismatch, stream val = " << bps
<< " hal val = " << config.bitsPerSample;
return false;
}
if (config.dataIntervalUs > 0) {
mPreferredDataIntervalUs =
std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
}
return true;
}
ndk::ScopedAStatus StreamBluetooth::prepareToClose() {
std::lock_guard guard(mLock);
mIsReadyToClose = true;
return ndk::ScopedAStatus::ok();
}
::android::status_t StreamBluetooth::standby() {
std::lock_guard guard(mLock);
if (!mIsInitialized) {
if (auto status = initialize(); status != ::android::OK) return status;
}
for (auto proxy : mBtDeviceProxies) {
if (!proxy->suspend()) {
LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to stand by ";
return -EIO;
}
}
return ::android::OK;
}
::android::status_t StreamBluetooth::start() {
std::lock_guard guard(mLock);
if (!mIsInitialized) return initialize();
return ::android::OK;
}
void StreamBluetooth::shutdown() {
std::lock_guard guard(mLock);
for (auto proxy : mBtDeviceProxies) {
proxy->stop();
proxy->unregisterPort();
}
mBtDeviceProxies.clear();
}
ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadata) {
std::lock_guard guard(mLock);
if (!mIsInitialized) return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
bool isOk = true;
if (isInput(metadata)) {
isOk = mBtDeviceProxies[0]->updateSinkMetadata(std::get<SinkMetadata>(metadata));
} else {
for (auto proxy : mBtDeviceProxies) {
if (!proxy->updateSourceMetadata(std::get<SourceMetadata>(metadata))) isOk = false;
}
}
return isOk ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
: StreamIn(std::move(context), microphones),
StreamBluetooth(&(StreamIn::mContext), sinkMetadata) {}
ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamOut(std::move(context), offloadInfo),
StreamBluetooth(&(StreamOut::mContext), sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core

View file

@ -47,5 +47,6 @@ std::unique_ptr<Configuration> getPrimaryConfiguration();
std::unique_ptr<Configuration> getRSubmixConfiguration();
std::unique_ptr<Configuration> getStubConfiguration();
std::unique_ptr<Configuration> getUsbConfiguration();
std::unique_ptr<Configuration> getBluetoothConfiguration();
} // namespace aidl::android::hardware::audio::core::internal

View file

@ -0,0 +1,233 @@
/*
* Copyright 2023 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.
*/
#pragma once
#include <condition_variable>
#include <mutex>
#include <android-base/thread_annotations.h>
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
#include <aidl/android/media/audio/common/AudioDeviceDescription.h>
#include "BluetoothAudioSessionControl.h"
namespace android::bluetooth::audio::aidl {
enum class BluetoothStreamState : uint8_t {
DISABLED = 0, // This stream is closing or Bluetooth profiles (A2DP/LE) is disabled
STANDBY,
STARTING,
STARTED,
SUSPENDING,
UNKNOWN,
};
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state);
/**
* Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
* Session Control. All methods are not thread safe, so users must acquire a
* lock. Note: currently, getState() of DevicePortProxy is only used for
* verbose logging, it is not locked, so the state may not be synchronized.
*/
class BluetoothAudioPort {
public:
BluetoothAudioPort() = default;
virtual ~BluetoothAudioPort() = default;
/**
* Fetch output control / data path of BluetoothAudioPort and setup
* callbacks into BluetoothAudioProvider. If registerPort() returns false, the audio
* HAL must delete this BluetoothAudioPort and return EINVAL to caller
*/
virtual bool registerPort(
const ::aidl::android::media::audio::common::AudioDeviceDescription&) = 0;
/**
* Unregister this BluetoothAudioPort from BluetoothAudioSessionControl.
* Audio HAL must delete this BluetoothAudioPort after calling this.
*/
virtual void unregisterPort() = 0;
/**
* When the Audio framework / HAL tries to query audio config about format,
* channel mask and sample rate, it uses this function to fetch from the
* Bluetooth stack
*/
virtual bool loadAudioConfig(
::aidl::android::hardware::bluetooth::audio::PcmConfiguration*) const = 0;
/**
* WAR to support Mono mode / 16 bits per sample
*/
virtual void forcePcmStereoToMono(bool) = 0;
/**
* When the Audio framework / HAL wants to change the stream state, it invokes
* these 3 functions to control the Bluetooth stack (Audio Control Path).
* Note: Both start() and suspend() will return true when there are no errors.
* Called by Audio framework / HAL to start the stream
*/
virtual bool start() = 0;
/**
* Called by Audio framework / HAL to suspend the stream
*/
virtual bool suspend() = 0;
/**
* Called by Audio framework / HAL to stop the stream
*/
virtual void stop() = 0;
/**
* Called by the Audio framework / HAL to fetch information about audio frames
* presented to an external sink, or frames presented fror an internal sink
*/
virtual bool getPresentationPosition(
::aidl::android::hardware::bluetooth::audio::PresentationPosition&) const = 0;
/**
* Called by the Audio framework / HAL when the metadata of the stream's
* source has been changed.
*/
virtual bool updateSourceMetadata(
const ::aidl::android::hardware::audio::common::SourceMetadata&) const {
return false;
}
/**
* Called by the Audio framework / HAL when the metadata of the stream's
* sink has been changed.
*/
virtual bool updateSinkMetadata(
const ::aidl::android::hardware::audio::common::SinkMetadata&) const {
return false;
}
/**
* Return the current BluetoothStreamState
*/
virtual BluetoothStreamState getState() const = 0;
/**
* Set the current BluetoothStreamState
*/
virtual bool setState(BluetoothStreamState) = 0;
virtual bool isA2dp() const = 0;
virtual bool isLeAudio() const = 0;
virtual bool getPreferredDataIntervalUs(size_t*) const = 0;
virtual size_t writeData(const void*, size_t) const { return 0; }
virtual size_t readData(void*, size_t) const { return 0; }
};
class BluetoothAudioPortAidl : public BluetoothAudioPort {
public:
BluetoothAudioPortAidl();
virtual ~BluetoothAudioPortAidl();
bool registerPort(const ::aidl::android::media::audio::common::AudioDeviceDescription&
description) override;
void unregisterPort() override;
bool loadAudioConfig(::aidl::android::hardware::bluetooth::audio::PcmConfiguration* audio_cfg)
const override;
void forcePcmStereoToMono(bool force) override { mIsStereoToMono = force; }
bool start() override;
bool suspend() override;
void stop() override;
bool getPresentationPosition(::aidl::android::hardware::bluetooth::audio::PresentationPosition&
presentation_position) const override;
bool updateSourceMetadata(const ::aidl::android::hardware::audio::common::SourceMetadata&
sourceMetadata) const override;
bool updateSinkMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
sinkMetadata) const override;
/**
* Return the current BluetoothStreamState
* Note: This method is used for logging, does not lock, so value returned may not be latest
*/
BluetoothStreamState getState() const override NO_THREAD_SAFETY_ANALYSIS;
bool setState(BluetoothStreamState state) override;
bool isA2dp() const override;
bool isLeAudio() const override;
bool getPreferredDataIntervalUs(size_t* interval_us) const override;
protected:
uint16_t mCookie;
BluetoothStreamState mState GUARDED_BY(mCvMutex);
::aidl::android::hardware::bluetooth::audio::SessionType mSessionType;
// WR to support Mono: True if fetching Stereo and mixing into Mono
bool mIsStereoToMono = false;
bool inUse() const;
std::string debugMessage() const;
private:
// start()/suspend() report state change status via callback. Wait until kMaxWaitingTimeMs or a
// state change after a call to start()/suspend() and analyse the returned status. Below mutex,
// conditional variable serves this purpose.
mutable std::mutex mCvMutex;
std::condition_variable mInternalCv GUARDED_BY(mCvMutex);
// Check and initialize session type for |devices| If failed, this
// BluetoothAudioPortAidl is not initialized and must be deleted.
bool initSessionType(
const ::aidl::android::media::audio::common::AudioDeviceDescription& description);
bool condWaitState(BluetoothStreamState state);
void controlResultHandler(
uint16_t cookie,
const ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus& status);
void sessionChangedHandler(uint16_t cookie);
};
class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl {
public:
// The audio data path to the Bluetooth stack (Software encoding)
size_t writeData(const void* buffer, size_t bytes) const override;
};
class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
public:
// The audio data path from the Bluetooth stack (Software decoded)
size_t readData(void* buffer, size_t bytes) const override;
};
} // namespace android::bluetooth::audio::aidl

View file

@ -33,7 +33,7 @@ class Module : public BnModule {
public:
// This value is used for all AudioPatches and reported by all streams.
static constexpr int32_t kLatencyMs = 10;
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
static std::shared_ptr<Module> createInstance(Type type);

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 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.
*/
#pragma once
#include "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
class ModuleBluetooth final : public Module {
public:
ModuleBluetooth() : Module(Type::BLUETOOTH) {}
private:
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 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.
*/
#pragma once
#include <mutex>
#include <vector>
#include "core-impl/DevicePortProxy.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
class StreamBluetooth : public StreamCommonImpl {
public:
StreamBluetooth(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
ndk::ScopedAStatus prepareToClose() override;
const ConnectedDevices& getConnectedDevices() const override;
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
private:
// Audio Pcm Config
const uint32_t mSampleRate;
const ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
const ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
const size_t mFrameSizeBytes;
const bool mIsInput;
size_t mPreferredDataIntervalUs;
size_t mPreferredFrameCount;
mutable std::mutex mLock;
bool mIsInitialized GUARDED_BY(mLock);
bool mIsReadyToClose GUARDED_BY(mLock);
std::vector<std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>>
mBtDeviceProxies GUARDED_BY(mLock);
::android::status_t initialize() REQUIRES(mLock);
bool checkConfigParams(::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config);
};
class StreamInBluetooth final : public StreamIn, public StreamBluetooth {
public:
friend class ndk::SharedRefBase;
StreamInBluetooth(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
class StreamOutBluetooth final : public StreamOut, public StreamBluetooth {
public:
friend class ndk::SharedRefBase;
StreamOutBluetooth(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
} // namespace aidl::android::hardware::audio::core

View file

@ -65,7 +65,8 @@ int main() {
return std::make_pair(module, moduleBinder);
};
auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
createModule(Module::Type::USB), createModule(Module::Type::STUB)};
createModule(Module::Type::USB), createModule(Module::Type::STUB),
createModule(Module::Type::BLUETOOTH)};
(void)modules;
ABinderProcess_joinThreadPool();