sysbta: Sync with r29

* Currently not working on my MTK devices. Investigation ongoing.
This commit is contained in:
Peter Cai 2024-03-12 22:51:16 -04:00
parent 9fb62e9fad
commit ddcc0a5fed
55 changed files with 17467 additions and 266 deletions

View file

@ -0,0 +1,73 @@
/*
* 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
namespace aidl::android::hardware::bluetooth::audio {
class A2dpBits {
const uint8_t* cdata_;
uint8_t* data_;
public:
A2dpBits(const std::vector<uint8_t>& vector) : cdata_(vector.data()) {}
A2dpBits(std::vector<uint8_t>& vector)
: cdata_(vector.data()), data_(vector.data()) {}
struct Range {
const int first, len;
constexpr Range(int first, int last)
: first(first), len(last - first + 1) {}
constexpr Range(int index) : first(index), len(1) {}
};
constexpr bool get(int bit) const {
return (cdata_[bit >> 3] >> (7 - (bit & 7))) & 1;
}
constexpr unsigned get(const Range& range) const {
unsigned v(0);
for (int i = 0; i < range.len; i++)
v |= get(range.first + i) << ((range.len - 1) - i);
return v;
}
constexpr void set(int bit, int value = 1) {
uint8_t m = 1 << (7 - (bit & 7));
if (value)
data_[bit >> 3] |= m;
else
data_[bit >> 3] &= ~m;
}
constexpr void set(const Range& range, int value) {
for (int i = 0; i < range.len; i++)
set(range.first + i, (value >> ((range.len - 1) - i)) & 1);
}
constexpr int find_active_bit(const Range& range) const {
unsigned v = get(range);
int i = 0;
for (; i < range.len && ((v >> i) & 1) == 0; i++)
;
return i < range.len && (v ^ (1 << i)) == 0
? range.first + (range.len - 1) - i
: -1;
}
};
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -22,6 +22,10 @@
#include <BluetoothAudioSessionReport.h>
#include <android-base/logging.h>
#include "A2dpOffloadCodecAac.h"
#include "A2dpOffloadCodecFactory.h"
#include "A2dpOffloadCodecSbc.h"
namespace aidl {
namespace android {
namespace hardware {
@ -48,19 +52,44 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
if (audio_config.getTag() != AudioConfiguration::a2dpConfig) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
session_type_, audio_config.get<AudioConfiguration::a2dpConfig>())) {
if (audio_config.getTag() == AudioConfiguration::Tag::a2dp) {
auto a2dp_config = audio_config.get<AudioConfiguration::Tag::a2dp>();
A2dpStatus a2dp_status = A2dpStatus::NOT_SUPPORTED_CODEC_TYPE;
if (a2dp_config.codecId ==
A2dpOffloadCodecSbc::GetInstance()->GetCodecId()) {
SbcParameters sbc_parameters;
a2dp_status = A2dpOffloadCodecSbc::GetInstance()->ParseConfiguration(
a2dp_config.configuration, &sbc_parameters);
} else if (a2dp_config.codecId ==
A2dpOffloadCodecAac::GetInstance()->GetCodecId()) {
AacParameters aac_parameters;
a2dp_status = A2dpOffloadCodecAac::GetInstance()->ParseConfiguration(
a2dp_config.configuration, &aac_parameters);
}
if (a2dp_status != A2dpStatus::OK) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
} else if (audio_config.getTag() == AudioConfiguration::Tag::a2dpConfig) {
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
session_type_,
audio_config.get<AudioConfiguration::a2dpConfig>())) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
} else {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return BluetoothAudioProvider::startSession(
host_if, audio_config, latency_modes, _aidl_return);
}
@ -73,6 +102,36 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady(
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration(
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) {
auto codec = A2dpOffloadCodecFactory::GetInstance()->GetCodec(codec_id);
if (!codec) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " - CodecId=" << codec_id.toString() << " is not found";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
*_aidl_return = codec->ParseConfiguration(configuration, codec_parameters);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus A2dpOffloadAudioProvider::getA2dpConfiguration(
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
const A2dpConfigurationHint& hint,
std::optional<audio::A2dpConfiguration>* _aidl_return) {
*_aidl_return = std::nullopt;
A2dpConfiguration avdtp_configuration;
if (A2dpOffloadCodecFactory::GetInstance()->GetConfiguration(
remote_a2dp_capabilities, hint, &avdtp_configuration))
*_aidl_return =
std::make_optional<A2dpConfiguration>(std::move(avdtp_configuration));
return ndk::ScopedAStatus::ok();
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware

View file

@ -34,7 +34,16 @@ class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes,
DataMQDesc* _aidl_return);
DataMQDesc* _aidl_return) override;
ndk::ScopedAStatus parseA2dpConfiguration(
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) override;
ndk::ScopedAStatus getA2dpConfiguration(
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
const A2dpConfigurationHint& hint,
std::optional<audio::A2dpConfiguration>* _aidl_return) override;
private:
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;

View file

@ -0,0 +1,47 @@
/*
* 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 <aidl/android/hardware/bluetooth/audio/A2dpStatus.h>
#include <aidl/android/hardware/bluetooth/audio/ChannelMode.h>
#include <aidl/android/hardware/bluetooth/audio/CodecParameters.h>
#include "BluetoothAudioProviderFactory.h"
namespace aidl::android::hardware::bluetooth::audio {
class A2dpOffloadCodec {
protected:
A2dpOffloadCodec(const CodecInfo& info) : info(info) {}
virtual ~A2dpOffloadCodec() {}
public:
const CodecInfo& info;
const CodecId& GetCodecId() const { return info.id; }
virtual A2dpStatus ParseConfiguration(
const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters) const = 0;
virtual bool BuildConfiguration(
const std::vector<uint8_t>& remote_capabilities,
const std::optional<CodecParameters>& hint,
std::vector<uint8_t>* configuration) const = 0;
};
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,378 @@
/*
* 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.
*/
#include "A2dpOffloadCodecAac.h"
#include "A2dpBits.h"
namespace aidl::android::hardware::bluetooth::audio {
/**
* AAC Local Capabilities
*/
enum : bool {
kEnableObjectTypeMpeg2AacLc = true,
kEnableObjectTypeMpeg4AacLc = true,
};
enum : bool {
kEnableSamplingFrequency44100 = true,
kEnableSamplingFrequency48000 = true,
kEnableSamplingFrequency88200 = false,
kEnableSamplingFrequency96000 = false,
};
enum : bool {
kEnableChannels1 = true,
kEnableChannels2 = true,
};
enum : bool {
kEnableVbrSupported = true,
};
enum : int {
kBitdepth = 24,
};
/**
* AAC Signaling format [A2DP - 4.5]
*/
// clang-format off
constexpr A2dpBits::Range kObjectType ( 0, 6 );
constexpr A2dpBits::Range kDrcEnable ( 7 );
constexpr A2dpBits::Range kSamplingFrequency ( 8, 19 );
constexpr A2dpBits::Range kChannels ( 20, 23 );
constexpr A2dpBits::Range kVbrSupported ( 24 );
constexpr A2dpBits::Range kBitrate ( 25, 47 );
constexpr size_t kCapabilitiesSize = 48/8;
// clang-format on
enum {
kObjectTypeMpeg2AacLc = kObjectType.first,
kObjectTypeMpeg4AacLc,
kObjectTypeMpeg4AacLtp,
kObjectTypeMpeg4AacScalable,
kObjectTypeMpeg4AacHeV1,
kObjectTypeMpeg4AacHeV2,
kObjectTypeMpeg4AacEldV2
};
enum {
kSamplingFrequency8000 = kSamplingFrequency.first,
kSamplingFrequency11025,
kSamplingFrequency12000,
kSamplingFrequency16000,
kSamplingFrequency22050,
kSamplingFrequency24000,
kSamplingFrequency32000,
kSamplingFrequency44100,
kSamplingFrequency48000,
kSamplingFrequency64000,
kSamplingFrequency88200,
kSamplingFrequency96000
};
enum { kChannels1 = kChannels.first, kChannels2, kChannels51, kChannels71 };
/**
* AAC Conversion functions
*/
static AacParameters::ObjectType GetObjectTypeEnum(int object_type) {
switch (object_type) {
case kObjectTypeMpeg2AacLc:
return AacParameters::ObjectType::MPEG2_AAC_LC;
case kObjectTypeMpeg4AacLc:
default:
return AacParameters::ObjectType::MPEG4_AAC_LC;
}
}
static int GetSamplingFrequencyBit(int32_t sampling_frequency) {
switch (sampling_frequency) {
case 8000:
return kSamplingFrequency8000;
case 11025:
return kSamplingFrequency11025;
case 12000:
return kSamplingFrequency12000;
case 16000:
return kSamplingFrequency16000;
case 22050:
return kSamplingFrequency22050;
case 24000:
return kSamplingFrequency24000;
case 32000:
return kSamplingFrequency32000;
case 44100:
return kSamplingFrequency44100;
case 48000:
return kSamplingFrequency48000;
case 64000:
return kSamplingFrequency64000;
case 88200:
return kSamplingFrequency88200;
case 96000:
return kSamplingFrequency96000;
default:
return -1;
}
}
static int32_t GetSamplingFrequencyValue(int sampling_frequency) {
switch (sampling_frequency) {
case kSamplingFrequency8000:
return 8000;
case kSamplingFrequency11025:
return 11025;
case kSamplingFrequency12000:
return 12000;
case kSamplingFrequency16000:
return 16000;
case kSamplingFrequency22050:
return 22050;
case kSamplingFrequency24000:
return 24000;
case kSamplingFrequency32000:
return 32000;
case kSamplingFrequency44100:
return 44100;
case kSamplingFrequency48000:
return 48000;
case kSamplingFrequency64000:
return 64000;
case kSamplingFrequency88200:
return 88200;
case kSamplingFrequency96000:
return 96000;
default:
return 0;
}
}
static int GetChannelsBit(ChannelMode channel_mode) {
switch (channel_mode) {
case ChannelMode::MONO:
return kChannels1;
case ChannelMode::STEREO:
return kChannels2;
default:
return -1;
}
}
static ChannelMode GetChannelModeEnum(int channel_mode) {
switch (channel_mode) {
case kChannels1:
return ChannelMode::MONO;
case kChannels2:
return ChannelMode::STEREO;
default:
return ChannelMode::UNKNOWN;
}
}
/**
* AAC Class implementation
*/
const A2dpOffloadCodecAac* A2dpOffloadCodecAac::GetInstance() {
static A2dpOffloadCodecAac instance;
return &instance;
}
A2dpOffloadCodecAac::A2dpOffloadCodecAac()
: A2dpOffloadCodec(info_),
info_({.id = CodecId(CodecId::A2dp::AAC), .name = "AAC"}) {
info_.transport.set<CodecInfo::Transport::Tag::a2dp>();
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
/* --- Setup Capabilities --- */
a2dp_info.capabilities.resize(kCapabilitiesSize);
std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0);
auto capabilities = A2dpBits(a2dp_info.capabilities);
capabilities.set(kObjectTypeMpeg2AacLc, kEnableObjectTypeMpeg2AacLc);
capabilities.set(kObjectTypeMpeg4AacLc, kEnableObjectTypeMpeg4AacLc);
capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100);
capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000);
capabilities.set(kSamplingFrequency88200, kEnableSamplingFrequency88200);
capabilities.set(kSamplingFrequency96000, kEnableSamplingFrequency96000);
capabilities.set(kChannels1, kEnableChannels1);
capabilities.set(kChannels2, kEnableChannels2);
capabilities.set(kVbrSupported, kEnableVbrSupported);
/* --- Setup Sampling Frequencies --- */
auto& sampling_frequency = a2dp_info.samplingFrequencyHz;
for (auto v : {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
64000, 88200, 96000})
if (capabilities.get(GetSamplingFrequencyBit(int32_t(v))))
sampling_frequency.push_back(v);
/* --- Setup Channel Modes --- */
auto& channel_modes = a2dp_info.channelMode;
for (auto v : {ChannelMode::MONO, ChannelMode::STEREO})
if (capabilities.get(GetChannelsBit(v))) channel_modes.push_back(v);
/* --- Setup Bitdepth --- */
a2dp_info.bitdepth.push_back(kBitdepth);
}
A2dpStatus A2dpOffloadCodecAac::ParseConfiguration(
const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, AacParameters* aac_parameters) const {
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
if (configuration.size() != a2dp_info.capabilities.size())
return A2dpStatus::BAD_LENGTH;
auto config = A2dpBits(configuration);
auto lcaps = A2dpBits(a2dp_info.capabilities);
/* --- Check Object Type --- */
int object_type = config.find_active_bit(kObjectType);
if (object_type < 0) return A2dpStatus::INVALID_OBJECT_TYPE;
if (!lcaps.get(object_type)) return A2dpStatus::NOT_SUPPORTED_OBJECT_TYPE;
/* --- Check Sampling Frequency --- */
int sampling_frequency = config.find_active_bit(kSamplingFrequency);
if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY;
if (!lcaps.get(sampling_frequency))
return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY;
/* --- Check Channels --- */
int channels = config.find_active_bit(kChannels);
if (channels < 0) return A2dpStatus::INVALID_CHANNELS;
if (!lcaps.get(channels)) return A2dpStatus::NOT_SUPPORTED_CHANNELS;
/* --- Check Bitrate --- */
bool vbr = config.get(kVbrSupported);
if (vbr && !lcaps.get(kVbrSupported)) return A2dpStatus::NOT_SUPPORTED_VBR;
int bitrate = config.get(kBitrate);
if (vbr && lcaps.get(kBitrate) && bitrate > lcaps.get(kBitrate))
return A2dpStatus::NOT_SUPPORTED_BIT_RATE;
/* --- Return --- */
codec_parameters->channelMode = GetChannelModeEnum(channels);
codec_parameters->samplingFrequencyHz =
GetSamplingFrequencyValue(sampling_frequency);
codec_parameters->bitdepth = kBitdepth;
codec_parameters->minBitrate = vbr ? 0 : bitrate;
codec_parameters->maxBitrate = bitrate;
if (aac_parameters)
aac_parameters->object_type = GetObjectTypeEnum(object_type);
return A2dpStatus::OK;
}
bool A2dpOffloadCodecAac::BuildConfiguration(
const std::vector<uint8_t>& remote_capabilities,
const std::optional<CodecParameters>& hint,
std::vector<uint8_t>* configuration) const {
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false;
auto lcaps = A2dpBits(a2dp_info.capabilities);
auto rcaps = A2dpBits(remote_capabilities);
configuration->resize(a2dp_info.capabilities.size());
std::fill(begin(*configuration), end(*configuration), 0);
auto config = A2dpBits(*configuration);
/* --- Select Object Type --- */
if (lcaps.get(kObjectTypeMpeg2AacLc) && rcaps.get(kObjectTypeMpeg2AacLc))
config.set(kObjectTypeMpeg2AacLc);
else if (lcaps.get(kObjectTypeMpeg4AacLc) && rcaps.get(kObjectTypeMpeg4AacLc))
config.set(kObjectTypeMpeg4AacLc);
else
return false;
/* --- Select Sampling Frequency --- */
auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1;
if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint))
config.set(sf_hint);
else if (lcaps.get(kSamplingFrequency96000) &&
rcaps.get(kSamplingFrequency96000))
config.set(kSamplingFrequency96000);
else if (lcaps.get(kSamplingFrequency88200) &&
rcaps.get(kSamplingFrequency88200))
config.set(kSamplingFrequency88200);
else if (lcaps.get(kSamplingFrequency48000) &&
rcaps.get(kSamplingFrequency48000))
config.set(kSamplingFrequency48000);
else if (lcaps.get(kSamplingFrequency44100) &&
rcaps.get(kSamplingFrequency44100))
config.set(kSamplingFrequency44100);
else
return false;
/* --- Select Channels --- */
auto ch_hint = hint ? GetChannelsBit(hint->channelMode) : -1;
if (ch_hint >= 0 && lcaps.get(ch_hint) && rcaps.get(ch_hint))
config.set(ch_hint);
else if (lcaps.get(kChannels2) && rcaps.get(kChannels2))
config.set(kChannels2);
else if (lcaps.get(kChannels1) && rcaps.get(kChannels1))
config.set(kChannels1);
else
return false;
/* --- Select Bitrate --- */
if (!hint || hint->minBitrate == 0)
config.set(kVbrSupported,
lcaps.get(kVbrSupported) && rcaps.get(kVbrSupported));
int32_t bitrate = lcaps.get(kBitrate);
if (hint && hint->maxBitrate > 0 && bitrate)
bitrate = std::min(hint->maxBitrate, bitrate);
else if (hint && hint->maxBitrate > 0)
bitrate = hint->maxBitrate;
config.set(kBitrate, bitrate);
return true;
}
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,57 @@
/*
* 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 "A2dpOffloadCodec.h"
namespace aidl::android::hardware::bluetooth::audio {
struct AacParameters : public CodecParameters {
enum class ObjectType { MPEG2_AAC_LC, MPEG4_AAC_LC };
ObjectType object_type;
};
class A2dpOffloadCodecAac : public A2dpOffloadCodec {
CodecInfo info_;
A2dpOffloadCodecAac();
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters,
AacParameters* aac_parameters) const;
public:
static const A2dpOffloadCodecAac* GetInstance();
A2dpStatus ParseConfiguration(
const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters) const override {
return ParseConfiguration(configuration, codec_parameters, nullptr);
}
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
AacParameters* aac_parameters) const {
return ParseConfiguration(configuration, aac_parameters, aac_parameters);
}
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
const std::optional<CodecParameters>& hint,
std::vector<uint8_t>* configuration) const override;
};
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,100 @@
/*
* 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.
*/
#include "A2dpOffloadCodecFactory.h"
#include <algorithm>
#include <cassert>
#include "A2dpOffloadCodecAac.h"
#include "A2dpOffloadCodecSbc.h"
namespace aidl::android::hardware::bluetooth::audio {
/**
* Local Capabilities Configuration
*/
enum : bool {
kEnableAac = true,
kEnableSbc = true,
};
/**
* Class implementation
*/
const A2dpOffloadCodecFactory* A2dpOffloadCodecFactory::GetInstance() {
static A2dpOffloadCodecFactory instance;
return &instance;
}
A2dpOffloadCodecFactory::A2dpOffloadCodecFactory()
: name("Offload"), codecs(ranked_codecs_) {
ranked_codecs_.reserve(kEnableAac + kEnableSbc);
if (kEnableAac) ranked_codecs_.push_back(A2dpOffloadCodecAac::GetInstance());
if (kEnableSbc) ranked_codecs_.push_back(A2dpOffloadCodecSbc::GetInstance());
}
const A2dpOffloadCodec* A2dpOffloadCodecFactory::GetCodec(CodecId id) const {
auto codec = std::find_if(begin(ranked_codecs_), end(ranked_codecs_),
[&](auto c) { return id == c->info.id; });
return codec != end(ranked_codecs_) ? *codec : nullptr;
}
bool A2dpOffloadCodecFactory::GetConfiguration(
const std::vector<A2dpRemoteCapabilities>& remote_capabilities,
const A2dpConfigurationHint& hint, A2dpConfiguration* configuration) const {
decltype(ranked_codecs_) codecs;
codecs.reserve(ranked_codecs_.size());
auto hinted_codec =
std::find_if(begin(ranked_codecs_), end(ranked_codecs_),
[&](auto c) { return hint.codecId == c->info.id; });
if (hinted_codec != end(ranked_codecs_)) codecs.push_back(*hinted_codec);
std::copy_if(begin(ranked_codecs_), end(ranked_codecs_),
std::back_inserter(codecs),
[&](auto c) { return c != *hinted_codec; });
for (auto codec : codecs) {
auto rc =
std::find_if(begin(remote_capabilities), end(remote_capabilities),
[&](auto& rc__) { return codec->info.id == rc__.id; });
if ((rc == end(remote_capabilities)) ||
!codec->BuildConfiguration(rc->capabilities, hint.codecParameters,
&configuration->configuration))
continue;
configuration->id = codec->info.id;
A2dpStatus status = codec->ParseConfiguration(configuration->configuration,
&configuration->parameters);
assert(status == A2dpStatus::OK);
configuration->remoteSeid = rc->seid;
return true;
}
return false;
}
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,41 @@
/*
* 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 "A2dpOffloadCodec.h"
namespace aidl::android::hardware::bluetooth::audio {
class A2dpOffloadCodecFactory {
std::vector<const A2dpOffloadCodec*> ranked_codecs_;
A2dpOffloadCodecFactory();
public:
const std::string name;
const std::vector<const A2dpOffloadCodec*>& codecs;
static const A2dpOffloadCodecFactory* GetInstance();
const A2dpOffloadCodec* GetCodec(CodecId id) const;
bool GetConfiguration(const std::vector<A2dpRemoteCapabilities>&,
const A2dpConfigurationHint& hint,
A2dpConfiguration* configuration) const;
};
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,510 @@
/*
* 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.
*/
#include "A2dpOffloadCodecSbc.h"
#include <algorithm>
#include "A2dpBits.h"
namespace aidl::android::hardware::bluetooth::audio {
/**
* SBC Local Capabilities
*/
enum : bool {
kEnableSamplingFrequency44100 = true,
kEnableSamplingFrequency48000 = true,
};
enum : bool {
kEnableChannelModeMono = true,
kEnableChannelModeDualChannel = true,
kEnableChannelModeStereo = true,
kEnableChannelModeJointStereo = true,
};
enum : bool {
kEnableBlockLength4 = true,
kEnableBlockLength8 = true,
kEnableBlockLength12 = true,
kEnableBlockLength16 = true,
};
enum : bool {
kEnableSubbands4 = true,
kEnableSubbands8 = true,
};
enum : bool {
kEnableAllocationMethodSnr = true,
kEnableAllocationMethodLoudness = true,
};
enum : uint8_t {
kDefaultMinimumBitpool = 2,
kDefaultMaximumBitpool = 250,
};
enum : int {
kBitdepth = 16,
};
/**
* SBC Signaling format [A2DP - 4.3]
*/
// clang-format off
constexpr A2dpBits::Range kSamplingFrequency ( 0, 3 );
constexpr A2dpBits::Range kChannelMode ( 4, 7 );
constexpr A2dpBits::Range kBlockLength ( 8, 11 );
constexpr A2dpBits::Range kSubbands ( 12, 13 );
constexpr A2dpBits::Range kAllocationMethod ( 14, 15 );
constexpr A2dpBits::Range kMinimumBitpool ( 16, 23 );
constexpr A2dpBits::Range kMaximumBitpool ( 24, 31 );
constexpr size_t kCapabilitiesSize = 32/8;
// clang-format on
enum {
kSamplingFrequency16000 = kSamplingFrequency.first,
kSamplingFrequency32000,
kSamplingFrequency44100,
kSamplingFrequency48000
};
enum {
kChannelModeMono = kChannelMode.first,
kChannelModeDualChannel,
kChannelModeStereo,
kChannelModeJointStereo
};
enum {
kBlockLength4 = kBlockLength.first,
kBlockLength8,
kBlockLength12,
kBlockLength16
};
enum { kSubbands8 = kSubbands.first, kSubbands4 };
enum {
kAllocationMethodSnr = kAllocationMethod.first,
kAllocationMethodLoudness
};
/**
* SBC Conversion functions
*/
static int GetSamplingFrequencyBit(int32_t sampling_frequency) {
switch (sampling_frequency) {
case 16000:
return kSamplingFrequency16000;
case 32000:
return kSamplingFrequency32000;
case 44100:
return kSamplingFrequency44100;
case 48000:
return kSamplingFrequency48000;
default:
return -1;
}
}
static int32_t GetSamplingFrequencyValue(int sampling_frequency) {
switch (sampling_frequency) {
case kSamplingFrequency16000:
return 16000;
case kSamplingFrequency32000:
return 32000;
case kSamplingFrequency44100:
return 44100;
case kSamplingFrequency48000:
return 48000;
default:
return 0;
}
}
static int GetChannelModeBit(ChannelMode channel_mode) {
switch (channel_mode) {
case ChannelMode::STEREO:
return kChannelModeJointStereo | kChannelModeStereo;
case ChannelMode::DUALMONO:
return kChannelModeDualChannel;
case ChannelMode::MONO:
return kChannelModeMono;
default:
return -1;
}
}
static ChannelMode GetChannelModeEnum(int channel_mode) {
switch (channel_mode) {
case kChannelModeMono:
return ChannelMode::MONO;
case kChannelModeDualChannel:
return ChannelMode::DUALMONO;
case kChannelModeStereo:
case kChannelModeJointStereo:
return ChannelMode::STEREO;
default:
return ChannelMode::UNKNOWN;
}
}
static int32_t GetBlockLengthValue(int block_length) {
switch (block_length) {
case kBlockLength4:
return 4;
case kBlockLength8:
return 8;
case kBlockLength12:
return 12;
case kBlockLength16:
return 16;
default:
return 0;
}
}
static int32_t GetSubbandsValue(int subbands) {
switch (subbands) {
case kSubbands4:
return 4;
case kSubbands8:
return 8;
default:
return 0;
}
}
static SbcParameters::AllocationMethod GetAllocationMethodEnum(
int allocation_method) {
switch (allocation_method) {
case kAllocationMethodSnr:
return SbcParameters::AllocationMethod::SNR;
case kAllocationMethodLoudness:
default:
return SbcParameters::AllocationMethod::LOUDNESS;
}
}
static int32_t GetSamplingFrequencyValue(const A2dpBits& configuration) {
return GetSamplingFrequencyValue(
configuration.find_active_bit(kSamplingFrequency));
}
static int32_t GetBlockLengthValue(const A2dpBits& configuration) {
return GetBlockLengthValue(configuration.find_active_bit(kBlockLength));
}
static int32_t GetSubbandsValue(const A2dpBits& configuration) {
return GetSubbandsValue(configuration.find_active_bit(kSubbands));
}
static int GetFrameSize(const A2dpBits& configuration, int bitpool) {
const int kSbcHeaderSize = 4;
int subbands = GetSubbandsValue(configuration);
int blocks = GetBlockLengthValue(configuration);
unsigned bits =
((4 * subbands) << !configuration.get(kChannelModeMono)) +
((blocks * bitpool) << configuration.get(kChannelModeDualChannel)) +
((configuration.get(kChannelModeJointStereo) ? subbands : 0));
return kSbcHeaderSize + ((bits + 7) >> 3);
}
static int GetBitrate(const A2dpBits& configuration, int bitpool) {
int sampling_frequency = GetSamplingFrequencyValue(configuration);
int subbands = GetSubbandsValue(configuration);
int blocks = GetBlockLengthValue(configuration);
int bits = 8 * GetFrameSize(configuration, bitpool);
return (bits * sampling_frequency) / (blocks * subbands);
}
static uint8_t GetBitpool(const A2dpBits& configuration, int bitrate) {
int bitpool = 0;
for (int i = 128; i; i >>= 1)
if (bitrate > GetBitrate(configuration, bitpool + i)) {
bitpool += i;
}
return std::clamp(bitpool, 2, 250);
}
/**
* SBC Class implementation
*/
const A2dpOffloadCodecSbc* A2dpOffloadCodecSbc::GetInstance() {
static A2dpOffloadCodecSbc instance;
return &instance;
}
A2dpOffloadCodecSbc::A2dpOffloadCodecSbc()
: A2dpOffloadCodec(info_),
info_({.id = CodecId(CodecId::A2dp::SBC), .name = "SBC"}) {
info_.transport.set<CodecInfo::Transport::Tag::a2dp>();
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
/* --- Setup Capabilities --- */
a2dp_info.capabilities.resize(kCapabilitiesSize);
std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0);
auto capabilities = A2dpBits(a2dp_info.capabilities);
capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100);
capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000);
capabilities.set(kChannelModeMono, kEnableChannelModeMono);
capabilities.set(kChannelModeDualChannel, kEnableChannelModeDualChannel);
capabilities.set(kChannelModeStereo, kEnableChannelModeStereo);
capabilities.set(kChannelModeJointStereo, kEnableChannelModeJointStereo);
capabilities.set(kBlockLength4, kEnableBlockLength4);
capabilities.set(kBlockLength8, kEnableBlockLength8);
capabilities.set(kBlockLength12, kEnableBlockLength12);
capabilities.set(kBlockLength16, kEnableBlockLength16);
capabilities.set(kSubbands4, kEnableSubbands4);
capabilities.set(kSubbands8, kEnableSubbands8);
capabilities.set(kSubbands4, kEnableSubbands4);
capabilities.set(kSubbands8, kEnableSubbands8);
capabilities.set(kAllocationMethodSnr, kEnableAllocationMethodSnr);
capabilities.set(kAllocationMethodLoudness, kEnableAllocationMethodLoudness);
capabilities.set(kMinimumBitpool, kDefaultMinimumBitpool);
capabilities.set(kMaximumBitpool, kDefaultMaximumBitpool);
/* --- Setup Sampling Frequencies --- */
auto& sampling_frequency = a2dp_info.samplingFrequencyHz;
for (auto v : {16000, 32000, 44100, 48000})
if (capabilities.get(GetSamplingFrequencyBit(int32_t(v))))
sampling_frequency.push_back(v);
/* --- Setup Channel Modes --- */
auto& channel_modes = a2dp_info.channelMode;
for (auto v : {ChannelMode::MONO, ChannelMode::DUALMONO, ChannelMode::STEREO})
if (capabilities.get(GetChannelModeBit(v))) channel_modes.push_back(v);
/* --- Setup Bitdepth --- */
a2dp_info.bitdepth.push_back(kBitdepth);
}
A2dpStatus A2dpOffloadCodecSbc::ParseConfiguration(
const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const {
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
if (configuration.size() != a2dp_info.capabilities.size())
return A2dpStatus::BAD_LENGTH;
auto config = A2dpBits(configuration);
auto lcaps = A2dpBits(a2dp_info.capabilities);
/* --- Check Sampling Frequency --- */
int sampling_frequency = config.find_active_bit(kSamplingFrequency);
if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY;
if (!lcaps.get(sampling_frequency))
return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY;
/* --- Check Channel Mode --- */
int channel_mode = config.find_active_bit(kChannelMode);
if (channel_mode < 0) return A2dpStatus::INVALID_CHANNEL_MODE;
if (!lcaps.get(channel_mode)) return A2dpStatus::NOT_SUPPORTED_CHANNEL_MODE;
/* --- Check Block Length --- */
int block_length = config.find_active_bit(kBlockLength);
if (block_length < 0) return A2dpStatus::INVALID_BLOCK_LENGTH;
/* --- Check Subbands --- */
int subbands = config.find_active_bit(kSubbands);
if (subbands < 0) return A2dpStatus::INVALID_SUBBANDS;
if (!lcaps.get(subbands)) return A2dpStatus::NOT_SUPPORTED_SUBBANDS;
/* --- Check Allocation Method --- */
int allocation_method = config.find_active_bit(kAllocationMethod);
if (allocation_method < 0) return A2dpStatus::INVALID_ALLOCATION_METHOD;
if (!lcaps.get(allocation_method))
return A2dpStatus::NOT_SUPPORTED_ALLOCATION_METHOD;
/* --- Check Bitpool --- */
uint8_t min_bitpool = config.get(kMinimumBitpool);
if (min_bitpool < 2 || min_bitpool > 250)
return A2dpStatus::INVALID_MINIMUM_BITPOOL_VALUE;
if (min_bitpool < lcaps.get(kMinimumBitpool))
return A2dpStatus::NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE;
uint8_t max_bitpool = config.get(kMaximumBitpool);
if (max_bitpool < 2 || max_bitpool > 250)
return A2dpStatus::INVALID_MAXIMUM_BITPOOL_VALUE;
if (max_bitpool > lcaps.get(kMaximumBitpool))
return A2dpStatus::NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE;
/* --- Return --- */
codec_parameters->channelMode = GetChannelModeEnum(channel_mode);
codec_parameters->samplingFrequencyHz =
GetSamplingFrequencyValue(sampling_frequency);
codec_parameters->bitdepth = kBitdepth;
codec_parameters->minBitrate = GetBitrate(config, min_bitpool);
codec_parameters->maxBitrate = GetBitrate(config, max_bitpool);
if (sbc_parameters) {
sbc_parameters->block_length = GetBlockLengthValue(block_length);
sbc_parameters->subbands = GetSubbandsValue(subbands);
sbc_parameters->allocation_method =
GetAllocationMethodEnum(allocation_method);
sbc_parameters->min_bitpool = min_bitpool;
sbc_parameters->max_bitpool = max_bitpool;
}
return A2dpStatus::OK;
}
bool A2dpOffloadCodecSbc::BuildConfiguration(
const std::vector<uint8_t>& remote_capabilities,
const std::optional<CodecParameters>& hint,
std::vector<uint8_t>* configuration) const {
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false;
auto lcaps = A2dpBits(a2dp_info.capabilities);
auto rcaps = A2dpBits(remote_capabilities);
configuration->resize(a2dp_info.capabilities.size());
std::fill(begin(*configuration), end(*configuration), 0);
auto config = A2dpBits(*configuration);
/* --- Select Sampling Frequency --- */
auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1;
if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint))
config.set(sf_hint);
else if (lcaps.get(kSamplingFrequency44100) &&
rcaps.get(kSamplingFrequency44100))
config.set(kSamplingFrequency44100);
else if (lcaps.get(kSamplingFrequency48000) &&
rcaps.get(kSamplingFrequency48000))
config.set(kSamplingFrequency48000);
else
return false;
/* --- Select Channel Mode --- */
auto cm_hint = hint ? GetChannelModeBit(hint->channelMode) : -1;
if (cm_hint >= 0 && lcaps.get(cm_hint) && rcaps.get(cm_hint))
config.set(cm_hint);
else if (lcaps.get(kChannelModeJointStereo) &&
rcaps.get(kChannelModeJointStereo))
config.set(kChannelModeJointStereo);
else if (lcaps.get(kChannelModeStereo) && rcaps.get(kChannelModeStereo))
config.set(kChannelModeStereo);
else if (lcaps.get(kChannelModeDualChannel) &&
rcaps.get(kChannelModeDualChannel))
config.set(kChannelModeDualChannel);
else if (lcaps.get(kChannelModeMono) && rcaps.get(kChannelModeMono))
config.set(kChannelModeMono);
else
return false;
/* --- Select Block Length --- */
if (lcaps.get(kBlockLength16) && rcaps.get(kBlockLength16))
config.set(kBlockLength16);
else if (lcaps.get(kBlockLength12) && rcaps.get(kBlockLength12))
config.set(kBlockLength12);
else if (lcaps.get(kBlockLength8) && rcaps.get(kBlockLength8))
config.set(kBlockLength8);
else if (lcaps.get(kBlockLength4) && rcaps.get(kBlockLength4))
config.set(kBlockLength4);
else
return false;
/* --- Select Subbands --- */
if (lcaps.get(kSubbands8) && rcaps.get(kSubbands8))
config.set(kSubbands8);
else if (lcaps.get(kSubbands4) && rcaps.get(kSubbands4))
config.set(kSubbands4);
else
return false;
/* --- Select Allocation method --- */
if (lcaps.get(kAllocationMethodLoudness) &&
rcaps.get(kAllocationMethodLoudness))
config.set(kAllocationMethodLoudness);
else if (lcaps.get(kAllocationMethodSnr) && rcaps.get(kAllocationMethodSnr))
config.set(kAllocationMethodSnr);
else
return false;
/* --- Select Bitpool --- */
uint8_t min_bitpool = rcaps.get(kMinimumBitpool);
uint8_t max_bitpool = rcaps.get(kMaximumBitpool);
if (min_bitpool < 2 || min_bitpool > 250 || max_bitpool < 2 ||
max_bitpool > 250 || min_bitpool > max_bitpool) {
min_bitpool = 2;
max_bitpool = 250;
}
min_bitpool = std::max(min_bitpool, uint8_t(lcaps.get(kMinimumBitpool)));
max_bitpool = std::max(max_bitpool, uint8_t(lcaps.get(kMaximumBitpool)));
if (hint) {
min_bitpool =
std::max(min_bitpool, GetBitpool(*configuration, hint->minBitrate));
if (hint->maxBitrate && hint->maxBitrate >= hint->minBitrate)
max_bitpool =
std::min(max_bitpool, GetBitpool(*configuration, hint->maxBitrate));
}
config.set(kMinimumBitpool, min_bitpool);
config.set(kMaximumBitpool, max_bitpool);
return true;
}
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -0,0 +1,61 @@
/*
* 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 "A2dpOffloadCodec.h"
namespace aidl::android::hardware::bluetooth::audio {
struct SbcParameters : public CodecParameters {
enum class AllocationMethod { SNR, LOUDNESS };
AllocationMethod allocation_method;
int block_length;
int subbands;
int min_bitpool;
int max_bitpool;
};
class A2dpOffloadCodecSbc : public A2dpOffloadCodec {
CodecInfo info_;
A2dpOffloadCodecSbc();
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters,
SbcParameters* sbc_parameters) const;
public:
static const A2dpOffloadCodecSbc* GetInstance();
A2dpStatus ParseConfiguration(
const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters) const override {
return ParseConfiguration(configuration, codec_parameters, nullptr);
}
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
SbcParameters* sbc_parameters) const {
return ParseConfiguration(configuration, sbc_parameters, sbc_parameters);
}
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
const std::optional<CodecParameters>& hint,
std::vector<uint8_t>* configuration) const override;
};
} // namespace aidl::android::hardware::bluetooth::audio

View file

@ -7,8 +7,13 @@ cc_binary {
"BluetoothAudioProvider.cpp",
"BluetoothAudioProviderFactory.cpp",
"A2dpOffloadAudioProvider.cpp",
"A2dpOffloadCodecAac.cpp",
"A2dpOffloadCodecFactory.cpp",
"A2dpOffloadCodecSbc.cpp",
"A2dpSoftwareAudioProvider.cpp",
"HearingAidAudioProvider.cpp",
"HfpOffloadAudioProvider.cpp",
"HfpSoftwareAudioProvider.cpp",
"LeAudioOffloadAudioProvider.cpp",
"LeAudioSoftwareAudioProvider.cpp",
"service_system.cpp",
@ -16,6 +21,9 @@ cc_binary {
header_libs: [
"libhardware_headers",
],
defaults: [
"latest_android_hardware_bluetooth_audio_ndk_shared",
],
shared_libs: [
"libbase",
"libbinder",
@ -25,7 +33,6 @@ cc_binary {
"libhidlbase",
"liblog",
"libutils",
"android.hardware.bluetooth.audio-V3-ndk",
"libbluetooth_audio_session_aidl_system",
],
required: [

View file

@ -21,15 +21,39 @@
#include <BluetoothAudioSessionReport.h>
#include <android-base/logging.h>
#include "A2dpOffloadCodecFactory.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
struct BluetoothAudioProviderContext {
SessionType session_type;
};
static void binderUnlinkedCallbackAidl(void* cookie) {
LOG(INFO) << __func__;
BluetoothAudioProviderContext* ctx =
static_cast<BluetoothAudioProviderContext*>(cookie);
delete ctx;
}
static void binderDiedCallbackAidl(void* cookie) {
LOG(INFO) << __func__;
BluetoothAudioProviderContext* ctx =
static_cast<BluetoothAudioProviderContext*>(cookie);
CHECK_NE(ctx, nullptr);
BluetoothAudioSessionReport::OnSessionEnded(ctx->session_type);
}
BluetoothAudioProvider::BluetoothAudioProvider() {
death_recipient_ = ::ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(binderDiedCallbackAidl));
AIBinder_DeathRecipient_setOnUnlinked(death_recipient_.get(),
binderUnlinkedCallbackAidl);
}
ndk::ScopedAStatus BluetoothAudioProvider::startSession(
@ -39,17 +63,21 @@ ndk::ScopedAStatus BluetoothAudioProvider::startSession(
DataMQDesc* _aidl_return) {
if (host_if == nullptr) {
*_aidl_return = DataMQDesc();
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< " Illegal argument";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
latency_modes_ = latencyModes;
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
stack_iface_ = host_if;
is_binder_died = false;
BluetoothAudioProviderContext* cookie =
new BluetoothAudioProviderContext{session_type_};
AIBinder_linkToDeath(stack_iface_->asBinder().get(), death_recipient_.get(),
this);
cookie);
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
onSessionReady(_aidl_return);
return ndk::ScopedAStatus::ok();
}
@ -60,10 +88,8 @@ ndk::ScopedAStatus BluetoothAudioProvider::endSession() {
if (stack_iface_ != nullptr) {
BluetoothAudioSessionReport::OnSessionEnded(session_type_);
if (!is_binder_died) {
AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
death_recipient_.get(), this);
}
AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
death_recipient_.get(), this);
} else {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
@ -77,10 +103,9 @@ ndk::ScopedAStatus BluetoothAudioProvider::endSession() {
ndk::ScopedAStatus BluetoothAudioProvider::streamStarted(
BluetoothAudioStatus status) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status);
if (stack_iface_ != nullptr) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status);
BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
status);
} else {
@ -108,8 +133,6 @@ ndk::ScopedAStatus BluetoothAudioProvider::streamSuspended(
ndk::ScopedAStatus BluetoothAudioProvider::updateAudioConfiguration(
const AudioConfiguration& audio_config) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
if (stack_iface_ == nullptr || audio_config_ == nullptr) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
@ -125,13 +148,13 @@ ndk::ScopedAStatus BluetoothAudioProvider::updateAudioConfiguration(
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
BluetoothAudioSessionReport::ReportAudioConfigChanged(session_type_,
*audio_config_);
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " | audio_config=" << audio_config.toString();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
bool allowed) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
if (stack_iface_ == nullptr) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
@ -143,19 +166,143 @@ ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
return ndk::ScopedAStatus::ok();
}
void BluetoothAudioProvider::binderDiedCallbackAidl(void* ptr) {
LOG(ERROR) << __func__ << " - BluetoothAudio Service died";
auto provider = static_cast<BluetoothAudioProvider*>(ptr);
if (provider == nullptr) {
LOG(ERROR) << __func__ << ": Null AudioProvider HAL died";
return;
}
provider->is_binder_died = true;
provider->endSession();
ndk::ScopedAStatus BluetoothAudioProvider::parseA2dpConfiguration(
[[maybe_unused]] const CodecId& codec_id,
[[maybe_unused]] const std::vector<uint8_t>& configuration,
[[maybe_unused]] CodecParameters* codec_parameters,
[[maybe_unused]] A2dpStatus* _aidl_return) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " is illegal";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
ndk::ScopedAStatus BluetoothAudioProvider::getA2dpConfiguration(
[[maybe_unused]] const std::vector<A2dpRemoteCapabilities>&
remote_a2dp_capabilities,
[[maybe_unused]] const A2dpConfigurationHint& hint,
[[maybe_unused]] std::optional<audio::A2dpConfiguration>* _aidl_return) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " is illegal";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
ndk::ScopedAStatus BluetoothAudioProvider::setCodecPriority(
const ::aidl::android::hardware::bluetooth::audio::CodecId& in_codecId,
int32_t in_priority) {
/* TODO: Implement */
(void)in_codecId;
(void)in_priority;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseConfiguration(
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSourceAudioCapabilities,
const std::vector<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioConfigurationRequirement>& in_requirements,
std::vector<::aidl::android::hardware::bluetooth::audio::
IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
_aidl_return) {
/* TODO: Implement */
(void)in_remoteSinkAudioCapabilities;
(void)in_remoteSourceAudioCapabilities;
(void)in_requirements;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseQosConfiguration(
const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioAseQosConfigurationRequirement& in_qosRequirement,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioAseQosConfigurationPair* _aidl_return) {
/* TODO: Implement */
(void)in_qosRequirement;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioAseDatapathConfiguration(
const ::aidl::android::hardware::bluetooth::audio::AudioContext& in_context,
const std::vector<::aidl::android::hardware::bluetooth::audio::
LeAudioConfiguration::StreamMap>& in_streamMap,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDataPathConfigurationPair* _aidl_return) {
/* TODO: Implement */
(void)in_context;
(void)in_streamMap;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus BluetoothAudioProvider::onSinkAseMetadataChanged(
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
AseState in_state,
int32_t cigId, int32_t cisId,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
in_metadata) {
/* TODO: Implement */
(void)in_state;
(void)cigId;
(void)cisId;
(void)in_metadata;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus BluetoothAudioProvider::onSourceAseMetadataChanged(
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
AseState in_state,
int32_t cigId, int32_t cisId,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
in_metadata) {
/* TODO: Implement */
(void)in_state;
(void)cigId;
(void)cisId;
(void)in_metadata;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus BluetoothAudioProvider::getLeAudioBroadcastConfiguration(
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioBroadcastConfigurationRequirement& in_requirement,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioBroadcastConfigurationSetting* _aidl_return) {
/* TODO: Implement */
(void)in_remoteSinkAudioCapabilities;
(void)in_requirement;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus
BluetoothAudioProvider::getLeAudioBroadcastDatapathConfiguration(
const ::aidl::android::hardware::bluetooth::audio::AudioContext& in_context,
const std::vector<::aidl::android::hardware::bluetooth::audio::
LeAudioBroadcastConfiguration::BroadcastStreamMap>&
in_streamMap,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDataPathConfiguration* _aidl_return) {
/* TODO: Implement */
(void)in_context;
(void)in_streamMap;
(void)_aidl_return;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl
} // namespace aidl

View file

@ -41,20 +41,86 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider {
ndk::ScopedAStatus startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes,
DataMQDesc* _aidl_return);
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
ndk::ScopedAStatus endSession();
ndk::ScopedAStatus streamStarted(BluetoothAudioStatus status);
ndk::ScopedAStatus streamSuspended(BluetoothAudioStatus status);
ndk::ScopedAStatus updateAudioConfiguration(
const AudioConfiguration& audio_config);
ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed);
ndk::ScopedAStatus setCodecPriority(
const ::aidl::android::hardware::bluetooth::audio::CodecId& in_codecId,
int32_t in_priority) override;
ndk::ScopedAStatus getLeAudioAseConfiguration(
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSourceAudioCapabilities,
const std::vector<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioConfigurationRequirement>& in_requirements,
std::vector<::aidl::android::hardware::bluetooth::audio::
IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
_aidl_return) override;
ndk::ScopedAStatus getLeAudioAseQosConfiguration(
const ::aidl::android::hardware::bluetooth::audio::
IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
in_qosRequirement,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioAseQosConfigurationPair* _aidl_return) override;
ndk::ScopedAStatus getLeAudioAseDatapathConfiguration(
const ::aidl::android::hardware::bluetooth::audio::AudioContext&
in_context,
const std::vector<::aidl::android::hardware::bluetooth::audio::
LeAudioConfiguration::StreamMap>& in_streamMap,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDataPathConfigurationPair* _aidl_return) override;
ndk::ScopedAStatus onSinkAseMetadataChanged(
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
AseState in_state,
int32_t cigId, int32_t cisId,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
in_metadata) override;
ndk::ScopedAStatus onSourceAseMetadataChanged(
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
AseState in_state,
int32_t cigId, int32_t cisId,
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::MetadataLtv>>>&
in_metadata) override;
ndk::ScopedAStatus getLeAudioBroadcastConfiguration(
const std::optional<std::vector<std::optional<
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDeviceCapabilities>>>& in_remoteSinkAudioCapabilities,
const ::aidl::android::hardware::bluetooth::audio::
IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioBroadcastConfigurationSetting* _aidl_return) override;
ndk::ScopedAStatus getLeAudioBroadcastDatapathConfiguration(
const ::aidl::android::hardware::bluetooth::audio::AudioContext&
in_context,
const std::vector<::aidl::android::hardware::bluetooth::audio::
LeAudioBroadcastConfiguration::BroadcastStreamMap>&
in_streamMap,
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioDataPathConfiguration* _aidl_return) override;
ndk::ScopedAStatus parseA2dpConfiguration(
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, A2dpStatus* _aidl_return);
ndk::ScopedAStatus getA2dpConfiguration(
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
const A2dpConfigurationHint& hint,
std::optional<audio::A2dpConfiguration>* _aidl_return);
virtual bool isValid(const SessionType& sessionType) = 0;
protected:
virtual ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) = 0;
static void binderDiedCallbackAidl(void* cookie_ptr);
::ndk::ScopedAIBinder_DeathRecipient death_recipient_;
@ -62,9 +128,7 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider {
std::unique_ptr<AudioConfiguration> audio_config_ = nullptr;
SessionType session_type_;
std::vector<LatencyMode> latency_modes_;
bool is_binder_died = false;
};
} // namespace audio
} // namespace bluetooth
} // namespace hardware

View file

@ -22,9 +22,12 @@
#include <android-base/logging.h>
#include "A2dpOffloadAudioProvider.h"
#include "A2dpOffloadCodecFactory.h"
#include "A2dpSoftwareAudioProvider.h"
#include "BluetoothAudioProvider.h"
#include "HearingAidAudioProvider.h"
#include "HfpOffloadAudioProvider.h"
#include "HfpSoftwareAudioProvider.h"
#include "LeAudioOffloadAudioProvider.h"
#include "LeAudioSoftwareAudioProvider.h"
@ -34,6 +37,9 @@ namespace hardware {
namespace bluetooth {
namespace audio {
static const std::string kLeAudioOffloadProviderName =
"LE_AUDIO_OFFLOAD_HARDWARE_OFFLOAD_PROVIDER";
BluetoothAudioProviderFactory::BluetoothAudioProviderFactory() {}
ndk::ScopedAStatus BluetoothAudioProviderFactory::openProvider(
@ -78,6 +84,15 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::openProvider(
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
provider = ndk::SharedRefBase::make<A2dpOffloadDecodingAudioProvider>();
break;
case SessionType::HFP_SOFTWARE_ENCODING_DATAPATH:
provider = ndk::SharedRefBase::make<HfpSoftwareOutputAudioProvider>();
break;
case SessionType::HFP_SOFTWARE_DECODING_DATAPATH:
provider = ndk::SharedRefBase::make<HfpSoftwareInputAudioProvider>();
break;
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
provider = ndk::SharedRefBase::make<HfpOffloadAudioProvider>();
break;
default:
provider = nullptr;
break;
@ -135,8 +150,43 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderCapabilities(
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo(
SessionType session_type, std::optional<ProviderInfo>* _aidl_return) {
*_aidl_return = std::nullopt;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type);
if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
auto& provider_info = _aidl_return->emplace();
provider_info.name = A2dpOffloadCodecFactory::GetInstance()->name;
for (auto codec : A2dpOffloadCodecFactory::GetInstance()->codecs)
provider_info.codecInfos.push_back(codec->info);
}
if (session_type ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
session_type ==
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
std::vector<CodecInfo> db_codec_info =
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(session_type);
if (!db_codec_info.empty()) {
auto& provider_info = _aidl_return->emplace();
provider_info.name = kLeAudioOffloadProviderName;
provider_info.codecInfos = db_codec_info;
*_aidl_return = provider_info;
return ndk::ScopedAStatus::ok();
}
}
return ndk::ScopedAStatus::ok();
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl
} // namespace aidl

View file

@ -35,6 +35,10 @@ class BluetoothAudioProviderFactory : public BnBluetoothAudioProviderFactory {
ndk::ScopedAStatus getProviderCapabilities(
const SessionType session_type,
std::vector<AudioCapabilities>* _aidl_return) override;
ndk::ScopedAStatus getProviderInfo(
SessionType in_sessionType,
std::optional<ProviderInfo>* _aidl_return) override;
};
} // namespace audio

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 "BTAudioProviderHfpHW"
#include "HfpOffloadAudioProvider.h"
#include <BluetoothAudioCodecs.h>
#include <BluetoothAudioSessionReport.h>
#include <android-base/logging.h>
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
HfpOffloadAudioProvider::HfpOffloadAudioProvider() {
session_type_ = SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH;
}
bool HfpOffloadAudioProvider::isValid(const SessionType& session_type) {
return (session_type == session_type_);
}
ndk::ScopedAStatus HfpOffloadAudioProvider::startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
if (audio_config.getTag() != AudioConfiguration::hfpConfig) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return BluetoothAudioProvider::startSession(host_if, audio_config,
latency_modes, _aidl_return);
}
ndk::ScopedAStatus HfpOffloadAudioProvider::onSessionReady(
DataMQDesc* _aidl_return) {
*_aidl_return = DataMQDesc();
BluetoothAudioSessionReport::OnSessionStarted(
session_type_, stack_iface_, nullptr, *audio_config_, latency_modes_);
return ndk::ScopedAStatus::ok();
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

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 "BluetoothAudioProvider.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
class HfpOffloadAudioProvider : public BluetoothAudioProvider {
public:
HfpOffloadAudioProvider();
bool isValid(const SessionType& sessionType) override;
ndk::ScopedAStatus startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
private:
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
};
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,140 @@
/*
* 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 "BTAudioProviderHfpSW"
#include "HfpSoftwareAudioProvider.h"
#include <BluetoothAudioCodecs.h>
#include <BluetoothAudioSessionReport.h>
#include <android-base/logging.h>
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
static constexpr uint32_t kBufferCount = 2; // two frame buffer
HfpSoftwareOutputAudioProvider::HfpSoftwareOutputAudioProvider()
: HfpSoftwareAudioProvider() {
session_type_ = SessionType::HFP_SOFTWARE_ENCODING_DATAPATH;
}
HfpSoftwareInputAudioProvider::HfpSoftwareInputAudioProvider()
: HfpSoftwareAudioProvider() {
session_type_ = SessionType::HFP_SOFTWARE_DECODING_DATAPATH;
}
HfpSoftwareAudioProvider::HfpSoftwareAudioProvider()
: BluetoothAudioProvider(), data_mq_(nullptr) {
}
bool HfpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
return (sessionType == session_type_);
}
ndk::ScopedAStatus HfpSoftwareAudioProvider::startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const PcmConfiguration& pcm_config =
audio_config.get<AudioConfiguration::pcmConfig>();
if (!BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(pcm_config)) {
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
<< pcm_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
bool isValidConfig = true;
if (pcm_config.bitsPerSample != 16) {
isValidConfig = false;
}
if (pcm_config.sampleRateHz != 8000 && pcm_config.sampleRateHz != 16000 &&
pcm_config.sampleRateHz != 32000) {
isValidConfig = false;
}
if (pcm_config.channelMode != ChannelMode::MONO) {
isValidConfig = false;
}
if (pcm_config.dataIntervalUs != 7500) {
isValidConfig = false;
}
int bytes_per_sample = pcm_config.bitsPerSample / 8;
uint32_t data_mq_size = kBufferCount * bytes_per_sample *
(pcm_config.sampleRateHz / 1000) *
pcm_config.dataIntervalUs / 1000;
if (!isValidConfig) {
LOG(ERROR) << __func__ << "Unexpected audio buffer size: " << data_mq_size
<< ", SampleRateHz: " << pcm_config.sampleRateHz
<< ", ChannelMode: " << toString(pcm_config.channelMode)
<< ", BitsPerSample: "
<< static_cast<int>(pcm_config.bitsPerSample)
<< ", BytesPerSample: " << bytes_per_sample
<< ", DataIntervalUs: " << pcm_config.dataIntervalUs
<< ", SessionType: " << toString(session_type_);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
LOG(INFO) << __func__ << " - size of audio buffer " << data_mq_size
<< " byte(s)";
std::unique_ptr<DataMQ> temp_data_mq(
new DataMQ(data_mq_size, /* EventFlag */ true));
if (temp_data_mq == nullptr || !temp_data_mq->isValid()) {
ALOGE_IF(!temp_data_mq, "failed to allocate data MQ");
ALOGE_IF(temp_data_mq && !temp_data_mq->isValid(), "data MQ is invalid");
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
data_mq_ = std::move(temp_data_mq);
return BluetoothAudioProvider::startSession(host_if, audio_config,
latency_modes, _aidl_return);
}
ndk::ScopedAStatus HfpSoftwareAudioProvider::onSessionReady(
DataMQDesc* _aidl_return) {
if (data_mq_ == nullptr || !data_mq_->isValid()) {
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
*_aidl_return = data_mq_->dupeDesc();
auto desc = data_mq_->dupeDesc();
BluetoothAudioSessionReport::OnSessionStarted(
session_type_, stack_iface_, &desc, *audio_config_, latency_modes_);
return ndk::ScopedAStatus::ok();
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,59 @@
/*
* 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 "BluetoothAudioProvider.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
class HfpSoftwareAudioProvider : public BluetoothAudioProvider {
public:
HfpSoftwareAudioProvider();
bool isValid(const SessionType& sessionType) override;
ndk::ScopedAStatus startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
private:
// audio data queue for software encoding
std::unique_ptr<DataMQ> data_mq_;
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
};
class HfpSoftwareOutputAudioProvider : public HfpSoftwareAudioProvider {
public:
HfpSoftwareOutputAudioProvider();
};
class HfpSoftwareInputAudioProvider : public HfpSoftwareAudioProvider {
public:
HfpSoftwareInputAudioProvider();
};
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -28,6 +28,73 @@ namespace hardware {
namespace bluetooth {
namespace audio {
constexpr uint8_t kLeAudioDirectionSink = 0x01;
constexpr uint8_t kLeAudioDirectionSource = 0x02;
const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t>
freq_to_support_bitmask_map = {
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ8000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ11025,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ11025},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ22050,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ22050},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ32000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ88200,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ88200},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ96000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ176400,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ176400},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ192000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ192000},
{CodecSpecificConfigurationLtv::SamplingFrequency::HZ384000,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ384000},
};
// Helper map from capability's tag to configuration's tag
std::map<CodecSpecificCapabilitiesLtv::Tag, CodecSpecificConfigurationLtv::Tag>
cap_to_cfg_tag_map = {
{CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies,
CodecSpecificConfigurationLtv::Tag::samplingFrequency},
{CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU,
CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU},
{CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations,
CodecSpecificConfigurationLtv::Tag::frameDuration},
{CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts,
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation},
{CodecSpecificCapabilitiesLtv::Tag::supportedOctetsPerCodecFrame,
CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame},
};
const std::map<CodecSpecificConfigurationLtv::FrameDuration, uint32_t>
fduration_to_support_fduration_map = {
{CodecSpecificConfigurationLtv::FrameDuration::US7500,
CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US7500},
{CodecSpecificConfigurationLtv::FrameDuration::US10000,
CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US10000},
};
std::map<int32_t, CodecSpecificConfigurationLtv::SamplingFrequency>
sampling_freq_map = {
{16000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
{48000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000},
{96000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000},
};
std::map<int32_t, CodecSpecificConfigurationLtv::FrameDuration>
frame_duration_map = {
{7500, CodecSpecificConfigurationLtv::FrameDuration::US7500},
{10000, CodecSpecificConfigurationLtv::FrameDuration::US10000},
};
LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
: LeAudioOffloadAudioProvider() {
session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
@ -55,24 +122,23 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
if (session_type_ ==
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
if (audio_config.getTag() != AudioConfiguration::leAudioBroadcastConfig) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
} else if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
<< audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const auto& le_audio_config =
audio_config.get<AudioConfiguration::leAudioConfig>();
if (!BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
session_type_, le_audio_config)) {
LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
<< le_audio_config.toString();
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return BluetoothAudioProvider::startSession(
host_if, audio_config, latency_modes, _aidl_return);
return BluetoothAudioProvider::startSession(host_if, audio_config,
latency_modes, _aidl_return);
}
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
@ -82,6 +148,644 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
*_aidl_return = DataMQDesc();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus LeAudioOffloadAudioProvider::setCodecPriority(
const CodecId& in_codecId, int32_t in_priority) {
codec_priority_map_[in_codecId] = in_priority;
return ndk::ScopedAStatus::ok();
};
bool LeAudioOffloadAudioProvider::isMatchedValidCodec(CodecId cfg_codec,
CodecId req_codec) {
auto priority = codec_priority_map_.find(cfg_codec);
if (priority != codec_priority_map_.end() && priority->second == -1)
return false;
return cfg_codec == req_codec;
}
bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext(
AudioContext setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
// If has no metadata, assume match
if (!capabilities.metadata.has_value()) return true;
for (auto metadata : capabilities.metadata.value()) {
if (!metadata.has_value()) continue;
if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
// Check all pref audio context to see if anything matched
auto& context = metadata.value()
.get<MetadataLtv::Tag::preferredAudioContexts>()
.values;
if (setting_context.bitmask & context.bitmask) return true;
}
}
return false;
}
bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq(
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
capability_freq) {
for (auto [freq, bitmask] : freq_to_support_bitmask_map)
if (cfg_freq == freq) return (capability_freq.bitmask & bitmask);
return false;
}
bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
capability_fduration) {
for (auto [fduration, bitmask] : fduration_to_support_fduration_map)
if (cfg_fduration == fduration)
return (capability_fduration.bitmask & bitmask);
return false;
}
bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
CodecSpecificConfigurationLtv::AudioChannelAllocation&
/*cfg_channel*/,
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
/*capability_channel*/) {
bool isMatched = true;
// TODO: how to match?
return isMatched;
}
bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU(
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu,
CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU&
capability_frame_sdu) {
return cfg_frame_sdu.value <= capability_frame_sdu.value;
}
bool LeAudioOffloadAudioProvider::isMatchedOctetsPerCodecFrame(
CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets,
CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame&
capability_octets) {
return cfg_octets.value >= capability_octets.minimum &&
cfg_octets.value <= capability_octets.maximum;
}
bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities) {
// Convert all codec_cfg into a map of tags -> correct data
std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
cfg_tag_map;
for (auto codec_cfg_data : codec_cfg)
cfg_tag_map[codec_cfg_data.getTag()] = codec_cfg_data;
for (auto& codec_capability : codec_capabilities) {
auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]);
// Cannot find tag for the capability:
if (cfg == cfg_tag_map.end()) return false;
// Matching logic for sampling frequency
if (codec_capability.getTag() ==
CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies) {
if (!isMatchedSamplingFreq(
cfg->second
.get<CodecSpecificConfigurationLtv::Tag::samplingFrequency>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedSamplingFrequencies>()))
return false;
} else if (codec_capability.getTag() ==
CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations) {
if (!isMatchedFrameDuration(
cfg->second
.get<CodecSpecificConfigurationLtv::Tag::frameDuration>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedFrameDurations>()))
return false;
} else if (codec_capability.getTag() ==
CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts) {
if (!isMatchedAudioChannel(
cfg->second.get<
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedAudioChannelCounts>()))
return false;
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
supportedMaxCodecFramesPerSDU) {
if (!isMatchedCodecFramesPerSDU(
cfg->second.get<
CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedMaxCodecFramesPerSDU>()))
return false;
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
supportedOctetsPerCodecFrame) {
if (!isMatchedOctetsPerCodecFrame(
cfg->second.get<
CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedOctetsPerCodecFrame>()))
return false;
}
}
return true;
}
bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
LeAudioAseConfiguration setting_cfg,
LeAudioAseConfiguration requirement_cfg) {
// Check matching for codec configuration <=> requirement ASE codec
// Also match if no CodecId requirement
if (requirement_cfg.codecId.has_value()) {
if (!setting_cfg.codecId.has_value()) return false;
if (!isMatchedValidCodec(setting_cfg.codecId.value(),
requirement_cfg.codecId.value()))
return false;
}
if (setting_cfg.targetLatency != requirement_cfg.targetLatency) return false;
// Ignore PHY requirement
// Check all codec configuration
std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
cfg_tag_map;
for (auto cfg : setting_cfg.codecConfiguration)
cfg_tag_map[cfg.getTag()] = cfg;
for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
// Directly compare CodecSpecificConfigurationLtv
auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
if (cfg == cfg_tag_map.end()) return false;
if (cfg->second != requirement_cfg) return false;
}
// Ignore vendor configuration and metadata requirement
return true;
}
bool LeAudioOffloadAudioProvider::isMatchedBISConfiguration(
LeAudioBisConfiguration bis_cfg,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) return false;
if (!isCapabilitiesMatchedCodecConfiguration(
bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities))
return false;
return true;
}
void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>&
direction_configurations,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations) {
for (auto direction_configuration : direction_configurations) {
if (!direction_configuration.has_value()) continue;
if (!direction_configuration.value().aseConfiguration.codecId.has_value())
continue;
if (!isMatchedValidCodec(
direction_configuration.value().aseConfiguration.codecId.value(),
capabilities.codecId))
continue;
// Check matching for codec configuration <=> codec capabilities
if (!isCapabilitiesMatchedCodecConfiguration(
direction_configuration.value().aseConfiguration.codecConfiguration,
capabilities.codecSpecificCapabilities))
continue;
valid_direction_configurations.push_back(direction_configuration);
}
}
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>&
direction_configurations,
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
requirements,
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations) {
for (auto direction_configuration : direction_configurations) {
if (!requirements.has_value()) {
// If there's no requirement, all are valid
valid_direction_configurations.push_back(direction_configuration);
continue;
}
if (!direction_configuration.has_value()) continue;
for (auto& requirement : requirements.value()) {
if (!requirement.has_value()) continue;
if (!isMatchedAseConfiguration(
direction_configuration.value().aseConfiguration,
requirement.value().aseConfiguration))
continue;
// Valid if match any requirement.
valid_direction_configurations.push_back(direction_configuration);
break;
}
}
}
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
* capabilities. The new setting will have a filtered list of
* AseDirectionConfiguration that matched the capabilities */
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
uint8_t direction) {
// Try to match context in metadata.
if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities))
return std::nullopt;
// Get a list of all matched AseDirectionConfiguration
// for the input direction
std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration = nullptr;
if (direction == kLeAudioDirectionSink) {
if (!setting.sinkAseConfiguration.has_value()) return std::nullopt;
direction_configuration = &setting.sinkAseConfiguration.value();
} else {
if (!setting.sourceAseConfiguration.has_value()) return std::nullopt;
direction_configuration = &setting.sourceAseConfiguration.value();
}
std::vector<std::optional<AseDirectionConfiguration>>
valid_direction_configuration;
filterCapabilitiesAseDirectionConfiguration(
*direction_configuration, capabilities, valid_direction_configuration);
if (valid_direction_configuration.empty()) return std::nullopt;
// Create a new LeAudioAseConfigurationSetting and return
LeAudioAseConfigurationSetting filtered_setting;
filtered_setting.audioContext = setting.audioContext;
filtered_setting.packing = setting.packing;
if (direction == kLeAudioDirectionSink) {
filtered_setting.sinkAseConfiguration = valid_direction_configuration;
} else {
filtered_setting.sourceAseConfiguration = valid_direction_configuration;
}
filtered_setting.flags = setting.flags;
return filtered_setting;
}
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
* requirement. The new setting will have a filtered list of
* AseDirectionConfiguration that matched the requirement */
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
requirement) {
// Try to match context in metadata.
if (setting.audioContext != requirement.audioContext) return std::nullopt;
// Check requirement for the correct direction
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>*
direction_requirement;
std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration;
if (setting.sinkAseConfiguration.has_value()) {
direction_configuration = &setting.sinkAseConfiguration.value();
direction_requirement = &requirement.sinkAseRequirement;
} else {
direction_configuration = &setting.sourceAseConfiguration.value();
direction_requirement = &requirement.sourceAseRequirement;
}
std::vector<std::optional<AseDirectionConfiguration>>
valid_direction_configuration;
filterRequirementAseDirectionConfiguration(*direction_configuration,
*direction_requirement,
valid_direction_configuration);
if (valid_direction_configuration.empty()) return std::nullopt;
// Create a new LeAudioAseConfigurationSetting and return
LeAudioAseConfigurationSetting filtered_setting;
filtered_setting.audioContext = setting.audioContext;
filtered_setting.packing = setting.packing;
if (setting.sinkAseConfiguration.has_value())
filtered_setting.sinkAseConfiguration = valid_direction_configuration;
else
filtered_setting.sourceAseConfiguration = valid_direction_configuration;
filtered_setting.flags = setting.flags;
return filtered_setting;
}
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSinkAudioCapabilities,
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSourceAudioCapabilities,
const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
in_requirements,
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
_aidl_return) {
// Get all configuration settings
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
ase_configuration_settings =
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
// Currently won't handle case where both sink and source capabilities
// are passed in. Only handle one of them.
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>*
in_remoteAudioCapabilities;
uint8_t direction = 0;
if (in_remoteSinkAudioCapabilities.has_value()) {
direction = kLeAudioDirectionSink;
in_remoteAudioCapabilities = &in_remoteSinkAudioCapabilities;
} else {
direction = kLeAudioDirectionSource;
in_remoteAudioCapabilities = &in_remoteSourceAudioCapabilities;
}
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
capability_matched_ase_configuration_settings;
// Matching with remote capabilities
for (auto& setting : ase_configuration_settings) {
for (auto& capability : in_remoteAudioCapabilities->value()) {
if (!capability.has_value()) continue;
auto filtered_ase_configuration_setting =
getCapabilitiesMatchedAseConfigurationSettings(
setting, capability.value(), direction);
if (filtered_ase_configuration_setting.has_value()) {
capability_matched_ase_configuration_settings.push_back(
filtered_ase_configuration_setting.value());
}
}
}
// Matching with requirements
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
for (auto& setting : capability_matched_ase_configuration_settings) {
for (auto& requirement : in_requirements) {
auto filtered_ase_configuration_setting =
getRequirementMatchedAseConfigurationSettings(setting, requirement);
if (filtered_ase_configuration_setting.has_value()) {
result.push_back(filtered_ase_configuration_setting.value());
}
}
}
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
};
bool LeAudioOffloadAudioProvider::isMatchedQosRequirement(
LeAudioAseQosConfiguration setting_qos,
AseQosDirectionRequirement requirement_qos) {
if (setting_qos.retransmissionNum !=
requirement_qos.preferredRetransmissionNum)
return false;
if (setting_qos.maxTransportLatencyMs > requirement_qos.maxTransportLatencyMs)
return false;
// Ignore other parameters, as they are not populated in the setting_qos
return true;
}
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
in_qosRequirement,
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return) {
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair result;
// Get all configuration settings
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
ase_configuration_settings =
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
// Direction QoS matching
// Only handle one direction input case
uint8_t direction = 0;
std::optional<AseQosDirectionRequirement> direction_qos_requirement =
std::nullopt;
if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
direction_qos_requirement = in_qosRequirement.sinkAseQosRequirement.value();
direction = kLeAudioDirectionSink;
} else if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
direction_qos_requirement =
in_qosRequirement.sourceAseQosRequirement.value();
direction = kLeAudioDirectionSource;
}
for (auto& setting : ase_configuration_settings) {
// Context matching
if (setting.audioContext != in_qosRequirement.contextType) continue;
// Match configuration flags
// Currently configuration flags are not populated, ignore.
// Get a list of all matched AseDirectionConfiguration
// for the input direction
std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration = nullptr;
if (direction == kLeAudioDirectionSink) {
if (!setting.sinkAseConfiguration.has_value()) continue;
direction_configuration = &setting.sinkAseConfiguration.value();
} else {
if (!setting.sourceAseConfiguration.has_value()) continue;
direction_configuration = &setting.sourceAseConfiguration.value();
}
for (auto cfg : *direction_configuration) {
if (!cfg.has_value()) continue;
// If no requirement, return the first QoS
if (!direction_qos_requirement.has_value()) {
result.sinkQosConfiguration = cfg.value().qosConfiguration;
result.sourceQosConfiguration = cfg.value().qosConfiguration;
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
}
// If has requirement, return the first matched QoS
// Try to match the ASE configuration
// and QoS with requirement
if (!cfg.value().qosConfiguration.has_value()) continue;
if (isMatchedAseConfiguration(
cfg.value().aseConfiguration,
direction_qos_requirement.value().aseConfiguration) &&
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
direction_qos_requirement.value())) {
if (direction == kLeAudioDirectionSink)
result.sinkQosConfiguration = cfg.value().qosConfiguration;
else
result.sourceQosConfiguration = cfg.value().qosConfiguration;
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
}
}
}
// No match, return empty QoS
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
};
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSinkAseMetadataChanged(
IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
int32_t /*in_cisId*/,
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
(void)in_state;
(void)in_metadata;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSourceAseMetadataChanged(
IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
int32_t /*in_cisId*/,
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
(void)in_state;
(void)in_metadata;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};
void LeAudioOffloadAudioProvider::getBroadcastSettings() {
if (!broadcast_settings.empty()) return;
LOG(INFO) << __func__ << ": Loading broadcast settings from provider info";
std::vector<CodecInfo> db_codec_info =
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
broadcast_settings.clear();
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
default_allocation.bitmask =
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
for (auto& codec_info : db_codec_info) {
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
continue;
auto& transport = codec_info.transport.get<CodecInfo::Transport::leAudio>();
LeAudioBroadcastConfigurationSetting setting;
// Default setting
setting.numBis = 1;
setting.phy = {Phy::TWO_M};
// Populate BIS configuration info using codec_info
LeAudioBisConfiguration bis_cfg;
bis_cfg.codecId = codec_info.id;
CodecSpecificConfigurationLtv::OctetsPerCodecFrame octets;
octets.value = transport.bitdepth[0];
bis_cfg.codecConfiguration = {
sampling_freq_map[transport.samplingFrequencyHz[0]], octets,
frame_duration_map[transport.frameDurationUs[0]], default_allocation};
// Add information to structure
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
sub_bis_cfg.numBis = 1;
sub_bis_cfg.bisConfiguration = bis_cfg;
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
sub_cfg.bisConfigurations = {sub_bis_cfg};
setting.subgroupsConfigurations = {sub_cfg};
broadcast_settings.push_back(setting);
}
LOG(INFO) << __func__
<< ": Done loading broadcast settings from provider info";
}
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
* capabilities. The new setting will have a filtered list of
* AseDirectionConfiguration that matched the capabilities */
std::optional<LeAudioBroadcastConfigurationSetting>
LeAudioOffloadAudioProvider::
getCapabilitiesMatchedBroadcastConfigurationSettings(
LeAudioBroadcastConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities&
capabilities) {
std::vector<IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration>
filter_subgroup;
for (auto& sub_cfg : setting.subgroupsConfigurations) {
std::vector<IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration>
filtered_bis_cfg;
for (auto& bis_cfg : sub_cfg.bisConfigurations)
if (isMatchedBISConfiguration(bis_cfg.bisConfiguration, capabilities)) {
filtered_bis_cfg.push_back(bis_cfg);
}
if (!filtered_bis_cfg.empty()) {
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration
subgroup_cfg;
subgroup_cfg.bisConfigurations = filtered_bis_cfg;
filter_subgroup.push_back(subgroup_cfg);
}
}
if (filter_subgroup.empty()) return std::nullopt;
// Create a new LeAudioAseConfigurationSetting and return
LeAudioBroadcastConfigurationSetting filtered_setting(setting);
filtered_setting.subgroupsConfigurations = filter_subgroup;
return filtered_setting;
}
ndk::ScopedAStatus
LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSinkAudioCapabilities,
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
LeAudioBroadcastConfigurationSetting* _aidl_return) {
getBroadcastSettings();
_aidl_return = nullptr;
// Match and filter capability
std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
if (!in_remoteSinkAudioCapabilities.has_value()) {
LOG(WARNING) << __func__ << ": Empty capability";
return ndk::ScopedAStatus::ok();
}
for (auto& setting : broadcast_settings) {
for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
if (!capability.has_value()) continue;
auto filtered_setting =
getCapabilitiesMatchedBroadcastConfigurationSettings(
setting, capability.value());
if (filtered_setting.has_value())
filtered_settings.push_back(filtered_setting.value());
}
}
if (filtered_settings.empty()) {
LOG(WARNING) << __func__ << ": Cannot match any remote capability";
return ndk::ScopedAStatus::ok();
}
// Match and return the first matched requirement
if (in_requirement.subgroupConfigurationRequirements.empty()) {
LOG(INFO) << __func__ << ": Empty requirement";
*_aidl_return = filtered_settings[0];
return ndk::ScopedAStatus::ok();
}
for (auto& setting : filtered_settings) {
// Further filter out bis configuration
LeAudioBroadcastConfigurationSetting filtered_setting(setting);
filtered_setting.subgroupsConfigurations.clear();
for (auto& sub_cfg : setting.subgroupsConfigurations) {
bool isMatched = false;
for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
// Matching number of BIS
if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size())
continue;
// Currently will ignore quality and context hint.
isMatched = true;
break;
}
if (isMatched)
filtered_setting.subgroupsConfigurations.push_back(sub_cfg);
}
// Return the first match
if (!filtered_setting.subgroupsConfigurations.empty()) {
LOG(INFO) << __func__ << ": Matched requirement";
*_aidl_return = filtered_setting;
return ndk::ScopedAStatus::ok();
}
}
LOG(WARNING) << __func__ << ": Cannot match any requirement";
return ndk::ScopedAStatus::ok();
};
} // namespace audio
} // namespace bluetooth

