diff --git a/system/bta/Android.bp b/system/bta/Android.bp index c03ba8c264e6f59e41a4104ffd2463399b9b2b18..5113ccf4b6ef951c1d4e3862f9cd7903a73b4570 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -102,13 +102,13 @@ cc_library_static { "le_audio/broadcaster/state_machine.cc", "le_audio/client.cc", "le_audio/client_parser.cc", + "le_audio/codec_interface.cc", "le_audio/codec_manager.cc", "le_audio/content_control_id_keeper.cc", "le_audio/devices.cc", "le_audio/hal_verifier.cc", "le_audio/le_audio_health_status.cc", "le_audio/le_audio_log_history.cc", - "le_audio/le_audio_set_configuration_provider.cc", "le_audio/le_audio_set_configuration_provider_json.cc", "le_audio/le_audio_types.cc", "le_audio/le_audio_utils.cc", @@ -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"], @@ -697,6 +768,7 @@ cc_test { "le_audio/le_audio_types.cc", "le_audio/le_audio_types_test.cc", "le_audio/metrics_collector_linux.cc", + "le_audio/mock_codec_interface.cc", "le_audio/mock_codec_manager.cc", "le_audio/mock_iso_manager.cc", "le_audio/state_machine.cc", @@ -775,6 +847,7 @@ cc_test { "le_audio/le_audio_utils.cc", "le_audio/metrics_collector.cc", "le_audio/metrics_collector_test.cc", + "le_audio/mock_codec_interface.cc", "le_audio/mock_codec_manager.cc", "le_audio/mock_iso_manager.cc", "le_audio/mock_state_machine.cc", @@ -805,7 +878,6 @@ cc_test { "libevent", "libflatbuffers-cpp", "libgmock", - "liblc3", "libosi", ], data: [ @@ -907,6 +979,7 @@ cc_test { "le_audio/broadcaster/state_machine.cc", "le_audio/broadcaster/state_machine_test.cc", "le_audio/le_audio_types.cc", + "le_audio/mock_codec_interface.cc", "le_audio/mock_codec_manager.cc", "le_audio/mock_iso_manager.cc", ], @@ -919,7 +992,6 @@ cc_test { "libbt-common", "libchrome", "libgmock", - "liblc3", ], sanitize: { cfi: true, @@ -964,6 +1036,7 @@ cc_test { "le_audio/le_audio_types.cc", "le_audio/le_audio_utils.cc", "le_audio/metrics_collector_linux.cc", + "le_audio/mock_codec_interface.cc", "le_audio/mock_codec_manager.cc", "le_audio/mock_iso_manager.cc", "test/common/mock_controller.cc", @@ -983,7 +1056,6 @@ cc_test { "libchrome", "libevent", "libgmock", - "liblc3", "libosi", ], sanitize: { diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc index fccbb91e7f22da12e119a235732ea9b8465e7655..175f79416a04c85ac299ade472815af1f6eb37eb 100644 --- a/system/bta/ag/bta_ag_cmd.cc +++ b/system/bta/ag/bta_ag_cmd.cc @@ -29,7 +29,7 @@ #include "bta/include/utl.h" #ifdef __ANDROID__ -#include "bta/le_audio/devices.h" +#include "bta_le_audio_api.h" #endif #include "device/include/interop.h" diff --git a/system/bta/include/bta_le_audio_api.h b/system/bta/include/bta_le_audio_api.h index 1d61bc3906fa7019290282d845c43ec3e9cdfe2e..ad7e7428d30ff93150ff7bb3554c54ac94deff93 100644 --- a/system/bta/include/bta_le_audio_api.h +++ b/system/bta/include/bta_le_audio_api.h @@ -42,7 +42,7 @@ class LeAudioClient { base::Closure initCb, base::Callback<bool()> hal_2_1_verifier, const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>& offloading_preference); - static void Cleanup(base::Callback<void()> cleanupCb); + static void Cleanup(void); static LeAudioClient* Get(void); static void DebugDump(int fd); @@ -90,7 +90,4 @@ class LeAudioClient { static bool GetAsesForStorage(const RawAddress& addr, std::vector<uint8_t>& out); static bool IsLeAudioClientRunning(); - - static void InitializeAudioSetConfigurationProvider(void); - static void CleanupAudioSetConfigurationProvider(void); }; diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc index b06fd2d6e028ebeecbadda96915b9c5daf90d9d4..19b7a14d283da09d59fb3ae4261a5321f1137776 100644 --- a/system/bta/le_audio/broadcaster/broadcaster.cc +++ b/system/bta/le_audio/broadcaster/broadcaster.cc @@ -22,12 +22,12 @@ #include "bta/include/bta_le_audio_api.h" #include "bta/include/bta_le_audio_broadcaster_api.h" #include "bta/le_audio/broadcaster/state_machine.h" +#include "bta/le_audio/codec_interface.h" #include "bta/le_audio/content_control_id_keeper.h" #include "bta/le_audio/le_audio_types.h" #include "bta/le_audio/le_audio_utils.h" #include "bta/le_audio/metrics_collector.h" #include "device/include/controller.h" -#include "embdrv/lc3/include/lc3.h" #include "gd/common/strings.h" #include "internal_include/stack_config.h" #include "osi/include/log.h" @@ -956,31 +956,21 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { void CheckAndReconfigureEncoders() { auto const& codec_id = codec_wrapper_.GetLeAudioCodecId(); - if (codec_id.coding_format != kLeAudioCodingFormatLC3) { - LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id.coding_format, - codec_id.vendor_company_id, codec_id.vendor_codec_id); - return; - } - - if (enc_audio_buffers_.size() != codec_wrapper_.GetNumChannels()) { - enc_audio_buffers_.resize(codec_wrapper_.GetNumChannels()); - } - - const int dt_us = codec_wrapper_.GetDataIntervalUs(); - const int sr_hz = codec_wrapper_.GetSampleRate(); - const auto encoder_bytes = lc3_encoder_size(dt_us, sr_hz); - const auto channel_bytes = codec_wrapper_.GetMaxSduSizePerChannel(); - /* TODO: We should act smart and reuse current configurations */ - encoders_.clear(); - encoders_mem_.clear(); - while (encoders_.size() < codec_wrapper_.GetNumChannels()) { - auto& encoder_buf = enc_audio_buffers_.at(encoders_.size()); - encoder_buf.resize(channel_bytes); - - encoders_mem_.emplace_back(malloc(encoder_bytes), &std::free); - encoders_.emplace_back( - lc3_setup_encoder(dt_us, sr_hz, 0, encoders_mem_.back().get())); + sw_enc_.clear(); + while (sw_enc_.size() != codec_wrapper_.GetNumChannels()) { + auto codec = le_audio::CodecInterface::CreateInstance(codec_id); + + auto codec_status = + codec->InitEncoder(codec_wrapper_.GetLeAudioCodecConfiguration(), + codec_wrapper_.GetLeAudioCodecConfiguration()); + if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) { + LOG_ERROR("Channel %d codec setup failed with err: %d", + (uint32_t)sw_enc_.size(), codec_status); + return; + } + + sw_enc_.emplace_back(std::move(codec)); } } @@ -992,23 +982,9 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { codec_wrapper_ = config; } - void encodeLc3Channel(lc3_encoder_t encoder, - std::vector<uint8_t>& out_buffer, - const std::vector<uint8_t>& data, - int initial_channel_offset, int pitch_samples, - int num_channels) { - auto encoder_status = - lc3_encode(encoder, LC3_PCM_FORMAT_S16, - (int16_t*)(data.data() + initial_channel_offset), - pitch_samples, out_buffer.size(), out_buffer.data()); - if (encoder_status != 0) { - LOG_ERROR("Encoding error=%d", encoder_status); - } - } - static void sendBroadcastData( const std::unique_ptr<BroadcastStateMachine>& broadcast, - std::vector<std::vector<uint8_t>>& encoded_channels) { + std::vector<std::unique_ptr<le_audio::CodecInterface>>& encoders) { auto const& config = broadcast->GetBigConfig(); if (config == std::nullopt) { LOG_ERROR( @@ -1019,15 +995,16 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { return; } - if (config->connection_handles.size() < encoded_channels.size()) { + if (config->connection_handles.size() < encoders.size()) { LOG_ERROR("Not enough BIS'es to broadcast all channels!"); return; } - for (uint8_t chan = 0; chan < encoded_channels.size(); ++chan) { - IsoManager::GetInstance()->SendIsoData(config->connection_handles[chan], - encoded_channels[chan].data(), - encoded_channels[chan].size()); + for (uint8_t chan = 0; chan < encoders.size(); ++chan) { + IsoManager::GetInstance()->SendIsoData( + config->connection_handles[chan], + (const uint8_t*)encoders[chan]->GetDecodedSamples().data(), + encoders[chan]->GetDecodedSamples().size() * 2); } } @@ -1042,9 +1019,9 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { /* Prepare encoded data for all channels */ for (uint8_t chan = 0; chan < num_channels; ++chan) { - /* TODO: Use encoder agnostic wrapper */ - encodeLc3Channel(encoders_[chan], enc_audio_buffers_[chan], data, - chan * bytes_per_sample, num_channels, num_channels); + auto initial_channel_offset = chan * bytes_per_sample; + sw_enc_[chan]->Encode(data.data() + initial_channel_offset, + num_channels, codec_wrapper_.GetFrameLen()); } /* Currently there is no way to broadcast multiple distinct streams. @@ -1056,7 +1033,7 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { if ((broadcast->GetState() == BroadcastStateMachine::State::STREAMING) && !broadcast->IsMuted()) - sendBroadcastData(broadcast, enc_audio_buffers_); + sendBroadcastData(broadcast, sw_enc_); } LOG_VERBOSE("All data sent."); } @@ -1103,9 +1080,7 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { private: BroadcastCodecWrapper codec_wrapper_; - std::vector<lc3_encoder_t> encoders_; - std::vector<std::unique_ptr<void, decltype(&std::free)>> encoders_mem_; - std::vector<std::vector<uint8_t>> enc_audio_buffers_; + std::vector<std::unique_ptr<le_audio::CodecInterface>> sw_enc_; } audio_receiver_; static class QueuedBroadcast { diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.cc b/system/bta/le_audio/broadcaster/broadcaster_types.cc index e2a5dc8ca45bdd220c03425915e079cb56cae7d2..6c1dd50b5a465e2deeb38d578466177d7de463dc 100644 --- a/system/bta/le_audio/broadcaster/broadcaster_types.cc +++ b/system/bta/le_audio/broadcaster/broadcaster_types.cc @@ -22,7 +22,6 @@ #include "bt_types.h" #include "bta_le_audio_broadcaster_api.h" #include "btm_ble_api_types.h" -#include "embdrv/lc3/include/lc3.h" #include "internal_include/stack_config.h" #include "osi/include/properties.h" @@ -374,10 +373,8 @@ types::LeAudioLtvMap BroadcastCodecWrapper::GetSubgroupCodecSpecData() const { }; if (codec_id.coding_format == kLeAudioCodecIdLc3.coding_format) { - uint16_t bc = - lc3_frame_bytes(source_codec_config.data_interval_us, codec_bitrate); codec_spec_ltvs[codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame] = - UINT16_TO_VEC_UINT8(bc); + UINT16_TO_VEC_UINT8(codec_frame_len); } if (source_codec_config.num_channels == 1) { diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index af0c883ff7834b7ed1de357f93747c87901cfbde..1d8ce220492d255c68fcb1ed8ef34815d58f45d9 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -34,12 +34,12 @@ #include "btif_profile_storage.h" #include "btm_iso_api.h" #include "client_parser.h" +#include "codec_interface.h" #include "codec_manager.h" #include "common/time_util.h" #include "content_control_id_keeper.h" #include "device/include/controller.h" #include "devices.h" -#include "embdrv/lc3/include/lc3.h" #include "gatt/bta_gattc_int.h" #include "gd/common/strings.h" #include "internal_include/stack_config.h" @@ -166,23 +166,6 @@ static void le_audio_health_status_callback(const RawAddress& addr, int group_id, LeAudioHealthBasedAction action); -inline uint8_t bits_to_bytes_per_sample(uint8_t bits_per_sample) { - // 24 bit audio stream is sent as unpacked, each sample takes 4 bytes. - if (bits_per_sample == 24) return 4; - - return bits_per_sample / 8; -} - -inline lc3_pcm_format bits_to_lc3_bits(uint8_t bits_per_sample) { - if (bits_per_sample == 16) return LC3_PCM_FORMAT_S16; - - if (bits_per_sample == 24) return LC3_PCM_FORMAT_S24; - - LOG_ALWAYS_FATAL("Encoder/decoder don't know how to handle %d", - bits_per_sample); - return LC3_PCM_FORMAT_S16; -} - class LeAudioClientImpl; LeAudioClientImpl* instance; std::mutex instance_mutex; @@ -248,12 +231,6 @@ class LeAudioClientImpl : public LeAudioClient { in_voip_call_(false), current_source_codec_config({0, 0, 0, 0}), current_sink_codec_config({0, 0, 0, 0}), - lc3_encoder_left_mem(nullptr), - lc3_encoder_right_mem(nullptr), - lc3_decoder_left_mem(nullptr), - lc3_decoder_right_mem(nullptr), - lc3_decoder_left(nullptr), - lc3_decoder_right(nullptr), le_audio_source_hal_client_(nullptr), le_audio_sink_hal_client_(nullptr), close_vbc_timeout_(alarm_new("LeAudioCloseVbcTimeout")), @@ -3061,27 +3038,12 @@ class LeAudioClientImpl : public LeAudioClient { void PrepareAndSendToTwoCises( const std::vector<uint8_t>& data, struct le_audio::stream_configuration* stream_conf) { - uint16_t byte_count = stream_conf->sink_octets_per_codec_frame; uint16_t left_cis_handle = 0; uint16_t right_cis_handle = 0; - uint16_t number_of_required_samples_per_channel; - - int dt_us = current_source_codec_config.data_interval_us; - int af_hz = audio_framework_source_config.sample_rate; - number_of_required_samples_per_channel = lc3_frame_samples(dt_us, af_hz); - - lc3_pcm_format bits_per_sample = - bits_to_lc3_bits(audio_framework_source_config.bits_per_sample); - uint8_t bytes_per_sample = - bits_to_bytes_per_sample(audio_framework_source_config.bits_per_sample); - - for (auto [cis_handle, audio_location] : stream_conf->sink_streams) { - if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft) - left_cis_handle = cis_handle; - if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyRight) - right_cis_handle = cis_handle; - } + uint16_t number_of_required_samples_per_channel = + sw_enc_left->GetNumOfSamplesPerChannel(); + uint8_t bytes_per_sample = sw_enc_left->GetNumOfBytesPerSample(); if (data.size() < bytes_per_sample * 2 /* channels */ * number_of_required_samples_per_channel) { LOG(ERROR) << __func__ << " Missing samples. Data size: " << +data.size() @@ -3091,29 +3053,30 @@ class LeAudioClientImpl : public LeAudioClient { return; } - std::vector<uint8_t> chan_left_enc(byte_count, 0); - std::vector<uint8_t> chan_right_enc(byte_count, 0); - - bool mono = (left_cis_handle == 0) || (right_cis_handle == 0); + for (auto [cis_handle, audio_location] : + stream_conf->stream_params.sink.stream_locations) { + if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft) + left_cis_handle = cis_handle; + if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyRight) + right_cis_handle = cis_handle; + } - if (!mono) { - lc3_encode(lc3_encoder_left, bits_per_sample, data.data(), 2, - chan_left_enc.size(), chan_left_enc.data()); - lc3_encode(lc3_encoder_right, bits_per_sample, - data.data() + bytes_per_sample, 2, chan_right_enc.size(), - chan_right_enc.data()); - } else { + uint16_t byte_count = + stream_conf->stream_params.sink.octets_per_codec_frame; + bool mix_to_mono = (left_cis_handle == 0) || (right_cis_handle == 0); + if (mix_to_mono) { std::vector<uint8_t> mono = mono_blend( data, bytes_per_sample, number_of_required_samples_per_channel); if (left_cis_handle) { - lc3_encode(lc3_encoder_left, bits_per_sample, mono.data(), 1, - chan_left_enc.size(), chan_left_enc.data()); + sw_enc_left->Encode(mono.data(), 1, byte_count); } if (right_cis_handle) { - lc3_encode(lc3_encoder_right, bits_per_sample, mono.data(), 1, - chan_right_enc.size(), chan_right_enc.data()); + sw_enc_left->Encode(mono.data(), 1, byte_count); } + } else { + sw_enc_left->Encode(data.data(), 2, byte_count); + sw_enc_right->Encode(data.data() + bytes_per_sample, 2, byte_count); } DLOG(INFO) << __func__ << " left_cis_handle: " << +left_cis_handle @@ -3121,59 +3084,52 @@ class LeAudioClientImpl : public LeAudioClient { /* Send data to the controller */ if (left_cis_handle) IsoManager::GetInstance()->SendIsoData( - left_cis_handle, chan_left_enc.data(), chan_left_enc.size()); + left_cis_handle, + (const uint8_t*)sw_enc_left->GetDecodedSamples().data(), + sw_enc_left->GetDecodedSamples().size() * 2); if (right_cis_handle) IsoManager::GetInstance()->SendIsoData( - right_cis_handle, chan_right_enc.data(), chan_right_enc.size()); + right_cis_handle, + (const uint8_t*)sw_enc_right->GetDecodedSamples().data(), + sw_enc_right->GetDecodedSamples().size() * 2); } void PrepareAndSendToSingleCis( const std::vector<uint8_t>& data, struct le_audio::stream_configuration* stream_conf) { - int num_channels = stream_conf->sink_num_of_channels; - uint16_t byte_count = stream_conf->sink_octets_per_codec_frame; - auto cis_handle = stream_conf->sink_streams.front().first; - uint16_t number_of_required_samples_per_channel; - - int dt_us = current_source_codec_config.data_interval_us; - int af_hz = audio_framework_source_config.sample_rate; - number_of_required_samples_per_channel = lc3_frame_samples(dt_us, af_hz); - lc3_pcm_format bits_per_sample = - bits_to_lc3_bits(audio_framework_source_config.bits_per_sample); - uint8_t bytes_per_sample = - bits_to_bytes_per_sample(audio_framework_source_config.bits_per_sample); - - if ((int)data.size() < (2 /* bytes per sample */ * num_channels * + uint16_t num_channels = stream_conf->stream_params.sink.num_of_channels; + uint16_t cis_handle = + stream_conf->stream_params.sink.stream_locations.front().first; + + uint16_t number_of_required_samples_per_channel = + sw_enc_left->GetNumOfSamplesPerChannel(); + uint8_t bytes_per_sample = sw_enc_left->GetNumOfBytesPerSample(); + if ((int)data.size() < (bytes_per_sample * num_channels * number_of_required_samples_per_channel)) { LOG(ERROR) << __func__ << "Missing samples"; return; } - std::vector<uint8_t> chan_encoded(num_channels * byte_count, 0); - if (num_channels == 1) { + uint16_t byte_count = + stream_conf->stream_params.sink.octets_per_codec_frame; + bool mix_to_mono = (num_channels == 1); + if (mix_to_mono) { /* Since we always get two channels from framework, lets make it mono here */ std::vector<uint8_t> mono = mono_blend( data, bytes_per_sample, number_of_required_samples_per_channel); - - auto err = lc3_encode(lc3_encoder_left, bits_per_sample, mono.data(), 1, - byte_count, chan_encoded.data()); - - if (err < 0) { - LOG(ERROR) << " error while encoding, error code: " << +err; - } + sw_enc_left->Encode(mono.data(), 1, byte_count); } else { - lc3_encode(lc3_encoder_left, bits_per_sample, (const int16_t*)data.data(), - 2, byte_count, chan_encoded.data()); - lc3_encode(lc3_encoder_right, bits_per_sample, - (const int16_t*)data.data() + 1, 2, byte_count, - chan_encoded.data() + byte_count); + sw_enc_left->Encode((const uint8_t*)data.data(), 2, byte_count); + // Output to the left channel buffer with `byte_count` offset + sw_enc_right->Encode((const uint8_t*)data.data() + 2, 2, byte_count, + &sw_enc_left->GetDecodedSamples(), byte_count); } - /* Send data to the controller */ - IsoManager::GetInstance()->SendIsoData(cis_handle, chan_encoded.data(), - chan_encoded.size()); + IsoManager::GetInstance()->SendIsoData( + cis_handle, (const uint8_t*)sw_enc_left->GetDecodedSamples().data(), + sw_enc_left->GetDecodedSamples().size() * 2); } const struct le_audio::stream_configuration* GetStreamSinkConfiguration( @@ -3181,7 +3137,7 @@ class LeAudioClientImpl : public LeAudioClient { const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; LOG_INFO("group_id: %d", group->group_id_); - if (stream_conf->sink_streams.size() == 0) { + if (stream_conf->stream_params.sink.stream_locations.size() == 0) { return nullptr; } @@ -3201,27 +3157,26 @@ class LeAudioClientImpl : public LeAudioClient { } auto stream_conf = group->stream_conf; - if ((stream_conf.sink_num_of_devices > 2) || - (stream_conf.sink_num_of_devices == 0) || - stream_conf.sink_streams.empty()) { + if ((stream_conf.stream_params.sink.num_of_devices > 2) || + (stream_conf.stream_params.sink.num_of_devices == 0) || + stream_conf.stream_params.sink.stream_locations.empty()) { LOG(ERROR) << __func__ << " Stream configufation is not valid."; return; } - if (stream_conf.sink_num_of_devices == 2) { - PrepareAndSendToTwoCises(data, &stream_conf); - } else if (stream_conf.sink_streams.size() == 2) { - /* Streaming to one device but 2 CISes */ + if ((stream_conf.stream_params.sink.num_of_devices == 2) || + (stream_conf.stream_params.sink.stream_locations.size() == 2)) { + /* Streaming to two devices or one device with 2 CISes */ PrepareAndSendToTwoCises(data, &stream_conf); } else { + /* Streaming to one device and 1 CIS */ PrepareAndSendToSingleCis(data, &stream_conf); } } void CleanCachedMicrophoneData() { - cached_channel_data_.clear(); cached_channel_timestamp_ = 0; - cached_channel_is_left_ = false; + cached_channel_ = nullptr; } /* Handles audio data packets coming from the controller */ @@ -3239,11 +3194,10 @@ class LeAudioClientImpl : public LeAudioClient { return; } - auto stream_conf = group->stream_conf; - uint16_t left_cis_handle = 0; uint16_t right_cis_handle = 0; - for (auto [cis_handle, audio_location] : stream_conf.source_streams) { + for (auto [cis_handle, audio_location] : + group->stream_conf.stream_params.source.stream_locations) { if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft) { left_cis_handle = cis_handle; } @@ -3253,97 +3207,43 @@ class LeAudioClientImpl : public LeAudioClient { } } - bool is_left = true; + auto decoder = sw_dec_left.get(); if (cis_conn_hdl == left_cis_handle) { - is_left = true; + decoder = sw_dec_left.get(); } else if (cis_conn_hdl == right_cis_handle) { - is_left = false; + decoder = sw_dec_right.get(); } else { LOG_ERROR("Received data for unknown handle: %04x", cis_conn_hdl); return; } - uint16_t required_for_channel_byte_count = - stream_conf.source_octets_per_codec_frame; - - int dt_us = current_sink_codec_config.data_interval_us; - int af_hz = audio_framework_sink_config.sample_rate; - lc3_pcm_format bits_per_sample = - bits_to_lc3_bits(audio_framework_sink_config.bits_per_sample); - - int pcm_size; - if (dt_us == 10000) { - if (af_hz == 44100) - pcm_size = 480; - else - pcm_size = af_hz / 100; - } else if (dt_us == 7500) { - if (af_hz == 44100) - pcm_size = 360; - else - pcm_size = (af_hz * 3) / 400; - } else { - LOG(ERROR) << "BAD dt_us: " << dt_us; - return; - } - - std::vector<int16_t> pcm_data_decoded(pcm_size, 0); - - int err = 0; - - if (required_for_channel_byte_count != size) { - LOG(INFO) << "Insufficient data for decoding and send, required: " - << int(required_for_channel_byte_count) - << ", received: " << int(size) << ", will do PLC"; - size = 0; - data = nullptr; - } - - lc3_decoder_t decoder_to_use = - is_left ? lc3_decoder_left : lc3_decoder_right; - - err = lc3_decode(decoder_to_use, data, size, bits_per_sample, - pcm_data_decoded.data(), 1 /* pitch */); - - if (err < 0) { - LOG(ERROR) << " bad decoding parameters: " << static_cast<int>(err); - return; - } - - /* AF == Audio Framework */ - bool af_is_stereo = (audio_framework_sink_config.num_channels == 2); - if (!left_cis_handle || !right_cis_handle) { /* mono or just one device connected */ - SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, - &pcm_data_decoded, nullptr); + decoder->Decode(data, size); + SendAudioDataToAF(&decoder->GetDecodedSamples()); return; } /* both devices are connected */ - if (cached_channel_timestamp_ == 0 && cached_channel_data_.empty()) { + if (cached_channel_ == nullptr || + cached_channel_->GetDecodedSamples().empty()) { /* First packet received, cache it. We need both channel data to send it * to AF. */ - cached_channel_data_ = pcm_data_decoded; + decoder->Decode(data, size); cached_channel_timestamp_ = timestamp; - cached_channel_is_left_ = is_left; + cached_channel_ = decoder; return; } /* We received either data for the other audio channel, or another * packet for same channel */ - - if (cached_channel_is_left_ != is_left) { + if (cached_channel_ != decoder) { /* It's data for the 2nd channel */ if (timestamp == cached_channel_timestamp_) { /* Ready to mix data and send out to AF */ - if (is_left) { - SendAudioDataToAF(true /* bt_got_stereo */, af_is_stereo, - &cached_channel_data_, &pcm_data_decoded); - } else { - SendAudioDataToAF(true /* bt_got_stereo */, af_is_stereo, - &pcm_data_decoded, &cached_channel_data_); - } + decoder->Decode(data, size); + SendAudioDataToAF(&sw_dec_left->GetDecodedSamples(), + &sw_dec_right->GetDecodedSamples()); CleanCachedMicrophoneData(); return; @@ -3352,18 +3252,11 @@ class LeAudioClientImpl : public LeAudioClient { /* 2nd Channel is in the future compared to the cached data. Send the cached data to AF, and keep the new channel data in cache. This should happen only during stream setup */ + SendAudioDataToAF(&decoder->GetDecodedSamples()); - if (cached_channel_is_left_) { - SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, - &cached_channel_data_, nullptr); - } else { - SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, nullptr, - &cached_channel_data_); - } - - cached_channel_data_ = pcm_data_decoded; + decoder->Decode(data, size); cached_channel_timestamp_ = timestamp; - cached_channel_is_left_ = is_left; + cached_channel_ = decoder; return; } @@ -3371,25 +3264,22 @@ class LeAudioClientImpl : public LeAudioClient { * data */ /* Send the cached data out */ - if (cached_channel_is_left_) { - SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, - &cached_channel_data_, nullptr); - } else { - SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, nullptr, - &cached_channel_data_); - } + SendAudioDataToAF(&decoder->GetDecodedSamples()); /* Cache the data in case 2nd channel connects */ - cached_channel_data_ = pcm_data_decoded; + decoder->Decode(data, size); cached_channel_timestamp_ = timestamp; - cached_channel_is_left_ = is_left; + cached_channel_ = decoder; } - void SendAudioDataToAF(bool bt_got_stereo, bool af_is_stereo, - std::vector<int16_t>* left, - std::vector<int16_t>* right) { + void SendAudioDataToAF(std::vector<int16_t>* left, + std::vector<int16_t>* right = nullptr) { uint16_t to_write = 0; uint16_t written = 0; + + bool af_is_stereo = (audio_framework_sink_config.num_channels == 2); + bool bt_got_stereo = (left != nullptr) & (right != nullptr); + if (!af_is_stereo) { if (!bt_got_stereo) { std::vector<int16_t>* mono = left ? left : right; @@ -3464,14 +3354,16 @@ class LeAudioClientImpl : public LeAudioClient { } LOG_DEBUG("Sink stream config (#%d):\n", - static_cast<int>(stream_conf->sink_streams.size())); - for (auto stream : stream_conf->sink_streams) { + static_cast<int>( + stream_conf->stream_params.sink.stream_locations.size())); + for (auto stream : stream_conf->stream_params.sink.stream_locations) { LOG_DEBUG("Cis handle: 0x%02x, allocation 0x%04x\n", stream.first, stream.second); } LOG_DEBUG("Source stream config (#%d):\n", - static_cast<int>(stream_conf->source_streams.size())); - for (auto stream : stream_conf->source_streams) { + static_cast<int>( + stream_conf->stream_params.source.stream_locations.size())); + for (auto stream : stream_conf->stream_params.source.stream_locations) { LOG_DEBUG("Cis handle: 0x%02x, allocation 0x%04x\n", stream.first, stream.second); } @@ -3480,38 +3372,42 @@ class LeAudioClientImpl : public LeAudioClient { group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink); if (CodecManager::GetInstance()->GetCodecLocation() == le_audio::types::CodecLocation::HOST) { - if (lc3_encoder_left_mem) { + if (sw_enc_left || sw_enc_right) { LOG(WARNING) << " The encoder instance should have been already released."; - free(lc3_encoder_left_mem); - lc3_encoder_left_mem = nullptr; - free(lc3_encoder_right_mem); - lc3_encoder_right_mem = nullptr; } - int dt_us = current_source_codec_config.data_interval_us; - int sr_hz = current_source_codec_config.sample_rate; - int af_hz = audio_framework_source_config.sample_rate; - unsigned enc_size = lc3_encoder_size(dt_us, af_hz); - - lc3_encoder_left_mem = malloc(enc_size); - lc3_encoder_right_mem = malloc(enc_size); + sw_enc_left = le_audio::CodecInterface::CreateInstance(stream_conf->id); + auto codec_status = sw_enc_left->InitEncoder( + audio_framework_source_config, current_source_codec_config); + if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) { + LOG_ERROR("Left channel codec setup failed with err: %d", codec_status); + return false; + } - lc3_encoder_left = - lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_left_mem); - lc3_encoder_right = - lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_right_mem); + sw_enc_right = le_audio::CodecInterface::CreateInstance(stream_conf->id); + codec_status = sw_enc_right->InitEncoder(audio_framework_source_config, + current_source_codec_config); + if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) { + LOG_ERROR("Right channel codec setup failed with err: %d", + codec_status); + return false; + } } le_audio_source_hal_client_->UpdateRemoteDelay(remote_delay_ms); 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; @@ -3521,7 +3417,7 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioDeviceGroup* group) { const struct le_audio::stream_configuration* stream_conf = &group->stream_conf; - if (stream_conf->source_streams.size() == 0) { + if (stream_conf->stream_params.source.stream_locations.size() == 0) { return nullptr; } LOG_INFO("configuration: %s", stream_conf->conf->name.c_str()); @@ -3547,56 +3443,52 @@ class LeAudioClientImpl : public LeAudioClient { if (CodecManager::GetInstance()->GetCodecLocation() == le_audio::types::CodecLocation::HOST) { - if (lc3_decoder_left_mem) { + if (sw_dec_left.get() || sw_dec_right.get()) { LOG(WARNING) << " The decoder instance should have been already released."; - free(lc3_decoder_left_mem); - lc3_decoder_left_mem = nullptr; - free(lc3_decoder_right_mem); - lc3_decoder_right_mem = nullptr; + } + sw_dec_left = le_audio::CodecInterface::CreateInstance(stream_conf->id); + auto codec_status = sw_dec_left->InitDecoder(current_sink_codec_config, + audio_framework_sink_config); + if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) { + LOG_ERROR("Left channel codec setup failed with err: %d", codec_status); + return; } - int dt_us = current_sink_codec_config.data_interval_us; - int sr_hz = current_sink_codec_config.sample_rate; - int af_hz = audio_framework_sink_config.sample_rate; - unsigned dec_size = lc3_decoder_size(dt_us, af_hz); - lc3_decoder_left_mem = malloc(dec_size); - lc3_decoder_right_mem = malloc(dec_size); - - lc3_decoder_left = - lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_left_mem); - lc3_decoder_right = - lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_right_mem); + sw_dec_right = le_audio::CodecInterface::CreateInstance(stream_conf->id); + codec_status = sw_dec_right->InitDecoder(current_sink_codec_config, + audio_framework_sink_config); + if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) { + LOG_ERROR("Right channel codec setup failed with err: %d", + codec_status); + return; + } } le_audio_sink_hal_client_->UpdateRemoteDelay(remote_delay_ms); 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)); } } void SuspendAudio(void) { CancelStreamingRequest(); - if (lc3_encoder_left_mem) { - free(lc3_encoder_left_mem); - lc3_encoder_left_mem = nullptr; - free(lc3_encoder_right_mem); - lc3_encoder_right_mem = nullptr; - } - - if (lc3_decoder_left_mem) { - free(lc3_decoder_left_mem); - lc3_decoder_left_mem = nullptr; - free(lc3_decoder_right_mem); - lc3_decoder_right_mem = nullptr; - } + if (sw_enc_left) sw_enc_left.reset(); + if (sw_enc_right) sw_enc_right.reset(); + if (sw_dec_left) sw_dec_left.reset(); + if (sw_dec_right) sw_dec_right.reset(); + CleanCachedMicrophoneData(); } void StopAudio(void) { SuspendAudio(); } @@ -3678,7 +3570,7 @@ class LeAudioClientImpl : public LeAudioClient { } } - void Cleanup(base::Callback<void()> cleanupCb) { + void Cleanup() { StopVbcCloseTimeout(); if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_); @@ -3692,8 +3584,6 @@ class LeAudioClientImpl : public LeAudioClient { leAudioDevices_.Cleanup(gatt_if_); if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_); - std::move(cleanupCb).Run(); - if (leAudioHealthStatus_) { leAudioHealthStatus_->Cleanup(); } @@ -4476,7 +4366,7 @@ class LeAudioClientImpl : public LeAudioClient { "invalid/unknown %s context metadata, using 'UNSPECIFIED' instead", (remote_dir == le_audio::types::kLeAudioDirectionSink) ? "sink" : "source"); - contexts_pair.get_ref(remote_dir) = + contexts_pair.get(remote_dir) = AudioContexts(LeAudioContextType::UNSPECIFIED); } @@ -4504,13 +4394,13 @@ class LeAudioClientImpl : public LeAudioClient { } LOG_DEBUG("Checking contexts: %s, against the available contexts: %s", - ToString(contexts_pair.get_ref(dir)).c_str(), + ToString(contexts_pair.get(dir)).c_str(), ToString(group_available_contexts).c_str()); auto unavail_contexts = - contexts_pair.get_ref(dir) & ~group_available_contexts; + contexts_pair.get(dir) & ~group_available_contexts; if (unavail_contexts.none()) continue; - contexts_pair.get_ref(dir) &= group_available_contexts; + contexts_pair.get(dir) &= group_available_contexts; auto unavail_but_supported = (unavail_contexts & group->GetSupportedContexts(dir)); if (unavail_but_supported.none() && @@ -4520,7 +4410,7 @@ class LeAudioClientImpl : public LeAudioClient { /* All unavailable are also unsupported - replace with UNSPECIFIED if * available */ - contexts_pair.get_ref(dir).set(LeAudioContextType::UNSPECIFIED); + contexts_pair.get(dir).set(LeAudioContextType::UNSPECIFIED); } else { LOG_DEBUG("Some contexts are supported but currently unavailable: %s!", ToString(unavail_but_supported).c_str()); @@ -4556,12 +4446,12 @@ class LeAudioClientImpl : public LeAudioClient { "Other direction is streaming. Aligning other direction" " metadata to match the current direciton context: %s", ToString(contexts_pair.get(other_dir)).c_str()); - contexts_pair.get_ref(dir) = contexts_pair.get(other_dir); + contexts_pair.get(dir) = contexts_pair.get(other_dir); } } else { LOG_DEBUG("Removing UNSPECIFIED from the remote sink context: %s", ToString(contexts_pair.get(other_dir)).c_str()); - contexts_pair.get_ref(dir).unset(LeAudioContextType::UNSPECIFIED); + contexts_pair.get(dir).unset(LeAudioContextType::UNSPECIFIED); } } } @@ -4697,7 +4587,7 @@ class LeAudioClientImpl : public LeAudioClient { LOG_DEBUG( "The other direction is not streaming bidirectional, ignore that " "context."); - remote_metadata.get_ref(remote_other_direction).clear(); + remote_metadata.get(remote_other_direction).clear(); } /* Mixed contexts in the voiceback channel scenarios can confuse the remote @@ -4711,13 +4601,13 @@ class LeAudioClientImpl : public LeAudioClient { "context"); if (!is_streaming_other_direction) { // Do not take the obsolete metadata - remote_metadata.get_ref(remote_other_direction).clear(); + remote_metadata.get(remote_other_direction).clear(); } - remote_metadata.get_ref(remote_other_direction) + remote_metadata.get(remote_other_direction) .unset_all(kLeAudioContextAllBidir); - remote_metadata.get_ref(remote_other_direction) + remote_metadata.get(remote_other_direction) .unset_all(kLeAudioContextAllRemoteSinkOnly); - remote_metadata.get_ref(remote_other_direction) + remote_metadata.get(remote_other_direction) .set_all(remote_metadata.get(remote_direction) & ~kLeAudioContextAllRemoteSinkOnly); } @@ -4742,9 +4632,9 @@ class LeAudioClientImpl : public LeAudioClient { /* Turn off bidirectional contexts on this direction to avoid mixing * with the other direction bidirectional context */ - remote_metadata.get_ref(remote_direction) + remote_metadata.get(remote_direction) .unset_all(kLeAudioContextAllBidir); - remote_metadata.get_ref(remote_direction) + remote_metadata.get(remote_direction) .set_all(remote_metadata.get(remote_other_direction)); } } @@ -4844,7 +4734,7 @@ class LeAudioClientImpl : public LeAudioClient { group, le_audio::types::kLeAudioDirectionSource) && (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); if (has_audio_source_configured) { - LOG_DEBUG( + LOG_INFO( "Audio source is already available in the current configuration " "context in %s. Not switching to %s right now.", ToString(configuration_context_type_).c_str(), @@ -4869,9 +4759,9 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioDeviceGroup* group, LeAudioContextType new_configuration_context, BidirectionalPair<AudioContexts> remote_contexts) { if (new_configuration_context != configuration_context_type_) { - LOG_DEBUG("Changing configuration context from %s to %s", - ToString(configuration_context_type_).c_str(), - ToString(new_configuration_context).c_str()); + LOG_INFO("Checking whether to change configuration context from %s to %s", + ToString(configuration_context_type_).c_str(), + ToString(new_configuration_context).c_str()); LeAudioLogHistory::Get()->AddLogHistory( kLogAfCallBt, active_group_id_, RawAddress::kEmpty, @@ -4886,7 +4776,7 @@ class LeAudioClientImpl : public LeAudioClient { } if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { - LOG_DEBUG( + LOG_INFO( "The %s configuration did not change. Updating the metadata to " "sink=%s, source=%s", ToString(configuration_context_type_).c_str(), @@ -5136,40 +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->sink_offloader_changed || stream_conf->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->source_offloader_changed || - stream_conf->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)) { @@ -5235,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_); @@ -5291,6 +5156,12 @@ class LeAudioClientImpl : public LeAudioClient { */ FALLTHROUGH; case GroupStreamStatus::IDLE: { + if (sw_enc_left) sw_enc_left.reset(); + if (sw_enc_right) sw_enc_right.reset(); + if (sw_dec_left) sw_dec_left.reset(); + if (sw_dec_right) sw_dec_right.reset(); + CleanCachedMicrophoneData(); + if (group) { UpdateLocationsAndContextsAvailability(group->group_id_); if (group->IsPendingConfiguration()) { @@ -5339,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_; @@ -5398,17 +5280,11 @@ class LeAudioClientImpl : public LeAudioClient { .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us, }; - void* lc3_encoder_left_mem; - void* lc3_encoder_right_mem; - - lc3_encoder_t lc3_encoder_left; - lc3_encoder_t lc3_encoder_right; + std::unique_ptr<le_audio::CodecInterface> sw_enc_left; + std::unique_ptr<le_audio::CodecInterface> sw_enc_right; - void* lc3_decoder_left_mem; - void* lc3_decoder_right_mem; - - lc3_decoder_t lc3_decoder_left; - lc3_decoder_t lc3_decoder_right; + std::unique_ptr<le_audio::CodecInterface> sw_dec_left; + std::unique_ptr<le_audio::CodecInterface> sw_dec_right; std::vector<uint8_t> encoded_data; std::unique_ptr<LeAudioSourceAudioHalClient> le_audio_source_hal_client_; @@ -5422,9 +5298,8 @@ class LeAudioClientImpl : public LeAudioClient { alarm_t* disable_timer_; static constexpr uint64_t kDeviceAttachDelayMs = 500; - std::vector<int16_t> cached_channel_data_; uint32_t cached_channel_timestamp_ = 0; - uint32_t cached_channel_is_left_; + le_audio::CodecInterface* cached_channel_ = nullptr; base::WeakPtrFactory<LeAudioClientImpl> weak_factory_{this}; @@ -5563,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; @@ -5756,7 +5635,7 @@ void LeAudioClient::DebugDump(int fd) { dprintf(fd, "\n"); } -void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) { +void LeAudioClient::Cleanup(void) { std::scoped_lock<std::mutex> lock(instance_mutex); if (!instance) { LOG(ERROR) << "Not initialized"; @@ -5765,7 +5644,7 @@ void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) { LeAudioClientImpl* ptr = instance; instance = nullptr; - ptr->Cleanup(cleanupCb); + ptr->Cleanup(); delete ptr; ptr = nullptr; diff --git a/system/bta/le_audio/client_linux.cc b/system/bta/le_audio/client_linux.cc index 4cc133f1e5019d4eb1ef03278c21578c08cb4480..f905add422fab2d4c5212dbc89c8e180d99c982a 100644 --- a/system/bta/le_audio/client_linux.cc +++ b/system/bta/le_audio/client_linux.cc @@ -50,7 +50,7 @@ void LeAudioClient::Initialize( base::Closure initCb, base::Callback<bool()> hal_2_1_verifier, const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>& offloading_preference) {} -void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) {} +void LeAudioClient::Cleanup(void) {} LeAudioClient* LeAudioClient::Get(void) { return nullptr; } void LeAudioClient::DebugDump(int fd) {} void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect, @@ -79,5 +79,3 @@ bool LeAudioClient::GetAsesForStorage(const RawAddress& addr, return false; } bool LeAudioClient::IsLeAudioClientRunning() { return false; } -void LeAudioClient::InitializeAudioSetConfigurationProvider(void) {} -void LeAudioClient::CleanupAudioSetConfigurationProvider(void) {} diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc index 5bcca9f5bbfd23c0cd1c7292b0ef2088b075d79e..9ac8c7374445670f673dfc937c31fb4557979992 100644 --- a/system/bta/le_audio/client_parser.cc +++ b/system/bta/le_audio/client_parser.cc @@ -321,15 +321,14 @@ bool PrepareAseCtpCodecConfig(const std::vector<struct ctp_codec_conf>& confs, confs.begin(), confs.end(), confs.size() * kCtpCodecConfMinLen + kAseNumSize + kCtpOpSize, [&conf_ents_str](size_t cur_len, auto const& conf) { - auto ltv_map = conf.codec_config.GetAsLtvMap(); - for (const auto& [type, value] : ltv_map.Values()) { + for (const auto& [type, value] : conf.codec_config.Values()) { conf_ents_str += "\ttype: " + std::to_string(type) + "\tlen: " + std::to_string(value.size()) + "\tdata: " + base::HexEncode(value.data(), value.size()) + "\n"; }; - return cur_len + ltv_map.RawPacketSize(); + return cur_len + conf.codec_config.RawPacketSize(); }); value.resize(msg_len); @@ -345,11 +344,10 @@ bool PrepareAseCtpCodecConfig(const std::vector<struct ctp_codec_conf>& confs, UINT16_TO_STREAM(msg, conf.codec_id.vendor_company_id); UINT16_TO_STREAM(msg, conf.codec_id.vendor_codec_id); - auto ltv_map = conf.codec_config.GetAsLtvMap(); - auto codec_spec_conf_len = ltv_map.RawPacketSize(); + auto codec_spec_conf_len = conf.codec_config.RawPacketSize(); UINT8_TO_STREAM(msg, codec_spec_conf_len); - msg = ltv_map.RawPacket(msg); + msg = conf.codec_config.RawPacket(msg); LOG(INFO) << __func__ << ", Codec configuration" << "\n\tAse id: " << loghex(conf.ase_id) diff --git a/system/bta/le_audio/client_parser.h b/system/bta/le_audio/client_parser.h index f0471382e195205b35b553a3cbb104ea588db61d..38a7ec1b31c8f052a1c91f18a5c612c23d960b89 100644 --- a/system/bta/le_audio/client_parser.h +++ b/system/bta/le_audio/client_parser.h @@ -161,7 +161,7 @@ struct ctp_codec_conf { uint8_t target_latency; uint8_t target_phy; types::LeAudioCodecId codec_id; - types::LeAudioLc3Config codec_config; + types::LeAudioLtvMap codec_config; }; constexpr uint16_t kCtpQosConfMinLen = 16; diff --git a/system/bta/le_audio/client_parser_test.cc b/system/bta/le_audio/client_parser_test.cc index dbb3f1c3dd529577bae16ecc7df20fa26a9ae3b9..c3f0ebcb3f2a1d53048777780040c0c47104cd89 100644 --- a/system/bta/le_audio/client_parser_test.cc +++ b/system/bta/le_audio/client_parser_test.cc @@ -1112,10 +1112,15 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigSingle) { types::LeAudioCodecId codec_id{.coding_format = 0x06, .vendor_company_id = 0x0203, .vendor_codec_id = 0x0405}; - types::LeAudioLc3Config codec_conf{.sampling_frequency = 0x10, - .frame_duration = 0x03, - .audio_channel_allocation = 0x04050607, - .octets_per_codec_frame = 0x0203}; + types::LeAudioLtvMap codec_conf = + types::LeAudioLtvMap() + .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x10) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration, + (uint8_t)0x03) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation, + (uint32_t)0x04050607) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame, + (uint16_t)0x0203); confs.push_back(ctp_codec_conf{ .ase_id = 0x05, @@ -1168,10 +1173,15 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigMultiple) { types::LeAudioCodecId codec_id{.coding_format = 0x06, .vendor_company_id = 0x0203, .vendor_codec_id = 0x0405}; - types::LeAudioLc3Config codec_conf{.sampling_frequency = 0x10, - .frame_duration = 0x03, - .audio_channel_allocation = 0x04050607, - .octets_per_codec_frame = 0x0203}; + types::LeAudioLtvMap codec_conf = + types::LeAudioLtvMap() + .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x10) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration, + (uint8_t)0x03) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation, + (uint32_t)0x04050607) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame, + (uint16_t)0x0203); confs.push_back(ctp_codec_conf{ .ase_id = 0x05, @@ -1219,10 +1229,15 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigMultiple) { types::LeAudioCodecId codec_id2{.coding_format = 0x16, .vendor_company_id = 0x1213, .vendor_codec_id = 0x1415}; - types::LeAudioLc3Config codec_conf2{.sampling_frequency = 0x11, - .frame_duration = 0x13, - .audio_channel_allocation = 0x14151617, - .octets_per_codec_frame = 0x1213}; + types::LeAudioLtvMap codec_conf2 = + types::LeAudioLtvMap() + .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x11) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration, + (uint8_t)0x13) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation, + (uint32_t)0x14151617) + .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame, + (uint16_t)0x1213); confs.push_back(ctp_codec_conf{ .ase_id = 0x15, diff --git a/system/bta/le_audio/codec_interface.cc b/system/bta/le_audio/codec_interface.cc new file mode 100644 index 0000000000000000000000000000000000000000..ad49c3aa7dc125c90aad23958243a7410240a4fb --- /dev/null +++ b/system/bta/le_audio/codec_interface.cc @@ -0,0 +1,296 @@ +/****************************************************************************** + * + * 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 "codec_interface.h" + +#include <memory> +#include <optional> +#include <vector> + +#include "embdrv/lc3/include/lc3.h" +#include "osi/include/log.h" + +namespace le_audio { + +struct CodecInterface::Impl { + Impl(const types::LeAudioCodecId& codec_id) : codec_id_(codec_id) {} + ~Impl() { Cleanup(); } + + bool IsReady() { return pcm_config_.has_value(); }; + + CodecInterface::Status InitEncoder( + const LeAudioCodecConfiguration& pcm_config, + const LeAudioCodecConfiguration& codec_config) { + // Output codec configuration + bt_codec_config_ = codec_config; + + // TODO: For now only blocks_per_sdu = 1 is supported + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + if (pcm_config_.has_value()) { + Cleanup(); + } + pcm_config_ = pcm_config; + + lc3_.pcm_format_ = (pcm_config_->bits_per_sample == 24) + ? LC3_PCM_FORMAT_S24 + : LC3_PCM_FORMAT_S16; + + // Prepare the encoder + const auto encoder_size = lc3_encoder_size( + bt_codec_config_.data_interval_us, pcm_config_->sample_rate); + lc3_.codec_mem_.reset(malloc(encoder_size)); + lc3_.encoder_ = lc3_setup_encoder( + bt_codec_config_.data_interval_us, bt_codec_config_.sample_rate, + pcm_config_->sample_rate, lc3_.codec_mem_.get()); + + return Status::STATUS_OK; + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return Status::STATUS_ERR_INVALID_CODEC_ID; + } + + CodecInterface::Status InitDecoder( + const LeAudioCodecConfiguration& codec_config, + const LeAudioCodecConfiguration& pcm_config) { + // Input codec configuration + bt_codec_config_ = codec_config; + + // TODO: For now only blocks_per_sdu = 1 is supported + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + if (pcm_config_.has_value()) { + Cleanup(); + } + pcm_config_ = pcm_config; + + lc3_.pcm_format_ = (pcm_config_->bits_per_sample == 24) + ? LC3_PCM_FORMAT_S24 + : LC3_PCM_FORMAT_S16; + + // Prepare the decoded output buffer + output_channel_samples_ = lc3_frame_samples( + bt_codec_config_.data_interval_us, pcm_config_->sample_rate); + adjustOutputBufferSizeIfNeeded(&output_channel_data_); + + // Prepare the decoder + const auto decoder_size = lc3_decoder_size( + bt_codec_config_.data_interval_us, pcm_config_->sample_rate); + lc3_.codec_mem_.reset(malloc(decoder_size)); + lc3_.decoder_ = lc3_setup_decoder( + bt_codec_config_.data_interval_us, bt_codec_config_.sample_rate, + pcm_config_->sample_rate, lc3_.codec_mem_.get()); + + return Status::STATUS_OK; + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return Status::STATUS_ERR_INVALID_CODEC_ID; + } + + std::vector<int16_t>& GetDecodedSamples() { return output_channel_data_; } + CodecInterface::Status Decode(uint8_t* data, uint16_t size) { + if (!IsReady()) { + LOG_ERROR("decoder not ready"); + return Status::STATUS_ERR_CODEC_NOT_READY; + } + + // For now only LC3 is supported + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + adjustOutputBufferSizeIfNeeded(&output_channel_data_); + auto err = lc3_decode(lc3_.decoder_, data, size, lc3_.pcm_format_, + output_channel_data_.data(), 1 /* stride */); + if (err < 0) { + LOG(ERROR) << " bad decoding parameters: " << static_cast<int>(err); + return Status::STATUS_ERR_CODING_ERROR; + } + + return Status::STATUS_OK; + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return Status::STATUS_ERR_INVALID_CODEC_ID; + } + + CodecInterface::Status Encode(const uint8_t* data, int stride, + uint16_t out_size, + std::vector<int16_t>* out_buffer = nullptr, + uint16_t out_offset = 0) { + if (!IsReady()) { + LOG_ERROR("decoder not ready"); + return Status::STATUS_ERR_CODEC_NOT_READY; + } + + if (out_size == 0) { + LOG_ERROR("out_size cannot be 0"); + return Status::STATUS_ERR_CODING_ERROR; + } + + // For now only LC3 is supported + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + // Prepare the encoded output buffer + if (out_buffer == nullptr) { + out_buffer = &output_channel_data_; + } + + // We have two bytes per sample in the buffer, while out_size and + // out_offset are in bytes + size_t channel_samples = (out_offset + out_size) / 2; + if (output_channel_samples_ < channel_samples) { + output_channel_samples_ = channel_samples; + } + adjustOutputBufferSizeIfNeeded(out_buffer); + + // Encode + auto err = + lc3_encode(lc3_.encoder_, lc3_.pcm_format_, data, stride, out_size, + ((uint8_t*)out_buffer->data()) + out_offset); + if (err < 0) { + LOG(ERROR) << " bad encoding parameters: " << static_cast<int>(err); + return Status::STATUS_ERR_CODING_ERROR; + } + + return Status::STATUS_OK; + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return Status::STATUS_ERR_INVALID_CODEC_ID; + } + + void Cleanup() { + pcm_config_ = std::nullopt; + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + lc3_.Cleanup(); + } + output_channel_data_.clear(); + output_channel_samples_ = 0; + } + + uint16_t GetNumOfSamplesPerChannel() { + if (!IsReady()) { + LOG_ERROR("decoder not ready"); + return 0; + } + + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + return lc3_frame_samples(bt_codec_config_.data_interval_us, + pcm_config_->sample_rate); + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return 0; + } + + uint8_t GetNumOfBytesPerSample() { + if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) { + return lc3_.bits_to_bytes_per_sample(bt_codec_config_.bits_per_sample); + } + + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format, + codec_id_.vendor_company_id, codec_id_.vendor_codec_id); + return 0; + } + + private: + inline void adjustOutputBufferSizeIfNeeded(std::vector<int16_t>* out_buffer) { + if (out_buffer->size() < output_channel_samples_) { + out_buffer->resize(output_channel_samples_); + } + } + + // BT codec params set when codec is initialized + types::LeAudioCodecId codec_id_; + LeAudioCodecConfiguration bt_codec_config_; + std::optional<LeAudioCodecConfiguration> pcm_config_; + + // Output buffer + std::vector<int16_t> output_channel_data_; + size_t output_channel_samples_ = 0; + + // LC3 + struct lc3_t { + static inline uint8_t bits_to_bytes_per_sample(uint8_t bits_per_sample) { + // 24 bit audio stream is sent as unpacked, each sample takes 4 bytes. + if (bits_per_sample == 24) return 4; + return bits_per_sample / 8; + } + + void Cleanup() { + decoder_ = nullptr; + encoder_ = nullptr; + codec_mem_.reset(); + } + + lc3_t() : codec_mem_(nullptr, &std::free) {} + lc3_pcm_format pcm_format_; + union { + lc3_decoder_t decoder_; + lc3_encoder_t encoder_; + }; + std::unique_ptr<void, decltype(&std::free)> codec_mem_; + } lc3_; +}; + +CodecInterface::CodecInterface(const types::LeAudioCodecId& codec_id) { + if (codec_id.coding_format == types::kLeAudioCodingFormatLC3) { + impl = new Impl(codec_id); + } else { + LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id.coding_format, + codec_id.vendor_company_id, codec_id.vendor_codec_id); + } +} + +CodecInterface::~CodecInterface() { delete impl; } + +bool CodecInterface::IsReady() { return impl->IsReady(); }; +CodecInterface::Status CodecInterface::InitEncoder( + const LeAudioCodecConfiguration& pcm_config, + const LeAudioCodecConfiguration& codec_config) { + return impl->InitEncoder(pcm_config, codec_config); +} +CodecInterface::Status CodecInterface::InitDecoder( + const LeAudioCodecConfiguration& codec_config, + const LeAudioCodecConfiguration& pcm_config) { + return impl->InitDecoder(codec_config, pcm_config); +} +std::vector<int16_t>& CodecInterface::GetDecodedSamples() { + return impl->GetDecodedSamples(); +} +CodecInterface::Status CodecInterface::Decode(uint8_t* data, uint16_t size) { + return impl->Decode(data, size); +} +CodecInterface::Status CodecInterface::Encode(const uint8_t* data, int stride, + uint16_t out_size, + std::vector<int16_t>* out_buffer, + uint16_t out_offset) { + return impl->Encode(data, stride, out_size, out_buffer, out_offset); +} +void CodecInterface::Cleanup() { return impl->Cleanup(); } + +uint16_t CodecInterface::GetNumOfSamplesPerChannel() { + return impl->GetNumOfSamplesPerChannel(); +}; +uint8_t CodecInterface::GetNumOfBytesPerSample() { + return impl->GetNumOfBytesPerSample(); +}; + +} // namespace le_audio diff --git a/system/bta/le_audio/codec_interface.h b/system/bta/le_audio/codec_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..cd2cca98c604d30eff5d6a54f02f28930a691e2c --- /dev/null +++ b/system/bta/le_audio/codec_interface.h @@ -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 + +#include <stdint.h> + +#include "audio_hal_client/audio_hal_client.h" +#include "le_audio_types.h" + +namespace le_audio { + +/* CodecInterface provides a thin abstraction layer above the codec instance. It + * manages the output buffers internally and resizes them automatically when + * needed. + * Multi-channel stream encoding requires multiple CodecInterface instances, but + * even then it is still possible to encode the stream data into a single output + * buffer. Thanks to the optional parameters to the encode() method, the + * internal buffer of the first instance can be used as an output buffer by the + * second instance, as long as equal life time of both instances is guaranteed. + * + */ +class CodecInterface { + public: + enum class Status { + STATUS_ERR_CODEC_NOT_READY = -128, + STATUS_ERR_INVALID_CODEC_ID = -127, + STATUS_ERR_CODING_ERROR = -1, + STATUS_OK = 0, + }; + + CodecInterface(const types::LeAudioCodecId& codec_id); + virtual ~CodecInterface(); + static std::unique_ptr<CodecInterface> CreateInstance( + const types::LeAudioCodecId& codec_id) { + return std::make_unique<CodecInterface>(codec_id); + } + virtual CodecInterface::Status InitEncoder( + const LeAudioCodecConfiguration& pcm_config, + const LeAudioCodecConfiguration& codec_config); + virtual CodecInterface::Status InitDecoder( + const LeAudioCodecConfiguration& codec_config, + const LeAudioCodecConfiguration& pcm_config); + virtual CodecInterface::Status Encode( + const uint8_t* data, int stride, uint16_t out_size, + std::vector<int16_t>* out_buffer = nullptr, uint16_t out_offset = 0); + virtual CodecInterface::Status Decode(uint8_t* data, uint16_t size); + virtual void Cleanup(); + virtual bool IsReady(); + virtual uint16_t GetNumOfSamplesPerChannel(); + virtual uint8_t GetNumOfBytesPerSample(); + virtual std::vector<int16_t>& GetDecodedSamples(); + + private: + struct Impl; + Impl* impl; +}; +} // namespace le_audio diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc index c104a4ecc3ae489c94f88ffac170efc7f0694780..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>> @@ -69,8 +84,7 @@ static std::unordered_map< struct codec_manager_impl { public: - codec_manager_impl( - const std::vector<btle_audio_codec_config_t>& offloading_preference) { + codec_manager_impl() { offload_enable_ = osi_property_get_bool( "ro.bluetooth.leaudio_offload.supported", false) && !osi_property_get_bool( @@ -95,9 +109,13 @@ struct codec_manager_impl { kIsoDataPathPlatformDefault, {}); btm_configure_data_path(btm_data_direction::CONTROLLER_TO_HOST, kIsoDataPathPlatformDefault, {}); - UpdateOffloadCapability(offloading_preference); SetCodecLocation(CodecLocation::ADSP); } + void start( + const std::vector<btle_audio_codec_config_t>& offloading_preference) { + le_audio::AudioSetConfigurationProvider::Initialize(GetCodecLocation()); + UpdateOffloadCapability(offloading_preference); + } ~codec_manager_impl() { if (GetCodecLocation() != CodecLocation::HOST) { btm_configure_data_path(btm_data_direction::HOST_TO_CONTROLLER, @@ -105,58 +123,47 @@ struct codec_manager_impl { btm_configure_data_path(btm_data_direction::CONTROLLER_TO_HOST, kIsoDataPathHci, {}); } + le_audio::AudioSetConfigurationProvider::Cleanup(); } 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.sink_streams.empty()) return; - - if (stream_conf.sink_is_initial || - LeAudioHalVerifier::SupportsStreamActiveApi()) { - sink_config.stream_map = - stream_conf.sink_offloader_streams_target_allocation; - } else { - sink_config.stream_map = - stream_conf.sink_offloader_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.sink_sample_frequency_hz; - sink_config.frame_duration = stream_conf.sink_frame_duration_us; - sink_config.octets_per_frame = stream_conf.sink_octets_per_codec_frame; - sink_config.blocks_per_sdu = stream_conf.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.source_streams.empty()) return; - - if (stream_conf.source_is_initial || - LeAudioHalVerifier::SupportsStreamActiveApi()) { - source_config.stream_map = - stream_conf.source_offloader_streams_target_allocation; - } else { - source_config.stream_map = - stream_conf.source_offloader_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.source_sample_frequency_hz; - source_config.frame_duration = stream_conf.source_frame_duration_us; - source_config.octets_per_frame = stream_conf.source_octets_per_codec_frame; - source_config.blocks_per_sdu = - stream_conf.source_codec_frames_blocks_per_sdu; - source_config.peer_delay_ms = delay_ms; - update_receiver(source_config); } const AudioSetConfigurations* GetOffloadCodecConfig( @@ -257,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; @@ -354,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; } @@ -394,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_; @@ -411,8 +531,8 @@ struct CodecManager::impl { void Start( const std::vector<btle_audio_codec_config_t>& offloading_preference) { LOG_ASSERT(!codec_manager_impl_); - codec_manager_impl_ = - std::make_unique<codec_manager_impl>(offloading_preference); + codec_manager_impl_ = std::make_unique<codec_manager_impl>(); + codec_manager_impl_->start(offloading_preference); } void Stop() { @@ -445,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) { - 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) { +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_->UpdateActiveSinkAudioConfig( - stream_conf, delay_ms, update_receiver); + pimpl_->codec_manager_impl_->UpdateActiveAudioConfig( + stream_params, delays_ms, update_receiver); } const AudioSetConfigurations* CodecManager::GetOffloadCodecConfig( @@ -491,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 8e2a51b0156893d1b6955a2352d20686903cf0ec..1159735725a847a745dd4cff13b5cc470992f32e 100644 --- a/system/bta/le_audio/codec_manager.h +++ b/system/bta/le_audio/codec_manager.h @@ -16,10 +16,22 @@ #pragma once +#include "hardware/bt_le_audio.h" #include "le_audio_types.h" 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; @@ -44,7 +56,6 @@ struct broadcast_offload_config { class CodecManager { public: - CodecManager(); virtual ~CodecManager() = default; static CodecManager* GetInstance(void) { static CodecManager* instance = new CodecManager(); @@ -54,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* @@ -72,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 1aed2b6c5928552ffc348699393cb30d2f1d5879..0feb62bfc3438aac7aac4e500e1f3e279077d954 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -158,30 +158,36 @@ int LeAudioDeviceGroup::NumOfConnected( void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) { LOG_INFO("Group %p, group_id %d", this, group_id_); - stream_conf.sink_streams.clear(); - stream_conf.sink_offloader_streams_target_allocation.clear(); - stream_conf.sink_offloader_streams_current_allocation.clear(); - stream_conf.sink_audio_channel_allocation = 0; - stream_conf.sink_num_of_channels = 0; - stream_conf.sink_num_of_devices = 0; - stream_conf.sink_sample_frequency_hz = 0; - stream_conf.sink_codec_frames_blocks_per_sdu = 0; - stream_conf.sink_octets_per_codec_frame = 0; - stream_conf.sink_frame_duration_us = 0; + auto direction = types::kLeAudioDirectionSink; + + auto& stream_params = stream_conf.stream_params.get(direction); + stream_params.stream_locations.clear(); + stream_params.audio_channel_allocation = 0; + stream_params.num_of_channels = 0; + stream_params.num_of_devices = 0; + stream_params.sample_frequency_hz = 0; + stream_params.codec_frames_blocks_per_sdu = 0; + stream_params.octets_per_codec_frame = 0; + stream_params.frame_duration_us = 0; + + CodecManager::GetInstance()->ClearCisConfiguration(direction); } void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) { LOG_INFO("Group %p, group_id %d", this, group_id_); - stream_conf.source_streams.clear(); - stream_conf.source_offloader_streams_target_allocation.clear(); - stream_conf.source_offloader_streams_current_allocation.clear(); - stream_conf.source_audio_channel_allocation = 0; - stream_conf.source_num_of_channels = 0; - stream_conf.source_num_of_devices = 0; - stream_conf.source_sample_frequency_hz = 0; - stream_conf.source_codec_frames_blocks_per_sdu = 0; - stream_conf.source_octets_per_codec_frame = 0; - stream_conf.source_frame_duration_us = 0; + auto direction = types::kLeAudioDirectionSource; + + auto& stream_params = stream_conf.stream_params.get(direction); + stream_params.stream_locations.clear(); + stream_params.audio_channel_allocation = 0; + stream_params.num_of_channels = 0; + stream_params.num_of_devices = 0; + stream_params.sample_frequency_hz = 0; + stream_params.codec_frames_blocks_per_sdu = 0; + stream_params.octets_per_codec_frame = 0; + stream_params.frame_duration_us = 0; + + CodecManager::GetInstance()->ClearCisConfiguration(direction); } void LeAudioDeviceGroup::CigClearCis(void) { @@ -194,26 +200,32 @@ void LeAudioDeviceGroup::CigClearCis(void) { void LeAudioDeviceGroup::Cleanup(void) { /* Bluetooth is off while streaming - disconnect CISes and remove CIG */ if (GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { - if (!stream_conf.sink_streams.empty()) { - for (auto [cis_handle, audio_location] : stream_conf.sink_streams) { + auto& sink_stream_locations = + stream_conf.stream_params.sink.stream_locations; + auto& source_stream_locations = + stream_conf.stream_params.source.stream_locations; + + if (!sink_stream_locations.empty()) { + for (const auto kv_pair : sink_stream_locations) { + auto cis_handle = kv_pair.first; bluetooth::hci::IsoManager::GetInstance()->DisconnectCis( cis_handle, HCI_ERR_PEER_USER); - if (stream_conf.source_streams.empty()) { + /* Check the other direction if disconnecting bidirectional CIS */ + if (source_stream_locations.empty()) { continue; } - uint16_t cis_hdl = cis_handle; - stream_conf.source_streams.erase( + source_stream_locations.erase( std::remove_if( - stream_conf.source_streams.begin(), - stream_conf.source_streams.end(), - [cis_hdl](auto& pair) { return pair.first == cis_hdl; }), - stream_conf.source_streams.end()); + source_stream_locations.begin(), source_stream_locations.end(), + [&cis_handle](auto& pair) { return pair.first == cis_handle; }), + source_stream_locations.end()); } } - if (!stream_conf.source_streams.empty()) { - for (auto [cis_handle, audio_location] : stream_conf.source_streams) { + /* Take care of the non-bidirectional CISes */ + if (!source_stream_locations.empty()) { + for (auto [cis_handle, _] : source_stream_locations) { bluetooth::hci::IsoManager::GetInstance()->DisconnectCis( cis_handle, HCI_ERR_PEER_USER); } @@ -1583,6 +1595,8 @@ bool LeAudioDevice::ConfigureAses( for (; needed_ase && ase; needed_ase--) { ase->active = true; ase->configured_for_context_type = context_type; + ase->is_codec_in_controller = ent.is_codec_in_controller; + ase->data_path_id = ent.data_path_id; active_ases++; /* In case of late connect, we could be here for STREAMING ase. @@ -1602,7 +1616,7 @@ bool LeAudioDevice::ConfigureAses( /*Let's choose audio channel allocation if not set */ ase->codec_config.audio_channel_allocation = PickAudioLocation(strategy, audio_locations, - group_audio_locations_memo.get_ref(ent.direction)); + group_audio_locations_memo.get(ent.direction)); /* Get default value if no requirement for specific frame blocks per sdu */ @@ -1881,25 +1895,20 @@ bool LeAudioDeviceGroup::IsMetadataChanged( } bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) const { + auto& sink_stream_locations = stream_conf.stream_params.sink.stream_locations; auto iter = std::find_if( - stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(), + sink_stream_locations.begin(), sink_stream_locations.end(), [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; }); - if (iter != stream_conf.sink_streams.end()) return true; + if (iter != sink_stream_locations.end()) return true; + auto& source_stream_locations = + stream_conf.stream_params.source.stream_locations; iter = std::find_if( - stream_conf.source_streams.begin(), stream_conf.source_streams.end(), + source_stream_locations.begin(), source_stream_locations.end(), [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; }); - return (iter != stream_conf.source_streams.end()); -} - -void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) { - if (direction == le_audio::types::kLeAudioDirectionSource) { - stream_conf.source_is_initial = false; - } else { - stream_conf.sink_is_initial = false; - } + return (iter != source_stream_locations.end()); } void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded( @@ -1908,185 +1917,67 @@ void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded( if (!IsCisPartOfCurrentStream(cis_conn_hdl)) return; - auto sink_channels = stream_conf.sink_num_of_channels; - auto source_channels = stream_conf.source_num_of_channels; - - if (!stream_conf.sink_streams.empty() || - !stream_conf.source_streams.empty()) { - stream_conf.sink_streams.erase( - std::remove_if( - stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(), - [leAudioDevice, &cis_conn_hdl, this](auto& pair) { - if (!cis_conn_hdl) { - cis_conn_hdl = pair.first; - } - auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl); - if (ases_pair.sink && cis_conn_hdl == pair.first) { - stream_conf.sink_num_of_devices--; - stream_conf.sink_num_of_channels -= - ases_pair.sink->codec_config.channel_count; - stream_conf.sink_audio_channel_allocation &= ~pair.second; - } - return (ases_pair.sink && cis_conn_hdl == pair.first); - }), - stream_conf.sink_streams.end()); + /* Cache the old values for comparison */ + auto old_sink_channels = stream_conf.stream_params.sink.num_of_channels; + auto old_source_channels = stream_conf.stream_params.source.num_of_channels; - stream_conf.source_streams.erase( + for (auto dir : + {types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) { + auto& params = stream_conf.stream_params.get(dir); + params.stream_locations.erase( std::remove_if( - stream_conf.source_streams.begin(), - stream_conf.source_streams.end(), - [leAudioDevice, &cis_conn_hdl, this](auto& pair) { + params.stream_locations.begin(), params.stream_locations.end(), + [leAudioDevice, &cis_conn_hdl, ¶ms, dir](auto& pair) { if (!cis_conn_hdl) { cis_conn_hdl = pair.first; } auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl); - if (ases_pair.source && cis_conn_hdl == pair.first) { - stream_conf.source_num_of_devices--; - stream_conf.source_num_of_channels -= - ases_pair.source->codec_config.channel_count; - stream_conf.source_audio_channel_allocation &= ~pair.second; + if (ases_pair.get(dir) && cis_conn_hdl == pair.first) { + params.num_of_devices--; + params.num_of_channels -= + ases_pair.get(dir)->codec_config.channel_count; + params.audio_channel_allocation &= ~pair.second; } - return (ases_pair.source && cis_conn_hdl == pair.first); + return (ases_pair.get(dir) && cis_conn_hdl == pair.first); }), - stream_conf.source_streams.end()); - - LOG_INFO( - " Sink Number Of Devices: %d" - ", Sink Number Of Channels: %d" - ", Source Number Of Devices: %d" - ", Source Number Of Channels: %d", - stream_conf.sink_num_of_devices, stream_conf.sink_num_of_channels, - stream_conf.source_num_of_devices, stream_conf.source_num_of_channels); + params.stream_locations.end()); } - if (stream_conf.sink_num_of_channels == 0) { + LOG_INFO( + " Sink Number Of Devices: %d" + ", Sink Number Of Channels: %d" + ", Source Number Of Devices: %d" + ", Source Number Of Channels: %d", + stream_conf.stream_params.sink.num_of_devices, + stream_conf.stream_params.sink.num_of_channels, + stream_conf.stream_params.source.num_of_devices, + stream_conf.stream_params.source.num_of_channels); + + if (stream_conf.stream_params.sink.num_of_channels == 0) { ClearSinksFromConfiguration(); } - if (stream_conf.source_num_of_channels == 0) { + if (stream_conf.stream_params.source.num_of_channels == 0) { ClearSourcesFromConfiguration(); } - /* Update offloader streams if needed */ - if (sink_channels > stream_conf.sink_num_of_channels) { - CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSink); + /* Update CodecManager CIS configuration */ + if (old_sink_channels > stream_conf.stream_params.sink.num_of_channels) { + CodecManager::GetInstance()->UpdateCisConfiguration( + cises_, + stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSink), + le_audio::types::kLeAudioDirectionSink); } - if (source_channels > stream_conf.source_num_of_channels) { - CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSource); + if (old_source_channels > stream_conf.stream_params.source.num_of_channels) { + 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.source_offloader_changed; - is_initial = &stream_conf.source_is_initial; - cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE; - streams = &stream_conf.source_streams; - offloader_streams_target_allocation = - &stream_conf.source_offloader_streams_target_allocation; - offloader_streams_current_allocation = - &stream_conf.source_offloader_streams_current_allocation; - tag = "Source"; - available_allocations = AdjustAllocationForOffloader( - stream_conf.source_audio_channel_allocation); - } else { - changed_flag = &stream_conf.sink_offloader_changed; - is_initial = &stream_conf.sink_is_initial; - cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK; - streams = &stream_conf.sink_streams; - offloader_streams_target_allocation = - &stream_conf.sink_offloader_streams_target_allocation; - offloader_streams_current_allocation = - &stream_conf.sink_offloader_streams_current_allocation; - tag = "Sink"; - available_allocations = - AdjustAllocationForOffloader(stream_conf.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& 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; } @@ -2362,11 +2253,12 @@ void LeAudioDeviceGroup::Dump(int fd, int active_group_id) const { << "\n" << " num of devices(connected): " << Size() << "(" << NumOfConnected() << ")\n" - << ", num of sinks(connected): " << stream_conf.sink_num_of_devices - << "(" << stream_conf.sink_streams.size() << ")\n" + << ", num of sinks(connected): " + << stream_conf.stream_params.sink.num_of_devices << "(" + << stream_conf.stream_params.sink.stream_locations.size() << ")\n" << " num of sources(connected): " - << stream_conf.source_num_of_devices << "(" - << stream_conf.source_streams.size() << ")\n" + << stream_conf.stream_params.source.num_of_devices << "(" + << stream_conf.stream_params.source.stream_locations.size() << ")\n" << " allocated CISes: " << static_cast<int>(cises_.size()); if (cises_.size() > 0) { diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h index 4c82563551cb30eb1a45116c249a35f52a9e761a..e1029e7d8b30aec453faf83b76a3ce3c8ed16535 100644 --- a/system/bta/le_audio/devices.h +++ b/system/bta/le_audio/devices.h @@ -186,9 +186,14 @@ class LeAudioDevice { bool reuse_cis_id); inline types::AudioContexts GetSupportedContexts( - int direction = (types::kLeAudioDirectionSink | - types::kLeAudioDirectionSource)) const { - return supp_contexts_.get(direction); + int direction = types::kLeAudioDirectionBoth) const { + ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth), + "Invalid direction used."); + + if (direction < types::kLeAudioDirectionBoth) + return supp_contexts_.get(direction); + else + return types::get_bidirectional(supp_contexts_); } inline void SetSupportedContexts( types::BidirectionalPair<types::AudioContexts> contexts) { @@ -196,9 +201,14 @@ class LeAudioDevice { } inline types::AudioContexts GetAvailableContexts( - int direction = (types::kLeAudioDirectionSink | - types::kLeAudioDirectionSource)) const { - return avail_contexts_.get(direction); + int direction = types::kLeAudioDirectionBoth) const { + ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth), + "Invalid direction used."); + + if (direction < types::kLeAudioDirectionBoth) + return avail_contexts_.get(direction); + else + return types::get_bidirectional(avail_contexts_); } void SetAvailableContexts( types::BidirectionalPair<types::AudioContexts> cont_val); @@ -406,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); @@ -475,21 +483,25 @@ class LeAudioDeviceGroup { group_available_contexts_.source.to_string().c_str()); } - inline types::AudioContexts GetAvailableContexts( - int direction = (types::kLeAudioDirectionSink | - types::kLeAudioDirectionSource)) const { - LOG_DEBUG( - " group id: %d, available contexts sink: %s, available contexts " - "source: " - "%s", - group_id_, group_available_contexts_.sink.to_string().c_str(), - group_available_contexts_.source.to_string().c_str()); - return group_available_contexts_.get(direction); + types::AudioContexts GetAvailableContexts( + int direction = types::kLeAudioDirectionBoth) const { + ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth), + "Invalid direction used."); + if (direction < types::kLeAudioDirectionBoth) { + LOG_DEBUG( + " group id: %d, available contexts sink: %s, available contexts " + "source: " + "%s", + group_id_, group_available_contexts_.sink.to_string().c_str(), + group_available_contexts_.source.to_string().c_str()); + return group_available_contexts_.get(direction); + } else { + return types::get_bidirectional(group_available_contexts_); + } } types::AudioContexts GetSupportedContexts( - int direction = (types::kLeAudioDirectionSink | - types::kLeAudioDirectionSource)) const; + int direction = types::kLeAudioDirectionBoth) const; types::BidirectionalPair<types::AudioContexts> GetLatestAvailableContexts( void) const; diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc index 64be816ac4daf1f1ba402112592a154506132ab4..89ff7c604186882a69d642ee1d9d739210c33bd2 100644 --- a/system/bta/le_audio/devices_test.cc +++ b/system/bta/le_audio/devices_test.cc @@ -431,7 +431,8 @@ class LeAudioAseConfigurationTest : public Test { group_ = new LeAudioDeviceGroup(group_id_); bluetooth::manager::SetMockBtmInterface(&btm_interface_); controller::SetMockControllerInterface(&controller_interface_); - ::le_audio::AudioSetConfigurationProvider::Initialize(); + ::le_audio::AudioSetConfigurationProvider::Initialize( + ::le_audio::types::CodecLocation::ADSP); MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_); ON_CALL(mock_csis_client_module_, Get()) .WillByDefault(Return(&mock_csis_client_module_)); diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index e8309a2358a991116c41f143cd9de6573ad97793..b8e670f6cf4a202884ef8ca7aa8369adad074d7f 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -813,19 +813,21 @@ class UnicastTestNoInit : public Test { /* Copied from state_machine.cc ProcessHciNotifSetupIsoDataPath */ if (ase.direction == le_audio::types::kLeAudioDirectionSource) { - auto iter = std::find_if(stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), - [cis_conn_hdl](auto& pair) { - return cis_conn_hdl == pair.first; - }); - - if (iter == stream_conf->source_streams.end()) { - stream_conf->source_streams.emplace_back( + auto iter = std::find_if( + stream_conf->stream_params.source.stream_locations.begin(), + stream_conf->stream_params.source.stream_locations.end(), + [cis_conn_hdl](auto& pair) { + return cis_conn_hdl == pair.first; + }); + + if (iter == + stream_conf->stream_params.source.stream_locations.end()) { + stream_conf->stream_params.source.stream_locations.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); - stream_conf->source_num_of_devices++; - stream_conf->source_num_of_channels += + stream_conf->stream_params.source.num_of_devices++; + stream_conf->stream_params.source.num_of_channels += ase.codec_config.channel_count; LOG_INFO( @@ -836,23 +838,25 @@ class UnicastTestNoInit : public Test { ", Source Number Of Channels: %d", +ase.cis_conn_hdl, +(*ase.codec_config.audio_channel_allocation), - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); + +stream_conf->stream_params.source.num_of_devices, + +stream_conf->stream_params.source.num_of_channels); } } else { - auto iter = std::find_if(stream_conf->sink_streams.begin(), - stream_conf->sink_streams.end(), - [cis_conn_hdl](auto& pair) { - return cis_conn_hdl == pair.first; - }); - - if (iter == stream_conf->sink_streams.end()) { - stream_conf->sink_streams.emplace_back( + auto iter = std::find_if( + stream_conf->stream_params.sink.stream_locations.begin(), + stream_conf->stream_params.sink.stream_locations.end(), + [cis_conn_hdl](auto& pair) { + return cis_conn_hdl == pair.first; + }); + + if (iter == + stream_conf->stream_params.sink.stream_locations.end()) { + stream_conf->stream_params.sink.stream_locations.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); - stream_conf->sink_num_of_devices++; - stream_conf->sink_num_of_channels += + stream_conf->stream_params.sink.num_of_devices++; + stream_conf->stream_params.sink.num_of_channels += ase.codec_config.channel_count; LOG_INFO( @@ -863,8 +867,8 @@ class UnicastTestNoInit : public Test { ", Sink Number Of Channels: %d", +ase.cis_conn_hdl, +(*ase.codec_config.audio_channel_allocation), - +stream_conf->sink_num_of_devices, - +stream_conf->sink_num_of_channels); + +stream_conf->stream_params.sink.num_of_devices, + +stream_conf->stream_params.sink.num_of_channels); } } } @@ -939,54 +943,66 @@ class UnicastTestNoInit : public Test { /* Copied from state_machine.cc ProcessHciNotifSetupIsoDataPath */ if (ase.direction == le_audio::types::kLeAudioDirectionSource) { - auto iter = std::find_if(stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), - [cis_conn_hdl](auto& pair) { - return cis_conn_hdl == pair.first; - }); - - if (iter == stream_conf->source_streams.end()) { - stream_conf->source_streams.emplace_back(std::make_pair( - ase.cis_conn_hdl, - *ase.codec_config.audio_channel_allocation)); - - stream_conf->source_num_of_devices++; - stream_conf->source_num_of_channels += + auto iter = std::find_if( + stream_conf->stream_params.source.stream_locations.begin(), + stream_conf->stream_params.source.stream_locations.end(), + [cis_conn_hdl](auto& pair) { + return cis_conn_hdl == pair.first; + }); + + if (iter == + stream_conf->stream_params.source.stream_locations.end()) { + stream_conf->stream_params.source.stream_locations + .emplace_back(std::make_pair( + ase.cis_conn_hdl, + *ase.codec_config.audio_channel_allocation)); + + stream_conf->stream_params.source.num_of_devices++; + stream_conf->stream_params.source.num_of_channels += ase.codec_config.channel_count; - stream_conf->source_audio_channel_allocation |= + stream_conf->stream_params.source.audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; - if (stream_conf->source_sample_frequency_hz == 0) { - stream_conf->source_sample_frequency_hz = + if (stream_conf->stream_params.source.sample_frequency_hz == + 0) { + stream_conf->stream_params.source.sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { - ASSERT_LOG(stream_conf->source_sample_frequency_hz == - ase.codec_config.GetSamplingFrequencyHz(), - "sample freq mismatch: %d!=%d", - stream_conf->source_sample_frequency_hz, - ase.codec_config.GetSamplingFrequencyHz()); + ASSERT_LOG( + stream_conf->stream_params.source.sample_frequency_hz == + ase.codec_config.GetSamplingFrequencyHz(), + "sample freq mismatch: %d!=%d", + stream_conf->stream_params.source.sample_frequency_hz, + ase.codec_config.GetSamplingFrequencyHz()); } - if (stream_conf->source_octets_per_codec_frame == 0) { - stream_conf->source_octets_per_codec_frame = + if (stream_conf->stream_params.source + .octets_per_codec_frame == 0) { + stream_conf->stream_params.source.octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { - ASSERT_LOG(stream_conf->source_octets_per_codec_frame == + ASSERT_LOG(stream_conf->stream_params.source + .octets_per_codec_frame == *ase.codec_config.octets_per_codec_frame, "octets per frame mismatch: %d!=%d", - stream_conf->source_octets_per_codec_frame, + stream_conf->stream_params.source + .octets_per_codec_frame, *ase.codec_config.octets_per_codec_frame); } - if (stream_conf->source_codec_frames_blocks_per_sdu == 0) { - stream_conf->source_codec_frames_blocks_per_sdu = + if (stream_conf->stream_params.source + .codec_frames_blocks_per_sdu == 0) { + stream_conf->stream_params.source + .codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( - stream_conf->source_codec_frames_blocks_per_sdu == + stream_conf->stream_params.source + .codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", - stream_conf->source_codec_frames_blocks_per_sdu, + stream_conf->stream_params.source + .codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } @@ -998,59 +1014,71 @@ class UnicastTestNoInit : public Test { ", Source Number Of Channels: %d", +ase.cis_conn_hdl, +(*ase.codec_config.audio_channel_allocation), - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); + +stream_conf->stream_params.source.num_of_devices, + +stream_conf->stream_params.source.num_of_channels); } } else { - auto iter = std::find_if(stream_conf->sink_streams.begin(), - stream_conf->sink_streams.end(), - [cis_conn_hdl](auto& pair) { - return cis_conn_hdl == pair.first; - }); - - if (iter == stream_conf->sink_streams.end()) { - stream_conf->sink_streams.emplace_back(std::make_pair( - ase.cis_conn_hdl, - *ase.codec_config.audio_channel_allocation)); - - stream_conf->sink_num_of_devices++; - stream_conf->sink_num_of_channels += + auto iter = std::find_if( + stream_conf->stream_params.sink.stream_locations.begin(), + stream_conf->stream_params.sink.stream_locations.end(), + [cis_conn_hdl](auto& pair) { + return cis_conn_hdl == pair.first; + }); + + if (iter == + stream_conf->stream_params.sink.stream_locations.end()) { + stream_conf->stream_params.sink.stream_locations.emplace_back( + std::make_pair( + ase.cis_conn_hdl, + *ase.codec_config.audio_channel_allocation)); + + stream_conf->stream_params.sink.num_of_devices++; + stream_conf->stream_params.sink.num_of_channels += ase.codec_config.channel_count; - stream_conf->sink_audio_channel_allocation |= + stream_conf->stream_params.sink.audio_channel_allocation |= *ase.codec_config.audio_channel_allocation; - if (stream_conf->sink_sample_frequency_hz == 0) { - stream_conf->sink_sample_frequency_hz = + if (stream_conf->stream_params.sink.sample_frequency_hz == + 0) { + stream_conf->stream_params.sink.sample_frequency_hz = ase.codec_config.GetSamplingFrequencyHz(); } else { - ASSERT_LOG(stream_conf->sink_sample_frequency_hz == - ase.codec_config.GetSamplingFrequencyHz(), - "sample freq mismatch: %d!=%d", - stream_conf->sink_sample_frequency_hz, - ase.codec_config.GetSamplingFrequencyHz()); + ASSERT_LOG( + stream_conf->stream_params.sink.sample_frequency_hz == + ase.codec_config.GetSamplingFrequencyHz(), + "sample freq mismatch: %d!=%d", + stream_conf->stream_params.sink.sample_frequency_hz, + ase.codec_config.GetSamplingFrequencyHz()); } - if (stream_conf->sink_octets_per_codec_frame == 0) { - stream_conf->sink_octets_per_codec_frame = + if (stream_conf->stream_params.sink.octets_per_codec_frame == + 0) { + stream_conf->stream_params.sink.octets_per_codec_frame = *ase.codec_config.octets_per_codec_frame; } else { - ASSERT_LOG(stream_conf->sink_octets_per_codec_frame == - *ase.codec_config.octets_per_codec_frame, - "octets per frame mismatch: %d!=%d", - stream_conf->sink_octets_per_codec_frame, - *ase.codec_config.octets_per_codec_frame); + ASSERT_LOG( + stream_conf->stream_params.sink + .octets_per_codec_frame == + *ase.codec_config.octets_per_codec_frame, + "octets per frame mismatch: %d!=%d", + stream_conf->stream_params.sink.octets_per_codec_frame, + *ase.codec_config.octets_per_codec_frame); } - if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) { - stream_conf->sink_codec_frames_blocks_per_sdu = + if (stream_conf->stream_params.sink + .codec_frames_blocks_per_sdu == 0) { + stream_conf->stream_params.sink + .codec_frames_blocks_per_sdu = *ase.codec_config.codec_frames_blocks_per_sdu; } else { ASSERT_LOG( - stream_conf->sink_codec_frames_blocks_per_sdu == + stream_conf->stream_params.sink + .codec_frames_blocks_per_sdu == *ase.codec_config.codec_frames_blocks_per_sdu, "codec_frames_blocks_per_sdu: %d!=%d", - stream_conf->sink_codec_frames_blocks_per_sdu, + stream_conf->stream_params.sink + .codec_frames_blocks_per_sdu, *ase.codec_config.codec_frames_blocks_per_sdu); } @@ -1062,8 +1090,8 @@ class UnicastTestNoInit : public Test { ", Sink Number Of Channels: %d", +ase.cis_conn_hdl, +(*ase.codec_config.audio_channel_allocation), - +stream_conf->sink_num_of_devices, - +stream_conf->sink_num_of_channels); + +stream_conf->stream_params.sink.num_of_devices, + +stream_conf->stream_params.sink.num_of_channels); } } } @@ -1118,49 +1146,51 @@ class UnicastTestNoInit : public Test { LeAudioDevice* leAudioDevice) { if (!group) return; auto* stream_conf = &group->stream_conf; - if (!stream_conf->sink_streams.empty() || - !stream_conf->source_streams.empty()) { - stream_conf->sink_streams.erase( - std::remove_if(stream_conf->sink_streams.begin(), - stream_conf->sink_streams.end(), - [leAudioDevice, &stream_conf](auto& pair) { - auto ases = leAudioDevice->GetAsesByCisConnHdl( - pair.first); - if (ases.sink) { - stream_conf->sink_num_of_devices--; - stream_conf->sink_num_of_channels -= - ases.sink->codec_config.channel_count; - - LOG_INFO( - ", Source Number Of Devices: %d" - ", Source Number Of Channels: %d", - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); - } - return ases.sink; - }), - stream_conf->sink_streams.end()); - - stream_conf->source_streams.erase( - std::remove_if(stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), - [leAudioDevice, &stream_conf](auto& pair) { - auto ases = leAudioDevice->GetAsesByCisConnHdl( - pair.first); - if (ases.source) { - stream_conf->source_num_of_devices--; - stream_conf->source_num_of_channels -= - ases.source->codec_config.channel_count; - - LOG_INFO( - ", Source Number Of Devices: %d" - ", Source Number Of Channels: %d", - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); - } - return ases.source; - }), - stream_conf->source_streams.end()); + if (!stream_conf->stream_params.sink.stream_locations.empty() || + !stream_conf->stream_params.source.stream_locations.empty()) { + stream_conf->stream_params.sink.stream_locations.erase( + std::remove_if( + stream_conf->stream_params.sink.stream_locations.begin(), + stream_conf->stream_params.sink.stream_locations.end(), + [leAudioDevice, &stream_conf](auto& pair) { + auto ases = + leAudioDevice->GetAsesByCisConnHdl(pair.first); + if (ases.sink) { + stream_conf->stream_params.sink.num_of_devices--; + stream_conf->stream_params.sink.num_of_channels -= + ases.sink->codec_config.channel_count; + + LOG_INFO( + ", Source Number Of Devices: %d" + ", Source Number Of Channels: %d", + +stream_conf->stream_params.source.num_of_devices, + +stream_conf->stream_params.source.num_of_channels); + } + return ases.sink; + }), + stream_conf->stream_params.sink.stream_locations.end()); + + stream_conf->stream_params.source.stream_locations.erase( + std::remove_if( + stream_conf->stream_params.source.stream_locations.begin(), + stream_conf->stream_params.source.stream_locations.end(), + [leAudioDevice, &stream_conf](auto& pair) { + auto ases = + leAudioDevice->GetAsesByCisConnHdl(pair.first); + if (ases.source) { + stream_conf->stream_params.source.num_of_devices--; + stream_conf->stream_params.source.num_of_channels -= + ases.source->codec_config.channel_count; + + LOG_INFO( + ", Source Number Of Devices: %d" + ", Source Number Of Channels: %d", + +stream_conf->stream_params.source.num_of_devices, + +stream_conf->stream_params.source.num_of_channels); + } + return ases.source; + }), + stream_conf->stream_params.source.stream_locations.end()); } group->CigUnassignCis(leAudioDevice); @@ -1190,12 +1220,13 @@ class UnicastTestNoInit : public Test { } /* Invalidate stream configuration if needed */ auto* stream_conf = &group->stream_conf; - if (!stream_conf->sink_streams.empty() || - !stream_conf->source_streams.empty()) { - stream_conf->sink_streams.erase( + if (!stream_conf->stream_params.sink.stream_locations.empty() || + !stream_conf->stream_params.source.stream_locations.empty()) { + stream_conf->stream_params.sink.stream_locations.erase( std::remove_if( - stream_conf->sink_streams.begin(), - stream_conf->sink_streams.end(), + stream_conf->stream_params.sink.stream_locations + .begin(), + stream_conf->stream_params.sink.stream_locations.end(), [leAudioDevice, &stream_conf](auto& pair) { auto ases = leAudioDevice->GetAsesByCisConnHdl(pair.first); @@ -1205,24 +1236,27 @@ class UnicastTestNoInit : public Test { ", ase pointer: %p", +(int)(pair.first), +ases.sink); if (ases.sink) { - stream_conf->sink_num_of_devices--; - stream_conf->sink_num_of_channels -= + stream_conf->stream_params.sink.num_of_devices--; + stream_conf->stream_params.sink.num_of_channels -= ases.sink->codec_config.channel_count; LOG_INFO( " Sink Number Of Devices: %d" ", Sink Number Of Channels: %d", - +stream_conf->sink_num_of_devices, - +stream_conf->sink_num_of_channels); + +stream_conf->stream_params.sink.num_of_devices, + +stream_conf->stream_params.sink + .num_of_channels); } return ases.sink; }), - stream_conf->sink_streams.end()); + stream_conf->stream_params.sink.stream_locations.end()); - stream_conf->source_streams.erase( + stream_conf->stream_params.source.stream_locations.erase( std::remove_if( - stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), + stream_conf->stream_params.source.stream_locations + .begin(), + stream_conf->stream_params.source.stream_locations + .end(), [leAudioDevice, &stream_conf](auto& pair) { auto ases = leAudioDevice->GetAsesByCisConnHdl(pair.first); @@ -1232,19 +1266,21 @@ class UnicastTestNoInit : public Test { ", ase pointer: %p", +(int)(pair.first), ases.source); if (ases.source) { - stream_conf->source_num_of_devices--; - stream_conf->source_num_of_channels -= + stream_conf->stream_params.source.num_of_devices--; + stream_conf->stream_params.source.num_of_channels -= ases.source->codec_config.channel_count; LOG_INFO( ", Source Number Of Devices: %d" ", Source Number Of Channels: %d", - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); + +stream_conf->stream_params.source + .num_of_devices, + +stream_conf->stream_params.source + .num_of_channels); } return ases.source; }), - stream_conf->source_streams.end()); + stream_conf->stream_params.source.stream_locations.end()); } group->CigUnassignCis(leAudioDevice); @@ -1256,38 +1292,39 @@ class UnicastTestNoInit : public Test { device != nullptr; device = group->GetNextDevice(device)) { /* Invalidate stream configuration if needed */ auto* stream_conf = &group->stream_conf; - if (!stream_conf->sink_streams.empty() || - !stream_conf->source_streams.empty()) { - stream_conf->sink_streams.erase( - std::remove_if(stream_conf->sink_streams.begin(), - stream_conf->sink_streams.end(), - [device, &stream_conf](auto& pair) { - auto ases = - device->GetAsesByCisConnHdl(pair.first); - - LOG_INFO( - ", sink ase to delete. Cis handle: %d" - ", ase pointer: %p", - +(int)(pair.first), +ases.sink); - if (ases.sink) { - stream_conf->sink_num_of_devices--; - stream_conf->sink_num_of_channels -= - ases.sink->codec_config.channel_count; - - LOG_INFO( - " Sink Number Of Devices: %d" - ", Sink Number Of Channels: %d", - +stream_conf->sink_num_of_devices, - +stream_conf->sink_num_of_channels); - } - return ases.sink; - }), - stream_conf->sink_streams.end()); - - stream_conf->source_streams.erase( + if (!stream_conf->stream_params.sink.stream_locations.empty() || + !stream_conf->stream_params.source.stream_locations.empty()) { + stream_conf->stream_params.sink.stream_locations.erase( + std::remove_if( + stream_conf->stream_params.sink.stream_locations.begin(), + stream_conf->stream_params.sink.stream_locations.end(), + [device, &stream_conf](auto& pair) { + auto ases = device->GetAsesByCisConnHdl(pair.first); + + LOG_INFO( + ", sink ase to delete. Cis handle: %d" + ", ase pointer: %p", + +(int)(pair.first), +ases.sink); + if (ases.sink) { + stream_conf->stream_params.sink.num_of_devices--; + stream_conf->stream_params.sink.num_of_channels -= + ases.sink->codec_config.channel_count; + + LOG_INFO( + " Sink Number Of Devices: %d" + ", Sink Number Of Channels: %d", + +stream_conf->stream_params.sink.num_of_devices, + +stream_conf->stream_params.sink.num_of_channels); + } + return ases.sink; + }), + stream_conf->stream_params.sink.stream_locations.end()); + + stream_conf->stream_params.source.stream_locations.erase( std::remove_if( - stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), + stream_conf->stream_params.source.stream_locations + .begin(), + stream_conf->stream_params.source.stream_locations.end(), [device, &stream_conf](auto& pair) { auto ases = device->GetAsesByCisConnHdl(pair.first); @@ -1296,19 +1333,20 @@ class UnicastTestNoInit : public Test { ", ase pointer: %p", +(int)(pair.first), +ases.source); if (ases.source) { - stream_conf->source_num_of_devices--; - stream_conf->source_num_of_channels -= + stream_conf->stream_params.source.num_of_devices--; + stream_conf->stream_params.source.num_of_channels -= ases.source->codec_config.channel_count; LOG_INFO( ", Source Number Of Devices: %d" ", Source Number Of Channels: %d", - +stream_conf->source_num_of_devices, - +stream_conf->source_num_of_channels); + +stream_conf->stream_params.source.num_of_devices, + +stream_conf->stream_params.source + .num_of_channels); } return ases.source; }), - stream_conf->source_streams.end()); + stream_conf->stream_params.source.stream_locations.end()); } group->CigUnassignCis(device); @@ -1369,7 +1407,8 @@ class UnicastTestNoInit : public Test { available_src_context_types_ = 0xffff; supported_snk_context_types_ = 0xffff; supported_src_context_types_ = 0xffff; - le_audio::AudioSetConfigurationProvider::Initialize(); + le_audio::AudioSetConfigurationProvider::Initialize( + ::le_audio::types::CodecLocation::ADSP); ASSERT_FALSE(LeAudioClient::IsLeAudioClientRunning()); } @@ -1399,7 +1438,7 @@ class UnicastTestNoInit : public Test { if (LeAudioClient::IsLeAudioClientRunning()) { EXPECT_CALL(mock_gatt_interface_, AppDeregister(gatt_if)).Times(1); - LeAudioClient::Cleanup(base::DoNothing()); + LeAudioClient::Cleanup(); ASSERT_FALSE(LeAudioClient::IsLeAudioClientRunning()); } diff --git a/system/bta/le_audio/le_audio_set_configuration_provider.cc b/system/bta/le_audio/le_audio_set_configuration_provider.cc deleted file mode 100644 index 1ffc04253065317544d6f33fcba6902ad9d1f990..0000000000000000000000000000000000000000 --- a/system/bta/le_audio/le_audio_set_configuration_provider.cc +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 "le_audio_set_configuration_provider.h" - -#include "bta_le_audio_api.h" - -void LeAudioClient::InitializeAudioSetConfigurationProvider(void) { - le_audio::AudioSetConfigurationProvider::Initialize(); -} - -void LeAudioClient::CleanupAudioSetConfigurationProvider(void) { - le_audio::AudioSetConfigurationProvider::Cleanup(); -} diff --git a/system/bta/le_audio/le_audio_set_configuration_provider.h b/system/bta/le_audio/le_audio_set_configuration_provider.h index 7310868d68bae4046fbb1fe744d5cfcf6f10b188..8ece4f217969c15e103c72703063cc0baa0046e9 100644 --- a/system/bta/le_audio/le_audio_set_configuration_provider.h +++ b/system/bta/le_audio/le_audio_set_configuration_provider.h @@ -27,7 +27,7 @@ class AudioSetConfigurationProvider { AudioSetConfigurationProvider(); virtual ~AudioSetConfigurationProvider() = default; static AudioSetConfigurationProvider* Get(); - static void Initialize(); + static void Initialize(types::CodecLocation location); static void DebugDump(int fd); static void Cleanup(); virtual const set_configurations::AudioSetConfigurations* GetConfigurations( diff --git a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc index 91a6bd5b37449df85b37a18ce072e40a9190a3ca..6f75ad1a5ab761de0435727c7056a9fe225b1b47 100644 --- a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc +++ b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc @@ -70,12 +70,12 @@ static const std::vector< struct AudioSetConfigurationProviderJson { static constexpr auto kDefaultScenario = "Media"; - AudioSetConfigurationProviderJson() { + AudioSetConfigurationProviderJson(types::CodecLocation location) { dual_swb_bidirection_supported_ = osi_property_get_bool( "persist.bluetooth.leaudio_dual_bidirection_swb." "supported", true); - ASSERT_LOG(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios), + ASSERT_LOG(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios, location), ": Unable to load le audio set configuration files."); } @@ -276,7 +276,7 @@ struct AudioSetConfigurationProviderJson { SetConfiguration SetConfigurationFromFlatSubconfig( const bluetooth::le_audio::AudioSetSubConfiguration* flat_subconfig, - QosConfigSetting qos) { + QosConfigSetting qos, types::CodecLocation location) { auto strategy_int = static_cast<int>(flat_subconfig->configuration_strategy()); @@ -290,12 +290,32 @@ struct AudioSetConfigurationProviderJson { ? static_cast<types::LeAudioConfigurationStrategy>(strategy_int) : types::LeAudioConfigurationStrategy::RFU; - return SetConfiguration( + auto config = SetConfiguration( flat_subconfig->direction(), flat_subconfig->device_cnt(), flat_subconfig->ase_cnt(), CodecCapabilitySettingFromFlat(flat_subconfig->codec_id(), flat_subconfig->codec_configuration()), qos, strategy); + + // Note that these parameters are set here since for now, we are using the + // common configuration source for all the codec locations. + switch (location) { + case types::CodecLocation::ADSP: + config.is_codec_in_controller = false; + config.data_path_id = + bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault; + break; + case types::CodecLocation::HOST: + config.is_codec_in_controller = false; + config.data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci; + break; + case types::CodecLocation::CONTROLLER: + config.is_codec_in_controller = true; + config.data_path_id = + bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault; + break; + } + return config; } static uint8_t ValidateTargetLatency(int flat_target_latency) { @@ -313,7 +333,8 @@ struct AudioSetConfigurationProviderJson { AudioSetConfiguration AudioSetConfigurationFromFlat( const bluetooth::le_audio::AudioSetConfiguration* flat_cfg, std::vector<const bluetooth::le_audio::CodecConfiguration*>* codec_cfgs, - std::vector<const bluetooth::le_audio::QosConfiguration*>* qos_cfgs) { + std::vector<const bluetooth::le_audio::QosConfiguration*>* qos_cfgs, + types::CodecLocation location) { ASSERT_LOG(flat_cfg != nullptr, "flat_cfg cannot be null"); std::string codec_config_key = flat_cfg->codec_config_name()->str(); auto* qos_config_key_array = flat_cfg->qos_config_name(); @@ -395,13 +416,13 @@ struct AudioSetConfigurationProviderJson { /* Load subconfigurations */ for (auto subconfig : *codec_cfg->subconfigurations()) { if (subconfig->direction() == le_audio::types::kLeAudioDirectionSink) { - processSubconfig(*subconfig, qos_sink, - dual_dev_one_chan_stereo_sink_swb, - single_dev_one_chan_stereo_sink_swb, subconfigs); + processSubconfig( + *subconfig, qos_sink, dual_dev_one_chan_stereo_sink_swb, + single_dev_one_chan_stereo_sink_swb, subconfigs, location); } else { - processSubconfig(*subconfig, qos_source, - dual_dev_one_chan_stereo_source_swb, - single_dev_one_chan_stereo_source_swb, subconfigs); + processSubconfig( + *subconfig, qos_source, dual_dev_one_chan_stereo_source_swb, + single_dev_one_chan_stereo_source_swb, subconfigs, location); } } } else { @@ -430,9 +451,10 @@ struct AudioSetConfigurationProviderJson { const bluetooth::le_audio::AudioSetSubConfiguration& subconfig, const QosConfigSetting& qos_setting, bool& dual_dev_one_chan_stereo_swb, bool& single_dev_one_chan_stereo_swb, - std::vector<SetConfiguration>& subconfigs) { + std::vector<SetConfiguration>& subconfigs, + types::CodecLocation location) { subconfigs.push_back( - SetConfigurationFromFlatSubconfig(&subconfig, qos_setting)); + SetConfigurationFromFlatSubconfig(&subconfig, qos_setting, location)); if (subconfigs.back().codec.GetConfigSamplingFrequency() < le_audio::LeAudioCodecConfiguration::kSampleRate32000) { @@ -448,7 +470,8 @@ struct AudioSetConfigurationProviderJson { } bool LoadConfigurationsFromFiles(const char* schema_file, - const char* content_file) { + const char* content_file, + types::CodecLocation location) { flatbuffers::Parser configurations_parser_; std::string configurations_schema_binary_content; bool ok = flatbuffers::LoadFile(schema_file, true, @@ -502,9 +525,9 @@ struct AudioSetConfigurationProviderJson { LOG_DEBUG(": Updating %d config entries.", flat_configs->size()); for (auto const& flat_cfg : *flat_configs) { - configurations_.insert( - {flat_cfg->name()->str(), - AudioSetConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs)}); + configurations_.insert({flat_cfg->name()->str(), + AudioSetConfigurationFromFlat( + flat_cfg, &codec_cfgs, &qos_cfgs, location)}); } return true; @@ -580,9 +603,10 @@ struct AudioSetConfigurationProviderJson { std::vector<std::pair<const char* /*schema*/, const char* /*content*/>> config_files, std::vector<std::pair<const char* /*schema*/, const char* /*content*/>> - scenario_files) { + scenario_files, + types::CodecLocation location) { for (auto [schema, content] : config_files) { - if (!LoadConfigurationsFromFiles(schema, content)) return false; + if (!LoadConfigurationsFromFiles(schema, content, location)) return false; } for (auto [schema, content] : scenario_files) { @@ -596,10 +620,10 @@ struct AudioSetConfigurationProvider::impl { impl(const AudioSetConfigurationProvider& config_provider) : config_provider_(config_provider) {} - void Initialize() { + void Initialize(types::CodecLocation location) { ASSERT_LOG(!config_provider_impl_, " Config provider not available."); config_provider_impl_ = - std::make_unique<AudioSetConfigurationProviderJson>(); + std::make_unique<AudioSetConfigurationProviderJson>(location); } void Cleanup() { @@ -652,13 +676,13 @@ std::mutex instance_mutex; AudioSetConfigurationProvider::AudioSetConfigurationProvider() : pimpl_(std::make_unique<AudioSetConfigurationProvider::impl>(*this)) {} -void AudioSetConfigurationProvider::Initialize() { +void AudioSetConfigurationProvider::Initialize(types::CodecLocation location) { std::scoped_lock<std::mutex> lock(instance_mutex); if (!config_provider) config_provider = std::make_unique<AudioSetConfigurationProvider>(); if (!config_provider->pimpl_->IsRunning()) - config_provider->pimpl_->Initialize(); + config_provider->pimpl_->Initialize(location); } void AudioSetConfigurationProvider::DebugDump(int fd) { diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc index 51c25654888bceb32eb9ae6d0c0c8a2cfef8a6e7..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) { @@ -907,6 +892,23 @@ std::ostream& operator<<(std::ostream& os, const AudioContexts& contexts) { return os; } +template <typename T> +const T& BidirectionalPair<T>::get(uint8_t direction) const { + ASSERT_LOG( + direction < types::kLeAudioDirectionBoth, + "Unsupported complex direction. Consider using get_bidirectional<>() " + "instead."); + return (direction == types::kLeAudioDirectionSink) ? sink : source; +} + +template <typename T> +T& BidirectionalPair<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; +} + /* Bidirectional getter trait for AudioContexts bidirectional pair */ template <> AudioContexts get_bidirectional(BidirectionalPair<AudioContexts> p) { @@ -928,7 +930,13 @@ AudioLocations get_bidirectional(BidirectionalPair<AudioLocations> bidir) { template struct BidirectionalPair<AudioContexts>; template struct BidirectionalPair<AudioLocations>; +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 851bd9c31fb04b1ec08628f855cfebc68bc5ec08..d70eb91dcb69003db902b23f29bfe84dd237d980 100644 --- a/system/bta/le_audio/le_audio_types.h +++ b/system/bta/le_audio/le_audio_types.h @@ -31,8 +31,6 @@ #include <variant> #include <vector> -#include "bta_groups.h" -#include "bta_le_audio_api.h" #include "bta_le_audio_uuids.h" #include "btm_iso_api_types.h" @@ -312,6 +310,8 @@ constexpr uint8_t kDefaultCsisSetSize = 2; constexpr uint8_t kLeAudioDirectionSink = 0x01; constexpr uint8_t kLeAudioDirectionSource = 0x02; +constexpr uint8_t kLeAudioDirectionBoth = + kLeAudioDirectionSink | kLeAudioDirectionSource; /* Audio stream config types */ constexpr uint8_t kFramingUnframedPduSupported = 0x00; @@ -477,31 +477,21 @@ struct BidirectionalPair { T sink; T source; - T get(uint8_t direction) const { - if (direction == - (types::kLeAudioDirectionSink | types::kLeAudioDirectionSource)) { - return get_bidirectional(*this); - } else if (direction == types::kLeAudioDirectionSink) { - return sink; - } - return source; - } - T& get_ref(uint8_t direction) { - return (direction == types::kLeAudioDirectionSink) ? sink : source; - } + const T& get(uint8_t direction) const; + T& get(uint8_t direction); BidirectionalPair<T>& operator=(const BidirectionalPair<T>&) = default; - bool operator==(const BidirectionalPair<T>& other) const { - return (sink == other.sink) && (source == other.source); - }; - bool operator!=(const BidirectionalPair<T>& other) const { - return (sink != other.sink) || (source != other.source); - }; }; template <typename T> T get_bidirectional(BidirectionalPair<T> p); +template <typename T> +bool operator==(const types::BidirectionalPair<T>& lhs, + const types::BidirectionalPair<T>& rhs) { + return (lhs.sink == rhs.sink) && (lhs.source == rhs.source); +} + /* Configuration strategy */ enum class LeAudioConfigurationStrategy : uint8_t { MONO_ONE_CIS_PER_DEVICE = 0x00, /* Common true wireless speakers */ @@ -554,8 +544,33 @@ class LeAudioLtvMap { : values(std::move(values)) {} std::optional<std::vector<uint8_t>> Find(uint8_t type) const; - void Add(uint8_t type, std::vector<uint8_t> value) { + LeAudioLtvMap& Add(uint8_t type, std::vector<uint8_t> value) { values.insert_or_assign(type, std::move(value)); + return *this; + } + LeAudioLtvMap& Add(uint8_t type, uint8_t value) { + std::vector<uint8_t> v(sizeof(value)); + auto ptr = v.data(); + + UINT8_TO_STREAM(ptr, value); + values.insert_or_assign(type, v); + return *this; + } + LeAudioLtvMap& Add(uint8_t type, uint16_t value) { + std::vector<uint8_t> v(sizeof(value)); + auto ptr = v.data(); + + UINT16_TO_STREAM(ptr, value); + values.insert_or_assign(type, std::move(v)); + return *this; + } + LeAudioLtvMap& Add(uint8_t type, uint32_t value) { + std::vector<uint8_t> v(sizeof(value)); + auto ptr = v.data(); + + UINT32_TO_STREAM(ptr, value); + values.insert_or_assign(type, std::move(v)); + return *this; } void Remove(uint8_t type) { values.erase(type); } bool IsEmpty() const { return values.empty(); } @@ -718,6 +733,13 @@ struct ase { uint8_t framing; uint8_t preferred_phy; + /* Set to true, if the codec is implemented in BT controller, false if it's + * implemented in host, or in separate DSP + */ + bool is_codec_in_controller; + /* Datapath ID used to configure an ISO channel for these ASEs */ + uint8_t data_path_id; + /* Qos configuration */ uint16_t max_sdu_size; uint8_t retrans_nb; @@ -794,6 +816,12 @@ struct SetConfiguration { uint8_t direction; /* Direction of set */ uint8_t device_cnt; /* How many devices must be in set */ uint8_t ase_cnt; /* How many ASE we need in configuration */ + + /* Whether the codec location is transparent to the controller */ + bool is_codec_in_controller = false; + /* Datapath ID used to configure an ISO channel for these ASEs */ + uint8_t data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci; + CodecCapabilitySetting codec; QosConfigSetting qos; types::LeAudioConfigurationStrategy strategy; @@ -834,15 +862,18 @@ 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; +struct stream_parameters { + /* For now we have always same frequency for all the channels */ + uint32_t sample_frequency_hz; + uint32_t frame_duration_us; + uint16_t octets_per_codec_frame; uint32_t audio_channel_allocation; - bool is_stream_active; + uint8_t codec_frames_blocks_per_sdu; + /* Number of channels is what we will request from audio framework */ + uint8_t num_of_channels; + int num_of_devices; + /* cis_handle, audio location*/ + std::vector<std::pair<uint16_t, uint32_t>> stream_locations; }; struct stream_configuration { @@ -853,43 +884,9 @@ struct stream_configuration { /* Pointer to chosen req */ const le_audio::set_configurations::AudioSetConfiguration* conf; - /* Sink configuration */ - /* For now we have always same frequency for all the channels */ - uint32_t sink_sample_frequency_hz; - uint32_t sink_frame_duration_us; - uint16_t sink_octets_per_codec_frame; - uint32_t sink_audio_channel_allocation; - uint8_t sink_codec_frames_blocks_per_sdu; - /* Number of channels is what we will request from audio framework */ - uint8_t sink_num_of_channels; - int sink_num_of_devices; - /* cis_handle, audio location*/ - std::vector<std::pair<uint16_t, uint32_t>> sink_streams; - /* cis_handle, target allocation, stream active state */ - std::vector<stream_map_info> sink_offloader_streams_target_allocation; - /* cis_handle, current allocation, stream active state */ - std::vector<stream_map_info> sink_offloader_streams_current_allocation; - bool sink_offloader_changed; - bool sink_is_initial; - - /* Source configuration */ - /* For now we have always same frequency for all the channels */ - uint32_t source_sample_frequency_hz; - uint32_t source_frame_duration_us; - uint16_t source_octets_per_codec_frame; - uint32_t source_audio_channel_allocation; - uint8_t source_codec_frames_blocks_per_sdu; - /* Number of channels is what we will request from audio framework */ - uint8_t source_num_of_channels; - int source_num_of_devices; - /* cis_handle, audio location*/ - std::vector<std::pair<uint16_t, uint32_t>> source_streams; - /* cis_handle, target allocation, stream active state */ - std::vector<stream_map_info> source_offloader_streams_target_allocation; - /* cis_handle, current allocation, stream active state */ - std::vector<stream_map_info> source_offloader_streams_current_allocation; - bool source_offloader_changed; - bool source_is_initial; + /* Sink & Source configuration */ + types::BidirectionalPair<stream_parameters> stream_params; + bool is_active; }; @@ -898,5 +895,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/le_audio_utils.cc b/system/bta/le_audio/le_audio_utils.cc index 50edab6972c5197ab252089ac9059d6c5f80fba0..76698ee6999b5f770ec52e935a6025ea7e426e10 100644 --- a/system/bta/le_audio/le_audio_utils.cc +++ b/system/bta/le_audio/le_audio_utils.cc @@ -211,9 +211,9 @@ AudioContexts GetAudioContextsFromSinkMetadata( "sink device. This may result in voice back channel malfunction."); } - LOG_DEBUG("Allowed contexts from sink metadata: %s (0x%08hx)", - bluetooth::common::ToString(all_track_contexts).c_str(), - all_track_contexts.value()); + LOG_INFO("Allowed contexts from sink metadata: %s (0x%08hx)", + bluetooth::common::ToString(all_track_contexts).c_str(), + all_track_contexts.value()); return all_track_contexts; } diff --git a/system/bta/le_audio/mock_codec_interface.cc b/system/bta/le_audio/mock_codec_interface.cc new file mode 100644 index 0000000000000000000000000000000000000000..c16aae7b723ace89a5bb98328b55900af8af711a --- /dev/null +++ b/system/bta/le_audio/mock_codec_interface.cc @@ -0,0 +1,67 @@ +/* + * Copyright 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 "mock_codec_interface.h" + +namespace le_audio { + +struct CodecInterface::Impl : public MockCodecInterface { + public: + Impl(const types::LeAudioCodecId& codec_id) { + output_channel_data_.resize(1); + }; + ~Impl() = default; + + std::vector<int16_t>& GetDecodedSamples() { return output_channel_data_; } + std::vector<int16_t> output_channel_data_; +}; + +CodecInterface::CodecInterface(const types::LeAudioCodecId& codec_id) { + impl = new Impl(codec_id); +} +CodecInterface::~CodecInterface() { delete impl; } +bool CodecInterface::IsReady() { return impl->IsReady(); }; +CodecInterface::Status CodecInterface::InitEncoder( + const LeAudioCodecConfiguration& pcm_config, + const LeAudioCodecConfiguration& codec_config) { + return impl->InitEncoder(pcm_config, codec_config); +} +CodecInterface::Status CodecInterface::InitDecoder( + const LeAudioCodecConfiguration& codec_config, + const LeAudioCodecConfiguration& pcm_config) { + return impl->InitDecoder(codec_config, pcm_config); +} +std::vector<int16_t>& CodecInterface::GetDecodedSamples() { + return impl->GetDecodedSamples(); +} +CodecInterface::Status CodecInterface::Decode(uint8_t* data, uint16_t size) { + return impl->Decode(data, size); +} +CodecInterface::Status CodecInterface::Encode(const uint8_t* data, int stride, + uint16_t out_size, + std::vector<int16_t>* out_buffer, + uint16_t out_offset) { + return impl->Encode(data, stride, out_size, out_buffer, out_offset); +} +void CodecInterface::Cleanup() { return impl->Cleanup(); } + +uint16_t CodecInterface::GetNumOfSamplesPerChannel() { + return impl->GetNumOfSamplesPerChannel(); +}; +uint8_t CodecInterface::GetNumOfBytesPerSample() { + return impl->GetNumOfBytesPerSample(); +}; +} // namespace le_audio diff --git a/system/bta/le_audio/mock_codec_interface.h b/system/bta/le_audio/mock_codec_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..d2acda6f61e531fe3db74f13ca6f81d92fa1d0ff --- /dev/null +++ b/system/bta/le_audio/mock_codec_interface.h @@ -0,0 +1,47 @@ +/* + * Copyright 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 <gmock/gmock.h> + +#include "codec_interface.h" + +class MockCodecInterface { + public: + MockCodecInterface() = default; + MockCodecInterface(const MockCodecInterface&) = delete; + MockCodecInterface& operator=(const MockCodecInterface&) = delete; + + virtual ~MockCodecInterface() = default; + + MOCK_METHOD((le_audio::CodecInterface::Status), InitEncoder, + (const le_audio::LeAudioCodecConfiguration& pcm_config, + const le_audio::LeAudioCodecConfiguration& codec_config)); + MOCK_METHOD(le_audio::CodecInterface::Status, InitDecoder, + (const le_audio::LeAudioCodecConfiguration& codec_config, + const le_audio::LeAudioCodecConfiguration& pcm_config)); + MOCK_METHOD(le_audio::CodecInterface::Status, Encode, + (const uint8_t* data, int stride, uint16_t out_size, + std::vector<int16_t>* out_buffer, uint16_t out_offset)); + MOCK_METHOD(le_audio::CodecInterface::Status, Decode, + (uint8_t * data, uint16_t size)); + MOCK_METHOD((void), Cleanup, ()); + MOCK_METHOD((bool), IsReady, ()); + MOCK_METHOD((uint16_t), GetNumOfSamplesPerChannel, ()); + MOCK_METHOD((uint8_t), GetNumOfBytesPerSample, ()); + MOCK_METHOD((std::vector<int16_t>&), GetDecodedSamples, ()); +}; 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 a0ff6a1f7d431fcf65b16f273140c799d77c10a0..f783824fb159bb7f13abd0bc5d3d785c01ebea30 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); @@ -1157,154 +1157,85 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { void AddCisToStreamConfiguration(LeAudioDeviceGroup* group, const struct ase* ase) { - uint16_t cis_conn_hdl = ase->cis_conn_hdl; + group->stream_conf.id = ase->codec_id; + + auto cis_conn_hdl = ase->cis_conn_hdl; + auto& params = group->stream_conf.stream_params.get(ase->direction); LOG_INFO("Adding cis handle 0x%04x (%s) to stream list", cis_conn_hdl, ase->direction == le_audio::types::kLeAudioDirectionSink ? "sink" : "source"); - auto* stream_conf = &group->stream_conf; - if (ase->direction == le_audio::types::kLeAudioDirectionSink) { - auto iter = std::find_if( - stream_conf->sink_streams.begin(), stream_conf->sink_streams.end(), - [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; }); - - ASSERT_LOG(iter == stream_conf->sink_streams.end(), - "Stream is already there 0x%04x", cis_conn_hdl); - - stream_conf->sink_streams.emplace_back(std::make_pair( - ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation)); - - stream_conf->sink_num_of_devices++; - stream_conf->sink_num_of_channels += ase->codec_config.channel_count; - stream_conf->sink_audio_channel_allocation |= - *ase->codec_config.audio_channel_allocation; - - if (stream_conf->sink_sample_frequency_hz == 0) { - stream_conf->sink_sample_frequency_hz = - ase->codec_config.GetSamplingFrequencyHz(); - } else { - ASSERT_LOG(stream_conf->sink_sample_frequency_hz == - ase->codec_config.GetSamplingFrequencyHz(), - "sample freq mismatch: %d!=%d", - stream_conf->sink_sample_frequency_hz, - ase->codec_config.GetSamplingFrequencyHz()); - } - if (stream_conf->sink_octets_per_codec_frame == 0) { - stream_conf->sink_octets_per_codec_frame = - *ase->codec_config.octets_per_codec_frame; - } else { - ASSERT_LOG(stream_conf->sink_octets_per_codec_frame == - *ase->codec_config.octets_per_codec_frame, - "octets per frame mismatch: %d!=%d", - stream_conf->sink_octets_per_codec_frame, - *ase->codec_config.octets_per_codec_frame); - } - - if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) { - stream_conf->sink_codec_frames_blocks_per_sdu = - *ase->codec_config.codec_frames_blocks_per_sdu; - } else { - ASSERT_LOG(stream_conf->sink_codec_frames_blocks_per_sdu == - *ase->codec_config.codec_frames_blocks_per_sdu, - "codec_frames_blocks_per_sdu: %d!=%d", - stream_conf->sink_codec_frames_blocks_per_sdu, - *ase->codec_config.codec_frames_blocks_per_sdu); - } + auto iter = std::find_if( + params.stream_locations.begin(), params.stream_locations.end(), + [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; }); + ASSERT_LOG(iter == params.stream_locations.end(), + "Stream is already there 0x%04x", cis_conn_hdl); - if (stream_conf->sink_frame_duration_us == 0) { - stream_conf->sink_frame_duration_us = - ase->codec_config.GetFrameDurationUs(); - } else { - ASSERT_LOG(stream_conf->sink_frame_duration_us == - ase->codec_config.GetFrameDurationUs(), - "frame_duration_us: %d!=%d", - stream_conf->sink_frame_duration_us, - ase->codec_config.GetFrameDurationUs()); - } + params.num_of_devices++; + params.num_of_channels += ase->codec_config.channel_count; - LOG_INFO( - " Added Sink Stream Configuration. CIS Connection Handle: %d" - ", Audio Channel Allocation: %d" - ", Sink Number Of Devices: %d" - ", Sink Number Of Channels: %d", - ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation, - stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels); + if (!ase->codec_config.audio_channel_allocation.has_value()) { + LOG_WARN("ASE has invalid audio location"); + } + auto ase_audio_channel_allocation = + ase->codec_config.audio_channel_allocation.value_or(0); + params.audio_channel_allocation |= ase_audio_channel_allocation; + params.stream_locations.emplace_back( + std::make_pair(ase->cis_conn_hdl, ase_audio_channel_allocation)); + if (params.sample_frequency_hz == 0) { + params.sample_frequency_hz = ase->codec_config.GetSamplingFrequencyHz(); } else { - /* Source case */ - auto iter = std::find_if( - stream_conf->source_streams.begin(), - stream_conf->source_streams.end(), - [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; }); - - ASSERT_LOG(iter == stream_conf->source_streams.end(), - "Stream is already there 0x%04x", cis_conn_hdl); - - stream_conf->source_streams.emplace_back(std::make_pair( - ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation)); - - stream_conf->source_num_of_devices++; - stream_conf->source_num_of_channels += ase->codec_config.channel_count; - stream_conf->source_audio_channel_allocation |= - *ase->codec_config.audio_channel_allocation; - - if (stream_conf->source_sample_frequency_hz == 0) { - stream_conf->source_sample_frequency_hz = - ase->codec_config.GetSamplingFrequencyHz(); - } else { - ASSERT_LOG(stream_conf->source_sample_frequency_hz == - ase->codec_config.GetSamplingFrequencyHz(), - "sample freq mismatch: %d!=%d", - stream_conf->source_sample_frequency_hz, - ase->codec_config.GetSamplingFrequencyHz()); - } - - if (stream_conf->source_octets_per_codec_frame == 0) { - stream_conf->source_octets_per_codec_frame = - *ase->codec_config.octets_per_codec_frame; - } else { - ASSERT_LOG(stream_conf->source_octets_per_codec_frame == - *ase->codec_config.octets_per_codec_frame, - "octets per frame mismatch: %d!=%d", - stream_conf->source_octets_per_codec_frame, - *ase->codec_config.octets_per_codec_frame); - } - - if (stream_conf->source_codec_frames_blocks_per_sdu == 0) { - stream_conf->source_codec_frames_blocks_per_sdu = - *ase->codec_config.codec_frames_blocks_per_sdu; - } else { - ASSERT_LOG(stream_conf->source_codec_frames_blocks_per_sdu == - *ase->codec_config.codec_frames_blocks_per_sdu, - "codec_frames_blocks_per_sdu: %d!=%d", - stream_conf->source_codec_frames_blocks_per_sdu, - *ase->codec_config.codec_frames_blocks_per_sdu); - } + ASSERT_LOG(params.sample_frequency_hz == + ase->codec_config.GetSamplingFrequencyHz(), + "sample freq mismatch: %d!=%d", params.sample_frequency_hz, + ase->codec_config.GetSamplingFrequencyHz()); + } - if (stream_conf->source_frame_duration_us == 0) { - stream_conf->source_frame_duration_us = - ase->codec_config.GetFrameDurationUs(); - } else { - ASSERT_LOG(stream_conf->source_frame_duration_us == - ase->codec_config.GetFrameDurationUs(), - "frame_duration_us: %d!=%d", - stream_conf->source_frame_duration_us, - ase->codec_config.GetFrameDurationUs()); - } + if (params.octets_per_codec_frame == 0) { + params.octets_per_codec_frame = *ase->codec_config.octets_per_codec_frame; + } else { + ASSERT_LOG(params.octets_per_codec_frame == + *ase->codec_config.octets_per_codec_frame, + "octets per frame mismatch: %d!=%d", + params.octets_per_codec_frame, + *ase->codec_config.octets_per_codec_frame); + } - LOG_INFO( - " Added Source Stream Configuration. CIS Connection Handle: %d" - ", Audio Channel Allocation: %d" - ", Source Number Of Devices: %d" - ", Source Number Of Channels: %d", - ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation, - stream_conf->source_num_of_devices, - stream_conf->source_num_of_channels); + if (params.codec_frames_blocks_per_sdu == 0) { + params.codec_frames_blocks_per_sdu = + *ase->codec_config.codec_frames_blocks_per_sdu; + } else { + ASSERT_LOG(params.codec_frames_blocks_per_sdu == + *ase->codec_config.codec_frames_blocks_per_sdu, + "codec_frames_blocks_per_sdu: %d!=%d", + params.codec_frames_blocks_per_sdu, + *ase->codec_config.codec_frames_blocks_per_sdu); } - /* Update offloader streams */ - group->CreateStreamVectorForOffloader(ase->direction); + if (params.frame_duration_us == 0) { + params.frame_duration_us = ase->codec_config.GetFrameDurationUs(); + } else { + ASSERT_LOG( + params.frame_duration_us == ase->codec_config.GetFrameDurationUs(), + "frame_duration_us: %d!=%d", params.frame_duration_us, + ase->codec_config.GetFrameDurationUs()); + } + + LOG_INFO( + " Added %s Stream Configuration. CIS Connection Handle: %d" + ", Audio Channel Allocation: %d" + ", Number Of Devices: %d" + ", Number Of Channels: %d", + (ase->direction == le_audio::types::kLeAudioDirectionSink ? "Sink" + : "Source"), + cis_conn_hdl, ase_audio_channel_allocation, params.num_of_devices, + params.num_of_channels); + + /* Update CodecManager stream configuration */ + state_machine_callbacks_->OnUpdatedCisConfiguration(group->group_id_, + ase->direction); } bool CigCreate(LeAudioDeviceGroup* group) { @@ -1526,22 +1457,15 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { } static void PrepareDataPath(int group_id, const struct ase* ase) { - /* TODO: Handle HW offloading decode as we handle here, force to use SW - * decode for now */ - auto data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci; - if (CodecManager::GetInstance()->GetCodecLocation() != - CodecLocation::HOST) { - data_path_id = bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault; - } - /* TODO: Need to set coding_format when we support the codec location inside - * the controller, force to use transparent for now */ bluetooth::hci::iso_manager::iso_data_path_params param = { .data_path_dir = ase->direction == le_audio::types::kLeAudioDirectionSink ? bluetooth::hci::iso_manager::kIsoDataPathDirectionIn : bluetooth::hci::iso_manager::kIsoDataPathDirectionOut, - .data_path_id = data_path_id, - .codec_id_format = bluetooth::hci::kIsoCodingFormatTransparent, + .data_path_id = ase->data_path_id, + .codec_id_format = ase->is_codec_in_controller + ? ase->codec_id.coding_format + : bluetooth::hci::kIsoCodingFormatTransparent, .codec_id_company = ase->codec_id.vendor_company_id, .codec_id_vendor = ase->codec_id.vendor_codec_id, .controller_delay = 0x00000000, @@ -1729,7 +1653,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { conf.target_latency = ase->target_latency; conf.target_phy = group->GetTargetPhy(ase->direction); conf.codec_id = ase->codec_id; - conf.codec_config = ase->codec_config; + // FIXME: Use LtvMap in ASE + conf.codec_config = ase->codec_config.GetAsLtvMap(); confs.push_back(conf); msg_stream << "ASE_ID " << +conf.ase_id << ","; 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 6c55c6710f07a1f3d232b28a972647402f124003..dac886f24877c1dab655f84b0d6fe120079730cd 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -165,9 +165,11 @@ 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 StateMachineTest : public Test { +class StateMachineTestBase : public Test { protected: uint8_t ase_id_last_assigned = types::ase::kAseIdInvalid; uint8_t additional_snk_ases = 0; @@ -181,7 +183,7 @@ class StateMachineTest : public Test { uint8_t overwrite_cis_status_idx_; std::vector<uint8_t> cis_status_; - void SetUp() override { + virtual void SetUp() override { bluetooth::common::InitFlags::Load(test_flags); reset_mock_function_count_map(); controller::SetMockControllerInterface(&mock_controller_); @@ -194,7 +196,6 @@ class StateMachineTest : public Test { do_not_send_cis_establish_event_ = false; cis_status_.clear(); - ::le_audio::AudioSetConfigurationProvider::Initialize(); LeAudioGroupStateMachine::Initialize(&mock_callbacks_); ContentControlIdKeeper::GetInstance()->Start(); @@ -243,7 +244,6 @@ class StateMachineTest : public Test { })); ConfigureIsoManagerMock(); - ConfigCodecManagerMock(); } void HandleCtpOperation(LeAudioDevice* device, std::vector<uint8_t> value, @@ -455,7 +455,7 @@ class StateMachineTest : public Test { }); } - void ConfigCodecManagerMock() { + void ConfigCodecManagerMock(types::CodecLocation location) { codec_manager_ = le_audio::CodecManager::GetInstance(); ASSERT_NE(codec_manager_, nullptr); std::vector<bluetooth::le_audio::btle_audio_codec_config_t> @@ -464,7 +464,7 @@ class StateMachineTest : public Test { mock_codec_manager_ = MockCodecManager::GetInstance(); ASSERT_NE(mock_codec_manager_, nullptr); ON_CALL(*mock_codec_manager_, GetCodecLocation()) - .WillByDefault(Return(types::CodecLocation::HOST)); + .WillByDefault(Return(location)); } void TearDown() override { @@ -1294,6 +1294,24 @@ class StateMachineTest : public Test { bool group_create_command_disallowed_ = false; }; +class StateMachineTest : public StateMachineTestBase { + void SetUp() override { + ConfigCodecManagerMock(types::CodecLocation::HOST); + ::le_audio::AudioSetConfigurationProvider::Initialize( + ::le_audio::types::CodecLocation::HOST); + StateMachineTestBase::SetUp(); + } +}; + +class StateMachineTestAdsp : public StateMachineTestBase { + void SetUp() override { + ConfigCodecManagerMock(types::CodecLocation::ADSP); + ::le_audio::AudioSetConfigurationProvider::Initialize( + ::le_audio::types::CodecLocation::ADSP); + StateMachineTestBase::SetUp(); + } +}; + TEST_F(StateMachineTest, testInit) { ASSERT_NE(LeAudioGroupStateMachine::Get(), nullptr); } @@ -3440,9 +3458,7 @@ TEST_F(StateMachineTest, testConfigureDataPathForHost) { /* Can be called for every context when fetching the configuration from the * AudioSetConfigurationProvider. */ - EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(types::CodecLocation::HOST)); + EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1)); // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -3468,7 +3484,8 @@ TEST_F(StateMachineTest, testConfigureDataPathForHost) { {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); } -TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { + +TEST_F(StateMachineTestAdsp, testConfigureDataPathForAdsp) { const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | @@ -3477,9 +3494,7 @@ TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { /* Can be called for every context when fetching the configuration from the * AudioSetConfigurationProvider. */ - EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(types::CodecLocation::ADSP)); + EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1)); // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -3507,7 +3522,7 @@ TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { .source = types::AudioContexts(context_type)})); } -TEST_F(StateMachineTest, testStreamConfigurationAdspDownMix) { +TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) { const auto context_type = kContextTypeConversational; const int leaudio_group_id = 4; const int num_devices = 2; @@ -3517,12 +3532,19 @@ TEST_F(StateMachineTest, 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)) - .WillRepeatedly(Return(types::CodecLocation::ADSP)); + EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1)); PrepareConfigureCodecHandler(group); PrepareConfigureQosHandler(group); @@ -3540,48 +3562,11 @@ TEST_F(StateMachineTest, testStreamConfigurationAdspDownMix) { {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); - ASSERT_EQ( - static_cast<int>( - group->stream_conf.sink_offloader_streams_target_allocation.size()), - 2); - ASSERT_EQ( - static_cast<int>( - group->stream_conf.source_offloader_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.sink_offloader_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.source_offloader_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.sink_offloader_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.source_offloader_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, @@ -5315,8 +5300,8 @@ TEST_F(StateMachineTest, testAclDropWithoutApriorCisDisconnection) { testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_); /* Separate CIS for dual CIS device is treated as sink device */ - ASSERT_EQ(group->stream_conf.sink_num_of_devices, 2); - ASSERT_EQ(group->stream_conf.sink_num_of_channels, 2); + ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_devices, 2); + ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_channels, 2); // Inject CIS and ACL disconnection of first device InjectAclDisconnected(group, firstDevice); @@ -5324,8 +5309,8 @@ TEST_F(StateMachineTest, testAclDropWithoutApriorCisDisconnection) { InjectCisDisconnected(group, lastDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST); InjectAclDisconnected(group, lastDevice); - ASSERT_EQ(group->stream_conf.sink_num_of_devices, 0); - ASSERT_EQ(group->stream_conf.sink_num_of_channels, 0); + ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_devices, 0); + ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_channels, 0); } } // namespace internal 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( diff --git a/system/btif/src/btif_le_audio.cc b/system/btif/src/btif_le_audio.cc index e219398c4220ffd38a0a6bca9e64618207df0ff7..ed3e35c83b304e43098570c6bd91c9e447f11fab 100644 --- a/system/btif/src/btif_le_audio.cc +++ b/system/btif/src/btif_le_audio.cc @@ -133,7 +133,6 @@ class LeAudioClientInterfaceImpl : public LeAudioClientInterface, LOG_INFO("supported codec: %s", codec.ToString().c_str()); } - LeAudioClient::InitializeAudioSetConfigurationProvider(); do_in_main_thread( FROM_HERE, Bind(&LeAudioClient::Initialize, this, jni_thread_wrapper( @@ -160,12 +159,7 @@ class LeAudioClientInterfaceImpl : public LeAudioClientInterface, initialized = false; - do_in_main_thread( - FROM_HERE, - Bind(&LeAudioClient::Cleanup, - jni_thread_wrapper( - FROM_HERE, - Bind(&LeAudioClient::CleanupAudioSetConfigurationProvider)))); + do_in_main_thread(FROM_HERE, Bind(&LeAudioClient::Cleanup)); } void RemoveDevice(const RawAddress& address) override { diff --git a/system/test/mock/mock_bta_leaudio.cc b/system/test/mock/mock_bta_leaudio.cc index affd643435b77568464e0574b14bea2a8e88fcf4..1ac5bb7981e699c7360fd232711e9e780c2fc5ce 100644 --- a/system/test/mock/mock_bta_leaudio.cc +++ b/system/test/mock/mock_bta_leaudio.cc @@ -80,10 +80,7 @@ bool LeAudioClient::GetAsesForStorage(const RawAddress& addr, return false; } -void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) { - std::move(cleanupCb).Run(); - inc_func_call_count(__func__); -} +void LeAudioClient::Cleanup(void) { inc_func_call_count(__func__); } LeAudioClient* LeAudioClient::Get(void) { inc_func_call_count(__func__); @@ -101,9 +98,3 @@ void LeAudioClient::Initialize( inc_func_call_count(__func__); } void LeAudioClient::DebugDump(int fd) { inc_func_call_count(__func__); } -void LeAudioClient::InitializeAudioSetConfigurationProvider() { - inc_func_call_count(__func__); -} -void LeAudioClient::CleanupAudioSetConfigurationProvider() { - inc_func_call_count(__func__); -}