diff --git a/system/bta/Android.bp b/system/bta/Android.bp index 8ebd078c50bdad034be2d5a3ef2134415e6cb7ce..5113ccf4b6ef951c1d4e3862f9cd7903a73b4570 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -648,6 +648,77 @@ prebuilt_etc { // bta unit tests for LE Audio // ======================================================== +cc_test { + name: "bluetooth_le_audio_codec_manager_test", + test_suites: ["device-tests"], + defaults: [ + "bluetooth_gtest_x86_asan_workaround", + "fluoride_defaults", + "mts_defaults", + ], + host_supported: true, + target: { + darwin: { + enabled: false, + }, + android: { + sanitize: { + misc_undefined: ["bounds"], + }, + whole_static_libs: [ + "libPlatformProperties", + ], + }, + }, + include_dirs: [ + "packages/modules/Bluetooth/system", + "packages/modules/Bluetooth/system/bta/include", + "packages/modules/Bluetooth/system/bta/test/common", + "packages/modules/Bluetooth/system/btif/include", + "packages/modules/Bluetooth/system/gd", + "packages/modules/Bluetooth/system/stack/include", + ], + srcs: [ + ":TestCommonMockFunctions", + ":TestMockBtaLeAudioHalVerifier", + ":TestStubOsi", + "le_audio/codec_manager.cc", + "le_audio/codec_manager_test.cc", + "le_audio/le_audio_set_configuration_provider_json.cc", + "le_audio/le_audio_types.cc", + "test/common/btm_api_mock.cc", + "test/common/mock_controller.cc", + ], + data: [ + ":audio_set_configurations_bfbs", + ":audio_set_configurations_json", + ":audio_set_scenarios_bfbs", + ":audio_set_scenarios_json", + ], + generated_headers: [ + "LeAudioSetConfigSchemas_h", + ], + shared_libs: [ + "android.hardware.bluetooth.audio@2.0", + "android.hardware.bluetooth.audio@2.1", + "libcrypto", + "libhidlbase", + "liblog", // __android_log_print + ], + static_libs: [ + "libbluetooth_gd", + "libbt-common", + "libchrome", + "libevent", + "libflatbuffers-cpp", + "libgmock", + "libosi", + ], + sanitize: { + cfi: false, + }, +} + cc_test { name: "bluetooth_le_audio_test", test_suites: ["device-tests"], diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index c41248fbe47397393ffb03d36b079dd1d2b082d8..19f7e409b88ad6b7bfcf7b1c8cd5264192e37460 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -3398,12 +3398,16 @@ class LeAudioClientImpl : public LeAudioClient { ConfirmLocalAudioSourceStreamingRequest(); if (!LeAudioHalVerifier::SupportsStreamActiveApi()) { - /* We update the target audio allocation before streamStarted that the - * offloder would know how to configure offloader encoder. We should check - * if we need to update the current - * allocation here as the target allocation and the current allocation is - * different */ - updateOffloaderIfNeeded(group); + /* We update the target audio allocation before streamStarted so that the + * CodecManager would know how to configure the encoder. */ + BidirectionalPair<uint16_t> delays_pair = { + .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink), + .source = + group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)}; + CodecManager::GetInstance()->UpdateActiveAudioConfig( + group->stream_conf.stream_params, delays_pair, + std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal, + le_audio_source_hal_client_.get(), std::placeholders::_1)); } return true; @@ -3464,12 +3468,16 @@ class LeAudioClientImpl : public LeAudioClient { ConfirmLocalAudioSinkStreamingRequest(); if (!LeAudioHalVerifier::SupportsStreamActiveApi()) { - /* We update the target audio allocation before streamStarted that the - * offloder would know how to configure offloader encoder. We should check - * if we need to update the current - * allocation here as the target allocation and the current allocation is - * different */ - updateOffloaderIfNeeded(group); + /* We update the target audio allocation before streamStarted so that the + * CodecManager would know how to configure the encoder. */ + BidirectionalPair<uint16_t> delays_pair = { + .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink), + .source = + group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)}; + CodecManager::GetInstance()->UpdateActiveAudioConfig( + group->stream_conf.stream_params, delays_pair, + std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal, + le_audio_source_hal_client_.get(), std::placeholders::_1)); } } @@ -5018,41 +5026,6 @@ class LeAudioClientImpl : public LeAudioClient { } } - void updateOffloaderIfNeeded(LeAudioDeviceGroup* group) { - if (CodecManager::GetInstance()->GetCodecLocation() != - le_audio::types::CodecLocation::ADSP) { - return; - } - - LOG_INFO("Group %p, group_id %d", group, group->group_id_); - - const auto* stream_conf = &group->stream_conf; - - if (stream_conf->offloader_config.sink.has_changed || - stream_conf->offloader_config.sink.is_initial) { - LOG_INFO("Update sink offloader streams"); - uint16_t remote_delay_ms = - group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink); - CodecManager::GetInstance()->UpdateActiveSourceAudioConfig( - *stream_conf, remote_delay_ms, - std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal, - le_audio_source_hal_client_.get(), std::placeholders::_1)); - group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSink); - } - - if (stream_conf->offloader_config.source.has_changed || - stream_conf->offloader_config.source.is_initial) { - LOG_INFO("Update source offloader streams"); - uint16_t remote_delay_ms = - group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource); - CodecManager::GetInstance()->UpdateActiveSinkAudioConfig( - *stream_conf, remote_delay_ms, - std::bind(&LeAudioSinkAudioHalClient::UpdateAudioConfigToHal, - le_audio_sink_hal_client_.get(), std::placeholders::_1)); - group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSource); - } - } - void NotifyUpperLayerGroupTurnedIdleDuringCall(int group_id) { if (!osi_property_get_bool(kNotifyUpperLayerAboutGroupBeingInIdleDuringCall, false)) { @@ -5118,7 +5091,16 @@ class LeAudioClientImpl : public LeAudioClient { } if (group) { - updateOffloaderIfNeeded(group); + BidirectionalPair<uint16_t> delays_pair = { + .sink = + group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink), + .source = group->GetRemoteDelay( + le_audio::types::kLeAudioDirectionSource)}; + CodecManager::GetInstance()->UpdateActiveAudioConfig( + group->stream_conf.stream_params, delays_pair, + std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal, + le_audio_source_hal_client_.get(), + std::placeholders::_1)); if (reconnection_mode_ == BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) { group->AddToAllowListNotConnectedGroupMembers(gatt_if_); @@ -5228,6 +5210,17 @@ class LeAudioClientImpl : public LeAudioClient { } } + void OnUpdatedCisConfiguration(int group_id, uint8_t direction) { + LeAudioDeviceGroup* group = aseGroups_.FindById(group_id); + if (!group) { + LOG_ERROR("Invalid group_id: %d", group_id); + return; + } + CodecManager::GetInstance()->UpdateCisConfiguration( + group->cises_, group->stream_conf.stream_params.get(direction), + direction); + } + private: tGATT_IF gatt_if_; bluetooth::le_audio::LeAudioClientCallbacks* callbacks_; @@ -5445,6 +5438,10 @@ class CallbacksImpl : public LeAudioGroupStateMachine::Callbacks { void OnStateTransitionTimeout(int group_id) override { if (instance) instance->OnLeAudioDeviceSetStateTimeout(group_id); } + + void OnUpdatedCisConfiguration(int group_id, uint8_t direction) { + if (instance) instance->OnUpdatedCisConfiguration(group_id, direction); + } }; CallbacksImpl stateMachineCallbacksImpl; diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc index dfe11ab401cd882d5d3602a82485000f32b6c1e4..0e13b12a4d4eb26caee4e6874079baa10252bdbf 100644 --- a/system/bta/le_audio/codec_manager.cc +++ b/system/bta/le_audio/codec_manager.cc @@ -39,9 +39,24 @@ using le_audio::set_configurations::AudioSetConfiguration; using le_audio::set_configurations::AudioSetConfigurations; using le_audio::set_configurations::SetConfiguration; +typedef struct offloader_stream_maps { + std::vector<le_audio::stream_map_info> streams_map_target; + std::vector<le_audio::stream_map_info> streams_map_current; + bool has_changed; + bool is_initial; +} offloader_stream_maps_t; } // namespace namespace le_audio { +template <> +offloader_stream_maps_t& types::BidirectionalPair<offloader_stream_maps_t>::get( + uint8_t direction) { + ASSERT_LOG(direction < types::kLeAudioDirectionBoth, + "Unsupported complex direction. Reference to a single complex" + " direction value is not supported."); + return (direction == types::kLeAudioDirectionSink) ? sink : source; +} + // The mapping for sampling rate, frame duration, and the QoS config static std::unordered_map< int, std::unordered_map<int, le_audio::broadcaster::BroadcastQosConfig>> @@ -112,62 +127,43 @@ struct codec_manager_impl { } CodecLocation GetCodecLocation(void) const { return codec_location_; } - void UpdateActiveSourceAudioConfig( - const le_audio::stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver) { - if (stream_conf.stream_params.sink.stream_locations.empty()) return; - - if (stream_conf.offloader_config.sink.is_initial || - LeAudioHalVerifier::SupportsStreamActiveApi()) { - sink_config.stream_map = - stream_conf.offloader_config.sink.streams_target_allocation; - } else { - sink_config.stream_map = - stream_conf.offloader_config.sink.streams_current_allocation; + void UpdateActiveAudioConfig( + const types::BidirectionalPair<stream_parameters>& stream_params, + types::BidirectionalPair<uint16_t> delays_ms, + std::function<void(const offload_config& config)> update_receiver) { + if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) { + return; } - // TODO: set the default value 16 for now, would change it if we support - // mode bits_per_sample - sink_config.bits_per_sample = 16; - sink_config.sampling_rate = - stream_conf.stream_params.sink.sample_frequency_hz; - sink_config.frame_duration = - stream_conf.stream_params.sink.frame_duration_us; - sink_config.octets_per_frame = - stream_conf.stream_params.sink.octets_per_codec_frame; - sink_config.blocks_per_sdu = - stream_conf.stream_params.sink.codec_frames_blocks_per_sdu; - sink_config.peer_delay_ms = delay_ms; - update_receiver(sink_config); - } - void UpdateActiveSinkAudioConfig( - const le_audio::stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver) { - if (stream_conf.stream_params.source.stream_locations.empty()) return; - - if (stream_conf.offloader_config.source.is_initial || - LeAudioHalVerifier::SupportsStreamActiveApi()) { - source_config.stream_map = - stream_conf.offloader_config.source.streams_target_allocation; - } else { - source_config.stream_map = - stream_conf.offloader_config.source.streams_current_allocation; + for (auto direction : {le_audio::types::kLeAudioDirectionSink, + le_audio::types::kLeAudioDirectionSource}) { + auto& stream_map = offloader_stream_maps.get(direction); + if (!stream_map.has_changed && !stream_map.is_initial) { + continue; + } + if (stream_params.get(direction).stream_locations.empty()) { + continue; + } + + le_audio::offload_config unicast_cfg = { + .stream_map = (stream_map.is_initial || + LeAudioHalVerifier::SupportsStreamActiveApi()) + ? stream_map.streams_map_target + : stream_map.streams_map_current, + // TODO: set the default value 16 for now, would change it if we + // support mode bits_per_sample + .bits_per_sample = 16, + .sampling_rate = stream_params.get(direction).sample_frequency_hz, + .frame_duration = stream_params.get(direction).frame_duration_us, + .octets_per_frame = + stream_params.get(direction).octets_per_codec_frame, + .blocks_per_sdu = + stream_params.get(direction).codec_frames_blocks_per_sdu, + .peer_delay_ms = delays_ms.get(direction), + }; + update_receiver(unicast_cfg); + stream_map.is_initial = false; } - // TODO: set the default value 16 for now, would change it if we support - // mode bits_per_sample - source_config.bits_per_sample = 16; - source_config.sampling_rate = - stream_conf.stream_params.source.sample_frequency_hz; - source_config.frame_duration = - stream_conf.stream_params.source.frame_duration_us; - source_config.octets_per_frame = - stream_conf.stream_params.source.octets_per_codec_frame; - source_config.blocks_per_sdu = - stream_conf.stream_params.source.codec_frames_blocks_per_sdu; - source_config.peer_delay_ms = delay_ms; - update_receiver(source_config); } const AudioSetConfigurations* GetOffloadCodecConfig( @@ -268,6 +264,119 @@ struct codec_manager_impl { update_receiver(broadcast_config); } + void ClearCisConfiguration(uint8_t direction) { + if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) { + return; + } + + auto& stream_map = offloader_stream_maps.get(direction); + stream_map.streams_map_target.clear(); + stream_map.streams_map_current.clear(); + } + + static uint32_t AdjustAllocationForOffloader(uint32_t allocation) { + if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) && + (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) { + return codec_spec_conf::kLeAudioLocationStereo; + } + if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) { + return codec_spec_conf::kLeAudioLocationFrontLeft; + } + if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) { + return codec_spec_conf::kLeAudioLocationFrontRight; + } + return 0; + } + + void UpdateCisConfiguration(const std::vector<struct types::cis>& cises, + const stream_parameters& stream_params, + uint8_t direction) { + if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) { + return; + } + + auto available_allocations = + AdjustAllocationForOffloader(stream_params.audio_channel_allocation); + if (available_allocations == 0) { + LOG_ERROR("There is no CIS connected"); + return; + } + + auto& stream_map = offloader_stream_maps.get(direction); + if (stream_map.streams_map_target.empty()) { + stream_map.is_initial = true; + } else if (stream_map.is_initial || + LeAudioHalVerifier::SupportsStreamActiveApi()) { + /* As multiple CISes phone call case, the target_allocation already have + * the previous data, but the is_initial flag not be cleared. We need to + * clear here to avoid make duplicated target allocation stream map. */ + stream_map.streams_map_target.clear(); + } + + stream_map.streams_map_current.clear(); + stream_map.has_changed = true; + bool all_cises_connected = + (available_allocations == codec_spec_conf::kLeAudioLocationStereo); + + /* If all the cises are connected as stream started, reset changed_flag that + * the bt stack wouldn't send another audio configuration for the connection + * status. */ + if (stream_map.is_initial && all_cises_connected) { + stream_map.has_changed = false; + } + + const std::string tag = types::BidirectionalPair<std::string>( + {.sink = "Sink", .source = "Source"}) + .get(direction); + + constexpr types::BidirectionalPair<types::CisType> cis_types = { + .sink = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SINK, + .source = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE}; + auto cis_type = cis_types.get(direction); + + for (auto const& cis_entry : cises) { + if ((cis_entry.type == types::CisType::CIS_TYPE_BIDIRECTIONAL || + cis_entry.type == cis_type) && + cis_entry.conn_handle != 0) { + uint32_t target_allocation = 0; + uint32_t current_allocation = 0; + bool is_active = false; + for (const auto& s : stream_params.stream_locations) { + if (s.first == cis_entry.conn_handle) { + is_active = true; + target_allocation = AdjustAllocationForOffloader(s.second); + current_allocation = target_allocation; + if (!all_cises_connected) { + /* Tell offloader to mix on this CIS.*/ + current_allocation = codec_spec_conf::kLeAudioLocationStereo; + } + break; + } + } + + if (target_allocation == 0) { + /* Take missing allocation for that one .*/ + target_allocation = + codec_spec_conf::kLeAudioLocationStereo & ~available_allocations; + } + + LOG_INFO( + "%s: Cis handle 0x%04x, target allocation 0x%08x, current " + "allocation 0x%08x, active: %d", + tag.c_str(), cis_entry.conn_handle, target_allocation, + current_allocation, is_active); + + if (stream_map.is_initial || + LeAudioHalVerifier::SupportsStreamActiveApi()) { + stream_map.streams_map_target.emplace_back(stream_map_info( + cis_entry.conn_handle, target_allocation, is_active)); + } + stream_map.streams_map_current.emplace_back(stream_map_info( + cis_entry.conn_handle, current_allocation, is_active)); + } + } + } + private: void SetCodecLocation(CodecLocation location) { if (offload_enable_ == false) return; @@ -365,7 +474,8 @@ struct codec_manager_impl { std::unordered_set<uint8_t> offload_preference_set; if (AudioSetConfigurationProvider::Get() == nullptr) { - LOG(ERROR) << __func__ << " Audio set configuration provider is not available."; + LOG(ERROR) << __func__ + << " Audio set configuration provider is not available."; return; } @@ -405,8 +515,7 @@ struct codec_manager_impl { CodecLocation codec_location_ = CodecLocation::HOST; bool offload_enable_ = false; - le_audio::offload_config sink_config; - le_audio::offload_config source_config; + types::BidirectionalPair<offloader_stream_maps_t> offloader_stream_maps; std::vector<le_audio::broadcast_offload_config> supported_broadcast_config; std::unordered_map<types::LeAudioContextType, AudioSetConfigurations> context_type_offload_config_map_; @@ -456,22 +565,13 @@ types::CodecLocation CodecManager::GetCodecLocation(void) const { return pimpl_->codec_manager_impl_->GetCodecLocation(); } -void CodecManager::UpdateActiveSourceAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver) { +void CodecManager::UpdateActiveAudioConfig( + const types::BidirectionalPair<stream_parameters>& stream_params, + types::BidirectionalPair<uint16_t> delays_ms, + std::function<void(const offload_config& config)> update_receiver) { if (pimpl_->IsRunning()) - pimpl_->codec_manager_impl_->UpdateActiveSourceAudioConfig( - stream_conf, delay_ms, update_receiver); -} - -void CodecManager::UpdateActiveSinkAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver) { - if (pimpl_->IsRunning()) - pimpl_->codec_manager_impl_->UpdateActiveSinkAudioConfig( - stream_conf, delay_ms, update_receiver); + pimpl_->codec_manager_impl_->UpdateActiveAudioConfig( + stream_params, delays_ms, update_receiver); } const AudioSetConfigurations* CodecManager::GetOffloadCodecConfig( @@ -502,4 +602,19 @@ void CodecManager::UpdateBroadcastConnHandle( } } +void CodecManager::UpdateCisConfiguration( + const std::vector<struct types::cis>& cises, + const stream_parameters& stream_params, uint8_t direction) { + if (pimpl_->IsRunning()) { + return pimpl_->codec_manager_impl_->UpdateCisConfiguration( + cises, stream_params, direction); + } +} + +void CodecManager::ClearCisConfiguration(uint8_t direction) { + if (pimpl_->IsRunning()) { + return pimpl_->codec_manager_impl_->ClearCisConfiguration(direction); + } +} + } // namespace le_audio diff --git a/system/bta/le_audio/codec_manager.h b/system/bta/le_audio/codec_manager.h index f79fb618da07ef97ef78e7926e432b5b5dd18db0..1159735725a847a745dd4cff13b5cc470992f32e 100644 --- a/system/bta/le_audio/codec_manager.h +++ b/system/bta/le_audio/codec_manager.h @@ -21,6 +21,17 @@ namespace le_audio { +struct stream_map_info { + stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation, + bool is_stream_active) + : stream_handle(stream_handle), + audio_channel_allocation(audio_channel_allocation), + is_stream_active(is_stream_active) {} + uint16_t stream_handle; + uint32_t audio_channel_allocation; + bool is_stream_active; +}; + struct offload_config { std::vector<stream_map_info> stream_map; uint8_t bits_per_sample; @@ -45,7 +56,6 @@ struct broadcast_offload_config { class CodecManager { public: - CodecManager(); virtual ~CodecManager() = default; static CodecManager* GetInstance(void) { static CodecManager* instance = new CodecManager(); @@ -55,14 +65,14 @@ class CodecManager { offloading_preference); void Stop(void); virtual types::CodecLocation GetCodecLocation(void) const; - virtual void UpdateActiveSourceAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver); - virtual void UpdateActiveSinkAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver); + virtual void UpdateCisConfiguration( + const std::vector<struct types::cis>& cises, + const stream_parameters& stream_params, uint8_t direction); + virtual void ClearCisConfiguration(uint8_t direction); + virtual void UpdateActiveAudioConfig( + const types::BidirectionalPair<stream_parameters>& stream_params, + types::BidirectionalPair<uint16_t> delays_ms, + std::function<void(const offload_config& config)> update_receiver); virtual const ::le_audio::set_configurations::AudioSetConfigurations* GetOffloadCodecConfig(::le_audio::types::LeAudioContextType ctx_type); virtual const ::le_audio::broadcast_offload_config* @@ -73,6 +83,7 @@ class CodecManager { update_receiver); private: + CodecManager(); struct impl; std::unique_ptr<impl> pimpl_; }; diff --git a/system/bta/le_audio/codec_manager_test.cc b/system/bta/le_audio/codec_manager_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..6d04f579170531bb55daa868963e29618c99fdf0 --- /dev/null +++ b/system/bta/le_audio/codec_manager_test.cc @@ -0,0 +1,316 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codec_manager.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "btm_api_mock.h" +#include "gd/common/init_flags.h" +#include "mock_controller.h" +#include "osi/include/properties.h" +#include "stack/acl/acl.h" + +using ::testing::_; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::Test; + +using bluetooth::hci::iso_manager::kIsoDataPathHci; +using bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault; +using le_audio::set_configurations::AudioSetConfiguration; +using le_audio::types::CodecLocation; +using le_audio::types::kLeAudioDirectionSink; +using le_audio::types::kLeAudioDirectionSource; + +void osi_property_set_bool(const char* key, bool value); + +std::vector<AudioSetConfiguration> offload_capabilities(0); + +const char* test_flags[] = { + "INIT_default_log_level_str=LOG_VERBOSE", +}; + +namespace bluetooth { +namespace audio { +namespace le_audio { +std::vector<AudioSetConfiguration> get_offload_capabilities() { + return offload_capabilities; +} +} // namespace le_audio +} // namespace audio +} // namespace bluetooth + +namespace le_audio { +namespace { + +static constexpr char kPropLeAudioOffloadSupported[] = + "ro.bluetooth.leaudio_offload.supported"; +static constexpr char kPropLeAudioOffloadDisabled[] = + "persist.bluetooth.leaudio_offload.disabled"; + +class CodecManagerTestBase : public Test { + public: + virtual void SetUp() override { + bluetooth::common::InitFlags::Load(test_flags); + + offload_capabilities.clear(); + + ON_CALL(controller_interface, SupportsBleIsochronousBroadcaster) + .WillByDefault(Return(true)); + ON_CALL(controller_interface, SupportsConfigureDataPath) + .WillByDefault(Return(true)); + + controller::SetMockControllerInterface(&controller_interface); + bluetooth::manager::SetMockBtmInterface(&btm_interface); + + codec_manager = CodecManager::GetInstance(); + } + + virtual void TearDown() override { + codec_manager->Stop(); + + bluetooth::manager::SetMockBtmInterface(nullptr); + controller::SetMockControllerInterface(nullptr); + } + + NiceMock<bluetooth::manager::MockBtmInterface> btm_interface; + NiceMock<controller::MockControllerInterface> controller_interface; + CodecManager* codec_manager; +}; + +/*----------------- ADSP codec manager tests ------------------*/ +class CodecManagerTestAdsp : public CodecManagerTestBase { + public: + virtual void SetUp() override { + // Enable the HW offloader + osi_property_set_bool(kPropLeAudioOffloadSupported, true); + osi_property_set_bool(kPropLeAudioOffloadDisabled, false); + + CodecManagerTestBase::SetUp(); + } +}; + +TEST_F(CodecManagerTestAdsp, test_init) { + ASSERT_EQ(codec_manager, CodecManager::GetInstance()); +} + +TEST_F(CodecManagerTestAdsp, test_start) { + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER, + kIsoDataPathPlatformDefault, _)) + .Times(1); + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST, + kIsoDataPathPlatformDefault, _)) + .Times(1); + + const std::vector<bluetooth::le_audio::btle_audio_codec_config_t> + offloading_preference(0); + codec_manager->Start(offloading_preference); + Mock::VerifyAndClearExpectations(&btm_interface); + + ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::ADSP); + + // Verify data path is reset on Stop() + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER, + kIsoDataPathHci, _)) + .Times(1); + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST, + kIsoDataPathHci, _)) + .Times(1); +} + +TEST_F(CodecManagerTestAdsp, testStreamConfigurationAdspDownMix) { + const std::vector<bluetooth::le_audio::btle_audio_codec_config_t> + offloading_preference(0); + codec_manager->Start(offloading_preference); + + // Current CIS configuration for two earbuds + std::vector<struct types::cis> cises{ + { + .id = 0x00, + .type = types::CisType::CIS_TYPE_BIDIRECTIONAL, + .conn_handle = 96, + }, + { + .id = 0x01, + .type = types::CisType::CIS_TYPE_BIDIRECTIONAL, + .conn_handle = 97, + }, + }; + + // Stream parameters + types::BidirectionalPair<stream_parameters> stream_params{ + .sink = + { + .audio_channel_allocation = + codec_spec_conf::kLeAudioLocationFrontLeft, + .sample_frequency_hz = 16000, + .frame_duration_us = 10000, + .octets_per_codec_frame = 40, + .codec_frames_blocks_per_sdu = 1, + .num_of_channels = 1, + .num_of_devices = 1, + .stream_locations = + { + std::pair<uint16_t, uint32_t>{ + 97 /*conn_handle*/, + codec_spec_conf::kLeAudioLocationFrontLeft}, + }, + }, + .source = + { + .audio_channel_allocation = + codec_spec_conf::kLeAudioLocationFrontLeft, + .sample_frequency_hz = 16000, + .frame_duration_us = 10000, + .octets_per_codec_frame = 40, + .codec_frames_blocks_per_sdu = 1, + .num_of_channels = 1, + .num_of_devices = 1, + { + std::pair<uint16_t, uint32_t>{ + 97 /*conn_handle*/, + codec_spec_conf::kLeAudioLocationBackLeft}, + }, + }, + }; + + codec_manager->UpdateCisConfiguration(cises, stream_params.sink, + kLeAudioDirectionSink); + codec_manager->UpdateCisConfiguration(cises, stream_params.source, + kLeAudioDirectionSource); + + // Verify the offloader config content + std::vector<offload_config> out_offload_configs; + codec_manager->UpdateActiveAudioConfig( + stream_params, {.sink = 44, .source = 44}, + [&out_offload_configs](const offload_config& config) { + out_offload_configs.push_back(config); + }); + + // Expect the same configuration for sink and source + ASSERT_EQ(2lu, out_offload_configs.size()); + for (const auto& config : out_offload_configs) { + uint32_t allocation = 0; + ASSERT_EQ(2lu, config.stream_map.size()); + for (const auto& info : config.stream_map) { + if (info.stream_handle == 96) { + ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontRight, + info.audio_channel_allocation); + // The disconnected should be inactive + ASSERT_FALSE(info.is_stream_active); + + } else if (info.stream_handle == 97) { + ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontLeft, + info.audio_channel_allocation); + // The connected should be active + ASSERT_TRUE(info.is_stream_active); + + } else { + ASSERT_EQ(97, info.stream_handle); + } + allocation |= info.audio_channel_allocation; + } + + ASSERT_EQ(16, config.bits_per_sample); + ASSERT_EQ(16000u, config.sampling_rate); + ASSERT_EQ(10000u, config.frame_duration); + ASSERT_EQ(40u, config.octets_per_frame); + ASSERT_EQ(1, config.blocks_per_sdu); + ASSERT_EQ(44, config.peer_delay_ms); + ASSERT_EQ(codec_spec_conf::kLeAudioLocationStereo, allocation); + } + + // Clear the CIS configuration map (no active CISes). + codec_manager->ClearCisConfiguration(kLeAudioDirectionSink); + codec_manager->ClearCisConfiguration(kLeAudioDirectionSource); + out_offload_configs.clear(); + codec_manager->UpdateActiveAudioConfig( + stream_params, {.sink = 44, .source = 44}, + [&out_offload_configs](const offload_config& config) { + out_offload_configs.push_back(config); + }); + + // Expect sink & source configurations with empty CIS channel allocation map. + ASSERT_EQ(2lu, out_offload_configs.size()); + for (const auto& config : out_offload_configs) { + ASSERT_EQ(0lu, config.stream_map.size()); + ASSERT_EQ(16, config.bits_per_sample); + ASSERT_EQ(16000u, config.sampling_rate); + ASSERT_EQ(10000u, config.frame_duration); + ASSERT_EQ(40u, config.octets_per_frame); + ASSERT_EQ(1, config.blocks_per_sdu); + ASSERT_EQ(44, config.peer_delay_ms); + } +} + +// TODO: Add the unit tests for: +// GetOffloadCodecConfig +// GetBroadcastOffloadConfig +// UpdateBroadcastConnHandle + +/*----------------- HOST codec manager tests ------------------*/ +class CodecManagerTestHost : public CodecManagerTestBase { + public: + virtual void SetUp() override { + // Enable the HW offloader + osi_property_set_bool(kPropLeAudioOffloadSupported, false); + osi_property_set_bool(kPropLeAudioOffloadDisabled, false); + + CodecManagerTestBase::SetUp(); + } +}; + +TEST_F(CodecManagerTestHost, test_init) { + ASSERT_EQ(codec_manager, CodecManager::GetInstance()); +} + +TEST_F(CodecManagerTestHost, test_start) { + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER, + kIsoDataPathPlatformDefault, _)) + .Times(0); + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST, + kIsoDataPathPlatformDefault, _)) + .Times(0); + + const std::vector<bluetooth::le_audio::btle_audio_codec_config_t> + offloading_preference(0); + codec_manager->Start(offloading_preference); + Mock::VerifyAndClearExpectations(&btm_interface); + + ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::HOST); + + // Verify data path is NOT reset on Stop() for the Host encoding session + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER, + kIsoDataPathHci, _)) + .Times(0); + EXPECT_CALL(btm_interface, + ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST, + kIsoDataPathHci, _)) + .Times(0); +} + +} // namespace +} // namespace le_audio diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index c13d2855f249f4936895a21e0e452b65f333786d..0feb62bfc3438aac7aac4e500e1f3e279077d954 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -59,18 +59,6 @@ using le_audio::types::LeAudioContextType; using le_audio::types::LeAudioLc3Config; namespace le_audio { -namespace types { -template struct BidirectionalPair<offloader_stream_config>; -template <> -offloader_stream_config& types::BidirectionalPair<offloader_stream_config>::get( - uint8_t direction) { - ASSERT_LOG(direction < types::kLeAudioDirectionBoth, - "Unsupported complex direction. Reference to a single complex" - " direction value is not supported."); - return (direction == types::kLeAudioDirectionSink) ? sink : source; -} -} // namespace types - std::ostream& operator<<(std::ostream& os, const DeviceConnectState& state) { const char* char_value_ = "UNKNOWN"; @@ -182,9 +170,7 @@ void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) { stream_params.octets_per_codec_frame = 0; stream_params.frame_duration_us = 0; - auto& offload_config = stream_conf.offloader_config.get(direction); - offload_config.streams_target_allocation.clear(); - offload_config.streams_current_allocation.clear(); + CodecManager::GetInstance()->ClearCisConfiguration(direction); } void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) { @@ -201,9 +187,7 @@ void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) { stream_params.octets_per_codec_frame = 0; stream_params.frame_duration_us = 0; - auto& offload_config = stream_conf.offloader_config.get(direction); - offload_config.streams_target_allocation.clear(); - offload_config.streams_current_allocation.clear(); + CodecManager::GetInstance()->ClearCisConfiguration(direction); } void LeAudioDeviceGroup::CigClearCis(void) { @@ -1927,14 +1911,6 @@ bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) const { return (iter != source_stream_locations.end()); } -void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) { - if (direction == le_audio::types::kLeAudioDirectionSource) { - stream_conf.offloader_config.source.is_initial = false; - } else { - stream_conf.offloader_config.sink.is_initial = false; - } -} - void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded( LeAudioDevice* leAudioDevice, uint16_t cis_conn_hdl) { LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl); @@ -1985,126 +1961,23 @@ void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded( ClearSourcesFromConfiguration(); } - /* Update offloader streams if needed */ + /* Update CodecManager CIS configuration */ if (old_sink_channels > stream_conf.stream_params.sink.num_of_channels) { - CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSink); + CodecManager::GetInstance()->UpdateCisConfiguration( + cises_, + stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSink), + le_audio::types::kLeAudioDirectionSink); } if (old_source_channels > stream_conf.stream_params.source.num_of_channels) { - CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSource); + CodecManager::GetInstance()->UpdateCisConfiguration( + cises_, + stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSource), + le_audio::types::kLeAudioDirectionSource); } CigUnassignCis(leAudioDevice); } -void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) { - if (CodecManager::GetInstance()->GetCodecLocation() != - le_audio::types::CodecLocation::ADSP) { - return; - } - - CisType cis_type; - std::vector<std::pair<uint16_t, uint32_t>>* streams; - std::vector<stream_map_info>* offloader_streams_target_allocation; - std::vector<stream_map_info>* offloader_streams_current_allocation; - std::string tag; - uint32_t available_allocations = 0; - bool* changed_flag; - bool* is_initial; - if (direction == le_audio::types::kLeAudioDirectionSource) { - changed_flag = &stream_conf.offloader_config.source.has_changed; - is_initial = &stream_conf.offloader_config.source.is_initial; - cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE; - streams = &stream_conf.stream_params.source.stream_locations; - offloader_streams_target_allocation = - &stream_conf.offloader_config.source.streams_target_allocation; - offloader_streams_current_allocation = - &stream_conf.offloader_config.source.streams_current_allocation; - tag = "Source"; - available_allocations = AdjustAllocationForOffloader( - stream_conf.stream_params.source.audio_channel_allocation); - } else { - changed_flag = &stream_conf.offloader_config.sink.has_changed; - is_initial = &stream_conf.offloader_config.sink.is_initial; - cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK; - streams = &stream_conf.stream_params.sink.stream_locations; - offloader_streams_target_allocation = - &stream_conf.offloader_config.sink.streams_target_allocation; - offloader_streams_current_allocation = - &stream_conf.offloader_config.sink.streams_current_allocation; - tag = "Sink"; - available_allocations = AdjustAllocationForOffloader( - stream_conf.stream_params.sink.audio_channel_allocation); - } - - if (available_allocations == 0) { - LOG_ERROR("There is no CIS connected"); - return; - } - - if (offloader_streams_target_allocation->size() == 0) { - *is_initial = true; - } else if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) { - // As multiple CISes phone call case, the target_allocation already have the - // previous data, but the is_initial flag not be cleared. We need to clear - // here to avoid make duplicated target allocation stream map. - offloader_streams_target_allocation->clear(); - } - - offloader_streams_current_allocation->clear(); - *changed_flag = true; - bool not_all_cises_connected = false; - if (available_allocations != codec_spec_conf::kLeAudioLocationStereo) { - not_all_cises_connected = true; - } - - /* If the all cises are connected as stream started, reset changed_flag that - * the bt stack wouldn't send another audio configuration for the connection - * status */ - if (*is_initial && !not_all_cises_connected) { - *changed_flag = false; - } - for (auto const& cis_entry : cises_) { - if ((cis_entry.type == CisType::CIS_TYPE_BIDIRECTIONAL || - cis_entry.type == cis_type) && - cis_entry.conn_handle != 0) { - uint32_t target_allocation = 0; - uint32_t current_allocation = 0; - bool is_active = false; - for (const auto& s : *streams) { - if (s.first == cis_entry.conn_handle) { - is_active = true; - target_allocation = AdjustAllocationForOffloader(s.second); - current_allocation = target_allocation; - if (not_all_cises_connected) { - /* Tell offloader to mix on this CIS.*/ - current_allocation = codec_spec_conf::kLeAudioLocationStereo; - } - break; - } - } - - if (target_allocation == 0) { - /* Take missing allocation for that one .*/ - target_allocation = - codec_spec_conf::kLeAudioLocationStereo & ~available_allocations; - } - - LOG_INFO( - "%s: Cis handle 0x%04x, target allocation 0x%08x, current " - "allocation 0x%08x, active: %d", - tag.c_str(), cis_entry.conn_handle, target_allocation, - current_allocation, is_active); - - if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) { - offloader_streams_target_allocation->emplace_back(stream_map_info( - cis_entry.conn_handle, target_allocation, is_active)); - } - offloader_streams_current_allocation->emplace_back(stream_map_info( - cis_entry.conn_handle, current_allocation, is_active)); - } - } -} - bool LeAudioDeviceGroup::IsPendingConfiguration(void) const { return stream_conf.pending_configuration; } diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h index 358d470daa1bb8463ac576f8cb9ecbe76d13a0f8..e1029e7d8b30aec453faf83b76a3ce3c8ed16535 100644 --- a/system/bta/le_audio/devices.h +++ b/system/bta/le_audio/devices.h @@ -416,8 +416,6 @@ class LeAudioDeviceGroup { bool IsMetadataChanged( const types::BidirectionalPair<types::AudioContexts>& context_types, const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) const; - void CreateStreamVectorForOffloader(uint8_t direction); - void StreamOffloaderUpdated(uint8_t direction); bool IsConfiguredForContext(types::LeAudioContextType context_type) const; void RemoveCisFromStreamIfNeeded(LeAudioDevice* leAudioDevice, uint16_t cis_conn_hdl); diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc index 283531a6f4e1a6e1194c117b5a98830f39ea13d5..f068ab9f55046f242852bb2aa0ce142c8e59b990 100644 --- a/system/bta/le_audio/le_audio_types.cc +++ b/system/bta/le_audio/le_audio_types.cc @@ -769,21 +769,6 @@ uint8_t GetMaxCodecFramesPerSduFromPac(const acs_ac_record* pac) { return 1; } -uint32_t AdjustAllocationForOffloader(uint32_t allocation) { - if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) && - (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) { - return codec_spec_conf::kLeAudioLocationStereo; - } - if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) { - return codec_spec_conf::kLeAudioLocationFrontLeft; - } - - if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) { - return codec_spec_conf::kLeAudioLocationFrontRight; - } - return 0; -} - namespace types { std::ostream& operator<<(std::ostream& os, const AudioStreamDataPathState& state) { @@ -945,9 +930,13 @@ AudioLocations get_bidirectional(BidirectionalPair<AudioLocations> bidir) { template struct BidirectionalPair<AudioContexts>; template struct BidirectionalPair<AudioLocations>; -template struct BidirectionalPair<std::vector<uint8_t>>; +template struct BidirectionalPair<CisType>; template struct BidirectionalPair<ase*>; +template struct BidirectionalPair<std::string>; +template struct BidirectionalPair<std::vector<uint8_t>>; +template struct BidirectionalPair<stream_configuration>; template struct BidirectionalPair<stream_parameters>; +template struct BidirectionalPair<uint16_t>; } // namespace types } // namespace le_audio diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h index c6b2c2aa5a7cd819bdc4a328bb2d0b897895693e..d59d12c33c91a4386148b597349e4c1e10eabbad 100644 --- a/system/bta/le_audio/le_audio_types.h +++ b/system/bta/le_audio/le_audio_types.h @@ -837,26 +837,6 @@ uint8_t get_num_of_devices_in_configuration( const AudioSetConfiguration* audio_set_configuration); } // namespace set_configurations -struct stream_map_info { - stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation, - bool is_stream_active) - : stream_handle(stream_handle), - audio_channel_allocation(audio_channel_allocation), - is_stream_active(is_stream_active) {} - uint16_t stream_handle; - uint32_t audio_channel_allocation; - bool is_stream_active; -}; - -struct offloader_stream_config { - /* cis_handle, target allocation, stream active state */ - std::vector<stream_map_info> streams_target_allocation; - /* cis_handle, current allocation, stream active state */ - std::vector<stream_map_info> streams_current_allocation; - bool has_changed; - bool is_initial; -}; - struct stream_parameters { /* For now we have always same frequency for all the channels */ uint32_t sample_frequency_hz; @@ -882,9 +862,6 @@ struct stream_configuration { /* Sink & Source configuration */ types::BidirectionalPair<stream_parameters> stream_params; - /* TODO: Make this struct free from the offloader specific data */ - types::BidirectionalPair<offloader_stream_config> offloader_config; - bool is_active; }; @@ -893,5 +870,4 @@ void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata, void AppendMetadataLtvEntryForStreamingContext( std::vector<uint8_t>& metadata, types::AudioContexts context_type); uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record); -uint32_t AdjustAllocationForOffloader(uint32_t allocation); } // namespace le_audio \ No newline at end of file diff --git a/system/bta/le_audio/mock_codec_manager.cc b/system/bta/le_audio/mock_codec_manager.cc index 2f8aaad427ba6454ef4ef799b0fa01d3bff67c0e..2e2818281ef671a09c3f71140e37cceba77b4640 100644 --- a/system/bta/le_audio/mock_codec_manager.cc +++ b/system/bta/le_audio/mock_codec_manager.cc @@ -37,22 +37,14 @@ types::CodecLocation CodecManager::GetCodecLocation() const { return pimpl_->GetCodecLocation(); } -void CodecManager::UpdateActiveSourceAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, +void CodecManager::UpdateActiveAudioConfig( + const types::BidirectionalPair<stream_parameters>& stream_params, + types::BidirectionalPair<uint16_t> delays_ms, std::function<void(const ::le_audio::offload_config& config)> update_receiver) { if (pimpl_) - return pimpl_->UpdateActiveSourceAudioConfig(stream_conf, delay_ms, - update_receiver); -} - -void CodecManager::UpdateActiveSinkAudioConfig( - const stream_configuration& stream_conf, uint16_t delay_ms, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver) { - if (pimpl_) - return pimpl_->UpdateActiveSinkAudioConfig(stream_conf, delay_ms, - update_receiver); + return pimpl_->UpdateActiveAudioConfig(stream_params, delays_ms, + update_receiver); } const set_configurations::AudioSetConfigurations* @@ -99,6 +91,17 @@ void CodecManager::Stop() { mock_codec_manager_pimpl_ = nullptr; } +void CodecManager::UpdateCisConfiguration( + const std::vector<struct types::cis>& cises, + const stream_parameters& stream_params, uint8_t direction) { + if (pimpl_) + return pimpl_->UpdateCisConfiguration(cises, stream_params, direction); +} + +void CodecManager::ClearCisConfiguration(uint8_t direction) { + if (pimpl_) return pimpl_->ClearCisConfiguration(direction); +} + // CodecManager::~CodecManager() = default; } // namespace le_audio diff --git a/system/bta/le_audio/mock_codec_manager.h b/system/bta/le_audio/mock_codec_manager.h index d73132d6d72a6cc8cdfec2749827c0961ff2d2ef..09f2b0636a59db340e2efea028ad1eeb435b1f1d 100644 --- a/system/bta/le_audio/mock_codec_manager.h +++ b/system/bta/le_audio/mock_codec_manager.h @@ -31,14 +31,10 @@ class MockCodecManager { virtual ~MockCodecManager() = default; MOCK_METHOD((le_audio::types::CodecLocation), GetCodecLocation, (), (const)); - MOCK_METHOD((void), UpdateActiveSourceAudioConfig, - (const le_audio::stream_configuration& stream_conf, - uint16_t delay, - std::function<void(const ::le_audio::offload_config& config)> - update_receiver)); - MOCK_METHOD((void), UpdateActiveSinkAudioConfig, - (const le_audio::stream_configuration& stream_conf, - uint16_t delay, + MOCK_METHOD((void), UpdateActiveAudioConfig, + (const le_audio::types::BidirectionalPair< + le_audio::stream_parameters>& stream_params, + le_audio::types::BidirectionalPair<uint16_t> delays_ms, std::function<void(const ::le_audio::offload_config& config)> update_receiver)); MOCK_METHOD((le_audio::set_configurations::AudioSetConfigurations*), @@ -51,6 +47,12 @@ class MockCodecManager { (const std::vector<uint16_t>& conn_handle, std::function<void(const ::le_audio::broadcast_offload_config& config)> update_receiver)); + MOCK_METHOD((void), UpdateCisConfiguration, + (const std::vector<struct le_audio::types::cis>& cises, + const le_audio::stream_parameters& stream_params, + uint8_t direction), + (const)); + MOCK_METHOD((void), ClearCisConfiguration, (uint8_t direction)); MOCK_METHOD((void), Start, ()); MOCK_METHOD((void), Stop, ()); diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index 75e670ed7b009a074a08148f6e6b5bd97dd83adb..74e8b02698449c040d8b4171ec983049a3b7a5ee 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -759,7 +759,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->IsAnyDeviceConnected()) { /* * ACL of one of the device has been dropped. If number of CISes has - * changed notify upper layer so the offloader can be updated with CIS + * changed notify upper layer so the CodecManager can be updated with CIS * information. */ if (!group->HaveAllCisesDisconnected()) { @@ -769,7 +769,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) { /* We keep streaming but want others to let know user that it might - * be need to update offloader with new CIS configuration + * be need to update CodecManager with new CIS configuration */ state_machine_callbacks_->StatusReportCb( group->group_id_, GroupStreamStatus::STREAMING); @@ -1235,8 +1235,9 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { cis_conn_hdl, ase_audio_channel_allocation, params.num_of_devices, params.num_of_channels); - /* Update offloader streams */ - group->CreateStreamVectorForOffloader(ase->direction); + /* Update CodecManager stream configuration */ + state_machine_callbacks_->OnUpdatedCisConfiguration(group->group_id_, + ase->direction); } bool CigCreate(LeAudioDeviceGroup* group) { diff --git a/system/bta/le_audio/state_machine.h b/system/bta/le_audio/state_machine.h index 596df9d27dd891f8848de9765cfea0723e55878c..fa690455bbe6e32578b051e1fdbcef9f0d9c8ef3 100644 --- a/system/bta/le_audio/state_machine.h +++ b/system/bta/le_audio/state_machine.h @@ -37,6 +37,7 @@ class LeAudioGroupStateMachine { virtual void StatusReportCb( int group_id, bluetooth::le_audio::GroupStreamStatus status) = 0; virtual void OnStateTransitionTimeout(int group_id) = 0; + virtual void OnUpdatedCisConfiguration(int group_id, uint8_t direction) = 0; }; virtual ~LeAudioGroupStateMachine() = default; diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc index 3d6a742ed8c4fb7c9b21c86e5e490f3eb2aa880c..32a46782e8a959740eaaa6b1de04dd88fbd19e16 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -165,6 +165,8 @@ class MockLeAudioGroupStateMachineCallbacks (int group_id, bluetooth::le_audio::GroupStreamStatus status), (override)); MOCK_METHOD((void), OnStateTransitionTimeout, (int group_id), (override)); + MOCK_METHOD((void), OnUpdatedCisConfiguration, + (int group_id, uint8_t direction), (override)); }; class StateMachineTestBase : public Test { @@ -3438,8 +3440,17 @@ TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) { leaudio_group_id, context_type, num_devices, types::AudioContexts(kContextTypeConversational)); - /* Can be called for every context when fetching the configuration from the - * AudioSetConfigurationProvider. + EXPECT_CALL(mock_callbacks_, + OnUpdatedCisConfiguration(group->group_id_, + le_audio::types::kLeAudioDirectionSink)) + .Times(1); + EXPECT_CALL(mock_callbacks_, + OnUpdatedCisConfiguration( + group->group_id_, le_audio::types::kLeAudioDirectionSource)) + .Times(1); + + /* Can be called for every context when fetching the configuration from + * the AudioSetConfigurationProvider. */ EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1)); @@ -3459,46 +3470,11 @@ TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) { {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); - ASSERT_EQ(static_cast<int>(group->stream_conf.offloader_config.sink - .streams_target_allocation.size()), - 2); - ASSERT_EQ(static_cast<int>(group->stream_conf.offloader_config.source - .streams_target_allocation.size()), - 2); - // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); - uint32_t allocation = 0; - for (const auto& s : - group->stream_conf.offloader_config.sink.streams_target_allocation) { - allocation |= s.audio_channel_allocation; - ASSERT_FALSE(allocation == 0); - } - ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo); - - allocation = 0; - for (const auto& s : - group->stream_conf.offloader_config.source.streams_target_allocation) { - allocation |= s.audio_channel_allocation; - ASSERT_FALSE(allocation == 0); - } - ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo); - - for (const auto& s : - group->stream_conf.offloader_config.sink.streams_target_allocation) { - ASSERT_TRUE((s.audio_channel_allocation != 0) && - (s.audio_channel_allocation != - codec_spec_conf::kLeAudioLocationStereo)); - } - - for (const auto& s : - group->stream_conf.offloader_config.source.streams_target_allocation) { - ASSERT_TRUE((s.audio_channel_allocation != 0) && - (s.audio_channel_allocation != - codec_spec_conf::kLeAudioLocationStereo)); - } + // Note: The actual channel mixing is verified by the CodecManager unit tests. } static void InjectCisDisconnected(LeAudioDeviceGroup* group, diff --git a/system/bta/test/common/mock_controller.cc b/system/bta/test/common/mock_controller.cc index 7cf28651c80bfdb998781c6228ec7ebef305a93f..1aeb5527b21cce0b6d4e290f66442f427701f666 100644 --- a/system/bta/test/common/mock_controller.cc +++ b/system/bta/test/common/mock_controller.cc @@ -57,6 +57,11 @@ bool supports_ble_connected_isochronous_stream_peripheral(void) { ->SupportsBleConnectedIsochronousStreamPeripheral(); } +bool supports_configure_data_path(void) { + LOG_ASSERT(controller_interface) << "Mock controller not set!"; + return controller_interface->SupportsConfigureDataPath(); +} + const controller_t* controller_get_interface() { static controller_t* controller_instance = new controller_t(); @@ -69,6 +74,8 @@ const controller_t* controller_get_interface() { &supports_ble_connected_isochronous_stream_central; controller_instance->supports_ble_connected_isochronous_stream_peripheral = &supports_ble_connected_isochronous_stream_peripheral; + controller_instance->supports_configure_data_path = + &supports_configure_data_path; return controller_instance; } diff --git a/system/bta/test/common/mock_controller.h b/system/bta/test/common/mock_controller.h index 6d27a18e72c4832837107438a49c999584301d3f..39f4d15234f26f0aad782353b7a8d57bdc0776d3 100644 --- a/system/bta/test/common/mock_controller.h +++ b/system/bta/test/common/mock_controller.h @@ -30,6 +30,7 @@ class ControllerInterface { virtual bool SupportsBleConnectedIsochronousStreamPeripheral(void) = 0; virtual bool SupportsBleIsochronousBroadcaster(void) = 0; virtual bool SupportsBle2mPhy(void) = 0; + virtual bool SupportsConfigureDataPath(void) = 0; virtual ~ControllerInterface() = default; }; @@ -44,6 +45,7 @@ class MockControllerInterface : public ControllerInterface { (override)); MOCK_METHOD((bool), SupportsBleIsochronousBroadcaster, (), (override)); MOCK_METHOD((bool), SupportsBle2mPhy, (), (override)); + MOCK_METHOD((bool), SupportsConfigureDataPath, (), (override)); }; void SetMockControllerInterface(