View file

@ -16,7 +16,12 @@
#pragma once
#include <map>
#include "BluetoothAudioProvider.h"
#include "aidl/android/hardware/bluetooth/audio/LeAudioAseConfiguration.h"
#include "aidl/android/hardware/bluetooth/audio/MetadataLtv.h"
#include "aidl/android/hardware/bluetooth/audio/SessionType.h"
namespace aidl {
namespace android {
@ -24,6 +29,19 @@ namespace hardware {
namespace bluetooth {
namespace audio {
using LeAudioAseConfigurationSetting =
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
using AseDirectionRequirement = IBluetoothAudioProvider::
LeAudioConfigurationRequirement::AseDirectionRequirement;
using AseDirectionConfiguration = IBluetoothAudioProvider::
LeAudioAseConfigurationSetting::AseDirectionConfiguration;
using AseQosDirectionRequirement = IBluetoothAudioProvider::
LeAudioAseQosConfigurationRequirement::AseQosDirectionRequirement;
using LeAudioAseQosConfiguration =
IBluetoothAudioProvider::LeAudioAseQosConfiguration;
using LeAudioBroadcastConfigurationSetting =
IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting;
class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
public:
LeAudioOffloadAudioProvider();
@ -33,11 +51,112 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
ndk::ScopedAStatus startSession(
const std::shared_ptr<IBluetoothAudioPort>& host_if,
const AudioConfiguration& audio_config,
const std::vector<LatencyMode>& latency_modes,
DataMQDesc* _aidl_return);
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return);
ndk::ScopedAStatus setCodecPriority(const CodecId& in_codecId,
int32_t in_priority) override;
ndk::ScopedAStatus getLeAudioAseConfiguration(
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSinkAudioCapabilities,
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSourceAudioCapabilities,
const std::vector<
IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
in_requirements,
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
_aidl_return) override;
ndk::ScopedAStatus getLeAudioAseQosConfiguration(
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
in_qosRequirement,
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return)
override;
ndk::ScopedAStatus onSourceAseMetadataChanged(
IBluetoothAudioProvider::AseState in_state, int32_t in_cigId,
int32_t in_cisId,
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata)
override;
ndk::ScopedAStatus onSinkAseMetadataChanged(
IBluetoothAudioProvider::AseState in_state, int32_t in_cigId,
int32_t in_cisId,
const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata)
override;
ndk::ScopedAStatus getLeAudioBroadcastConfiguration(
const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
in_remoteSinkAudioCapabilities,
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
LeAudioBroadcastConfigurationSetting* _aidl_return) override;
private:
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
std::map<CodecId, uint32_t> codec_priority_map_;
std::vector<LeAudioBroadcastConfigurationSetting> broadcast_settings;
// Private matching function definitions
bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec);
bool isCapabilitiesMatchedContext(
AudioContext setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
bool isMatchedSamplingFreq(
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
capability_freq);
bool isMatchedFrameDuration(
CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
capability_fduration);
bool isMatchedAudioChannel(
CodecSpecificConfigurationLtv::AudioChannelAllocation& cfg_channel,
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
capability_channel);
bool isMatchedCodecFramesPerSDU(
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu,
CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU&
capability_frame_sdu);
bool isMatchedOctetsPerCodecFrame(
CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets,
CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame&
capability_octets);
bool isCapabilitiesMatchedCodecConfiguration(
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg,
LeAudioAseConfiguration requirement_cfg);
bool isMatchedBISConfiguration(
LeAudioBisConfiguration bis_cfg,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
void filterCapabilitiesAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>&
direction_configurations,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations);
void filterRequirementAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>&
direction_configurations,
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
requirements,
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations);
std::optional<LeAudioAseConfigurationSetting>
getCapabilitiesMatchedAseConfigurationSettings(
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
uint8_t direction);
std::optional<LeAudioAseConfigurationSetting>
getRequirementMatchedAseConfigurationSettings(
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
requirement);
bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
AseQosDirectionRequirement requirement_qos);
std::optional<LeAudioBroadcastConfigurationSetting>
getCapabilitiesMatchedBroadcastConfigurationSettings(
LeAudioBroadcastConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
void getBroadcastSettings();
};
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {

View file

@ -1,7 +1,7 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.hardware.bluetooth.audio</name>
<version>3</version>
<version>4</version>
<fqname>IBluetoothAudioProviderFactory/sysbta</fqname>
</hal>
<hal>

View file

@ -9,8 +9,11 @@ cc_library_shared {
"utils.cc",
],
header_libs: ["libhardware_headers"],
defaults: [
"latest_android_hardware_audio_common_ndk_shared",
"latest_android_hardware_bluetooth_audio_ndk_shared",
],
shared_libs: [
"android.hardware.bluetooth.audio-V3-ndk",
"libbluetooth_audio_session_aidl_system",
"libaudioutils",
"libbase",

View file

@ -17,7 +17,6 @@
#define LOG_TAG "BTAudioHw"
#include <android-base/logging.h>
#include <errno.h>
#include <hardware/hardware.h>
#include <log/log.h>
#include <malloc.h>
@ -39,7 +38,8 @@ static int adev_set_parameters(struct audio_hw_device* dev,
LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
<< "]";
if (params.find("A2dpSuspended") == params.end()) {
if (params.find("A2dpSuspended") == params.end() &&
params.find("LeAudioSuspended") == params.end()) {
return -ENOSYS;
}
@ -99,6 +99,67 @@ static int adev_get_mic_mute(const struct audio_hw_device* dev, bool* state) {
return -ENOSYS;
}
static int adev_create_audio_patch(struct audio_hw_device* device,
unsigned int num_sources,
const struct audio_port_config* sources,
unsigned int num_sinks,
const struct audio_port_config* sinks,
audio_patch_handle_t* handle) {
if (device == nullptr || sources == nullptr || sinks == nullptr ||
handle == nullptr || num_sources != 1 || num_sinks == 0 ||
num_sinks > AUDIO_PATCH_PORTS_MAX) {
return -EINVAL;
}
if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) {
return -EINVAL;
}
} else if (sources[0].type == AUDIO_PORT_TYPE_MIX) {
for (unsigned int i = 0; i < num_sinks; i++) {
if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
if (*handle == AUDIO_PATCH_HANDLE_NONE) {
*handle = ++bluetooth_device->next_unique_id;
}
LOG(INFO) << __func__ << ": device=" << std::hex << sinks[0].ext.device.type
<< " handle: " << *handle;
return 0;
}
static int adev_release_audio_patch(struct audio_hw_device* device,
audio_patch_handle_t patch_handle) {
if (device == nullptr) {
return -EINVAL;
}
LOG(INFO) << __func__ << ": patch_handle=" << patch_handle;
return 0;
}
static int adev_get_audio_port_v7(struct audio_hw_device* device,
struct audio_port_v7* port) {
if (device == nullptr || port == nullptr) {
return -EINVAL;
}
return -ENOSYS;
}
static int adev_get_audio_port(struct audio_hw_device* device,
struct audio_port* port) {
if (device == nullptr || port == nullptr) {
return -EINVAL;
}
return -ENOSYS;
}
static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
static int adev_close(hw_device_t* device) {
@ -117,7 +178,7 @@ static int adev_open(const hw_module_t* module, const char* name,
if (!adev) return -ENOMEM;
adev->common.tag = HARDWARE_DEVICE_TAG;
adev->common.version = AUDIO_DEVICE_API_VERSION_2_0;
adev->common.version = AUDIO_DEVICE_API_VERSION_3_2;
adev->common.module = (struct hw_module_t*)module;
adev->common.close = adev_close;
@ -138,6 +199,10 @@ static int adev_open(const hw_module_t* module, const char* name,
adev->dump = adev_dump;
adev->set_master_mute = adev_set_master_mute;
adev->get_master_mute = adev_get_master_mute;
adev->create_audio_patch = adev_create_audio_patch;
adev->release_audio_patch = adev_release_audio_patch;
adev->get_audio_port_v7 = adev_get_audio_port_v7;
adev->get_audio_port = adev_get_audio_port;
*device = &adev->common;
return 0;

View file

@ -34,12 +34,17 @@ namespace bluetooth {
namespace audio {
namespace aidl {
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::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::media::audio::common::AudioContentType;
using ::aidl::android::media::audio::common::AudioSource;
using ::aidl::android::media::audio::common::AudioUsage;
using ::android::base::StringPrintf;
using ControlResultCallback = std::function<void(
@ -94,6 +99,20 @@ audio_format_t BitsPerSampleToAudioFormat(uint8_t bits_per_sample,
}
}
std::vector<std::string> CovertAudioTagFromV7(char* tags_v7) {
std::vector<std::string> tags;
char tags_copy[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE];
strlcpy(tags_copy, tags_v7, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
char* tag = strtok(tags_copy, ";");
while (tag != NULL) {
tags.push_back(tag);
tag = strtok(NULL, ";");
}
return tags;
}
// The maximum time to wait in std::condition_variable::wait_for()
constexpr unsigned int kMaxWaitingTimeMs = 4500;
@ -577,7 +596,7 @@ bool BluetoothAudioPortAidl::GetPresentationPosition(
}
void BluetoothAudioPortAidl::UpdateSourceMetadata(
const source_metadata* source_metadata) const {
const source_metadata_v7* source_metadata) const {
if (!in_use()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return;
@ -586,13 +605,25 @@ void BluetoothAudioPortAidl::UpdateSourceMetadata(
<< ", cookie=" << StringPrintf("%#hx", cookie_)
<< ", state=" << state_ << ", " << source_metadata->track_count
<< " track(s)";
if (source_metadata->track_count == 0) return;
ssize_t track_count = source_metadata->track_count;
if (track_count == 0) return;
SourceMetadata hal_source_metadata;
hal_source_metadata.tracks.resize(track_count);
for (int i = 0; i < track_count; i++) {
hal_source_metadata.tracks[i].usage =
static_cast<AudioUsage>(source_metadata->tracks[i].base.usage);
hal_source_metadata.tracks[i].contentType = static_cast<AudioContentType>(
source_metadata->tracks[i].base.content_type);
hal_source_metadata.tracks[i].tags =
std::move(CovertAudioTagFromV7(source_metadata->tracks[i].tags));
}
BluetoothAudioSessionControl::UpdateSourceMetadata(session_type_,
*source_metadata);
hal_source_metadata);
}
void BluetoothAudioPortAidl::UpdateSinkMetadata(
const sink_metadata* sink_metadata) const {
const sink_metadata_v7* sink_metadata) const {
if (!in_use()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
return;
@ -601,9 +632,20 @@ void BluetoothAudioPortAidl::UpdateSinkMetadata(
<< ", cookie=" << StringPrintf("%#hx", cookie_)
<< ", state=" << state_ << ", " << sink_metadata->track_count
<< " track(s)";
if (sink_metadata->track_count == 0) return;
ssize_t track_count = sink_metadata->track_count;
if (track_count == 0) return;
SinkMetadata hal_sink_metadata;
hal_sink_metadata.tracks.resize(track_count);
for (int i = 0; i < track_count; i++) {
hal_sink_metadata.tracks[i].source =
static_cast<AudioSource>(sink_metadata->tracks[i].base.source);
hal_sink_metadata.tracks[i].gain = sink_metadata->tracks[i].base.gain;
hal_sink_metadata.tracks[i].tags =
std::move(CovertAudioTagFromV7(sink_metadata->tracks[i].tags));
}
BluetoothAudioSessionControl::UpdateSinkMetadata(session_type_,
*sink_metadata);
hal_sink_metadata);
}
BluetoothStreamState BluetoothAudioPortAidl::GetState() const { return state_; }

View file

@ -16,6 +16,8 @@
#pragma once
#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/SessionType.h>
#include <hardware/audio.h>
@ -93,12 +95,6 @@ class BluetoothAudioPort {
return false;
}
/***
* Called by the Audio framework / HAL when the metadata of the stream's
* source has been changed.
***/
virtual void UpdateSourceMetadata(const source_metadata*) const {};
/***
* Return the current BluetoothStreamState
***/
@ -113,6 +109,8 @@ class BluetoothAudioPort {
virtual bool IsA2dp() const { return false; }
virtual bool IsLeAudio() const { return false; }
virtual bool GetPreferredDataIntervalUs(size_t* interval_us) const {
return false;
};
@ -146,14 +144,13 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort {
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* byte,
timespec* timestamp) const override;
void UpdateSourceMetadata(
const source_metadata* source_metadata) const override;
void UpdateSourceMetadata(const source_metadata_v7* source_metadata) const;
/***
* Called by the Audio framework / HAL when the metadata of the stream's
* sink has been changed.
***/
virtual void UpdateSinkMetadata(const sink_metadata* sink_metadata) const;
virtual void UpdateSinkMetadata(const sink_metadata_v7* sink_metadata) const;
BluetoothStreamState GetState() const override;
@ -165,6 +162,20 @@ class BluetoothAudioPortAidl : public BluetoothAudioPort {
SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}
bool IsLeAudio() const override {
return session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
session_type_ ==
SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
session_type_ ==
SessionType::
LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
protected:

View file

@ -486,7 +486,7 @@ bool BluetoothAudioPortHidl::GetPresentationPosition(
return retval;
}
void BluetoothAudioPortHidl::UpdateSourceMetadata(
void BluetoothAudioPortHidl::UpdateTracksMetadata(
const source_metadata* source_metadata) const {
if (!in_use()) {
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";

View file

@ -55,8 +55,7 @@ class BluetoothAudioPortHidl : public BluetoothAudioPort {
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* bytes,
timespec* timestamp) const override;
void UpdateSourceMetadata(
const source_metadata* source_metadata) const override;
void UpdateTracksMetadata(const source_metadata* source_metadata) const;
BluetoothStreamState GetState() const override;
@ -69,6 +68,17 @@ class BluetoothAudioPortHidl : public BluetoothAudioPort {
SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH;
}
bool IsLeAudio() const override {
return session_type_hidl_ ==
SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
session_type_hidl_ ==
SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH ||
session_type_hidl_ ==
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_hidl_ ==
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
}
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
protected:

View file

@ -20,7 +20,6 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <errno.h>
#include <inttypes.h>
#include <log/log.h>
#include <string.h>
@ -34,6 +33,7 @@
#include "utils.h"
using ::android::base::StringPrintf;
using ::android::bluetooth::audio::utils::FrameCount;
using ::android::bluetooth::audio::utils::GetAudioParamString;
using ::android::bluetooth::audio::utils::ParseAudioParams;
@ -335,7 +335,7 @@ static int out_set_parameters(struct audio_stream* stream,
}
}
if (params.find("routing") != params.end()) {
if (params.find(AUDIO_PARAMETER_STREAM_ROUTING) != params.end()) {
auto routing_param = params.find("routing");
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
<< ", stream param '" << routing_param->first.c_str() << "="
@ -365,8 +365,33 @@ static int out_set_parameters(struct audio_stream* stream,
}
}
if (params.find("closing") != params.end()) {
if (params["closing"] == "true") {
if (params.find("LeAudioSuspended") != params.end() &&
out->bluetooth_output_->IsLeAudio()) {
LOG(INFO) << __func__ << ": LeAudioSuspended found LEAudio="
<< out->bluetooth_output_->IsLeAudio();
if (params["LeAudioSuspended"] == "true") {
LOG(INFO) << __func__ << ": LeAudioSuspended true, state="
<< out->bluetooth_output_->GetState()
<< " stream param standby";
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
LOG(INFO) << __func__ << ": Stream is started, suspending LE Audio";
} else if (out->bluetooth_output_->GetState() !=
BluetoothStreamState::DISABLED) {
LOG(INFO) << __func__ << ": Stream is disabled, suspending LE Audio";
}
} else {
LOG(INFO) << __func__ << ": LeAudioSuspended false, state="
<< out->bluetooth_output_->GetState()
<< " stream param standby";
if (out->bluetooth_output_->GetState() ==
BluetoothStreamState::DISABLED) {
LOG(INFO) << __func__ << ": Stream is disabled, unsuspending LE Audio";
}
}
}
if (params.find(AUDIO_PARAMETER_KEY_CLOSING) != params.end()) {
if (params[AUDIO_PARAMETER_KEY_CLOSING] == "true") {
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
<< " stream param closing, disallow any writes?";
if (out->bluetooth_output_->GetState() !=
@ -378,8 +403,8 @@ static int out_set_parameters(struct audio_stream* stream,
}
}
if (params.find("exiting") != params.end()) {
if (params["exiting"] == "1") {
if (params.find(AUDIO_PARAMETER_KEY_EXITING) != params.end()) {
if (params[AUDIO_PARAMETER_KEY_EXITING] == "1") {
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
<< " stream param exiting";
if (out->bluetooth_output_->GetState() !=
@ -678,21 +703,32 @@ static int out_get_presentation_position(const struct audio_stream_out* stream,
return 0;
}
static void out_update_source_metadata(
static void out_update_source_metadata_v7(
struct audio_stream_out* stream,
const struct source_metadata* source_metadata) {
const struct source_metadata_v7* source_metadata_v7) {
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
std::unique_lock<std::mutex> lock(out->mutex_);
if (source_metadata == nullptr || source_metadata->track_count == 0) {
if (source_metadata_v7 == nullptr || source_metadata_v7->track_count == 0) {
return;
}
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
<< ", " << source_metadata->track_count << " track(s)";
out->bluetooth_output_->UpdateSourceMetadata(source_metadata);
}
<< ", " << source_metadata_v7->track_count << " track(s)";
if (!out->is_aidl) {
struct source_metadata source_metadata;
source_metadata.track_count = source_metadata_v7->track_count;
struct playback_track_metadata playback_track;
playback_track_metadata_from_v7(&playback_track,
source_metadata_v7->tracks);
source_metadata.tracks = &playback_track;
static size_t frame_count(size_t microseconds, uint32_t sample_rate) {
return (microseconds * sample_rate) / 1000000;
static_cast<::android::bluetooth::audio::hidl::BluetoothAudioPortHidl*>(
out->bluetooth_output_.get())
->UpdateTracksMetadata(&source_metadata);
} else {
static_cast<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl*>(
out->bluetooth_output_.get())
->UpdateSourceMetadata(source_metadata_v7);
}
}
int adev_open_output_stream(struct audio_hw_device* dev,
@ -740,7 +776,10 @@ int adev_open_output_stream(struct audio_hw_device* dev,
out->stream_out_.pause = out_pause;
out->stream_out_.resume = out_resume;
out->stream_out_.get_presentation_position = out_get_presentation_position;
out->stream_out_.update_source_metadata = out_update_source_metadata;
out->stream_out_.update_source_metadata_v7 = out_update_source_metadata_v7;
/** Fix Coverity Scan Issue @{ */
out->channel_mask_ = AUDIO_CHANNEL_NONE;
/** @} */
if (!out->bluetooth_output_->LoadAudioConfig(config)) {
LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
@ -782,7 +821,7 @@ int adev_open_output_stream(struct audio_hw_device* dev,
}
out->frames_count_ =
frame_count(out->preferred_data_interval_us, out->sample_rate_);
FrameCount(out->preferred_data_interval_us, out->sample_rate_);
out->frames_rendered_ = 0;
out->frames_presented_ = 0;
@ -1182,8 +1221,9 @@ static int in_set_microphone_field_dimension(
return -ENOSYS;
}
static void in_update_sink_metadata(struct audio_stream_in* stream,
const struct sink_metadata* sink_metadata) {
static void in_update_sink_metadata_v7(
struct audio_stream_in* stream,
const struct sink_metadata_v7* sink_metadata) {
LOG(INFO) << __func__;
if (sink_metadata == nullptr || sink_metadata->track_count == 0) {
return;
@ -1254,7 +1294,7 @@ int adev_open_input_stream(struct audio_hw_device* dev,
in->stream_in_.set_microphone_direction = in_set_microphone_direction;
in->stream_in_.set_microphone_field_dimension =
in_set_microphone_field_dimension;
in->stream_in_.update_sink_metadata = in_update_sink_metadata;
in->stream_in_.update_sink_metadata_v7 = in_update_sink_metadata_v7;
if (!in->bluetooth_input_->LoadAudioConfig(config)) {
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
@ -1277,7 +1317,7 @@ int adev_open_input_stream(struct audio_hw_device* dev,
}
in->frames_count_ =
frame_count(in->preferred_data_interval_us, in->sample_rate_);
FrameCount(in->preferred_data_interval_us, in->sample_rate_);
in->frames_presented_ = 0;
BluetoothStreamIn* in_ptr = in.release();

View file

@ -81,6 +81,7 @@ struct BluetoothAudioDevice {
std::mutex mutex_;
std::list<BluetoothStreamOut*> opened_stream_outs_ =
std::list<BluetoothStreamOut*>(0);
uint32_t next_unique_id = 1;
};
struct BluetoothStreamIn {

View file

@ -57,6 +57,10 @@ std::string GetAudioParamString(
return sout.str();
}
size_t FrameCount(uint64_t microseconds, uint32_t sample_rate) {
return (microseconds * sample_rate) / 1000000;
}
} // namespace utils
} // namespace audio
} // namespace bluetooth

View file

@ -42,6 +42,7 @@ std::unordered_map<std::string, std::string> ParseAudioParams(
std::string GetAudioParamString(
std::unordered_map<std::string, std::string>& params_map);
size_t FrameCount(uint64_t microseconds, uint32_t sample_rate);
} // namespace utils
} // namespace audio
} // namespace bluetooth

View file

@ -0,0 +1,142 @@
/*
* Copyright 2019 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 <gtest/gtest.h>
#include <unordered_map>
#include "utils.h"
namespace {
using ::android::bluetooth::audio::utils::FrameCount;
using ::android::bluetooth::audio::utils::ParseAudioParams;
class UtilsTest : public testing::Test {
protected:
void SetUp() override {}
void TearDown() override { map_.clear(); }
std::unordered_map<std::string, std::string> map_;
};
TEST_F(UtilsTest, HashMapEmptyParams) {
std::string params = "";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapDelimitOnly) {
std::string params = ";";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapNotKeyValuePair) {
std::string params = "key0";
map_ = ParseAudioParams(params);
// map = {[key0]=""}
EXPECT_EQ(map_.size(), 1);
EXPECT_NE(map_.find("key0"), map_.end());
EXPECT_EQ(map_["key0"].length(), 0);
}
TEST_F(UtilsTest, HashMapEmptyValue) {
std::string params = "key0=";
map_ = ParseAudioParams(params);
// map = {[key0]=""}
EXPECT_EQ(map_.size(), 1);
EXPECT_NE(map_.find("key0"), map_.end());
EXPECT_EQ(map_["key0"].length(), 0);
}
TEST_F(UtilsTest, HashMapEmptyValueWithDelimit) {
std::string params = "key0=;";
map_ = ParseAudioParams(params);
// map = {[key0]=""}
EXPECT_EQ(map_.size(), 1);
EXPECT_NE(map_.find("key0"), map_.end());
EXPECT_EQ(map_["key0"].length(), 0);
}
TEST_F(UtilsTest, HashMapOneKeyValuePair) {
std::string params = "key0=value0";
map_ = ParseAudioParams(params);
// map = {[key0]="value0"}
EXPECT_EQ(map_.size(), 1);
EXPECT_EQ(map_["key0"], "value0");
}
TEST_F(UtilsTest, HashMapOneKeyValuePairWithDelimit) {
std::string params = "key0=value0;";
map_ = ParseAudioParams(params);
// map = {[key0]="value0"}
EXPECT_EQ(map_.size(), 1);
EXPECT_EQ(map_["key0"], "value0");
}
TEST_F(UtilsTest, HashMapTwoKeyValuePairs) {
std::string params = "key0=value0;key1=value1";
map_ = ParseAudioParams(params);
// map = {[key0]="value0", [key1]="value1"}
EXPECT_EQ(map_.size(), 2);
EXPECT_EQ(map_["key0"], "value0");
EXPECT_EQ(map_["key1"], "value1");
}
TEST_F(UtilsTest, HashMapEmptyKey) {
std::string params = "=value";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapEmptyKeyWithDelimit) {
std::string params = "=value;";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapEquivalentOnly) {
std::string params = "=";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapNoKeyValuePair) {
std::string params = "=;";
map_ = ParseAudioParams(params);
// map = {}
EXPECT_TRUE(map_.empty());
}
TEST_F(UtilsTest, HashMapTwoPairsWithFirstKeyEmpty) {
std::string params = "=value0;key1=value1";
map_ = ParseAudioParams(params);
// map = {[key1]="value1"}
EXPECT_EQ(map_.size(), 1);
EXPECT_EQ(map_["key1"], "value1");
}
TEST_F(UtilsTest, FrameCountTest) {
EXPECT_EQ(FrameCount(120000, 44100), 5292);
EXPECT_EQ(FrameCount(7500, 32000), 240);
}
} // namespace

View file

@ -29,9 +29,22 @@ cc_library_shared {
"aidl_session/BluetoothAudioCodecs.cpp",
"aidl_session/BluetoothAudioSession.cpp",
"aidl_session/HidlToAidlMiddleware.cpp",
"aidl_session/BluetoothLeAudioCodecsProvider.cpp",
"aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp",
],
export_include_dirs: ["aidl_session/"],
header_libs: ["libhardware_headers"],
header_libs: [
"libhardware_headers",
"libxsdc-utils",
],
generated_sources: ["le_audio_codec_capabilities_system"],
generated_headers: [
"le_audio_codec_capabilities_system",
"AIDLLeAudioSetConfigSchemas_h_system",
],
defaults: [
"latest_android_hardware_bluetooth_audio_ndk_shared",
],
shared_libs: [
"android.hardware.bluetooth.audio@2.0",
"android.hardware.bluetooth.audio@2.1",
@ -40,7 +53,83 @@ cc_library_shared {
"libbinder_ndk",
"libfmq",
"liblog",
"android.hardware.bluetooth.audio-V3-ndk",
"libhidlbase",
"libxml2",
"libflatbuffers-cpp",
],
}
xsd_config {
name: "le_audio_codec_capabilities_system",
srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"],
package_name: "aidl.android.hardware.bluetooth.audio.setting",
api_dir: "le_audio_codec_capabilities/schema",
root_elements: ["leAudioOffloadSetting"],
}
genrule {
name: "AIDLLeAudioSetConfigSchemas_h_system",
tools: [
"flatc",
],
cmd: "$(location flatc) -I hardware/interfaces/bluetooth/audio/utils/ -o $(genDir) --cpp $(in) ",
srcs: [
"le_audio_configuration_set/audio_set_configurations.fbs",
"le_audio_configuration_set/audio_set_scenarios.fbs",
],
out: [
"audio_set_configurations_generated.h",
"audio_set_scenarios_generated.h",
],
}
// Binary generation
genrule {
name: "AIDLLeAudioSetScenariosSchema_bfbs_system",
tools: [
"flatc",
],
cmd: "$(location flatc) -I device/peter/gsi/bluetooth/audio/utils/ -b --schema -o $(genDir) $(in) ",
srcs: [
"le_audio_configuration_set/audio_set_scenarios.fbs",
],
out: [
"audio_set_scenarios.bfbs",
],
}
genrule {
name: "AIDLLeAudioSetConfigsSchema_bfbs_system",
tools: [
"flatc",
],
cmd: "$(location flatc) -I device/peter/gsi/bluetooth/audio/utils/ -b --schema -o $(genDir) $(in) ",
srcs: [
"le_audio_configuration_set/audio_set_configurations.fbs",
],
out: [
"audio_set_configurations.bfbs",
],
}
// Add to prebuilt etc
prebuilt_etc {
name: "aidl_audio_set_scenarios_bfbs_system",
src: ":AIDLLeAudioSetScenariosSchema_bfbs_system",
filename: "aidl_audio_set_scenarios.bfbs",
sub_dir: "aidl/le_audio",
}
prebuilt_etc {
name: "aidl_audio_set_scenarios_json_system",
src: "le_audio_configuration_set/audio_set_scenarios.json",
filename: "aidl_audio_set_scenarios.json",
sub_dir: "aidl/le_audio",
}
prebuilt_etc {
name: "aidl_audio_set_configurations_bfbs_system",
src: ":AIDLLeAudioSetConfigsSchema_bfbs",
filename: "aidl_audio_set_configurations.bfbs",
sub_dir: "aidl/le_audio",
}
prebuilt_etc {
name: "aidl_audio_set_configurations_json_system",
src: "le_audio_configuration_set/audio_set_configurations.json",
filename: "aidl_audio_set_configurations.json",
sub_dir: "aidl/le_audio",
}

View file

@ -32,6 +32,9 @@
#include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h>
#include <android-base/logging.h>
#include "BluetoothLeAudioAseConfigurationSettingProvider.h"
#include "BluetoothLeAudioCodecsProvider.h"
namespace aidl {
namespace android {
namespace hardware {
@ -39,7 +42,7 @@ namespace bluetooth {
namespace audio {
static const PcmCapabilities kDefaultSoftwarePcmCapabilities = {
.sampleRateHz = {16000, 24000, 32000, 44100, 48000, 88200, 96000},
.sampleRateHz = {8000, 16000, 24000, 32000, 44100, 48000, 88200, 96000},
.channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
.bitsPerSample = {16, 24, 32},
.dataIntervalUs = {},
@ -95,67 +98,8 @@ const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
{.codecType = CodecType::OPUS, .capabilities = {}}};
std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities;
static const UnicastCapability kInvalidUnicastCapability = {
.codecType = CodecType::UNKNOWN};
static const BroadcastCapability kInvalidBroadcastCapability = {
.codecType = CodecType::UNKNOWN};
// Default Supported Codecs
// LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30
static const Lc3Capabilities kLc3Capability_16_1 = {
.samplingFrequencyHz = {16000},
.frameDurationUs = {7500},
.octetsPerFrame = {30}};
// Default Supported Codecs
// LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40
static const Lc3Capabilities kLc3Capability_16_2 = {
.samplingFrequencyHz = {16000},
.frameDurationUs = {10000},
.octetsPerFrame = {40}};
// Default Supported Codecs
// LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60
static const Lc3Capabilities kLc3Capability_24_2 = {
.samplingFrequencyHz = {24000},
.frameDurationUs = {10000},
.octetsPerFrame = {60}};
// Default Supported Codecs
// LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80
static const Lc3Capabilities kLc3Capability_32_2 = {
.samplingFrequencyHz = {32000},
.frameDurationUs = {10000},
.octetsPerFrame = {80}};
// Default Supported Codecs
// LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120
static const Lc3Capabilities kLc3Capability_48_4 = {
.samplingFrequencyHz = {48000},
.frameDurationUs = {10000},
.octetsPerFrame = {120}};
static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = {
kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2,
kLc3Capability_16_2, kLc3Capability_16_1};
static AudioLocation stereoAudio = static_cast<AudioLocation>(
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
static AudioLocation monoAudio = AudioLocation::UNKNOWN;
// Stores the supported setting of audio location, connected device, and the
// channel count for each device
std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>>
supportedDeviceSetting = {
// Stereo, two connected device, one for L one for R
std::make_tuple(stereoAudio, 2, 1),
// Stereo, one connected device for both L and R
std::make_tuple(stereoAudio, 1, 2),
// Mono
std::make_tuple(monoAudio, 1, 1)};
std::unordered_map<SessionType, std::vector<CodecInfo>>
kDefaultOffloadLeAudioCodecInfoMap;
template <class T>
bool BluetoothAudioCodecs::ContainedInVector(
@ -321,19 +265,6 @@ bool BluetoothAudioCodecs::IsOffloadOpusConfigurationValid(
return false;
}
bool BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
const SessionType& session_type, const LeAudioConfiguration&) {
if (session_type !=
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
session_type !=
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
session_type !=
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
return false;
}
return true;
}
std::vector<PcmCapabilities>
BluetoothAudioCodecs::GetSoftwarePcmCapabilities() {
return {kDefaultSoftwarePcmCapabilities};
@ -384,6 +315,8 @@ BluetoothAudioCodecs::GetA2dpOffloadCodecCapabilities(
case CodecType::VENDOR:
case CodecType::LC3:
case CodecType::APTX_ADAPTIVE:
case CodecType::APTX_ADAPTIVE_LE:
case CodecType::APTX_ADAPTIVE_LEX:
break;
}
}
@ -449,6 +382,8 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
}
break;
case CodecType::APTX_ADAPTIVE:
case CodecType::APTX_ADAPTIVE_LE:
case CodecType::APTX_ADAPTIVE_LEX:
case CodecType::LC3:
case CodecType::UNKNOWN:
case CodecType::VENDOR:
@ -457,19 +392,6 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
return false;
}
UnicastCapability composeUnicastLc3Capability(
AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount,
const Lc3Capabilities& capability) {
return {
.codecType = CodecType::LC3,
.supportedChannel = audioLocation,
.deviceCount = deviceCnt,
.channelCountPerDevice = channelCount,
.leAudioCodecCapabilities =
UnicastCapability::LeAudioCodecCapabilities(capability),
};
}
std::vector<LeAudioCodecCapabilitiesSetting>
BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
const SessionType& session_type) {
@ -483,37 +405,44 @@ BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
}
if (kDefaultOffloadLeAudioCapabilities.empty()) {
for (auto [audioLocation, deviceCnt, channelCount] :
supportedDeviceSetting) {
for (auto capability : supportedLc3CapabilityList) {
UnicastCapability lc3Capability = composeUnicastLc3Capability(
audioLocation, deviceCnt, channelCount, capability);
UnicastCapability lc3MonoDecodeCapability =
composeUnicastLc3Capability(monoAudio, 1, 1, capability);
auto le_audio_offload_setting =
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
kDefaultOffloadLeAudioCapabilities =
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
le_audio_offload_setting);
}
return kDefaultOffloadLeAudioCapabilities;
}
// Adds the capability for encode only
kDefaultOffloadLeAudioCapabilities.push_back(
{.unicastEncodeCapability = lc3Capability,
.unicastDecodeCapability = kInvalidUnicastCapability,
.broadcastCapability = kInvalidBroadcastCapability});
// Adds the capability for decode only
kDefaultOffloadLeAudioCapabilities.push_back(
{.unicastEncodeCapability = kInvalidUnicastCapability,
.unicastDecodeCapability = lc3Capability,
.broadcastCapability = kInvalidBroadcastCapability});
// Adds the capability for the case that encode and decode exist at the
// same time
kDefaultOffloadLeAudioCapabilities.push_back(
{.unicastEncodeCapability = lc3Capability,
.unicastDecodeCapability = lc3MonoDecodeCapability,
.broadcastCapability = kInvalidBroadcastCapability});
}
}
std::vector<CodecInfo> BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
const SessionType& session_type) {
if (session_type !=
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
session_type !=
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
session_type !=
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
return std::vector<CodecInfo>();
}
return kDefaultOffloadLeAudioCapabilities;
if (kDefaultOffloadLeAudioCodecInfoMap.empty()) {
auto le_audio_offload_setting =
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
auto kDefaultOffloadLeAudioCodecInfoMap =
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
le_audio_offload_setting);
}
auto codec_info_map_iter =
kDefaultOffloadLeAudioCodecInfoMap.find(session_type);
if (codec_info_map_iter == kDefaultOffloadLeAudioCodecInfoMap.end())
return std::vector<CodecInfo>();
return codec_info_map_iter->second;
}
std::vector<LeAudioAseConfigurationSetting>
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings() {
return AudioSetConfigurationProviderJson::
GetLeAudioAseConfigurationSettings();
}
} // namespace audio

View file

@ -18,6 +18,8 @@
#include <aidl/android/hardware/bluetooth/audio/CodecCapabilities.h>
#include <aidl/android/hardware/bluetooth/audio/CodecConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/CodecInfo.h>
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.h>
#include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
#include <aidl/android/hardware/bluetooth/audio/LeAudioConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/OpusConfiguration.h>
@ -33,6 +35,9 @@ namespace hardware {
namespace bluetooth {
namespace audio {
using LeAudioAseConfigurationSetting =
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
class BluetoothAudioCodecs {
public:
static std::vector<PcmCapabilities> GetSoftwarePcmCapabilities();
@ -44,11 +49,13 @@ class BluetoothAudioCodecs {
static bool IsOffloadCodecConfigurationValid(
const SessionType& session_type, const CodecConfiguration& codec_config);
static bool IsOffloadLeAudioConfigurationValid(
const SessionType& session_type, const LeAudioConfiguration&);
static std::vector<LeAudioCodecCapabilitiesSetting>
GetLeAudioOffloadCodecCapabilities(const SessionType& session_type);
static std::vector<CodecInfo> GetLeAudioOffloadCodecInfo(
const SessionType& session_type);
static std::vector<LeAudioAseConfigurationSetting>
GetLeAudioAseConfigurationSettings();
private:
template <typename T>

View file

@ -20,6 +20,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android/binder_manager.h>
#include <hardware/audio.h>
#include "BluetoothAudioSession.h"
@ -94,6 +95,8 @@ const AudioConfiguration BluetoothAudioSession::GetAudioConfig() {
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
return AudioConfiguration(CodecConfiguration{});
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
return AudioConfiguration(HfpConfiguration{});
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
return AudioConfiguration(LeAudioConfiguration{});
@ -114,8 +117,16 @@ void BluetoothAudioSession::ReportAudioConfigChanged(
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
return;
}
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
LOG(ERROR) << __func__ << " invalid audio config type for SessionType ="
<< toString(session_type_);
return;
}
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
if (observers_.empty()) {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO port state observer";
@ -145,6 +156,7 @@ bool BluetoothAudioSession::IsSessionReady() {
session_type_ ==
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
session_type_ == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH ||
(data_mq_ != nullptr && data_mq_->isValid()));
return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr;
}
@ -266,6 +278,8 @@ bool BluetoothAudioSession::UpdateAudioConfig(
bool is_software_session =
(session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::HFP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::HFP_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
session_type_ ==
@ -274,23 +288,37 @@ bool BluetoothAudioSession::UpdateAudioConfig(
bool is_offload_a2dp_session =
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH);
bool is_offload_le_audio_session =
bool is_offload_hfp_session =
session_type_ == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH;
bool is_offload_le_audio_unicast_session =
(session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
bool is_offload_le_audio_broadcast_session =
(session_type_ ==
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
auto audio_config_tag = audio_config.getTag();
bool is_software_audio_config =
(is_software_session &&
audio_config_tag == AudioConfiguration::pcmConfig);
bool is_a2dp_offload_audio_config =
(is_offload_a2dp_session &&
audio_config_tag == AudioConfiguration::a2dpConfig);
bool is_le_audio_offload_audio_config =
(is_offload_le_audio_session &&
(audio_config_tag == AudioConfiguration::a2dp ||
audio_config_tag == AudioConfiguration::a2dpConfig));
bool is_hfp_offload_audio_config =
(is_offload_hfp_session &&
audio_config_tag == AudioConfiguration::hfpConfig);
bool is_le_audio_offload_unicast_audio_config =
(is_offload_le_audio_unicast_session &&
audio_config_tag == AudioConfiguration::leAudioConfig);
bool is_le_audio_offload_broadcast_audio_config =
(is_offload_le_audio_broadcast_session &&
audio_config_tag == AudioConfiguration::leAudioBroadcastConfig);
if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
!is_le_audio_offload_audio_config) {
!is_hfp_offload_audio_config &&
!is_le_audio_offload_unicast_audio_config &&
!is_le_audio_offload_broadcast_audio_config) {
return false;
}
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
@ -423,8 +451,21 @@ void BluetoothAudioSession::ReportControlStatus(bool start_resp,
}
void BluetoothAudioSession::ReportLowLatencyModeAllowedChanged(bool allowed) {
if (session_type_ != SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
return;
}
std::lock_guard<std::recursive_mutex> guard(mutex_);
low_latency_allowed_ = allowed;
// TODO(b/294498919): Remove this after there is API to update latency mode
// after audio session started. If low_latency_allowed_ is true, the session
// can support LOW_LATENCY and FREE LatencyMode.
if (low_latency_allowed_) {
if (std::find(latency_modes_.begin(), latency_modes_.end(),
LatencyMode::LOW_LATENCY) == latency_modes_.end()) {
LOG(INFO) << __func__ << " - insert LOW_LATENCY LatencyMode";
latency_modes_.push_back(LatencyMode::LOW_LATENCY);
}
}
if (observers_.empty()) {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO port state observer";
@ -461,23 +502,9 @@ bool BluetoothAudioSession::GetPresentationPosition(
void BluetoothAudioSession::UpdateSourceMetadata(
const struct source_metadata& source_metadata) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return;
}
ssize_t track_count = source_metadata.track_count;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
<< track_count << " track(s)";
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
return;
}
SourceMetadata hal_source_metadata;
hal_source_metadata.tracks.resize(track_count);
for (int i = 0; i < track_count; i++) {
@ -494,33 +521,14 @@ void BluetoothAudioSession::UpdateSourceMetadata(
<< toString(hal_source_metadata.tracks[i].contentType)
<< ", gain=" << hal_source_metadata.tracks[i].gain;
}
auto hal_retval = stack_iface_->updateSourceMetadata(hal_source_metadata);
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
}
UpdateSourceMetadata(hal_source_metadata);
}
void BluetoothAudioSession::UpdateSinkMetadata(
const struct sink_metadata& sink_metadata) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return;
}
ssize_t track_count = sink_metadata.track_count;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
<< track_count << " track(s)";
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
return;
}
SinkMetadata hal_sink_metadata;
hal_sink_metadata.tracks.resize(track_count);
for (int i = 0; i < track_count; i++) {
@ -535,12 +543,57 @@ void BluetoothAudioSession::UpdateSinkMetadata(
<< ", dest_device_address="
<< sink_metadata.tracks[i].dest_device_address;
}
UpdateSinkMetadata(hal_sink_metadata);
}
bool BluetoothAudioSession::UpdateSourceMetadata(
const SourceMetadata& hal_source_metadata) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return false;
}
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
return false;
}
auto hal_retval = stack_iface_->updateSourceMetadata(hal_source_metadata);
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
return false;
}
return true;
}
bool BluetoothAudioSession::UpdateSinkMetadata(
const SinkMetadata& hal_sink_metadata) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return false;
}
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
return false;
}
auto hal_retval = stack_iface_->updateSinkMetadata(hal_sink_metadata);
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
return false;
}
return true;
}
std::vector<LatencyMode> BluetoothAudioSession::GetSupportedLatencyModes() {
@ -550,15 +603,32 @@ std::vector<LatencyMode> BluetoothAudioSession::GetSupportedLatencyModes() {
<< " has NO session";
return std::vector<LatencyMode>();
}
if (low_latency_allowed_) return latency_modes_;
std::vector<LatencyMode> modes;
for (LatencyMode mode : latency_modes_) {
if (mode == LatencyMode::LOW_LATENCY)
// ignore those low latency mode if Bluetooth stack doesn't allow
continue;
modes.push_back(mode);
std::vector<LatencyMode> supported_latency_modes;
if (session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
for (LatencyMode mode : latency_modes_) {
if (mode == LatencyMode::LOW_LATENCY) {
// LOW_LATENCY is not supported for LE_HARDWARE_OFFLOAD_ENC sessions
continue;
}
supported_latency_modes.push_back(mode);
}
} else {
for (LatencyMode mode : latency_modes_) {
if (!low_latency_allowed_ && mode == LatencyMode::LOW_LATENCY) {
// ignore LOW_LATENCY mode if Bluetooth stack doesn't allow
continue;
}
if (mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_SOFTWARE ||
mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_HARDWARE) {
// DSA_SW and DSA_HW only supported for LE_HARDWARE_OFFLOAD_ENC sessions
continue;
}
supported_latency_modes.push_back(mode);
}
}
return modes;
return supported_latency_modes;
}
void BluetoothAudioSession::SetLatencyMode(const LatencyMode& latency_mode) {

View file

@ -23,12 +23,15 @@
#include <aidl/android/hardware/bluetooth/audio/LatencyMode.h>
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
#include <fmq/AidlMessageQueue.h>
#include <hardware/audio.h>
#include <mutex>
#include <unordered_map>
#include <vector>
// To avoid inclusion of hardware/audio.h
struct sink_metadata;
struct source_metadata;
namespace aidl {
namespace android {
namespace hardware {
@ -183,6 +186,9 @@ class BluetoothAudioSession {
bool GetPresentationPosition(PresentationPosition& presentation_position);
void UpdateSourceMetadata(const struct source_metadata& source_metadata);
void UpdateSinkMetadata(const struct sink_metadata& sink_metadata);
// New versions for AIDL-only clients.
bool UpdateSourceMetadata(const SourceMetadata& hal_source_metadata);
bool UpdateSinkMetadata(const SinkMetadata& hal_sink_metadata);
std::vector<LatencyMode> GetSupportedLatencyModes();
void SetLatencyMode(const LatencyMode& latency_mode);
@ -220,8 +226,10 @@ class BluetoothAudioSession {
static inline std::atomic<bool> is_aidl_checked = false;
static inline std::atomic<bool> is_aidl_available = false;
// BEGIN sysbta
static inline const std::string kDefaultAudioProviderFactoryInterface =
std::string() + IBluetoothAudioProviderFactory::descriptor + "/sysbta";
// END sysbta
};
class BluetoothAudioSessionInstance {

View file

@ -84,6 +84,8 @@ class BluetoothAudioSessionControl {
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
return AudioConfiguration(CodecConfiguration{});
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
return AudioConfiguration(HfpConfiguration{});
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
return AudioConfiguration(LeAudioConfiguration{});
@ -156,6 +158,26 @@ class BluetoothAudioSessionControl {
}
}
static bool UpdateSourceMetadata(const SessionType& session_type,
const SourceMetadata& source_metadata) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->UpdateSourceMetadata(source_metadata);
}
return false;
}
static bool UpdateSinkMetadata(const SessionType& session_type,
const SinkMetadata& sink_metadata) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->UpdateSinkMetadata(sink_metadata);
}
return false;
}
static std::vector<LatencyMode> GetSupportedLatencyModes(
const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =

View file

@ -0,0 +1,760 @@
/*
* 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 STREAM_TO_UINT8(u8, p) \
{ \
(u8) = (uint8_t)(*(p)); \
(p) += 1; \
}
#define STREAM_TO_UINT16(u16, p) \
{ \
(u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \
(p) += 2; \
}
#define STREAM_TO_UINT32(u32, p) \
{ \
(u32) = (((uint32_t)(*(p))) + ((((uint32_t)(*((p) + 1)))) << 8) + \
((((uint32_t)(*((p) + 2)))) << 16) + \
((((uint32_t)(*((p) + 3)))) << 24)); \
(p) += 4; \
}
#define LOG_TAG "BTAudioAseConfigAidl"
#include "BluetoothLeAudioAseConfigurationSettingProvider.h"
#include <aidl/android/hardware/bluetooth/audio/AudioConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/AudioContext.h>
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
#include <aidl/android/hardware/bluetooth/audio/CodecId.h>
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificCapabilitiesLtv.h>
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificConfigurationLtv.h>
#include <aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h>
#include <aidl/android/hardware/bluetooth/audio/LeAudioAseConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/Phy.h>
#include <android-base/logging.h>
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
/* Internal structure definition */
std::map<std::string,
std::tuple<std::vector<std::optional<AseDirectionConfiguration>>,
std::vector<std::optional<AseDirectionConfiguration>>,
ConfigurationFlags>>
configurations_;
std::vector<LeAudioAseConfigurationSetting> ase_configuration_settings_;
constexpr uint8_t kIsoDataPathHci = 0x00;
constexpr uint8_t kIsoDataPathPlatformDefault = 0x01;
constexpr uint8_t kIsoDataPathDisabled = 0xFF;
constexpr uint8_t kLeAudioDirectionSink = 0x01;
constexpr uint8_t kLeAudioDirectionSource = 0x02;
constexpr uint8_t kLeAudioDirectionBoth =
kLeAudioDirectionSink | kLeAudioDirectionSource;
/* Sampling Frequencies */
constexpr uint8_t kLeAudioSamplingFreq8000Hz = 0x01;
constexpr uint8_t kLeAudioSamplingFreq11025Hz = 0x02;
constexpr uint8_t kLeAudioSamplingFreq16000Hz = 0x03;
constexpr uint8_t kLeAudioSamplingFreq22050Hz = 0x04;
constexpr uint8_t kLeAudioSamplingFreq24000Hz = 0x05;
constexpr uint8_t kLeAudioSamplingFreq32000Hz = 0x06;
constexpr uint8_t kLeAudioSamplingFreq44100Hz = 0x07;
constexpr uint8_t kLeAudioSamplingFreq48000Hz = 0x08;
constexpr uint8_t kLeAudioSamplingFreq88200Hz = 0x09;
constexpr uint8_t kLeAudioSamplingFreq96000Hz = 0x0A;
constexpr uint8_t kLeAudioSamplingFreq176400Hz = 0x0B;
constexpr uint8_t kLeAudioSamplingFreq192000Hz = 0x0C;
constexpr uint8_t kLeAudioSamplingFreq384000Hz = 0x0D;
/* Frame Durations */
constexpr uint8_t kLeAudioCodecFrameDur7500us = 0x00;
constexpr uint8_t kLeAudioCodecFrameDur10000us = 0x01;
/* Audio Allocations */
constexpr uint32_t kLeAudioLocationNotAllowed = 0x00000000;
constexpr uint32_t kLeAudioLocationFrontLeft = 0x00000001;
constexpr uint32_t kLeAudioLocationFrontRight = 0x00000002;
constexpr uint32_t kLeAudioLocationFrontCenter = 0x00000004;
constexpr uint32_t kLeAudioLocationLowFreqEffects1 = 0x00000008;
constexpr uint32_t kLeAudioLocationBackLeft = 0x00000010;
constexpr uint32_t kLeAudioLocationBackRight = 0x00000020;
constexpr uint32_t kLeAudioLocationFrontLeftOfCenter = 0x00000040;
constexpr uint32_t kLeAudioLocationFrontRightOfCenter = 0x00000080;
constexpr uint32_t kLeAudioLocationBackCenter = 0x00000100;
constexpr uint32_t kLeAudioLocationLowFreqEffects2 = 0x00000200;
constexpr uint32_t kLeAudioLocationSideLeft = 0x00000400;
constexpr uint32_t kLeAudioLocationSideRight = 0x00000800;
constexpr uint32_t kLeAudioLocationTopFrontLeft = 0x00001000;
constexpr uint32_t kLeAudioLocationTopFrontRight = 0x00002000;
constexpr uint32_t kLeAudioLocationTopFrontCenter = 0x00004000;
constexpr uint32_t kLeAudioLocationTopCenter = 0x00008000;
constexpr uint32_t kLeAudioLocationTopBackLeft = 0x00010000;
constexpr uint32_t kLeAudioLocationTopBackRight = 0x00020000;
constexpr uint32_t kLeAudioLocationTopSideLeft = 0x00040000;
constexpr uint32_t kLeAudioLocationTopSideRight = 0x00080000;
constexpr uint32_t kLeAudioLocationTopBackCenter = 0x00100000;
constexpr uint32_t kLeAudioLocationBottomFrontCenter = 0x00200000;
constexpr uint32_t kLeAudioLocationBottomFrontLeft = 0x00400000;
constexpr uint32_t kLeAudioLocationBottomFrontRight = 0x00800000;
constexpr uint32_t kLeAudioLocationFrontLeftWide = 0x01000000;
constexpr uint32_t kLeAudioLocationFrontRightWide = 0x02000000;
constexpr uint32_t kLeAudioLocationLeftSurround = 0x04000000;
constexpr uint32_t kLeAudioLocationRightSurround = 0x08000000;
constexpr uint32_t kLeAudioLocationAnyLeft =
kLeAudioLocationFrontLeft | kLeAudioLocationBackLeft |
kLeAudioLocationFrontLeftOfCenter | kLeAudioLocationSideLeft |
kLeAudioLocationTopFrontLeft | kLeAudioLocationTopBackLeft |
kLeAudioLocationTopSideLeft | kLeAudioLocationBottomFrontLeft |
kLeAudioLocationFrontLeftWide | kLeAudioLocationLeftSurround;
constexpr uint32_t kLeAudioLocationAnyRight =
kLeAudioLocationFrontRight | kLeAudioLocationBackRight |
kLeAudioLocationFrontRightOfCenter | kLeAudioLocationSideRight |
kLeAudioLocationTopFrontRight | kLeAudioLocationTopBackRight |
kLeAudioLocationTopSideRight | kLeAudioLocationBottomFrontRight |
kLeAudioLocationFrontRightWide | kLeAudioLocationRightSurround;
constexpr uint32_t kLeAudioLocationStereo =
kLeAudioLocationFrontLeft | kLeAudioLocationFrontRight;
/* Octets Per Frame */
constexpr uint16_t kLeAudioCodecFrameLen30 = 30;
constexpr uint16_t kLeAudioCodecFrameLen40 = 40;
constexpr uint16_t kLeAudioCodecFrameLen60 = 60;
constexpr uint16_t kLeAudioCodecFrameLen80 = 80;
constexpr uint16_t kLeAudioCodecFrameLen100 = 100;
constexpr uint16_t kLeAudioCodecFrameLen120 = 120;
/* Helper map for matching various sampling frequency notations */
const std::map<uint8_t, CodecSpecificConfigurationLtv::SamplingFrequency>
sampling_freq_map = {
{kLeAudioSamplingFreq8000Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000},
{kLeAudioSamplingFreq16000Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
{kLeAudioSamplingFreq24000Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000},
{kLeAudioSamplingFreq32000Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000},
{kLeAudioSamplingFreq44100Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ44100},
{kLeAudioSamplingFreq48000Hz,
CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000}};
/* Helper map for matching various frame durations notations */
const std::map<uint8_t, CodecSpecificConfigurationLtv::FrameDuration>
frame_duration_map = {
{kLeAudioCodecFrameDur7500us,
CodecSpecificConfigurationLtv::FrameDuration::US7500},
{kLeAudioCodecFrameDur10000us,
CodecSpecificConfigurationLtv::FrameDuration::US10000}};
/* Helper map for matching various audio channel allocation notations */
std::map<uint32_t, uint32_t> audio_channel_allocation_map = {
{kLeAudioLocationNotAllowed,
CodecSpecificConfigurationLtv::AudioChannelAllocation::NOT_ALLOWED},
{kLeAudioLocationFrontLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT},
{kLeAudioLocationFrontRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT},
{kLeAudioLocationFrontCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER},
{kLeAudioLocationLowFreqEffects1,
CodecSpecificConfigurationLtv::AudioChannelAllocation::
LOW_FREQUENCY_EFFECTS_1},
{kLeAudioLocationBackLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_LEFT},
{kLeAudioLocationBackRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_RIGHT},
{kLeAudioLocationFrontLeftOfCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::
FRONT_LEFT_OF_CENTER},
{kLeAudioLocationFrontRightOfCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::
FRONT_RIGHT_OF_CENTER},
{kLeAudioLocationBackCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_CENTER},
{kLeAudioLocationLowFreqEffects2,
CodecSpecificConfigurationLtv::AudioChannelAllocation::
LOW_FREQUENCY_EFFECTS_2},
{kLeAudioLocationSideLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_LEFT},
{kLeAudioLocationSideRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_RIGHT},
{kLeAudioLocationTopFrontLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_LEFT},
{kLeAudioLocationTopFrontRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_RIGHT},
{kLeAudioLocationTopFrontCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_CENTER},
{kLeAudioLocationTopCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_CENTER},
{kLeAudioLocationTopBackLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_LEFT},
{kLeAudioLocationTopBackRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_RIGHT},
{kLeAudioLocationTopSideLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_LEFT},
{kLeAudioLocationTopSideRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_RIGHT},
{kLeAudioLocationTopBackCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_CENTER},
{kLeAudioLocationBottomFrontCenter,
CodecSpecificConfigurationLtv::AudioChannelAllocation::
BOTTOM_FRONT_CENTER},
{kLeAudioLocationBottomFrontLeft,
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_LEFT},
{kLeAudioLocationBottomFrontRight,
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_RIGHT},
{kLeAudioLocationFrontLeftWide,
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT_WIDE},
{kLeAudioLocationFrontRightWide,
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT_WIDE},
{kLeAudioLocationLeftSurround,
CodecSpecificConfigurationLtv::AudioChannelAllocation::LEFT_SURROUND},
{kLeAudioLocationRightSurround,
CodecSpecificConfigurationLtv::AudioChannelAllocation::RIGHT_SURROUND},
};
static const std::vector<
std::pair<const char* /*schema*/, const char* /*content*/>>
kLeAudioSetConfigs = {{"/vendor/etc/aidl/le_audio/"
"aidl_audio_set_configurations.bfbs",
"/vendor/etc/aidl/le_audio/"
"aidl_audio_set_configurations.json"}};
static const std::vector<
std::pair<const char* /*schema*/, const char* /*content*/>>
kLeAudioSetScenarios = {{"/vendor/etc/aidl/le_audio/"
"aidl_audio_set_scenarios.bfbs",
"/vendor/etc/aidl/le_audio/"
"aidl_audio_set_scenarios.json"}};
/* Implementation */
std::vector<LeAudioAseConfigurationSetting>
AudioSetConfigurationProviderJson::GetLeAudioAseConfigurationSettings() {
AudioSetConfigurationProviderJson::LoadAudioSetConfigurationProviderJson();
return ase_configuration_settings_;
}
void AudioSetConfigurationProviderJson::
LoadAudioSetConfigurationProviderJson() {
if (configurations_.empty() || ase_configuration_settings_.empty()) {
ase_configuration_settings_.clear();
configurations_.clear();
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
CodecLocation::HOST);
if (!loaded)
LOG(ERROR) << ": Unable to load le audio set configuration files.";
} else
LOG(INFO) << ": Reusing loaded le audio set configuration";
}
const le_audio::CodecSpecificConfiguration*
AudioSetConfigurationProviderJson::LookupCodecSpecificParam(
const flatbuffers::Vector<flatbuffers::Offset<
le_audio::CodecSpecificConfiguration>>* flat_codec_specific_params,
le_audio::CodecSpecificLtvGenericTypes type) {
auto it = std::find_if(
flat_codec_specific_params->cbegin(), flat_codec_specific_params->cend(),
[&type](const auto& csc) { return (csc->type() == type); });
return (it != flat_codec_specific_params->cend()) ? *it : nullptr;
}
void AudioSetConfigurationProviderJson::populateAudioChannelAllocation(
CodecSpecificConfigurationLtv::AudioChannelAllocation&
audio_channel_allocation,
uint32_t audio_location) {
audio_channel_allocation.bitmask = 0;
for (auto [allocation, bitmask] : audio_channel_allocation_map) {
if (audio_location & allocation)
audio_channel_allocation.bitmask |= bitmask;
}
}
void AudioSetConfigurationProviderJson::populateConfigurationData(
LeAudioAseConfiguration& ase,
const flatbuffers::Vector<
flatbuffers::Offset<le_audio::CodecSpecificConfiguration>>*
flat_codec_specific_params) {
uint8_t sampling_frequency = 0;
uint8_t frame_duration = 0;
uint32_t audio_channel_allocation = 0;
uint16_t octets_per_codec_frame = 0;
uint8_t codec_frames_blocks_per_sdu = 0;
auto param = LookupCodecSpecificParam(
flat_codec_specific_params,
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_SAMPLING_FREQUENCY);
if (param) {
auto ptr = param->compound_value()->value()->data();
STREAM_TO_UINT8(sampling_frequency, ptr);
}
param = LookupCodecSpecificParam(
flat_codec_specific_params,
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_FRAME_DURATION);
if (param) {
auto ptr = param->compound_value()->value()->data();
STREAM_TO_UINT8(frame_duration, ptr);
}
param = LookupCodecSpecificParam(
flat_codec_specific_params,
le_audio::
CodecSpecificLtvGenericTypes_SUPPORTED_AUDIO_CHANNEL_ALLOCATION);
if (param) {
auto ptr = param->compound_value()->value()->data();
STREAM_TO_UINT32(audio_channel_allocation, ptr);
}
param = LookupCodecSpecificParam(
flat_codec_specific_params,
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_OCTETS_PER_CODEC_FRAME);
if (param) {
auto ptr = param->compound_value()->value()->data();
STREAM_TO_UINT16(octets_per_codec_frame, ptr);
}
param = LookupCodecSpecificParam(
flat_codec_specific_params,
le_audio::
CodecSpecificLtvGenericTypes_SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU);
if (param) {
auto ptr = param->compound_value()->value()->data();
STREAM_TO_UINT8(codec_frames_blocks_per_sdu, ptr);
}
// Make the correct value
ase.codecConfiguration = std::vector<CodecSpecificConfigurationLtv>();
auto sampling_freq_it = sampling_freq_map.find(sampling_frequency);
if (sampling_freq_it != sampling_freq_map.end())
ase.codecConfiguration.push_back(sampling_freq_it->second);
auto frame_duration_it = frame_duration_map.find(frame_duration);
if (frame_duration_it != frame_duration_map.end())
ase.codecConfiguration.push_back(frame_duration_it->second);
CodecSpecificConfigurationLtv::AudioChannelAllocation channel_allocation;
populateAudioChannelAllocation(channel_allocation, audio_channel_allocation);
ase.codecConfiguration.push_back(channel_allocation);
auto octet_structure = CodecSpecificConfigurationLtv::OctetsPerCodecFrame();
octet_structure.value = octets_per_codec_frame;
ase.codecConfiguration.push_back(octet_structure);
auto frame_sdu_structure =
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
ase.codecConfiguration.push_back(frame_sdu_structure);
// TODO: Channel count
}
void AudioSetConfigurationProviderJson::populateAseConfiguration(
LeAudioAseConfiguration& ase,
const le_audio::AudioSetSubConfiguration* flat_subconfig,
const le_audio::QosConfiguration* qos_cfg) {
// Target latency
switch (qos_cfg->target_latency()) {
case le_audio::AudioSetConfigurationTargetLatency::
AudioSetConfigurationTargetLatency_BALANCED_RELIABILITY:
ase.targetLatency =
LeAudioAseConfiguration::TargetLatency::BALANCED_LATENCY_RELIABILITY;
break;
case le_audio::AudioSetConfigurationTargetLatency::
AudioSetConfigurationTargetLatency_HIGH_RELIABILITY:
ase.targetLatency =
LeAudioAseConfiguration::TargetLatency::HIGHER_RELIABILITY;
break;
case le_audio::AudioSetConfigurationTargetLatency::
AudioSetConfigurationTargetLatency_LOW:
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::LOWER;
break;
default:
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::UNDEFINED;
break;
};
ase.targetPhy = Phy::TWO_M;
// Making CodecId
if (flat_subconfig->codec_id()->coding_format() ==
(uint8_t)CodecId::Core::LC3) {
ase.codecId = CodecId::Core::LC3;
} else {
auto vendorC = CodecId::Vendor();
vendorC.codecId = flat_subconfig->codec_id()->vendor_codec_id();
vendorC.id = flat_subconfig->codec_id()->vendor_company_id();
ase.codecId = vendorC;
}
// Codec configuration data
populateConfigurationData(ase, flat_subconfig->codec_configuration());
}
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
LeAudioAseQosConfiguration& qos,
const le_audio::QosConfiguration* qos_cfg) {
qos.maxTransportLatencyMs = qos_cfg->max_transport_latency();
qos.retransmissionNum = qos_cfg->retransmission_number();
}
// Parse into AseDirectionConfiguration
AseDirectionConfiguration
AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig(
const le_audio::AudioSetSubConfiguration* flat_subconfig,
const le_audio::QosConfiguration* qos_cfg, CodecLocation location) {
AseDirectionConfiguration direction_conf;
LeAudioAseConfiguration ase;
LeAudioAseQosConfiguration qos;
LeAudioDataPathConfiguration path;
// Translate into LeAudioAseConfiguration
populateAseConfiguration(ase, flat_subconfig, qos_cfg);
// Translate into LeAudioAseQosConfiguration
populateAseQosConfiguration(qos, qos_cfg);
// Translate location to data path id
switch (location) {
case CodecLocation::ADSP:
path.isoDataPathConfiguration.isTransparent = true;
path.dataPathId = kIsoDataPathPlatformDefault;
break;
case CodecLocation::HOST:
path.isoDataPathConfiguration.isTransparent = true;
path.dataPathId = kIsoDataPathHci;
break;
case CodecLocation::CONTROLLER:
path.isoDataPathConfiguration.isTransparent = false;
path.dataPathId = kIsoDataPathPlatformDefault;
break;
}
direction_conf.aseConfiguration = ase;
direction_conf.qosConfiguration = qos;
direction_conf.dataPathConfiguration = path;
return direction_conf;
}
// Parse into AseDirectionConfiguration and the ConfigurationFlags
// and put them in the given list.
void AudioSetConfigurationProviderJson::processSubconfig(
const le_audio::AudioSetSubConfiguration* subconfig,
const le_audio::QosConfiguration* qos_cfg,
std::vector<std::optional<AseDirectionConfiguration>>&
directionAseConfiguration,
CodecLocation location) {
directionAseConfiguration.push_back(
SetConfigurationFromFlatSubconfig(subconfig, qos_cfg, location));
}
void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
const le_audio::AudioSetConfiguration* flat_cfg,
std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
std::vector<const le_audio::QosConfiguration*>* qos_cfgs,
CodecLocation location,
std::vector<std::optional<AseDirectionConfiguration>>&
sourceAseConfiguration,
std::vector<std::optional<AseDirectionConfiguration>>& sinkAseConfiguration,
ConfigurationFlags& /*configurationFlags*/) {
if (flat_cfg == nullptr) {
LOG(ERROR) << "flat_cfg cannot be null";
return;
}
std::string codec_config_key = flat_cfg->codec_config_name()->str();
auto* qos_config_key_array = flat_cfg->qos_config_name();
constexpr std::string_view default_qos = "QoS_Config_Balanced_Reliability";
std::string qos_sink_key(default_qos);
std::string qos_source_key(default_qos);
/* We expect maximum two QoS settings. First for Sink and second for Source
*/
if (qos_config_key_array->size() > 0) {
qos_sink_key = qos_config_key_array->Get(0)->str();
if (qos_config_key_array->size() > 1) {
qos_source_key = qos_config_key_array->Get(1)->str();
} else {
qos_source_key = qos_sink_key;
}
}
LOG(INFO) << "Audio set config " << flat_cfg->name()->c_str()
<< ": codec config " << codec_config_key.c_str() << ", qos_sink "
<< qos_sink_key.c_str() << ", qos_source "
<< qos_source_key.c_str();
// Find the first qos config that match the name
const le_audio::QosConfiguration* qos_sink_cfg = nullptr;
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
if ((*i)->name()->str() == qos_sink_key) {
qos_sink_cfg = *i;
break;
}
}
const le_audio::QosConfiguration* qos_source_cfg = nullptr;
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
if ((*i)->name()->str() == qos_source_key) {
qos_source_cfg = *i;
break;
}
}
// First codec_cfg with the same name
const le_audio::CodecConfiguration* codec_cfg = nullptr;
for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) {
if ((*i)->name()->str() == codec_config_key) {
codec_cfg = *i;
break;
}
}
// Process each subconfig and put it into the correct list
if (codec_cfg != nullptr && codec_cfg->subconfigurations()) {
/* Load subconfigurations */
for (auto subconfig : *codec_cfg->subconfigurations()) {
if (subconfig->direction() == kLeAudioDirectionSink) {
processSubconfig(subconfig, qos_sink_cfg, sinkAseConfiguration,
location);
} else {
processSubconfig(subconfig, qos_source_cfg, sourceAseConfiguration,
location);
}
}
} else {
if (codec_cfg == nullptr) {
LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str()
<< " found";
} else {
LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
<< "' has no valid subconfigurations.";
}
}
// TODO: Populate information for ConfigurationFlags
}
bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles(
const char* schema_file, const char* content_file, CodecLocation location) {
flatbuffers::Parser configurations_parser_;
std::string configurations_schema_binary_content;
bool ok = flatbuffers::LoadFile(schema_file, true,
&configurations_schema_binary_content);
LOG(INFO) << __func__ << ": Loading file " << schema_file;
if (!ok) return ok;
/* Load the binary schema */
ok = configurations_parser_.Deserialize(
(uint8_t*)configurations_schema_binary_content.c_str(),
configurations_schema_binary_content.length());
if (!ok) return ok;
/* Load the content from JSON */
std::string configurations_json_content;
LOG(INFO) << __func__ << ": Loading file " << content_file;
ok = flatbuffers::LoadFile(content_file, false, &configurations_json_content);
if (!ok) return ok;
/* Parse */
LOG(INFO) << __func__ << ": Parse JSON content";
ok = configurations_parser_.Parse(configurations_json_content.c_str());
if (!ok) return ok;
/* Import from flatbuffers */
LOG(INFO) << __func__ << ": Build flat buffer structure";
auto configurations_root = le_audio::GetAudioSetConfigurations(
configurations_parser_.builder_.GetBufferPointer());
if (!configurations_root) return false;
auto flat_qos_configs = configurations_root->qos_configurations();
if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0))
return false;
LOG(DEBUG) << ": Updating " << flat_qos_configs->size()
<< " qos config entries.";
std::vector<const le_audio::QosConfiguration*> qos_cfgs;
for (auto const& flat_qos_cfg : *flat_qos_configs) {
qos_cfgs.push_back(flat_qos_cfg);
}
auto flat_codec_configs = configurations_root->codec_configurations();
if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0))
return false;
LOG(DEBUG) << ": Updating " << flat_codec_configs->size()
<< " codec config entries.";
std::vector<const le_audio::CodecConfiguration*> codec_cfgs;
for (auto const& flat_codec_cfg : *flat_codec_configs) {
codec_cfgs.push_back(flat_codec_cfg);
}
auto flat_configs = configurations_root->configurations();
if ((flat_configs == nullptr) || (flat_configs->size() == 0)) return false;
LOG(DEBUG) << ": Updating " << flat_configs->size() << " config entries.";
for (auto const& flat_cfg : *flat_configs) {
// Create 3 vector to use
std::vector<std::optional<AseDirectionConfiguration>>
sourceAseConfiguration;
std::vector<std::optional<AseDirectionConfiguration>> sinkAseConfiguration;
ConfigurationFlags configurationFlags;
PopulateAseConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs, location,
sourceAseConfiguration,
sinkAseConfiguration, configurationFlags);
if (sourceAseConfiguration.empty() && sinkAseConfiguration.empty())
continue;
configurations_[flat_cfg->name()->str()] = std::make_tuple(
sourceAseConfiguration, sinkAseConfiguration, configurationFlags);
}
return true;
}
bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles(
const char* schema_file, const char* content_file) {
flatbuffers::Parser scenarios_parser_;
std::string scenarios_schema_binary_content;
bool ok = flatbuffers::LoadFile(schema_file, true,
&scenarios_schema_binary_content);
LOG(INFO) << __func__ << ": Loading file " << schema_file;
if (!ok) return ok;
/* Load the binary schema */
ok = scenarios_parser_.Deserialize(
(uint8_t*)scenarios_schema_binary_content.c_str(),
scenarios_schema_binary_content.length());
if (!ok) return ok;
/* Load the content from JSON */
LOG(INFO) << __func__ << ": Loading file " << content_file;
std::string scenarios_json_content;
ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content);
if (!ok) return ok;
/* Parse */
LOG(INFO) << __func__ << ": Parse json content";
ok = scenarios_parser_.Parse(scenarios_json_content.c_str());
if (!ok) return ok;
/* Import from flatbuffers */
LOG(INFO) << __func__ << ": Build flat buffer structure";
auto scenarios_root = le_audio::GetAudioSetScenarios(
scenarios_parser_.builder_.GetBufferPointer());
if (!scenarios_root) return false;
auto flat_scenarios = scenarios_root->scenarios();
if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0))
return false;
LOG(INFO) << __func__ << ": Turn flat buffer into structure";
AudioContext media_context = AudioContext();
media_context.bitmask =
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
AudioContext::UNSPECIFIED | AudioContext::MEDIA);
AudioContext conversational_context = AudioContext();
conversational_context.bitmask =
(AudioContext::RINGTONE_ALERTS | AudioContext::CONVERSATIONAL);
AudioContext live_context = AudioContext();
live_context.bitmask = AudioContext::LIVE_AUDIO;
AudioContext game_context = AudioContext();
game_context.bitmask = AudioContext::GAME;
AudioContext voice_assistants_context = AudioContext();
voice_assistants_context.bitmask = AudioContext::VOICE_ASSISTANTS;
LOG(DEBUG) << "Updating " << flat_scenarios->size() << " scenarios.";
for (auto const& scenario : *flat_scenarios) {
LOG(DEBUG) << "Scenario " << scenario->name()->c_str()
<< " configs: " << scenario->configurations()->size();
if (!scenario->configurations()) continue;
std::string scenario_name = scenario->name()->c_str();
AudioContext context;
if (scenario_name == "Media")
context = AudioContext(media_context);
else if (scenario_name == "Conversational")
context = AudioContext(conversational_context);
else if (scenario_name == "Live")
context = AudioContext(live_context);
else if (scenario_name == "Game")
context = AudioContext(game_context);
else if (scenario_name == "VoiceAssistants")
context = AudioContext(voice_assistants_context);
for (auto it = scenario->configurations()->begin();
it != scenario->configurations()->end(); ++it) {
auto config_name = it->str();
auto configuration = configurations_.find(config_name);
if (configuration == configurations_.end()) continue;
LOG(DEBUG) << "Getting configuration with name: " << config_name;
auto [source, sink, flags] = configuration->second;
// Each configuration will create a LeAudioAseConfigurationSetting
// with the same {context, packing}
// and different data
LeAudioAseConfigurationSetting setting;
setting.audioContext = context;
// TODO: Packing
setting.sourceAseConfiguration = source;
setting.sinkAseConfiguration = sink;
setting.flags = flags;
// Add to list of setting
LOG(DEBUG) << "Pushing configuration to list: " << config_name;
ase_configuration_settings_.push_back(setting);
}
}
return true;
}
bool AudioSetConfigurationProviderJson::LoadContent(
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
config_files,
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
scenario_files,
CodecLocation location) {
for (auto [schema, content] : config_files) {
if (!LoadConfigurationsFromFiles(schema, content, location)) return false;
}
for (auto [schema, content] : scenario_files) {
if (!LoadScenariosFromFiles(schema, content)) return false;
}
return true;
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,125 @@
/*
* 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.
*/
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.h>
#include <map>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include "audio_set_configurations_generated.h"
#include "audio_set_scenarios_generated.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
using LeAudioAseConfigurationSetting =
IBluetoothAudioProvider::LeAudioAseConfigurationSetting;
using AseDirectionConfiguration = IBluetoothAudioProvider::
LeAudioAseConfigurationSetting::AseDirectionConfiguration;
using LeAudioAseQosConfiguration =
IBluetoothAudioProvider::LeAudioAseQosConfiguration;
using LeAudioDataPathConfiguration =
IBluetoothAudioProvider::LeAudioDataPathConfiguration;
enum class CodecLocation {
HOST,
ADSP,
CONTROLLER,
};
class AudioSetConfigurationProviderJson {
public:
static std::vector<LeAudioAseConfigurationSetting>
GetLeAudioAseConfigurationSettings();
private:
static void LoadAudioSetConfigurationProviderJson();
static const le_audio::CodecSpecificConfiguration* LookupCodecSpecificParam(
const flatbuffers::Vector<flatbuffers::Offset<
le_audio::CodecSpecificConfiguration>>* flat_codec_specific_params,
le_audio::CodecSpecificLtvGenericTypes type);
static void populateAudioChannelAllocation(
CodecSpecificConfigurationLtv::AudioChannelAllocation&
audio_channel_allocation,
uint32_t audio_location);
static void populateConfigurationData(
LeAudioAseConfiguration& ase,
const flatbuffers::Vector<
flatbuffers::Offset<le_audio::CodecSpecificConfiguration>>*
flat_codec_specific_params);
static void populateAseConfiguration(
LeAudioAseConfiguration& ase,
const le_audio::AudioSetSubConfiguration* flat_subconfig,
const le_audio::QosConfiguration* qos_cfg);
static void populateAseQosConfiguration(
LeAudioAseQosConfiguration& qos,
const le_audio::QosConfiguration* qos_cfg);
static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
const le_audio::AudioSetSubConfiguration* flat_subconfig,
const le_audio::QosConfiguration* qos_cfg, CodecLocation location);
static void processSubconfig(
const le_audio::AudioSetSubConfiguration* subconfig,
const le_audio::QosConfiguration* qos_cfg,
std::vector<std::optional<AseDirectionConfiguration>>&
directionAseConfiguration,
CodecLocation location);
static void PopulateAseConfigurationFromFlat(
const le_audio::AudioSetConfiguration* flat_cfg,
std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
std::vector<const le_audio::QosConfiguration*>* qos_cfgs,
CodecLocation location,
std::vector<std::optional<AseDirectionConfiguration>>&
sourceAseConfiguration,
std::vector<std::optional<AseDirectionConfiguration>>&
sinkAseConfiguration,
ConfigurationFlags& configurationFlags);
static bool LoadConfigurationsFromFiles(const char* schema_file,
const char* content_file,
CodecLocation location);
static bool LoadScenariosFromFiles(const char* schema_file,
const char* content_file);
static bool LoadContent(
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
config_files,
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
scenario_files,
CodecLocation location);
};
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,555 @@
/*
* Copyright (C) 2022 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 <set>
#include "aidl/android/hardware/bluetooth/audio/ChannelMode.h"
#include "aidl/android/hardware/bluetooth/audio/CodecId.h"
#include "aidl_android_hardware_bluetooth_audio_setting_enums.h"
#define LOG_TAG "BTAudioCodecsProviderAidl"
#include "BluetoothLeAudioCodecsProvider.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
static const char* kLeAudioCodecCapabilitiesFile =
"/vendor/etc/le_audio_codec_capabilities.xml";
static const AudioLocation kStereoAudio = static_cast<AudioLocation>(
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN;
static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
static bool isInvalidFileContent = false;
std::optional<setting::LeAudioOffloadSetting>
BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
if (!leAudioCodecCapabilities.empty() || isInvalidFileContent) {
return std::nullopt;
}
auto le_audio_offload_setting =
setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
if (!le_audio_offload_setting.has_value()) {
LOG(ERROR) << __func__ << ": Failed to read "
<< kLeAudioCodecCapabilitiesFile;
}
return le_audio_offload_setting;
}
std::unordered_map<SessionType, std::vector<CodecInfo>>
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
// Load from previous storage if present
if (!session_codecs_map_.empty()) return session_codecs_map_;
isInvalidFileContent = true;
if (!le_audio_offload_setting.has_value()) return {};
// Load scenario, configuration, codec configuration and strategy
LoadConfigurationToMap(le_audio_offload_setting);
if (supported_scenarios_.empty() || configuration_map_.empty() ||
codec_configuration_map_.empty() || strategy_configuration_map_.empty())
return {};
// Map each configuration into a CodecInfo
std::unordered_map<std::string, CodecInfo> config_codec_info_map_;
for (auto& p : configuration_map_) {
// Initialize new CodecInfo for the config
auto config_name = p.first;
if (config_codec_info_map_.count(config_name) == 0)
config_codec_info_map_[config_name] = CodecInfo();
// Getting informations from codecConfig and strategyConfig
const auto codec_config_name = p.second.getCodecConfiguration();
const auto strategy_config_name = p.second.getStrategyConfiguration();
const auto codec_configuration_map_iter =
codec_configuration_map_.find(codec_config_name);
if (codec_configuration_map_iter == codec_configuration_map_.end())
continue;
const auto strategy_configuration_map_iter =
strategy_configuration_map_.find(strategy_config_name);
if (strategy_configuration_map_iter == strategy_configuration_map_.end())
continue;
const auto& codec_config = codec_configuration_map_iter->second;
const auto codec = codec_config.getCodec();
const auto& strategy_config = strategy_configuration_map_iter->second;
const auto strategy_config_channel_count =
strategy_config.getChannelCount();
// Initiate information
auto& codec_info = config_codec_info_map_[config_name];
switch (codec) {
case setting::CodecType::LC3:
codec_info.name = "LC3";
codec_info.id = CodecId::Core::LC3;
break;
default:
codec_info.name = "UNDEFINE";
codec_info.id = CodecId::Vendor();
break;
}
codec_info.transport =
CodecInfo::Transport::make<CodecInfo::Transport::Tag::leAudio>();
// Mapping codec configuration information
auto& transport =
codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
transport.samplingFrequencyHz.push_back(
codec_config.getSamplingFrequency());
// Mapping octetsPerCodecFrame to bitdepth for easier comparison.
transport.bitdepth.push_back(codec_config.getOctetsPerCodecFrame());
transport.frameDurationUs.push_back(codec_config.getFrameDurationUs());
switch (strategy_config.getAudioLocation()) {
case setting::AudioLocation::MONO:
if (strategy_config_channel_count == 1)
transport.channelMode.push_back(ChannelMode::MONO);
else
transport.channelMode.push_back(ChannelMode::DUALMONO);
break;
case setting::AudioLocation::STEREO:
transport.channelMode.push_back(ChannelMode::STEREO);
break;
default:
transport.channelMode.push_back(ChannelMode::UNKNOWN);
break;
}
}
// Goes through every scenario, deduplicate configuration
std::set<std::string> encoding_config, decoding_config, broadcast_config;
for (auto& s : supported_scenarios_) {
if (s.hasEncode()) encoding_config.insert(s.getEncode());
if (s.hasDecode()) decoding_config.insert(s.getDecode());
if (s.hasBroadcast()) broadcast_config.insert(s.getBroadcast());
}
// Split by session types and add results
const auto encoding_path =
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
const auto decoding_path =
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
const auto broadcast_path =
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
session_codecs_map_ =
std::unordered_map<SessionType, std::vector<CodecInfo>>();
session_codecs_map_[encoding_path] = std::vector<CodecInfo>();
session_codecs_map_[decoding_path] = std::vector<CodecInfo>();
session_codecs_map_[broadcast_path] = std::vector<CodecInfo>();
session_codecs_map_[encoding_path].reserve(encoding_config.size());
session_codecs_map_[decoding_path].reserve(decoding_config.size());
session_codecs_map_[broadcast_path].reserve(broadcast_config.size());
for (auto& c : encoding_config)
session_codecs_map_[encoding_path].push_back(config_codec_info_map_[c]);
for (auto& c : decoding_config)
session_codecs_map_[decoding_path].push_back(config_codec_info_map_[c]);
for (auto& c : broadcast_config)
session_codecs_map_[broadcast_path].push_back(config_codec_info_map_[c]);
isInvalidFileContent = session_codecs_map_.empty();
return session_codecs_map_;
}
std::vector<LeAudioCodecCapabilitiesSetting>
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
if (!leAudioCodecCapabilities.empty()) {
return leAudioCodecCapabilities;
}
isInvalidFileContent = true;
if (!le_audio_offload_setting.has_value()) {
LOG(ERROR)
<< __func__
<< ": input le_audio_offload_setting content need to be non empty";
return {};
}
LoadConfigurationToMap(le_audio_offload_setting);
if (supported_scenarios_.empty() || configuration_map_.empty() ||
codec_configuration_map_.empty() || strategy_configuration_map_.empty())
return {};
leAudioCodecCapabilities =
ComposeLeAudioCodecCapabilities(supported_scenarios_);
isInvalidFileContent = leAudioCodecCapabilities.empty();
return leAudioCodecCapabilities;
}
void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() {
leAudioCodecCapabilities.clear();
configuration_map_.clear();
codec_configuration_map_.clear();
strategy_configuration_map_.clear();
session_codecs_map_.clear();
supported_scenarios_.clear();
}
std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
std::vector<setting::Scenario> supported_scenarios;
if (le_audio_offload_setting->hasScenarioList()) {
for (const auto& scenario_list :
le_audio_offload_setting->getScenarioList()) {
if (!scenario_list.hasScenario()) {
continue;
}
for (const auto& scenario : scenario_list.getScenario()) {
if (scenario.hasEncode() && scenario.hasDecode()) {
supported_scenarios.push_back(scenario);
}
}
}
}
return supported_scenarios;
}
void BluetoothLeAudioCodecsProvider::UpdateConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
if (le_audio_offload_setting->hasConfigurationList()) {
for (const auto& configuration_list :
le_audio_offload_setting->getConfigurationList()) {
if (!configuration_list.hasConfiguration()) {
continue;
}
for (const auto& configuration : configuration_list.getConfiguration()) {
if (configuration.hasName() && configuration.hasCodecConfiguration() &&
configuration.hasStrategyConfiguration()) {
configuration_map_.insert(
make_pair(configuration.getName(), configuration));
}
}
}
}
}
void BluetoothLeAudioCodecsProvider::UpdateCodecConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
if (le_audio_offload_setting->hasCodecConfigurationList()) {
for (const auto& codec_configuration_list :
le_audio_offload_setting->getCodecConfigurationList()) {
if (!codec_configuration_list.hasCodecConfiguration()) {
continue;
}
for (const auto& codec_configuration :
codec_configuration_list.getCodecConfiguration()) {
if (IsValidCodecConfiguration(codec_configuration)) {
codec_configuration_map_.insert(
make_pair(codec_configuration.getName(), codec_configuration));
}
}
}
}
}
void BluetoothLeAudioCodecsProvider::UpdateStrategyConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
if (le_audio_offload_setting->hasStrategyConfigurationList()) {
for (const auto& strategy_configuration_list :
le_audio_offload_setting->getStrategyConfigurationList()) {
if (!strategy_configuration_list.hasStrategyConfiguration()) {
continue;
}
for (const auto& strategy_configuration :
strategy_configuration_list.getStrategyConfiguration()) {
if (IsValidStrategyConfiguration(strategy_configuration)) {
strategy_configuration_map_.insert(make_pair(
strategy_configuration.getName(), strategy_configuration));
}
}
}
}
}
void BluetoothLeAudioCodecsProvider::LoadConfigurationToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
ClearLeAudioCodecCapabilities();
supported_scenarios_ = GetScenarios(le_audio_offload_setting);
if (supported_scenarios_.empty()) {
LOG(ERROR) << __func__ << ": No scenarios in "
<< kLeAudioCodecCapabilitiesFile;
return;
}
UpdateConfigurationsToMap(le_audio_offload_setting);
if (configuration_map_.empty()) {
LOG(ERROR) << __func__ << ": No configurations in "
<< kLeAudioCodecCapabilitiesFile;
return;
}
UpdateCodecConfigurationsToMap(le_audio_offload_setting);
if (codec_configuration_map_.empty()) {
LOG(ERROR) << __func__ << ": No codec configurations in "
<< kLeAudioCodecCapabilitiesFile;
return;
}
UpdateStrategyConfigurationsToMap(le_audio_offload_setting);
if (strategy_configuration_map_.empty()) {
LOG(ERROR) << __func__ << ": No strategy configurations in "
<< kLeAudioCodecCapabilitiesFile;
return;
}
}
std::vector<LeAudioCodecCapabilitiesSetting>
BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities(
const std::vector<setting::Scenario>& supported_scenarios) {
std::vector<LeAudioCodecCapabilitiesSetting> le_audio_codec_capabilities;
for (const auto& scenario : supported_scenarios) {
UnicastCapability unicast_encode_capability =
GetUnicastCapability(scenario.getEncode());
UnicastCapability unicast_decode_capability =
GetUnicastCapability(scenario.getDecode());
BroadcastCapability broadcast_capability = {.codecType =
CodecType::UNKNOWN};
if (scenario.hasBroadcast()) {
broadcast_capability = GetBroadcastCapability(scenario.getBroadcast());
}
// At least one capability should be valid
if (unicast_encode_capability.codecType == CodecType::UNKNOWN &&
unicast_decode_capability.codecType == CodecType::UNKNOWN &&
broadcast_capability.codecType == CodecType::UNKNOWN) {
LOG(ERROR) << __func__ << ": None of the capability is valid.";
continue;
}
le_audio_codec_capabilities.push_back(
{.unicastEncodeCapability = unicast_encode_capability,
.unicastDecodeCapability = unicast_decode_capability,
.broadcastCapability = broadcast_capability});
}
return le_audio_codec_capabilities;
}
UnicastCapability BluetoothLeAudioCodecsProvider::GetUnicastCapability(
const std::string& coding_direction) {
if (coding_direction == "invalid") {
return {.codecType = CodecType::UNKNOWN};
}
auto configuration_iter = configuration_map_.find(coding_direction);
if (configuration_iter == configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
auto codec_configuration_iter = codec_configuration_map_.find(
configuration_iter->second.getCodecConfiguration());
if (codec_configuration_iter == codec_configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
auto strategy_configuration_iter = strategy_configuration_map_.find(
configuration_iter->second.getStrategyConfiguration());
if (strategy_configuration_iter == strategy_configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
CodecType codec_type =
GetCodecType(codec_configuration_iter->second.getCodec());
if (codec_type == CodecType::LC3) {
return ComposeUnicastCapability(
codec_type,
GetAudioLocation(
strategy_configuration_iter->second.getAudioLocation()),
strategy_configuration_iter->second.getConnectedDevice(),
strategy_configuration_iter->second.getChannelCount(),
ComposeLc3Capability(codec_configuration_iter->second));
} else if (codec_type == CodecType::APTX_ADAPTIVE_LE ||
codec_type == CodecType::APTX_ADAPTIVE_LEX) {
return ComposeUnicastCapability(
codec_type,
GetAudioLocation(
strategy_configuration_iter->second.getAudioLocation()),
strategy_configuration_iter->second.getConnectedDevice(),
strategy_configuration_iter->second.getChannelCount(),
ComposeAptxAdaptiveLeCapability(codec_configuration_iter->second));
}
return {.codecType = CodecType::UNKNOWN};
}
BroadcastCapability BluetoothLeAudioCodecsProvider::GetBroadcastCapability(
const std::string& coding_direction) {
if (coding_direction == "invalid") {
return {.codecType = CodecType::UNKNOWN};
}
auto configuration_iter = configuration_map_.find(coding_direction);
if (configuration_iter == configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
auto codec_configuration_iter = codec_configuration_map_.find(
configuration_iter->second.getCodecConfiguration());
if (codec_configuration_iter == codec_configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
auto strategy_configuration_iter = strategy_configuration_map_.find(
configuration_iter->second.getStrategyConfiguration());
if (strategy_configuration_iter == strategy_configuration_map_.end()) {
return {.codecType = CodecType::UNKNOWN};
}
CodecType codec_type =
GetCodecType(codec_configuration_iter->second.getCodec());
std::vector<std::optional<Lc3Capabilities>> bcastLc3Cap(
1, std::optional(ComposeLc3Capability(codec_configuration_iter->second)));
if (codec_type == CodecType::LC3) {
return ComposeBroadcastCapability(
codec_type,
GetAudioLocation(
strategy_configuration_iter->second.getAudioLocation()),
strategy_configuration_iter->second.getChannelCount(), bcastLc3Cap);
}
return {.codecType = CodecType::UNKNOWN};
}
template <class T>
BroadcastCapability BluetoothLeAudioCodecsProvider::ComposeBroadcastCapability(
const CodecType& codec_type, const AudioLocation& audio_location,
const uint8_t& channel_count, const std::vector<T>& capability) {
return {.codecType = codec_type,
.supportedChannel = audio_location,
.channelCountPerStream = channel_count,
.leAudioCodecCapabilities = std::optional(capability)};
}
template <class T>
UnicastCapability BluetoothLeAudioCodecsProvider::ComposeUnicastCapability(
const CodecType& codec_type, const AudioLocation& audio_location,
const uint8_t& device_cnt, const uint8_t& channel_count,
const T& capability) {
return {
.codecType = codec_type,
.supportedChannel = audio_location,
.deviceCount = device_cnt,
.channelCountPerDevice = channel_count,
.leAudioCodecCapabilities =
UnicastCapability::LeAudioCodecCapabilities(capability),
};
}
Lc3Capabilities BluetoothLeAudioCodecsProvider::ComposeLc3Capability(
const setting::CodecConfiguration& codec_configuration) {
return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
.frameDurationUs = {codec_configuration.getFrameDurationUs()},
.octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
}
AptxAdaptiveLeCapabilities
BluetoothLeAudioCodecsProvider::ComposeAptxAdaptiveLeCapability(
const setting::CodecConfiguration& codec_configuration) {
return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
.frameDurationUs = {codec_configuration.getFrameDurationUs()},
.octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
}
AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation(
const setting::AudioLocation& audio_location) {
switch (audio_location) {
case setting::AudioLocation::MONO:
return kMonoAudio;
case setting::AudioLocation::STEREO:
return kStereoAudio;
default:
return AudioLocation::UNKNOWN;
}
}
CodecType BluetoothLeAudioCodecsProvider::GetCodecType(
const setting::CodecType& codec_type) {
switch (codec_type) {
case setting::CodecType::LC3:
return CodecType::LC3;
case setting::CodecType::APTX_ADAPTIVE_LE:
return CodecType::APTX_ADAPTIVE_LE;
case setting::CodecType::APTX_ADAPTIVE_LEX:
return CodecType::APTX_ADAPTIVE_LEX;
default:
return CodecType::UNKNOWN;
}
}
bool BluetoothLeAudioCodecsProvider::IsValidCodecConfiguration(
const setting::CodecConfiguration& codec_configuration) {
return codec_configuration.hasName() && codec_configuration.hasCodec() &&
codec_configuration.hasSamplingFrequency() &&
codec_configuration.hasFrameDurationUs() &&
codec_configuration.hasOctetsPerCodecFrame();
}
bool BluetoothLeAudioCodecsProvider::IsValidStrategyConfiguration(
const setting::StrategyConfiguration& strategy_configuration) {
if (!strategy_configuration.hasName() ||
!strategy_configuration.hasAudioLocation() ||
!strategy_configuration.hasConnectedDevice() ||
!strategy_configuration.hasChannelCount()) {
return false;
}
if (strategy_configuration.getAudioLocation() ==
setting::AudioLocation::STEREO) {
if ((strategy_configuration.getConnectedDevice() == 2 &&
strategy_configuration.getChannelCount() == 1) ||
(strategy_configuration.getConnectedDevice() == 1 &&
strategy_configuration.getChannelCount() == 2)) {
// Stereo
// 1. two connected device, one for L one for R
// 2. one connected device for both L and R
return true;
} else if (strategy_configuration.getConnectedDevice() == 0 &&
strategy_configuration.getChannelCount() == 2) {
// Broadcast
return true;
}
} else if (strategy_configuration.getAudioLocation() ==
setting::AudioLocation::MONO) {
if (strategy_configuration.getConnectedDevice() == 1 &&
strategy_configuration.getChannelCount() == 1) {
// Mono
return true;
}
}
return false;
}
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,115 @@
/*
* Copyright (C) 2022 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 <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
#include <android-base/logging.h>
#include <unordered_map>
#include <vector>
#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
#include "aidl/android/hardware/bluetooth/audio/SessionType.h"
#include "aidl_android_hardware_bluetooth_audio_setting.h"
namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
class BluetoothLeAudioCodecsProvider {
public:
static std::optional<setting::LeAudioOffloadSetting>
ParseFromLeAudioOffloadSettingFile();
static std::vector<LeAudioCodecCapabilitiesSetting>
GetLeAudioCodecCapabilities(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static void ClearLeAudioCodecCapabilities();
static std::unordered_map<SessionType, std::vector<CodecInfo>>
GetLeAudioCodecInfo(const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
private:
static inline std::vector<setting::Scenario> supported_scenarios_;
static inline std::unordered_map<std::string, setting::Configuration>
configuration_map_;
static inline std::unordered_map<std::string, setting::CodecConfiguration>
codec_configuration_map_;
static inline std::unordered_map<std::string, setting::StrategyConfiguration>
strategy_configuration_map_;
static inline std::unordered_map<SessionType, std::vector<CodecInfo>>
session_codecs_map_;
static std::vector<setting::Scenario> GetScenarios(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static void UpdateConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static void UpdateCodecConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static void UpdateStrategyConfigurationsToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static void LoadConfigurationToMap(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting);
static std::vector<LeAudioCodecCapabilitiesSetting>
ComposeLeAudioCodecCapabilities(
const std::vector<setting::Scenario>& supported_scenarios);
static UnicastCapability GetUnicastCapability(
const std::string& coding_direction);
static BroadcastCapability GetBroadcastCapability(
const std::string& coding_direction);
template <class T>
static inline UnicastCapability ComposeUnicastCapability(
const CodecType& codec_type, const AudioLocation& audio_location,
const uint8_t& device_cnt, const uint8_t& channel_count,
const T& capability);
template <class T>
static inline BroadcastCapability ComposeBroadcastCapability(
const CodecType& codec_type, const AudioLocation& audio_location,
const uint8_t& channel_count, const std::vector<T>& capability);
static inline Lc3Capabilities ComposeLc3Capability(
const setting::CodecConfiguration& codec_configuration);
static inline AptxAdaptiveLeCapabilities ComposeAptxAdaptiveLeCapability(
const setting::CodecConfiguration& codec_configuration);
static inline AudioLocation GetAudioLocation(
const setting::AudioLocation& audio_location);
static inline CodecType GetCodecType(const setting::CodecType& codec_type);
static inline bool IsValidCodecConfiguration(
const setting::CodecConfiguration& codec_configuration);
static inline bool IsValidStrategyConfiguration(
const setting::StrategyConfiguration& strategy_configuration);
};
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -0,0 +1,383 @@
/*
* Copyright (C) 2022 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 <gtest/gtest.h>
#include <optional>
#include <tuple>
#include "BluetoothLeAudioCodecsProvider.h"
using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
using aidl::android::hardware::bluetooth::audio::
LeAudioCodecCapabilitiesSetting;
using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
using aidl::android::hardware::bluetooth::audio::setting::
CodecConfigurationList;
using aidl::android::hardware::bluetooth::audio::setting::CodecType;
using aidl::android::hardware::bluetooth::audio::setting::Configuration;
using aidl::android::hardware::bluetooth::audio::setting::ConfigurationList;
using aidl::android::hardware::bluetooth::audio::setting::LeAudioOffloadSetting;
using aidl::android::hardware::bluetooth::audio::setting::Scenario;
using aidl::android::hardware::bluetooth::audio::setting::ScenarioList;
using aidl::android::hardware::bluetooth::audio::setting::StrategyConfiguration;
using aidl::android::hardware::bluetooth::audio::setting::
StrategyConfigurationList;
typedef std::tuple<std::vector<ScenarioList>, std::vector<ConfigurationList>,
std::vector<CodecConfigurationList>,
std::vector<StrategyConfigurationList>>
OffloadSetting;
// Define valid components for each list
// Scenario
static const Scenario kValidScenario(std::make_optional("OneChanStereo_16_1"),
std::make_optional("OneChanStereo_16_1"),
std::nullopt);
static const Scenario kValidBroadcastScenario(
std::nullopt, std::nullopt, std::make_optional("BcastStereo_16_2"));
// Configuration
static const Configuration kValidConfigOneChanStereo_16_1(
std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
// CodecConfiguration
static const CodecConfiguration kValidCodecLC3_16k_1(
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
std::nullopt, std::make_optional(16000), std::make_optional(7500),
std::make_optional(30), std::nullopt);
// StrategyConfiguration
static const StrategyConfiguration kValidStrategyStereoOneCis(
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO), std::make_optional(2),
std::make_optional(1));
static const StrategyConfiguration kValidStrategyStereoTwoCis(
std::make_optional("STEREO_TWO_CISES_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO), std::make_optional(1),
std::make_optional(2));
static const StrategyConfiguration kValidStrategyMonoOneCis(
std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::MONO), std::make_optional(1),
std::make_optional(1));
static const StrategyConfiguration kValidStrategyBroadcastStereo(
std::make_optional("BROADCAST_STEREO"),
std::make_optional(AudioLocation::STEREO), std::make_optional(0),
std::make_optional(2));
// Define valid test list built from above valid components
// Scenario, Configuration, CodecConfiguration, StrategyConfiguration
static const std::vector<ScenarioList> kValidScenarioList = {ScenarioList(
std::vector<Scenario>{kValidScenario, kValidBroadcastScenario})};
static const std::vector<ConfigurationList> kValidConfigurationList = {
ConfigurationList(
std::vector<Configuration>{kValidConfigOneChanStereo_16_1})};
static const std::vector<CodecConfigurationList> kValidCodecConfigurationList =
{CodecConfigurationList(
std::vector<CodecConfiguration>{kValidCodecLC3_16k_1})};
static const std::vector<StrategyConfigurationList>
kValidStrategyConfigurationList = {
StrategyConfigurationList(std::vector<StrategyConfiguration>{
kValidStrategyStereoOneCis, kValidStrategyStereoTwoCis,
kValidStrategyMonoOneCis, kValidStrategyBroadcastStereo})};
class BluetoothLeAudioCodecsProviderTest
: public ::testing::TestWithParam<OffloadSetting> {
public:
static std::vector<OffloadSetting> CreateTestCases(
const std::vector<ScenarioList>& scenario_lists,
const std::vector<ConfigurationList>& configuration_lists,
const std::vector<CodecConfigurationList>& codec_configuration_lists,
const std::vector<StrategyConfigurationList>&
strategy_configuration_lists) {
// make each vector in output test_cases has only one element
// to match the input of test params
// normally only one vector in input has multiple elements
// we just split elements in this vector to several vector
std::vector<OffloadSetting> test_cases;
for (const auto& scenario_list : scenario_lists) {
for (const auto& configuration_list : configuration_lists) {
for (const auto& codec_configuration_list : codec_configuration_lists) {
for (const auto& strategy_configuration_list :
strategy_configuration_lists) {
test_cases.push_back(CreateTestCase(
scenario_list, configuration_list, codec_configuration_list,
strategy_configuration_list));
}
}
}
}
return test_cases;
}
protected:
void Initialize() {
BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities();
}
std::vector<LeAudioCodecCapabilitiesSetting> RunTestCase() {
auto& [scenario_lists, configuration_lists, codec_configuration_lists,
strategy_configuration_lists] = GetParam();
LeAudioOffloadSetting le_audio_offload_setting(
scenario_lists, configuration_lists, codec_configuration_lists,
strategy_configuration_lists);
auto le_audio_codec_capabilities =
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
std::make_optional(le_audio_offload_setting));
return le_audio_codec_capabilities;
}
private:
static inline OffloadSetting CreateTestCase(
const ScenarioList& scenario_list,
const ConfigurationList& configuration_list,
const CodecConfigurationList& codec_configuration_list,
const StrategyConfigurationList& strategy_configuration_list) {
return std::make_tuple(
std::vector<ScenarioList>{scenario_list},
std::vector<ConfigurationList>{configuration_list},
std::vector<CodecConfigurationList>{codec_configuration_list},
std::vector<StrategyConfigurationList>{strategy_configuration_list});
}
};
class GetScenariosTest : public BluetoothLeAudioCodecsProviderTest {
public:
static std::vector<ScenarioList> CreateInvalidScenarios() {
std::vector<ScenarioList> invalid_scenario_test_cases;
invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
Scenario(std::nullopt, std::make_optional("OneChanStereo_16_1"),
std::nullopt)}));
invalid_scenario_test_cases.push_back(ScenarioList(
std::vector<Scenario>{Scenario(std::make_optional("OneChanStereo_16_1"),
std::nullopt, std::nullopt)}));
invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
Scenario(std::nullopt, std::nullopt, std::nullopt)}));
invalid_scenario_test_cases.push_back(
ScenarioList(std::vector<Scenario>{}));
return invalid_scenario_test_cases;
}
};
TEST_P(GetScenariosTest, InvalidScenarios) {
Initialize();
auto le_audio_codec_capabilities = RunTestCase();
ASSERT_TRUE(le_audio_codec_capabilities.empty());
}
class UpdateConfigurationsToMapTest
: public BluetoothLeAudioCodecsProviderTest {
public:
static std::vector<ConfigurationList> CreateInvalidConfigurations() {
std::vector<ConfigurationList> invalid_configuration_test_cases;
invalid_configuration_test_cases.push_back(
ConfigurationList(std::vector<Configuration>{
Configuration(std::nullopt, std::make_optional("LC3_16k_1"),
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
invalid_configuration_test_cases.push_back(
ConfigurationList(std::vector<Configuration>{Configuration(
std::make_optional("OneChanStereo_16_1"), std::nullopt,
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
invalid_configuration_test_cases.push_back(
ConfigurationList(std::vector<Configuration>{
Configuration(std::make_optional("OneChanStereo_16_1"),
std::make_optional("LC3_16k_1"), std::nullopt)}));
invalid_configuration_test_cases.push_back(
ConfigurationList(std::vector<Configuration>{}));
return invalid_configuration_test_cases;
}
};
TEST_P(UpdateConfigurationsToMapTest, InvalidConfigurations) {
Initialize();
auto le_audio_codec_capabilities = RunTestCase();
ASSERT_TRUE(le_audio_codec_capabilities.empty());
}
class UpdateCodecConfigurationsToMapTest
: public BluetoothLeAudioCodecsProviderTest {
public:
static std::vector<CodecConfigurationList>
CreateInvalidCodecConfigurations() {
std::vector<CodecConfigurationList> invalid_codec_configuration_test_cases;
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
std::vector<CodecConfiguration>{CodecConfiguration(
std::nullopt, std::make_optional(CodecType::LC3), std::nullopt,
std::make_optional(16000), std::make_optional(7500),
std::make_optional(30), std::nullopt)}));
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
std::vector<CodecConfiguration>{CodecConfiguration(
std::make_optional("LC3_16k_1"), std::nullopt, std::nullopt,
std::make_optional(16000), std::make_optional(7500),
std::make_optional(30), std::nullopt)}));
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
std::vector<CodecConfiguration>{CodecConfiguration(
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
std::nullopt, std::nullopt, std::make_optional(7500),
std::make_optional(30), std::nullopt)}));
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
std::vector<CodecConfiguration>{CodecConfiguration(
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
std::nullopt, std::make_optional(16000), std::nullopt,
std::make_optional(30), std::nullopt)}));
invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
std::vector<CodecConfiguration>{CodecConfiguration(
std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
std::nullopt, std::make_optional(16000), std::make_optional(7500),
std::nullopt, std::nullopt)}));
invalid_codec_configuration_test_cases.push_back(
CodecConfigurationList(std::vector<CodecConfiguration>{}));
return invalid_codec_configuration_test_cases;
}
};
TEST_P(UpdateCodecConfigurationsToMapTest, InvalidCodecConfigurations) {
Initialize();
auto le_audio_codec_capabilities = RunTestCase();
ASSERT_TRUE(le_audio_codec_capabilities.empty());
}
class UpdateStrategyConfigurationsToMapTest
: public BluetoothLeAudioCodecsProviderTest {
public:
static std::vector<StrategyConfigurationList>
CreateInvalidStrategyConfigurations() {
std::vector<StrategyConfigurationList>
invalid_strategy_configuration_test_cases;
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO),
std::make_optional(2), std::make_optional(2))}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO),
std::make_optional(2), std::make_optional(2))}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::nullopt, std::make_optional(AudioLocation::STEREO),
std::make_optional(2), std::make_optional(1))}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"), std::nullopt,
std::make_optional(2), std::make_optional(1))}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO), std::nullopt,
std::make_optional(1))}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(
std::vector<StrategyConfiguration>{StrategyConfiguration(
std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
std::make_optional(AudioLocation::STEREO),
std::make_optional(2), std::nullopt)}));
invalid_strategy_configuration_test_cases.push_back(
StrategyConfigurationList(std::vector<StrategyConfiguration>{}));
return invalid_strategy_configuration_test_cases;
}
};
TEST_P(UpdateStrategyConfigurationsToMapTest, InvalidStrategyConfigurations) {
Initialize();
auto le_audio_codec_capabilities = RunTestCase();
ASSERT_TRUE(le_audio_codec_capabilities.empty());
}
class ComposeLeAudioCodecCapabilitiesTest
: public BluetoothLeAudioCodecsProviderTest {
public:
};
TEST_P(ComposeLeAudioCodecCapabilitiesTest, CodecCapabilitiesNotEmpty) {
Initialize();
auto le_audio_codec_capabilities = RunTestCase();
ASSERT_TRUE(!le_audio_codec_capabilities.empty());
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
INSTANTIATE_TEST_SUITE_P(
BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
GetScenariosTest::CreateInvalidScenarios(), kValidConfigurationList,
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateConfigurationsToMapTest);
INSTANTIATE_TEST_SUITE_P(
BluetoothLeAudioCodecsProviderTest, UpdateConfigurationsToMapTest,
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
kValidScenarioList,
UpdateConfigurationsToMapTest::CreateInvalidConfigurations(),
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
UpdateCodecConfigurationsToMapTest);
INSTANTIATE_TEST_SUITE_P(
BluetoothLeAudioCodecsProviderTest, UpdateCodecConfigurationsToMapTest,
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
kValidScenarioList, kValidConfigurationList,
UpdateCodecConfigurationsToMapTest::CreateInvalidCodecConfigurations(),
kValidStrategyConfigurationList)));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
UpdateStrategyConfigurationsToMapTest);
INSTANTIATE_TEST_SUITE_P(
BluetoothLeAudioCodecsProviderTest, UpdateStrategyConfigurationsToMapTest,
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
kValidScenarioList, kValidConfigurationList,
kValidCodecConfigurationList,
UpdateStrategyConfigurationsToMapTest::
CreateInvalidStrategyConfigurations())));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
ComposeLeAudioCodecCapabilitiesTest);
INSTANTIATE_TEST_SUITE_P(
BluetoothLeAudioCodecsProviderTest, ComposeLeAudioCodecCapabilitiesTest,
::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
kValidScenarioList, kValidConfigurationList,
kValidCodecConfigurationList, kValidStrategyConfigurationList)));
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -467,6 +467,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1(
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
audio_config.get<AudioConfiguration::a2dpConfig>()));
break;
case AudioConfiguration::a2dp:
break;
case AudioConfiguration::leAudioConfig:
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1(
audio_config.get<AudioConfiguration::leAudioConfig>()));
@ -475,6 +477,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1(
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_broadcast_config_2_1(
audio_config.get<AudioConfiguration::leAudioBroadcastConfig>()));
break;
default:
LOG(FATAL) << __func__ << ": unexpected AudioConfiguration";
}
return hidl_audio_config;
}
@ -583,7 +587,7 @@ bool HidlToAidlMiddleware_2_0::GetPresentationPosition(
*total_bytes_readed = presentation_position.transmittedOctets;
if (data_position)
*data_position = {
.tv_sec = static_cast<__kernel_old_time_t>(
.tv_sec = static_cast<long>(
presentation_position.transmittedOctetsTimestamp.tvSec),
.tv_nsec = static_cast<long>(
presentation_position.transmittedOctetsTimestamp.tvNSec)};

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!---
This is an example to configure LE Audio hardware offload supported capability settings
In the follow list, there would be only one list in this file. Add element into each list as needed.
codecConfigurationList:
Supported codec capability along with its parameter setting
strategyConfigurationList:
ASE Configuration strategies
configurationList:
For each configuration , there are two attributes
- codecConfiguration
- strategyConfiguration
scenarioList:
There would be only one `scenarios` group
For each scenario, the are two attributes
- encode
- decode
If a scenario is unidirectional, mark another direction as `invalid`
The configuration should be chosen from `configurationList`
-->
<leAudioOffloadSetting>
<scenarioList>
<!-- encode only -->
<scenario encode="OneChanMono_16_1" decode="invalid"/>
<scenario encode="TwoChanStereo_16_1" decode="invalid"/>
<scenario encode="OneChanStereo_16_1" decode="invalid"/>
<scenario encode="OneChanMono_16_2" decode="invalid"/>
<scenario encode="TwoChanStereo_16_2" decode="invalid"/>
<scenario encode="OneChanStereo_16_2" decode="invalid"/>
<scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_48" decode="invalid"/>
<scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_96" decode="invalid"/>
<scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_48" decode="invalid"/>
<scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_96" decode="invalid"/>
<!-- encode and decode -->
<scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/>
<scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/>
<scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/>
<scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/>
<scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/>
<scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/>
<scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/>
<scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/>
<!-- broadcast -->
<scenario encode="invalid" decode="invalid" broadcast="BcastStereo_16_2"/>
</scenarioList>
<configurationList>
<configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
<configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
<configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
<configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/>
<configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
<configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
<configuration name="BcastStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="BROADCAST_STEREO"/>
<configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LE_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
<configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LE_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
<configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LEX_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
<configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LEX_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
</configurationList>
<codecConfigurationList>
<codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/>
<codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/>
<codecConfiguration name="APTX_ADAPTIVE_LE_48k" codec="APTX_ADAPTIVE_LE" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
<codecConfiguration name="APTX_ADAPTIVE_LE_96k" codec="APTX_ADAPTIVE_LE" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
<codecConfiguration name="APTX_ADAPTIVE_LEX_48k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
<codecConfiguration name="APTX_ADAPTIVE_LEX_96k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
</codecConfigurationList>
<strategyConfigurationList>
<strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/>
<strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/>
<strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/>
<strategyConfiguration name="BROADCAST_STEREO" audioLocation="STEREO" connectedDevice="0" channelCount="2"/>
</strategyConfigurationList>
</leAudioOffloadSetting>

View file

@ -0,0 +1,77 @@
<!-- LE Audio Offload Codec Capability Schema -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="leAudioOffloadSetting">
<xs:complexType>
<xs:element ref="scenarioList" minOccurs="1" maxOccurs="1"/>
<xs:element ref="configurationList" minOccurs="1" maxOccurs="1"/>
<xs:element ref="codecConfigurationList" minOccurs="1" maxOccurs="1"/>
<xs:element ref="strategyConfigurationList" minOccurs="1" maxOccurs="1"/>
</xs:complexType>
</xs:element>
<xs:element name="scenarioList">
<xs:complexType>
<xs:element ref="scenario" minOccurs="1" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
<xs:element name="configurationList">
<xs:complexType>
<xs:element ref="configuration" minOccurs="1" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
<xs:element name="codecConfigurationList">
<xs:complexType>
<xs:element ref="codecConfiguration" minOccurs="1" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
<xs:element name="strategyConfigurationList">
<xs:complexType>
<xs:element ref="strategyConfiguration" minOccurs="1" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
<xs:element name="scenario">
<xs:complexType>
<xs:attribute name="encode" type="xs:string"/>
<xs:attribute name="decode" type="xs:string"/>
<xs:attribute name="broadcast" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="configuration">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="codecConfiguration" type="xs:string"/>
<xs:attribute name="strategyConfiguration" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="codecConfiguration">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="codec" type="codecType"/>
<xs:attribute name="pcmBitDepth" type="xs:unsignedByte"/>
<xs:attribute name="samplingFrequency" type="xs:int"/>
<xs:attribute name="frameDurationUs" type="xs:int"/>
<xs:attribute name="octetsPerCodecFrame" type="xs:int"/>
<xs:attribute name="codecFrameBlocksPerSdu" type="xs:unsignedByte"/>
</xs:complexType>
</xs:element>
<xs:element name="strategyConfiguration">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="audioLocation" type="audioLocation"/>
<xs:attribute name="connectedDevice" type="xs:unsignedByte"/>
<xs:attribute name="channelCount" type="xs:unsignedByte"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="audioLocation">
<xs:restriction base="xs:string">
<xs:enumeration value="MONO"/>
<xs:enumeration value="STEREO"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="codecType">
<xs:restriction base="xs:string">
<xs:enumeration value="LC3"/>
<xs:enumeration value="APTX_ADAPTIVE_LE"/>
<xs:enumeration value="APTX_ADAPTIVE_LEX"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View file

@ -0,0 +1,107 @@
// Signature format: 2.0
package aidl.android.hardware.bluetooth.audio.setting {
public enum AudioLocation {
method public String getRawName();
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation MONO;
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.AudioLocation STEREO;
}
public class CodecConfiguration {
ctor public CodecConfiguration();
method public aidl.android.hardware.bluetooth.audio.setting.CodecType getCodec();
method public short getCodecFrameBlocksPerSdu();
method public int getFrameDurationUs();
method public String getName();
method public int getOctetsPerCodecFrame();
method public short getPcmBitDepth();
method public int getSamplingFrequency();
method public void setCodec(aidl.android.hardware.bluetooth.audio.setting.CodecType);
method public void setCodecFrameBlocksPerSdu(short);
method public void setFrameDurationUs(int);
method public void setName(String);
method public void setOctetsPerCodecFrame(int);
method public void setPcmBitDepth(short);
method public void setSamplingFrequency(int);
}
public class CodecConfigurationList {
ctor public CodecConfigurationList();
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.CodecConfiguration> getCodecConfiguration();
}
public enum CodecType {
method public String getRawName();
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LE;
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LEX;
enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType LC3;
}
public class Configuration {
ctor public Configuration();
method public String getCodecConfiguration();
method public String getName();
method public String getStrategyConfiguration();
method public void setCodecConfiguration(String);
method public void setName(String);
method public void setStrategyConfiguration(String);
}
public class ConfigurationList {
ctor public ConfigurationList();
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Configuration> getConfiguration();
}
public class LeAudioOffloadSetting {
ctor public LeAudioOffloadSetting();
method public aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList getCodecConfigurationList();
method public aidl.android.hardware.bluetooth.audio.setting.ConfigurationList getConfigurationList();
method public aidl.android.hardware.bluetooth.audio.setting.ScenarioList getScenarioList();
method public aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList getStrategyConfigurationList();
method public void setCodecConfigurationList(aidl.android.hardware.bluetooth.audio.setting.CodecConfigurationList);
method public void setConfigurationList(aidl.android.hardware.bluetooth.audio.setting.ConfigurationList);
method public void setScenarioList(aidl.android.hardware.bluetooth.audio.setting.ScenarioList);
method public void setStrategyConfigurationList(aidl.android.hardware.bluetooth.audio.setting.StrategyConfigurationList);
}
public class Scenario {
ctor public Scenario();
method public String getBroadcast();
method public String getDecode();
method public String getEncode();
method public void setBroadcast(String);
method public void setDecode(String);
method public void setEncode(String);
}
public class ScenarioList {
ctor public ScenarioList();
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.Scenario> getScenario();
}
public class StrategyConfiguration {
ctor public StrategyConfiguration();
method public aidl.android.hardware.bluetooth.audio.setting.AudioLocation getAudioLocation();
method public short getChannelCount();
method public short getConnectedDevice();
method public String getName();
method public void setAudioLocation(aidl.android.hardware.bluetooth.audio.setting.AudioLocation);
method public void setChannelCount(short);
method public void setConnectedDevice(short);
method public void setName(String);
}
public class StrategyConfigurationList {
ctor public StrategyConfigurationList();
method public java.util.List<aidl.android.hardware.bluetooth.audio.setting.StrategyConfiguration> getStrategyConfiguration();
}
public class XmlParser {
ctor public XmlParser();
method public static aidl.android.hardware.bluetooth.audio.setting.LeAudioOffloadSetting readLeAudioOffloadSetting(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
}
}

View file

@ -0,0 +1 @@
// Signature format: 2.0

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2022 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.
*
*/
namespace aidl.android.hardware.bluetooth.audio.le_audio;
enum CodecSpecificLtvGenericTypes : byte {
SUPPORTED_SAMPLING_FREQUENCY = 0x01,
SUPPORTED_FRAME_DURATION = 0x02,
SUPPORTED_AUDIO_CHANNEL_ALLOCATION = 0x03,
SUPPORTED_OCTETS_PER_CODEC_FRAME = 0x04,
SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU = 0x05,
}
/// Note: Holds either a single value (when `value_width == 0`) or multiple
/// values if `value.length()` is no-remainder divisible by the non-zero
/// `value_width`.
/// Note: Consider extending it with `flags` field, to hold additional info like
/// IsBitfield, IsRange, etc. if we need these type-specific validations.
table CompoundValue {
value: [ubyte] (required);
value_width: ubyte = 0;
}
table CodecSpecificConfiguration {
name: string;
type: ubyte (key);
compound_value: CompoundValue;
}
struct CodecId {
coding_format: ubyte;
vendor_company_id : ushort;
vendor_codec_id : ushort;
}
enum AudioSetConfigurationStrategy : byte {
MONO_ONE_CIS_PER_DEVICE = 0x00,
STEREO_TWO_CISES_PER_DEVICE = 0x01,
STEREO_ONE_CIS_PER_DEVICE = 0x02,
}
enum AudioSetConfigurationDirection : byte {
SINK = 0x01,
SOURCE = 0x02,
}
enum AudioSetConfigurationTargetLatency : byte {
LOW = 0x01,
BALANCED_RELIABILITY = 0x02,
HIGH_RELIABILITY = 0x03,
}
table AudioSetSubConfiguration {
device_cnt: ubyte;
ase_cnt: ubyte;
direction: AudioSetConfigurationDirection = SINK;
configuration_strategy: AudioSetConfigurationStrategy;
codec_id : CodecId (required);
codec_configuration: [CodecSpecificConfiguration] (required);
}
table CodecConfiguration {
name: string (key, required);
subconfigurations: [AudioSetSubConfiguration] (required);
}
table QosConfiguration {
name: string (key, required);
target_latency: AudioSetConfigurationTargetLatency = BALANCED_RELIABILITY;
retransmission_number: ubyte;
max_transport_latency : ushort;
}
/// Each set configration can contain multiple logical subconfigurations, which
/// all must be configurable with the current set of audio devices. For example,
/// one can define multiple output stream configurations with different
/// qualities, or assign different configurations to each stream direction.
table AudioSetConfiguration {
name: string (key, required);
codec_config_name: string (required);
qos_config_name: [string] (required);
}
table AudioSetConfigurations {
_comments_: [string];
configurations: [AudioSetConfiguration] (required);
codec_configurations: [CodecConfiguration] (required);
qos_configurations: [QosConfiguration] (required);
}
root_type AudioSetConfigurations;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2022 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.
*
*/
namespace aidl.android.hardware.bluetooth.audio.le_audio;
/// Scenario represents the use case such as "Media", "Conversation", etc.
/// Each scenario can list any number of codec configurations by their names in
/// the order of preference. That means if the first entry does not meet all
/// the current requirements (such as peer device capabilities etc.) next
/// configurations are being checked.
///
/// The referenced codec configurations are defined by the
/// audio_set_configurations.fbs schema and loaded from a different source file.
/// Multiple scenarios can reference same codec configurations.
table AudioSetScenario {
_comments_: [string];
name: string (key, required);
configurations: [string] (required);
}
table AudioSetScenarios {
_comments_: [string];
scenarios: [AudioSetScenario] (required);
}
root_type AudioSetScenarios;

View file

@ -0,0 +1,304 @@
{
"_comments_": [
"== Audio Set Scenarios ==",
" Each defined scenario references externally defined audio set",
" configurations, listed in the order of priority."
],
"scenarios": [
{
"name": "Conversational",
"configurations": [
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_2",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_2",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_1",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_2",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_2",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
"DualDev_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanStereoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_48_4_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_48_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_48_3_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_48_1_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_32_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_32_1_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_24_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_24_1_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_16_1_Balanced_Reliability",
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_Server_Prefered_1",
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_60oct_R3_L22_1",
"DualDev_OneChanMonoSnk_16_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_16_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_16_2_Balanced_Reliability"
]
},
{
"name": "Media",
"configurations": [
"DualDev_OneChanStereoSnk_48_4_High_Reliability",
"DualDev_OneChanStereoSnk_48_4_2",
"DualDev_OneChanStereoSnk_48_2_High_Reliability",
"DualDev_OneChanStereoSnk_48_2_2",
"DualDev_OneChanStereoSnk_48_3_High_Reliability",
"DualDev_OneChanStereoSnk_48_3_2",
"DualDev_OneChanStereoSnk_48_1_High_Reliability",
"DualDev_OneChanStereoSnk_48_1_2",
"DualDev_OneChanStereoSnk_24_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_24_2_2",
"DualDev_OneChanStereoSnk_16_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_16_2_2",
"DualDev_OneChanStereoSnk_16_1_Balanced_Reliability",
"DualDev_OneChanStereoSnk_16_1_2",
"SingleDev_OneChanStereoSnk_48_4_High_Reliability",
"SingleDev_OneChanStereoSnk_48_4_2",
"SingleDev_OneChanStereoSnk_48_2_High_Reliability",
"SingleDev_OneChanStereoSnk_48_2_2",
"SingleDev_OneChanStereoSnk_48_3_High_Reliability",
"SingleDev_OneChanStereoSnk_48_3_2",
"SingleDev_OneChanStereoSnk_48_1_High_Reliability",
"SingleDev_OneChanStereoSnk_48_1_2",
"SingleDev_OneChanStereoSnk_24_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_24_2_2",
"SingleDev_OneChanStereoSnk_16_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_16_2_2",
"SingleDev_OneChanStereoSnk_16_1_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_16_1_2",
"SingleDev_TwoChanStereoSnk_48_4_High_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_2",
"SingleDev_TwoChanStereoSnk_48_4_High_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_2",
"SingleDev_TwoChanStereoSnk_48_2_High_Reliability",
"SingleDev_TwoChanStereoSnk_48_2_2",
"SingleDev_TwoChanStereoSnk_48_3_High_Reliability",
"SingleDev_TwoChanStereoSnk_48_3_2",
"SingleDev_TwoChanStereoSnk_48_1_High_Reliability",
"SingleDev_TwoChanStereoSnk_48_1_2",
"SingleDev_TwoChanStereoSnk_24_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_24_2_2",
"SingleDev_TwoChanStereoSnk_16_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_16_2_2",
"SingleDev_TwoChanStereoSnk_16_1_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_16_1_2",
"SingleDev_OneChanMonoSnk_48_4_High_Reliability",
"SingleDev_OneChanMonoSnk_48_4_2",
"SingleDev_OneChanMonoSnk_48_2_High_Reliability",
"SingleDev_OneChanMonoSnk_48_2_2",
"SingleDev_OneChanMonoSnk_48_3_High_Reliability",
"SingleDev_OneChanMonoSnk_48_3_2",
"SingleDev_OneChanMonoSnk_48_1_High_Reliability",
"SingleDev_OneChanMonoSnk_48_1_2",
"SingleDev_OneChanMonoSnk_32_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_32_2_2",
"SingleDev_OneChanMonoSnk_32_1_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_32_1_2",
"SingleDev_OneChanMonoSnk_24_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_24_2_2",
"SingleDev_OneChanMonoSnk_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_16_2_2",
"SingleDev_OneChanMonoSnk_16_1_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_16_1_2",
"VND_DualDev_OneChanStereoSnk_48khz_100octs_High_Reliability_1",
"VND_DualDev_OneChanStereoSnk_48khz_100octs_R15_L70_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_100octs_High_Reliability_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_100octs_R15_L70_1",
"VND_SingleDev_OneChanStereoSnk_48khz_100octs_High_Reliability_1",
"VND_SingleDev_OneChanStereoSnk_48khz_100octs_R15_L70_1",
"DualDev_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanStereoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability"
]
},
{
"name": "Game",
"configurations": [
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_TwoChanStereoSrc_16khz_30octs_Balanced_Reliability_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_TwoChanStereoSrc_16khz_30octs_R3_L12_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_High_Reliability_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_48_2_Low_Latency",
"DualDev_OneChanStereoSnk_48_3_Low_Latency",
"DualDev_OneChanStereoSnk_48_1_Low_Latency",
"DualDev_OneChanStereoSnk_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_32_1_Low_Latency",
"DualDev_OneChanStereoSnk_24_2_Low_Latency",
"DualDev_OneChanStereoSnk_24_1_Low_Latency",
"DualDev_OneChanStereoSnk_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_48_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_48_3_Low_Latency",
"SingleDev_TwoChanStereoSnk_48_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_32_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_24_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_24_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_16_1_Low_Latency",
"SingleDev_OneChanStereoSnk_48_2_Low_Latency",
"SingleDev_OneChanStereoSnk_48_3_Low_Latency",
"SingleDev_OneChanStereoSnk_48_1_Low_Latency",
"SingleDev_OneChanStereoSnk_32_2_Low_Latency",
"SingleDev_OneChanStereoSnk_32_1_Low_Latency",
"SingleDev_OneChanStereoSnk_24_2_Low_Latency",
"SingleDev_OneChanStereoSnk_24_1_Low_Latency",
"SingleDev_OneChanStereoSnk_16_2_Low_Latency",
"SingleDev_OneChanStereoSnk_16_1_Low_Latency"
]
},
{
"name": "VoiceAssistants",
"configurations": [
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_16_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_24_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_32_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
"DualDev_OneChanDoubleStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_16_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_24_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_TwoChanStereoSrc_32_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
"SingleDev_OneChanStereoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_24_2_Balanced_Reliability",
"SingleDev_OneChanMonoSnk_48_4_OneChanMonoSrc_32_2_Balanced_Reliability"
]
},
{
"name": "Live",
"configurations": [
"VND_SingleDev_TwoChanStereoSrc_48khz_100octs_Balanced_Reliability_1",
"VND_SingleDev_TwoChanStereoSrc_48khz_100octs_R11_L40_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
"DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
"SingleDev_OneChanMonoSrc_48_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_48_1_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_32_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_32_1_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_16_2_Balanced_Reliability",
"SingleDev_OneChanMonoSrc_16_1_Balanced_Reliability"
]
}
]
}