Default implementation of getLeAudioBroadcastConfiguration for
LE Audio Offload provider
Bug: 306225778
Test: mmm hardware/interfaces/bluetooth/audio/aidl/default
Change-Id: I5bfb4a84a8222603c64ac51a28b5962825182676
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
index 275cfec..e55a434 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
@@ -154,6 +154,8 @@
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();
@@ -161,13 +163,14 @@
provider_info.name = A2dpOffloadCodecFactory::GetInstance()->name;
for (auto codec : A2dpOffloadCodecFactory::GetInstance()->codecs)
provider_info.codecInfos.push_back(codec->info);
- } else 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) {
+ }
+
+ 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()) {
@@ -175,8 +178,8 @@
provider_info.name = kLeAudioOffloadProviderName;
provider_info.codecInfos = db_codec_info;
*_aidl_return = provider_info;
+ return ndk::ScopedAStatus::ok();
}
- return ndk::ScopedAStatus::ok();
}
return ndk::ScopedAStatus::ok();
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index 426171d..cff3b25 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -82,6 +82,19 @@
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;
@@ -307,6 +320,16 @@
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,
@@ -597,7 +620,8 @@
};
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSinkAseMetadataChanged(
- IBluetoothAudioProvider::AseState in_state,
+ 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;
@@ -605,13 +629,95 @@
};
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSourceAseMetadataChanged(
- IBluetoothAudioProvider::AseState in_state,
+ 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<
@@ -619,13 +725,66 @@
in_remoteSinkAudioCapabilities,
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
- IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting*
- _aidl_return) {
- /* TODO: Implement */
- (void)in_remoteSinkAudioCapabilities;
- (void)in_requirement;
- (void)_aidl_return;
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ 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
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
index b6037c3..2785e7f 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
@@ -21,6 +21,7 @@
#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 {
@@ -38,6 +39,8 @@
LeAudioAseQosConfigurationRequirement::AseQosDirectionRequirement;
using LeAudioAseQosConfiguration =
IBluetoothAudioProvider::LeAudioAseQosConfiguration;
+using LeAudioBroadcastConfigurationSetting =
+ IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting;
class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
public:
@@ -68,12 +71,14 @@
in_qosRequirement,
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return)
override;
- ndk::ScopedAStatus onSinkAseMetadataChanged(
- IBluetoothAudioProvider::AseState in_state,
+ 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 onSourceAseMetadataChanged(
- IBluetoothAudioProvider::AseState in_state,
+ 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(
@@ -82,12 +87,12 @@
in_remoteSinkAudioCapabilities,
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
- IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting*
- _aidl_return) override;
+ 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);
@@ -119,6 +124,9 @@
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,
@@ -144,6 +152,11 @@
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 {
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index c18d48e..88f2f97 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -1515,7 +1515,7 @@
}
bool OpenSession(int32_t sample_rate, int8_t bits_per_sample,
- ChannelMode channel_mode, int32_t data_interval_us) {
+ ChannelMode channel_mode, int32_t data_interval_us) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
.channelMode = channel_mode,
@@ -1552,9 +1552,9 @@
for (auto sample_rate : hfp_sample_rates_) {
for (auto bits_per_sample : hfp_bits_per_samples_) {
for (auto channel_mode : hfp_channel_modes_) {
- for (auto data_interval_us: hfp_data_interval_us_) {
- EXPECT_TRUE(OpenSession(sample_rate, bits_per_sample,
- channel_mode, data_interval_us));
+ for (auto data_interval_us : hfp_data_interval_us_) {
+ EXPECT_TRUE(OpenSession(sample_rate, bits_per_sample, channel_mode,
+ data_interval_us));
EXPECT_TRUE(audio_provider_->endSession().isOk());
}
}
@@ -1582,7 +1582,7 @@
}
bool OpenSession(int32_t sample_rate, int8_t bits_per_sample,
- ChannelMode channel_mode, int32_t data_interval_us) {
+ ChannelMode channel_mode, int32_t data_interval_us) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
.channelMode = channel_mode,
@@ -1616,10 +1616,10 @@
for (auto sample_rate : hfp_sample_rates_) {
for (auto bits_per_sample : hfp_bits_per_samples_) {
for (auto channel_mode : hfp_channel_modes_) {
- for (auto data_interval_us: hfp_data_interval_us_) {
- EXPECT_TRUE(OpenSession(sample_rate, bits_per_sample,
- channel_mode, data_interval_us));
- EXPECT_TRUE(audio_provider_->endSession().isOk());
+ for (auto data_interval_us : hfp_data_interval_us_) {
+ EXPECT_TRUE(OpenSession(sample_rate, bits_per_sample, channel_mode,
+ data_interval_us));
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
}
}
}
@@ -1899,7 +1899,7 @@
}
bool OpenSession(CodecId codec_id, int connection_handle, bool nrec,
- bool controller_codec) {
+ bool controller_codec) {
// Check if can open session with a Hfp configuration
HfpConfiguration hfp_configuration{
.codecId = codec_id,
@@ -3041,6 +3041,22 @@
}
}
+TEST_P(BluetoothAudioProviderLeAudioBroadcastHardwareAidl,
+ GetEmptyBroadcastConfigurationEmptyCapability) {
+ std::vector<std::optional<LeAudioDeviceCapabilities>> empty_capability;
+ IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement
+ empty_requirement;
+
+ IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting* configuration =
+ new IBluetoothAudioProvider::LeAudioBroadcastConfigurationSetting();
+
+ // Check empty capability for source direction
+ auto aidl_retval = audio_provider_->getLeAudioBroadcastConfiguration(
+ empty_capability, empty_requirement, configuration);
+
+ ASSERT_TRUE(aidl_retval.isOk());
+}
+
/**
* Test whether each provider of type
* SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH can be