diff --git a/system/bta/Android.bp b/system/bta/Android.bp index d8037fb7b5e2b01904d60bf794cf73f3b6bdfc39..d1b02bc9d0a8803c614a240689189ac08bf4ea05 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -582,6 +582,7 @@ cc_test { "le_audio/metrics_collector_linux.cc", "le_audio/mock_iso_manager.cc", "test/common/mock_controller.cc", + "test/common/mock_csis_client.cc", "le_audio/state_machine.cc", "le_audio/state_machine_test.cc", "le_audio/storage_helper.cc", diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc index 9f1cc919816e78e4ea26de8d66ea624df68567d6..44b3a2648cbe9850602005dc20bf5a8dce8d5f2a 100644 --- a/system/bta/csis/csis_client.cc +++ b/system/bta/csis/csis_client.cc @@ -526,6 +526,16 @@ class CsisClientImpl : public CsisClient { } } + int GetDesiredSize(int group_id) override { + auto csis_group = FindCsisGroup(group_id); + if (!csis_group) { + LOG_INFO("Unknown group %d", group_id); + return -1; + } + + return csis_group->GetDesiredSize(); + } + bool SerializeSets(const RawAddress& addr, std::vector<uint8_t>& out) const { auto device = FindDeviceByAddress(addr); if (device == nullptr) { diff --git a/system/bta/csis/csis_types.h b/system/bta/csis/csis_types.h index 9dcecf422209324a3aafb4c4c26fc960a66bb758..1a373a639ff0948917f2f347093bf941bf4bfb9f 100644 --- a/system/bta/csis/csis_types.h +++ b/system/bta/csis/csis_types.h @@ -64,7 +64,7 @@ struct hdl_pair { /* CSIS Types */ static constexpr uint8_t kDefaultScanDurationS = 5; -static constexpr uint8_t kDefaultCsisSetSize = 2; +static constexpr uint8_t kDefaultCsisSetSize = 1; static constexpr uint8_t kUnknownRank = 0xff; /* Enums */ diff --git a/system/bta/include/bta_csis_api.h b/system/bta/include/bta_csis_api.h index fe864f5af857943f64eb1ff8c8cf6b4bc9a21a4a..44dca04ff15858563908ac3bad7b5505d47ffd27 100644 --- a/system/bta/include/bta_csis_api.h +++ b/system/bta/include/bta_csis_api.h @@ -47,6 +47,7 @@ class CsisClient { bluetooth::Uuid uuid = bluetooth::groups::kGenericContextUuid) = 0; virtual void LockGroup(int group_id, bool lock, CsisLockCb cb) = 0; virtual std::vector<RawAddress> GetDeviceList(int group_id) = 0; + virtual int GetDesiredSize(int group_id) = 0; }; } // namespace csis } // namespace bluetooth diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index d9eb47b63a159edb821fab24c9bd5b1e9baa4581..67bacc5b9b68d3843be0218b378aee13e2b4fcf2 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -22,6 +22,7 @@ #include <map> #include "audio_hal_client/audio_hal_client.h" +#include "bta_csis_api.h" #include "bta_gatt_queue.h" #include "bta_groups.h" #include "bta_le_audio_api.h" @@ -964,10 +965,16 @@ void LeAudioDeviceGroup::CigGenerateCisIds( uint8_t cis_count_bidir = 0; uint8_t cis_count_unidir_sink = 0; uint8_t cis_count_unidir_source = 0; - get_cis_count(confs, GetGroupStrategy(), + int csis_group_size = + bluetooth::csis::CsisClient::Get()->GetDesiredSize(group_id_); + /* If this is CSIS group, the csis_group_size will be > 0, otherwise -1. + * If the last happen it means, group size is 1 */ + int group_size = csis_group_size > 0 ? csis_group_size : 1; + + get_cis_count(*confs, group_size, GetGroupStrategy(), GetAseCount(types::kLeAudioDirectionSink), - GetAseCount(types::kLeAudioDirectionSource), &cis_count_bidir, - &cis_count_unidir_sink, &cis_count_unidir_source); + GetAseCount(types::kLeAudioDirectionSource), cis_count_bidir, + cis_count_unidir_sink, cis_count_unidir_source); uint8_t idx = 0; while (cis_count_bidir > 0) { @@ -977,7 +984,6 @@ void LeAudioDeviceGroup::CigGenerateCisIds( .type = CisType::CIS_TYPE_BIDIRECTIONAL, .conn_handle = 0, }; - cises_.push_back(cis_entry); cis_count_bidir--; idx++; @@ -1042,18 +1048,21 @@ bool LeAudioDeviceGroup::CigAssignCisIds(LeAudioDevice* leAudioDevice) { cis_id = GetFirstFreeCisId(CisType::CIS_TYPE_BIDIRECTIONAL); } - if (cis_id == kInvalidCisId) { - LOG_ERROR(" Unable to get free Bi-Directional CIS ID"); - return false; - } + if (cis_id != kInvalidCisId) { + ase->cis_id = cis_id; + matching_bidir_ase->cis_id = cis_id; + cises_[cis_id].addr = leAudioDevice->address_; - ase->cis_id = cis_id; - matching_bidir_ase->cis_id = cis_id; - cises_[cis_id].addr = leAudioDevice->address_; + LOG_INFO( + " ASE ID: %d and ASE ID: %d, assigned Bi-Directional CIS ID: %d", + +ase->id, +matching_bidir_ase->id, +ase->cis_id); + continue; + } - LOG_INFO(" ASE ID: %d and ASE ID: %d, assigned Bi-Directional CIS ID: %d", - +ase->id, +matching_bidir_ase->id, +ase->cis_id); - continue; + LOG_WARN( + " ASE ID: %d, unable to get free Bi-Directional CIS ID but maybe " + "thats fine. Try using unidirectional.", + ase->id); } if (ase->direction == types::kLeAudioDirectionSink) { @@ -1264,7 +1273,7 @@ bool LeAudioDeviceGroup::IsConfigurationSupported( if (ent.direction == types::kLeAudioDirectionSink && strategy != required_snk_strategy) { - LOG_INFO(" Sink strategy mismatch (%d!=%d)", + LOG_INFO(" Sink strategy mismatch group!=cfg.entry (%d!=%d)", static_cast<int>(required_snk_strategy), static_cast<int>(strategy)); return false; @@ -1532,12 +1541,12 @@ bool LeAudioDevice::ConfigureAses( std::vector<uint8_t>()); } - DLOG(INFO) << __func__ << " device=" << address_ - << ", activated ASE id=" << +ase->id - << ", direction=" << +ase->direction - << ", max_sdu_size=" << +ase->max_sdu_size - << ", cis_id=" << +ase->cis_id - << ", target_latency=" << +ent.target_latency; + LOG_DEBUG( + "device=%s, activated ASE id=%d, direction=%s, max_sdu_size=%d, " + "cis_id=%d, target_latency=%d", + address_.ToString().c_str(), ase->id, + (ent.direction == 1 ? "snk" : "src"), ase->max_sdu_size, ase->cis_id, + ent.target_latency); ase = GetFirstInactiveAse(ent.direction, reuse_cis_id); } @@ -1572,9 +1581,9 @@ bool LeAudioDeviceGroup::ConfigureAses( types::AudioLocations group_src_audio_locations = 0; for (const auto& ent : (*audio_set_conf).confs) { - DLOG(INFO) << __func__ - << " Looking for requirements: " << audio_set_conf->name << " - " - << (ent.direction == 1 ? "snk" : "src"); + LOG_DEBUG(" Looking for requirements: %s, - %s", + audio_set_conf->name.c_str(), + (ent.direction == 1 ? "snk" : "src")); uint8_t required_device_cnt = ent.device_cnt; uint8_t max_required_ase_per_dev = @@ -1582,10 +1591,11 @@ bool LeAudioDeviceGroup::ConfigureAses( uint8_t active_ase_num = 0; le_audio::types::LeAudioConfigurationStrategy strategy = ent.strategy; - DLOG(INFO) << __func__ << " Number of devices: " << +required_device_cnt - << " number of ASEs: " << +ent.ase_cnt - << " Max ASE per device: " << +max_required_ase_per_dev - << " strategy: " << (int)strategy; + LOG_DEBUG( + "Number of devices: %d number of ASEs: %d, Max ASE per device: %d " + "strategy: %d", + required_device_cnt, ent.ase_cnt, max_required_ase_per_dev, + (int)strategy); for (auto* device = GetFirstDeviceWithActiveContext(context_type); device != nullptr && required_device_cnt > 0; @@ -1618,14 +1628,14 @@ bool LeAudioDeviceGroup::ConfigureAses( if (required_device_cnt > 0) { /* Don't left any active devices if requirements are not met */ - LOG(ERROR) << __func__ << " could not configure all the devices"; + LOG_ERROR(" could not configure all the devices"); Deactivate(); return false; } } - LOG(INFO) << "Choosed ASE Configuration for group: " << this->group_id_ - << " configuration: " << audio_set_conf->name; + LOG_INFO("Choosed ASE Configuration for group: %d, configuration: %s", + group_id_, audio_set_conf->name.c_str()); configuration_context_type_ = context_type; metadata_context_type_ = metadata_context_type; @@ -1901,21 +1911,22 @@ bool LeAudioDeviceGroup::Configure(LeAudioContextType context_type, const set_configurations::AudioSetConfiguration* conf = available_context_to_configuration_map[context_type]; - DLOG(INFO) << __func__; - if (!conf) { - LOG(ERROR) << __func__ << ", requested context type: " - << loghex(static_cast<uint16_t>(context_type)) - << ", is in mismatch with cached available contexts"; + LOG_ERROR( + ", requested context type: %s , is in mismatch with cached available " + "contexts ", + bluetooth::common::ToString(context_type).c_str()); return false; } - DLOG(INFO) << __func__ << " setting context type: " << int(context_type); + LOG_DEBUG(" setting context type: %s", + bluetooth::common::ToString(context_type).c_str()); if (!ConfigureAses(conf, context_type, metadata_context_type, ccid_list)) { - LOG(ERROR) << __func__ << ", requested pick ASE config context type: " - << loghex(static_cast<uint16_t>(context_type)) - << ", is in mismatch with cached available contexts"; + LOG_ERROR( + ", requested context type: %s , is in mismatch with cached available " + "contexts", + bluetooth::common::ToString(context_type).c_str()); return false; } diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc index 8dbedf61f4a009d2b85c7444acca12b3ceac4896..a7a3cda1ae6aeca2cfbaf80b09c2b963f58e52a4 100644 --- a/system/bta/le_audio/devices_test.cc +++ b/system/bta/le_audio/devices_test.cc @@ -24,6 +24,8 @@ #include "le_audio_set_configuration_provider.h" #include "le_audio_types.h" #include "mock_controller.h" +#include "mock_csis_client.h" +#include "os/log.h" #include "stack/btm/btm_int_types.h" tACL_CONN* btm_bda_to_acl(const RawAddress& bda, tBT_TRANSPORT transport) { @@ -42,6 +44,9 @@ using ::le_audio::LeAudioDevices; using ::le_audio::types::AseState; using ::le_audio::types::AudioContexts; using ::le_audio::types::LeAudioContextType; +using testing::_; +using testing::Invoke; +using testing::Return; using testing::Test; RawAddress GetTestAddress(int index) { @@ -399,12 +404,23 @@ class LeAudioAseConfigurationTest : public Test { bluetooth::manager::SetMockBtmInterface(&btm_interface_); controller::SetMockControllerInterface(&controller_interface_); ::le_audio::AudioSetConfigurationProvider::Initialize(); + MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_); + ON_CALL(mock_csis_client_module_, Get()) + .WillByDefault(Return(&mock_csis_client_module_)); + ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) + .WillByDefault(Return(true)); + ON_CALL(mock_csis_client_module_, GetDeviceList(_)) + .WillByDefault(Invoke([this](int group_id) { return addresses_; })); + ON_CALL(mock_csis_client_module_, GetDesiredSize(_)) + .WillByDefault( + Invoke([this](int group_id) { return (int)(addresses_.size()); })); } void TearDown() override { controller::SetMockControllerInterface(nullptr); bluetooth::manager::SetMockBtmInterface(nullptr); devices_.clear(); + addresses_.clear(); delete group_; ::le_audio::AudioSetConfigurationProvider::Cleanup(); } @@ -416,6 +432,10 @@ class LeAudioAseConfigurationTest : public Test { auto device = (std::make_shared<LeAudioDevice>( GetTestAddress(index), DeviceConnectState::DISCONNECTED)); devices_.push_back(device); + LOG_INFO(" addresses %d", (int)(addresses_.size())); + addresses_.push_back(device->address_); + LOG_INFO(" Addresses %d", (int)(addresses_.size())); + group_->AddNode(device); int ase_id = 1; @@ -760,9 +780,11 @@ class LeAudioAseConfigurationTest : public Test { const int group_id_ = 6; std::vector<std::shared_ptr<LeAudioDevice>> devices_; + std::vector<RawAddress> addresses_; LeAudioDeviceGroup* group_ = nullptr; bluetooth::manager::MockBtmInterface btm_interface_; controller::MockControllerInterface controller_interface_; + MockCsisClient mock_csis_client_module_; }; TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_ringtone) { diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index f5913ec82ccd9aa29d8d2929266ffc203c8a8504..e1be899391e9d6ecedacf6d2efcaaa2b27fb3110 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -3289,6 +3289,9 @@ TEST_F(UnicastTest, TwoEarbudsStreaming) { codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + // Start streaming EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); @@ -3359,6 +3362,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchNoReconfigure) { codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + // Start streaming EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); @@ -3445,6 +3451,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { constexpr int gmcs_ccid = 1; constexpr int gtbs_ccid = 2; + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + // Start streaming MEDIA EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); @@ -3494,8 +3503,10 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) { ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) .WillByDefault(Return(true)); - // First earbud const RawAddress test_address0 = GetTestAddress(0); + const RawAddress test_address1 = GetTestAddress(1); + + // First earbud ConnectCsisDevice(test_address0, 1 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft, codec_spec_conf::kLeAudioLocationFrontLeft, group_size, @@ -3506,6 +3517,9 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) { EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); LeAudioClient::Get()->GroupSetActive(group_id); + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); @@ -3518,7 +3532,6 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) { TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); // Second earbud connects during stream - const RawAddress test_address1 = GetTestAddress(1); ConnectCsisDevice(test_address1, 2 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight, codec_spec_conf::kLeAudioLocationFrontRight, group_size, @@ -3556,6 +3569,9 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnected) { codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + // Audio sessions are started only when device gets active EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); @@ -3624,6 +3640,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingProfileDisconnect) { codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int group_id) { return 2; })); + // Audio sessions are started only when device gets active EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc index c0d6ed5d91796c60078fd38d9510a7362b8a6a02..29d69ec57f1eea544df0f20599c212b65568f1c4 100644 --- a/system/bta/le_audio/le_audio_types.cc +++ b/system/bta/le_audio/le_audio_types.cc @@ -70,124 +70,154 @@ static uint8_t min_req_devices_cnt( return curr_min_req_devices_cnt; } -void get_cis_count(const AudioSetConfigurations* audio_set_confs, - types::LeAudioConfigurationStrategy strategy, - int group_ase_snk_cnt, int group_ase_src_count, - uint8_t* cis_count_bidir, uint8_t* cis_count_unidir_sink, - uint8_t* cis_count_unidir_source) { - LOG_INFO(" strategy %d, sink ases: %d, source ases %d", - static_cast<int>(strategy), group_ase_snk_cnt, group_ase_src_count); - - for (auto audio_set_conf : *audio_set_confs) { - std::pair<uint8_t /* sink */, uint8_t /* source */> snk_src_pair(0, 0); - uint8_t bidir_count = 0; - uint8_t unidir_sink_count = 0; - uint8_t unidir_source_count = 0; - - LOG_INFO("%s ", audio_set_conf->name.c_str()); - bool stategy_mismatch = false; - for (auto ent : (*audio_set_conf).confs) { - if (ent.strategy != strategy) { - LOG_DEBUG("Strategy does not match (%d != %d)- skip this configuration", - static_cast<int>(ent.strategy), static_cast<int>(strategy)); - stategy_mismatch = true; - break; - } - if (ent.direction == kLeAudioDirectionSink) { - snk_src_pair.first += ent.ase_cnt; - } - if (ent.direction == kLeAudioDirectionSource) { - snk_src_pair.second += ent.ase_cnt; - } +inline void get_cis_count(const AudioSetConfiguration& audio_set_conf, + int expected_device_cnt, + types::LeAudioConfigurationStrategy strategy, + int avail_group_sink_ase_count, + int avail_group_source_ase_count, + uint8_t& out_current_cis_count_bidir, + uint8_t& out_current_cis_count_unidir_sink, + uint8_t& out_current_cis_count_unidir_source) { + LOG_INFO("%s", audio_set_conf.name.c_str()); + + /* Sum up the requirements from all subconfigs. They usually have different + * directions. + */ + types::BidirectionalPair<uint8_t> config_ase_count = {0, 0}; + int config_device_cnt = 0; + + for (auto ent : audio_set_conf.confs) { + if ((ent.direction == kLeAudioDirectionSink) && + (ent.strategy != strategy)) { + LOG_DEBUG("Strategy does not match (%d != %d)- skip this configuration", + static_cast<int>(ent.strategy), static_cast<int>(strategy)); + return; } - if (stategy_mismatch) { - continue; + /* Sum up sink and source ases */ + if (ent.direction == kLeAudioDirectionSink) { + config_ase_count.sink += ent.ase_cnt; + } + if (ent.direction == kLeAudioDirectionSource) { + config_ase_count.source += ent.ase_cnt; } - /* Before we start adding another CISes, ignore scenarios which cannot - * satisfied because of the number of ases - */ + /* Calculate the max device count */ + config_device_cnt = + std::max(static_cast<uint8_t>(config_device_cnt), ent.device_cnt); + } - if (group_ase_snk_cnt == 0 && snk_src_pair.first > 0) { - LOG_DEBUG("Group does not have sink ASEs"); - continue; - } + LOG_DEBUG("Config sink ases: %d, source ases: %d, device count: %d", + config_ase_count.sink, config_ase_count.source, config_device_cnt); - if (group_ase_src_count == 0 && snk_src_pair.second > 0) { - LOG_DEBUG("Group does not have source ASEs"); - continue; - } + /* Reject configurations not matching our device count */ + if (expected_device_cnt != config_device_cnt) { + LOG_DEBUG(" Device cnt %d != %d", expected_device_cnt, config_device_cnt); + return; + } - /* Configuration list is set in the prioritized order. - * it might happen that more prio configuration can be supported - * and is already taken into account. - * Now let's try to ignore ortogonal configuration which would just - * increase our demant on number of CISes but will never happen - */ + /* Reject configurations requiring sink ASES if our group has none */ + if ((avail_group_sink_ase_count == 0) && (config_ase_count.sink > 0)) { + LOG_DEBUG("Group does not have sink ASEs"); + return; + } - if (snk_src_pair.first == 0 && - (*cis_count_unidir_sink > 0 || *cis_count_bidir > 0)) { - LOG_DEBUG( - "More prio configuration using sink ASEs has been taken into " - "account"); - continue; - } - if (snk_src_pair.second == 0 && - (*cis_count_unidir_source > 0 || *cis_count_bidir > 0)) { - LOG_DEBUG( - "More prio configuration using source ASEs has been taken into " - "account"); - continue; - } + /* Reject configurations requiring source ASES if our group has none */ + if ((avail_group_source_ase_count == 0) && (config_ase_count.source > 0)) { + LOG_DEBUG("Group does not have source ASEs"); + return; + } - bidir_count = std::min(snk_src_pair.first, snk_src_pair.second); - unidir_sink_count = ((snk_src_pair.first - bidir_count) > 0) - ? (snk_src_pair.first - bidir_count) - : 0; - unidir_source_count = ((snk_src_pair.second - bidir_count) > 0) - ? (snk_src_pair.second - bidir_count) - : 0; - - *cis_count_bidir = std::max(bidir_count, *cis_count_bidir); - - /* Check if we can reduce a number of unicast CISes in case bidirectional - * are use in other or this scenario */ - if (bidir_count < *cis_count_bidir) { - /* Since we already have bidirectional cises available from other - * scenarios, let's decrease number of unicast sinks in this scenario. - */ - uint8_t available_bidir = *cis_count_bidir - bidir_count; - unidir_sink_count = - unidir_sink_count - std::min(unidir_sink_count, available_bidir); - unidir_source_count = - unidir_source_count - std::min(unidir_source_count, available_bidir); - } else if (bidir_count > *cis_count_bidir) { - /* Lets decrease number of the unicast cises from previouse scenarios */ - uint8_t available_bidir = bidir_count - *cis_count_bidir; - *cis_count_unidir_sink = - *cis_count_unidir_sink - - std::min(*cis_count_unidir_sink, available_bidir); - *cis_count_unidir_source = - *cis_count_unidir_source - - std::min(*cis_count_unidir_source, available_bidir); + /* If expected group size is 1, then make sure device has enough ASEs */ + if (expected_device_cnt == 1) { + if ((config_ase_count.sink > avail_group_sink_ase_count) || + (config_ase_count.source > avail_group_source_ase_count)) { + LOG_DEBUG("Single device group with not enought sink/source ASEs"); + return; } + } - *cis_count_unidir_sink = - std::max(unidir_sink_count, *cis_count_unidir_sink); - *cis_count_unidir_source = - std::max(unidir_source_count, *cis_count_unidir_source); + /* Configuration list is set in the prioritized order. + * it might happen that a higher prio configuration can be supported + * and is already taken into account (out_current_cis_count_* is non zero). + * Now let's try to ignore ortogonal configuration which would just + * increase our demant on number of CISes but will never happen + */ + if (config_ase_count.sink == 0 && (out_current_cis_count_unidir_sink > 0 || + out_current_cis_count_bidir > 0)) { + LOG_INFO( + "Higher prio configuration using sink ASEs has been taken into " + "account"); + return; + } + if (config_ase_count.source == 0 && + (out_current_cis_count_unidir_source > 0 || + out_current_cis_count_bidir > 0)) { LOG_INFO( + "Higher prio configuration using source ASEs has been taken into " + "account"); + return; + } + + /* Check how many bidirectional cises we can use */ + uint8_t config_bidir_cis_count = + std::min(config_ase_count.sink, config_ase_count.source); + /* Count the remaining unidirectional cises */ + uint8_t config_unidir_sink_cis_count = + config_ase_count.sink - config_bidir_cis_count; + uint8_t config_unidir_source_cis_count = + config_ase_count.source - config_bidir_cis_count; + + /* WARNING: Minipolicy which prioritizes bidirectional configs */ + if (config_bidir_cis_count > out_current_cis_count_bidir) { + /* Correct all counters to represent this single config */ + out_current_cis_count_bidir = config_bidir_cis_count; + out_current_cis_count_unidir_sink = config_unidir_sink_cis_count; + out_current_cis_count_unidir_source = config_unidir_source_cis_count; + + } else if (out_current_cis_count_bidir == 0) { + /* No bidirectionals possible yet. Calculate for unidirectional cises. */ + if ((out_current_cis_count_unidir_sink == 0) && + (out_current_cis_count_unidir_source == 0)) { + out_current_cis_count_unidir_sink = config_unidir_sink_cis_count; + out_current_cis_count_unidir_source = config_unidir_source_cis_count; + } + } +} + +void get_cis_count(const AudioSetConfigurations& audio_set_confs, + int expected_device_cnt, + types::LeAudioConfigurationStrategy strategy, + int avail_group_ase_snk_cnt, int avail_group_ase_src_count, + uint8_t& out_cis_count_bidir, + uint8_t& out_cis_count_unidir_sink, + uint8_t& out_cis_count_unidir_source) { + LOG_INFO( + " strategy %d, group avail sink ases: %d, group avail source ases %d " + "expected_device_count %d", + static_cast<int>(strategy), avail_group_ase_snk_cnt, + avail_group_ase_src_count, expected_device_cnt); + + /* Look for the most optimal configuration and store the needed cis counts */ + for (auto audio_set_conf : audio_set_confs) { + get_cis_count(*audio_set_conf, expected_device_cnt, strategy, + avail_group_ase_snk_cnt, avail_group_ase_src_count, + out_cis_count_bidir, out_cis_count_unidir_sink, + out_cis_count_unidir_source); + + LOG_DEBUG( "Intermediate step: Bi-Directional: %d," " Uni-Directional Sink: %d, Uni-Directional Source: %d ", - *cis_count_bidir, *cis_count_unidir_sink, *cis_count_unidir_source); + out_cis_count_bidir, out_cis_count_unidir_sink, + out_cis_count_unidir_source); } LOG_INFO( " Maximum CIS count, Bi-Directional: %d," " Uni-Directional Sink: %d, Uni-Directional Source: %d", - *cis_count_bidir, *cis_count_unidir_sink, *cis_count_unidir_source); + out_cis_count_bidir, out_cis_count_unidir_sink, + out_cis_count_unidir_source); } bool check_if_may_cover_scenario(const AudioSetConfigurations* audio_set_confs, diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h index 67dbeb4a3dbdd6e061b733d505f9ec8c200f561d..b5935a5365af923c60437c150298add93a82743a 100644 --- a/system/bta/le_audio/le_audio_types.h +++ b/system/bta/le_audio/le_audio_types.h @@ -751,11 +751,12 @@ static constexpr uint32_t kChannelAllocationStereo = codec_spec_conf::kLeAudioLocationFrontRight; /* Declarations */ -void get_cis_count(const AudioSetConfigurations* audio_set_configurations, +void get_cis_count(const AudioSetConfigurations& audio_set_configurations, + int expected_device_cnt, types::LeAudioConfigurationStrategy strategy, int group_ase_snk_cnt, int group_ase_src_count, - uint8_t* cis_count_bidir, uint8_t* cis_count_unidir_sink, - uint8_t* cis_count_unidir_source); + uint8_t& cis_count_bidir, uint8_t& cis_count_unidir_sink, + uint8_t& cis_count_unidir_source); bool check_if_may_cover_scenario( const AudioSetConfigurations* audio_set_configurations, uint8_t group_size); bool check_if_may_cover_scenario( diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc index da848102846e41add8cae4746c69a06971818482..f9fcb7caa2adc5bf9b2c5241a6ed5db65bb8f74c 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -32,6 +32,7 @@ #include "le_audio_set_configuration_provider.h" #include "mock_codec_manager.h" #include "mock_controller.h" +#include "mock_csis_client.h" #include "mock_iso_manager.h" #include "types/bt_transport.h" @@ -53,6 +54,10 @@ std::map<std::string, int> mock_function_count_map; extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_; void osi_property_set_bool(const char* key, bool value); +static const char* test_flags[] = { + "INIT_logging_debug_enabled_for_all=true", + nullptr, +}; constexpr uint8_t media_ccid = 0xC0; constexpr auto media_context = @@ -146,11 +151,6 @@ static RawAddress GetTestAddress(uint8_t index) { return {{0xC0, 0xDE, 0xC0, 0xDE, 0x00, index}}; } -static uint8_t ase_id_last_assigned; -static uint8_t additional_ases = 0; -static uint8_t channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel; -static uint16_t sample_freq_ = codec_specific::kCapSamplingFrequency16000Hz; - class MockLeAudioGroupStateMachineCallbacks : public LeAudioGroupStateMachine::Callbacks { public: @@ -169,20 +169,36 @@ class MockLeAudioGroupStateMachineCallbacks class StateMachineTest : public Test { protected: + uint8_t ase_id_last_assigned = types::ase::kAseIdInvalid; + uint8_t additional_snk_ases = 0; + uint8_t additional_src_ases = 0; + uint8_t channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel; + uint16_t sample_freq_ = codec_specific::kCapSamplingFrequency16000Hz; + void SetUp() override { + bluetooth::common::InitFlags::Load(test_flags); mock_function_count_map.clear(); controller::SetMockControllerInterface(&mock_controller_); bluetooth::manager::SetMockBtmInterface(&btm_interface); gatt::SetMockBtaGattInterface(&gatt_interface); gatt::SetMockBtaGattQueue(&gatt_queue); - ase_id_last_assigned = types::ase::kAseIdInvalid; - additional_ases = 0; ::le_audio::AudioSetConfigurationProvider::Initialize(); LeAudioGroupStateMachine::Initialize(&mock_callbacks_); ContentControlIdKeeper::GetInstance()->Start(); + MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_); + ON_CALL(mock_csis_client_module_, Get()) + .WillByDefault(Return(&mock_csis_client_module_)); + ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) + .WillByDefault(Return(true)); + ON_CALL(mock_csis_client_module_, GetDeviceList(_)) + .WillByDefault(Invoke([this](int group_id) { return addresses_; })); + ON_CALL(mock_csis_client_module_, GetDesiredSize(_)) + .WillByDefault( + Invoke([this](int group_id) { return (int)(addresses_.size()); })); + // Support 2M Phy ON_CALL(mock_controller_, SupportsBle2mPhy()).WillByDefault(Return(true)); ON_CALL(btm_interface, IsPhy2mSupported(_, _)).WillByDefault(Return(true)); @@ -445,6 +461,7 @@ class StateMachineTest : public Test { ase_ctp_handlers[i] = nullptr; le_audio_devices_.clear(); + addresses_.clear(); cached_codec_configuration_map_.clear(); cached_ase_to_cis_id_map_.clear(); LeAudioGroupStateMachine::Cleanup(); @@ -492,6 +509,7 @@ class StateMachineTest : public Test { } le_audio_devices_.push_back(leAudioDevice); + addresses_.push_back(leAudioDevice->address_); return std::move(leAudioDevice); } @@ -511,10 +529,9 @@ class StateMachineTest : public Test { return &(*group); } - static void InjectAseStateNotification(types::ase* ase, LeAudioDevice* device, - LeAudioDeviceGroup* group, - uint8_t new_state, - void* new_state_params) { + void InjectAseStateNotification(types::ase* ase, LeAudioDevice* device, + LeAudioDeviceGroup* group, uint8_t new_state, + void* new_state_params) { // Prepare additional params switch (new_state) { case ascs::kAseStateCodecConfigured: { @@ -661,7 +678,7 @@ class StateMachineTest : public Test { }); } - static void InjectInitialIdleNotification(LeAudioDeviceGroup* group) { + void InjectInitialIdleNotification(LeAudioDeviceGroup* group) { for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { for (auto& ase : device->ases_) { @@ -686,18 +703,18 @@ class StateMachineTest : public Test { uint8_t num_ase_src; switch (context_type) { case kContextTypeRingtone: - num_ase_snk = 1 + additional_ases; - num_ase_src = 0; + num_ase_snk = 1 + additional_snk_ases; + num_ase_src = 0 + additional_src_ases; break; case kContextTypeMedia: - num_ase_snk = 2 + additional_ases; - num_ase_src = 0; + num_ase_snk = 2 + additional_snk_ases; + num_ase_src = 0 + additional_src_ases; break; case kContextTypeConversational: - num_ase_snk = 1 + additional_ases; - num_ase_src = 1; + num_ase_snk = 1 + additional_snk_ases; + num_ase_src = 1 + additional_src_ases; break; default: @@ -941,7 +958,7 @@ class StateMachineTest : public Test { void PrepareEnableHandler(LeAudioDeviceGroup* group, int verify_ase_count = 0, bool inject_enabling = true) { ase_ctp_handlers[ascs::kAseCtpOpcodeEnable] = - [group, verify_ase_count, inject_enabling]( + [group, verify_ase_count, inject_enabling, this]( LeAudioDevice* device, std::vector<uint8_t> value, GATT_WRITE_OP_CB cb, void* cb_data) { auto num_ase = value[1]; @@ -989,9 +1006,9 @@ class StateMachineTest : public Test { void PrepareDisableHandler(LeAudioDeviceGroup* group, int verify_ase_count = 0) { ase_ctp_handlers[ascs::kAseCtpOpcodeDisable] = - [group, verify_ase_count](LeAudioDevice* device, - std::vector<uint8_t> value, - GATT_WRITE_OP_CB cb, void* cb_data) { + [group, verify_ase_count, this](LeAudioDevice* device, + std::vector<uint8_t> value, + GATT_WRITE_OP_CB cb, void* cb_data) { auto num_ase = value[1]; // Verify ase count if needed @@ -1035,9 +1052,9 @@ class StateMachineTest : public Test { void PrepareReceiverStartReady(LeAudioDeviceGroup* group, int verify_ase_count = 0) { ase_ctp_handlers[ascs::kAseCtpOpcodeReceiverStartReady] = - [group, verify_ase_count](LeAudioDevice* device, - std::vector<uint8_t> value, - GATT_WRITE_OP_CB cb, void* cb_data) { + [group, verify_ase_count, this](LeAudioDevice* device, + std::vector<uint8_t> value, + GATT_WRITE_OP_CB cb, void* cb_data) { auto num_ase = value[1]; // Verify ase count if needed @@ -1073,9 +1090,9 @@ class StateMachineTest : public Test { void PrepareReceiverStopReady(LeAudioDeviceGroup* group, int verify_ase_count = 0) { ase_ctp_handlers[ascs::kAseCtpOpcodeReceiverStopReady] = - [group, verify_ase_count](LeAudioDevice* device, - std::vector<uint8_t> value, - GATT_WRITE_OP_CB cb, void* cb_data) { + [group, verify_ase_count, this](LeAudioDevice* device, + std::vector<uint8_t> value, + GATT_WRITE_OP_CB cb, void* cb_data) { auto num_ase = value[1]; // Verify ase count if needed @@ -1143,6 +1160,7 @@ class StateMachineTest : public Test { }; } + MockCsisClient mock_csis_client_module_; NiceMock<controller::MockControllerInterface> mock_controller_; NiceMock<bluetooth::manager::MockBtmInterface> btm_interface; gatt::MockBtaGattInterface gatt_interface; @@ -1163,6 +1181,7 @@ class StateMachineTest : public Test { MockLeAudioGroupStateMachineCallbacks mock_callbacks_; std::vector<std::shared_ptr<LeAudioDevice>> le_audio_devices_; + std::vector<RawAddress> addresses_; std::map<uint8_t, std::unique_ptr<LeAudioDeviceGroup>> le_audio_device_groups_; bool group_create_command_disallowed_ = false; @@ -1179,6 +1198,9 @@ TEST_F(StateMachineTest, testCleanup) { } TEST_F(StateMachineTest, testConfigureCodecSingle) { + /* Device is banded headphones with 1x snk + 0x src ase + * (1xunidirectional CIS) with channel count 2 (for stereo + */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 2; channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | @@ -1259,6 +1281,11 @@ TEST_F(StateMachineTest, testConfigureCodecMulti) { } TEST_F(StateMachineTest, testConfigureQosSingle) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; + additional_src_ases = 1; const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 3; @@ -1269,8 +1296,8 @@ TEST_F(StateMachineTest, testConfigureQosSingle) { * should have been configured. */ auto* leAudioDevice = group->GetFirstDevice(); - PrepareConfigureCodecHandler(group, 1); - PrepareConfigureQosHandler(group, 1); + PrepareConfigureCodecHandler(group, 2); + PrepareConfigureQosHandler(group, 2); // Start the configuration and stream Media content EXPECT_CALL(gatt_queue, @@ -1296,6 +1323,11 @@ TEST_F(StateMachineTest, testConfigureQosSingle) { } TEST_F(StateMachineTest, testConfigureQosSingleRecoverCig) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; + additional_src_ases = 1; const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 3; @@ -1309,8 +1341,8 @@ TEST_F(StateMachineTest, testConfigureQosSingleRecoverCig) { * should have been configured. */ auto* leAudioDevice = group->GetFirstDevice(); - PrepareConfigureCodecHandler(group, 1); - PrepareConfigureQosHandler(group, 1); + PrepareConfigureCodecHandler(group, 2); + PrepareConfigureQosHandler(group, 2); // Start the configuration and stream Media content EXPECT_CALL(gatt_queue, @@ -1382,14 +1414,19 @@ TEST_F(StateMachineTest, testConfigureQosMultiple) { } TEST_F(StateMachineTest, testStreamSingle) { + /* Device is banded headphones with 1x snk + 0x src ase + * (1xunidirectional CIS) with channel count 2 (for stereo + */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); - /* Since we prepared device with Ringtone context in mind, only one ASE - * should have been configured. + /* Ringtone with channel count 1 for single device and 1 ASE sink will + * end up with 1 Sink ASE being configured. */ PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); @@ -1427,18 +1464,21 @@ TEST_F(StateMachineTest, testStreamSingle) { } TEST_F(StateMachineTest, testStreamSkipEnablingSink) { - const auto context_type = kContextTypeRingtone; + /* Device is banded headphones with 2x snk + none src ase + * (2x unidirectional CIS) + */ + const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); - /* Since we prepared device with Ringtone context in mind, only one ASE - * should have been configured. + /* For Media context type with channel count 1 and two ASEs, + * there should have be 2 Ases configured configured. */ - PrepareConfigureCodecHandler(group, 1); - PrepareConfigureQosHandler(group, 1); - PrepareEnableHandler(group, 1, false); + PrepareConfigureCodecHandler(group, 2); + PrepareConfigureQosHandler(group, 2); + PrepareEnableHandler(group, 2, false); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, @@ -1448,7 +1488,7 @@ TEST_F(StateMachineTest, testStreamSkipEnablingSink) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); @@ -1472,18 +1512,23 @@ TEST_F(StateMachineTest, testStreamSkipEnablingSink) { } TEST_F(StateMachineTest, testStreamSkipEnablingSinkSource) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional CIS) + */ const auto context_type = kContextTypeConversational; const int leaudio_group_id = 4; + additional_snk_ases = 1; + // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); - /* Since we prepared device with Conversional context in mind, one Sink ASE - * and one Source ASE should have been configured. + /* Since we prepared device with Conversional context in mind, + * 2 Sink ASEs and 1 Source ASE should have been configured. */ - PrepareConfigureCodecHandler(group, 2); - PrepareConfigureQosHandler(group, 2); - PrepareEnableHandler(group, 2, false); + PrepareConfigureCodecHandler(group, 3); + PrepareConfigureQosHandler(group, 3); + PrepareEnableHandler(group, 3, false); PrepareReceiverStartReady(group, 1); auto* leAudioDevice = group->GetFirstDevice(); @@ -1494,7 +1539,7 @@ TEST_F(StateMachineTest, testStreamSkipEnablingSinkSource) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(3); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); @@ -1623,19 +1668,23 @@ TEST_F(StateMachineTest, testStreamMultiple) { } TEST_F(StateMachineTest, testDisableSingle) { + /* Device is banded headphones with 2x snk + 0x src ase + * (2xunidirectional CIS) + */ + additional_snk_ases = 1; const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); - /* Since we prepared device with Ringtone context in mind, only one ASE - * should have been configured. + /* Ringtone context plus additional ASE with channel count 1 + * gives us 2 ASE which should have been configured. */ - PrepareConfigureCodecHandler(group, 1); - PrepareConfigureQosHandler(group, 1); - PrepareEnableHandler(group, 1); - PrepareDisableHandler(group, 1); + PrepareConfigureCodecHandler(group, 2); + PrepareConfigureQosHandler(group, 2); + PrepareEnableHandler(group, 2); + PrepareDisableHandler(group, 2); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, @@ -1645,13 +1694,13 @@ TEST_F(StateMachineTest, testDisableSingle) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL( *mock_iso_manager_, RemoveIsoDataPath( _, bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput)) - .Times(1); - EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1); + .Times(2); + EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); InjectInitialIdleNotification(group); @@ -1752,6 +1801,10 @@ TEST_F(StateMachineTest, testDisableMultiple) { } TEST_F(StateMachineTest, testDisableBidirectional) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; const auto context_type = kContextTypeConversational; const int leaudio_group_id = 4; @@ -1761,10 +1814,10 @@ TEST_F(StateMachineTest, testDisableBidirectional) { /* Since we prepared device with Conversional context in mind, Sink and Source * ASEs should have been configured. */ - PrepareConfigureCodecHandler(group, 2); - PrepareConfigureQosHandler(group, 2); - PrepareEnableHandler(group, 2); - PrepareDisableHandler(group, 2); + PrepareConfigureCodecHandler(group, 3); + PrepareConfigureQosHandler(group, 3); + PrepareEnableHandler(group, 3); + PrepareDisableHandler(group, 3); PrepareReceiverStartReady(group, 1); PrepareReceiverStopReady(group, 1); @@ -1776,15 +1829,48 @@ TEST_F(StateMachineTest, testDisableBidirectional) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); - EXPECT_CALL( - *mock_iso_manager_, - RemoveIsoDataPath( - _, - bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput | - bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionOutput)) - .Times(1); - EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(3); + bool removed_bidirectional = false; + bool removed_unidirectional = false; + + /* Check data path removal */ + ON_CALL(*mock_iso_manager_, RemoveIsoDataPath) + .WillByDefault(Invoke([&removed_bidirectional, &removed_unidirectional, + this](uint16_t conn_handle, + uint8_t data_path_dir) { + /* Set flags for verification */ + if (data_path_dir == + (bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput | + bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionOutput)) { + removed_bidirectional = true; + } else if (data_path_dir == bluetooth::hci::iso_manager:: + kRemoveIsoDataPathDirectionInput) { + removed_unidirectional = true; + } + + /* Copied from default handler of RemoveIsoDataPath*/ + auto dev_it = + std::find_if(le_audio_devices_.begin(), le_audio_devices_.end(), + [&conn_handle](auto& dev) { + auto ases = dev->GetAsesByCisConnHdl(conn_handle); + return (ases.sink || ases.source); + }); + if (dev_it == le_audio_devices_.end()) { + return; + } + + for (auto& kv_pair : le_audio_device_groups_) { + auto& group = kv_pair.second; + if (group->IsDeviceInTheGroup(dev_it->get())) { + LeAudioGroupStateMachine::Get()->ProcessHciNotifRemoveIsoDataPath( + group.get(), dev_it->get(), 0, conn_handle); + return; + } + } + /* End of copy */ + })); + + EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); // Start the configuration and stream Media content @@ -1802,11 +1888,18 @@ TEST_F(StateMachineTest, testDisableBidirectional) { // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED); + ASSERT_EQ(removed_bidirectional, true); + ASSERT_EQ(removed_unidirectional, true); } TEST_F(StateMachineTest, testReleaseSingle) { + /* Device is banded headphones with 1x snk + 0x src ase + * (1xunidirectional CIS) with channel count 2 (for stereo) + */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -1861,8 +1954,13 @@ TEST_F(StateMachineTest, testReleaseSingle) { } TEST_F(StateMachineTest, testReleaseCachingSingle) { + /* Device is banded headphones with 1x snk + 0x src ase + * (1xunidirectional CIS) + */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -1931,7 +2029,7 @@ TEST_F(StateMachineTest, channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | kLeAudioCodecLC3ChannelCountTwoChannel; - additional_ases = 2; + additional_snk_ases = 2; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -2017,7 +2115,7 @@ TEST_F(StateMachineTest, channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | kLeAudioCodecLC3ChannelCountTwoChannel; - additional_ases = 2; + additional_snk_ases = 2; /* Prepare fake connected device group with update of Media and Conversational * contexts */ @@ -2175,6 +2273,10 @@ TEST_F(StateMachineTest, testReleaseMultiple) { } TEST_F(StateMachineTest, testReleaseBidirectional) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; const auto context_type = kContextTypeConversational; const auto leaudio_group_id = 6; @@ -2184,12 +2286,12 @@ TEST_F(StateMachineTest, testReleaseBidirectional) { /* Since we prepared device with Conversional context in mind, Sink and Source * ASEs should have been configured. */ - PrepareConfigureCodecHandler(group, 2); - PrepareConfigureQosHandler(group, 2); - PrepareEnableHandler(group, 2); - PrepareDisableHandler(group, 2); + PrepareConfigureCodecHandler(group, 3); + PrepareConfigureQosHandler(group, 3); + PrepareEnableHandler(group, 3); + PrepareDisableHandler(group, 3); PrepareReceiverStartReady(group, 1); - PrepareReleaseHandler(group, 2); + PrepareReleaseHandler(group, 3); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, @@ -2199,9 +2301,9 @@ TEST_F(StateMachineTest, testReleaseBidirectional) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); - EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(1); - EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(3); + EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); + EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); @@ -2223,6 +2325,10 @@ TEST_F(StateMachineTest, testReleaseBidirectional) { } TEST_F(StateMachineTest, testDisableAndReleaseBidirectional) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; const auto context_type = kContextTypeConversational; const int leaudio_group_id = 4; @@ -2232,13 +2338,13 @@ TEST_F(StateMachineTest, testDisableAndReleaseBidirectional) { /* Since we prepared device with Conversional context in mind, Sink and Source * ASEs should have been configured. */ - PrepareConfigureCodecHandler(group, 2); - PrepareConfigureQosHandler(group, 2); - PrepareEnableHandler(group, 2); - PrepareDisableHandler(group, 2); + PrepareConfigureCodecHandler(group, 3); + PrepareConfigureQosHandler(group, 3); + PrepareEnableHandler(group, 3); + PrepareDisableHandler(group, 3); PrepareReceiverStartReady(group, 1); PrepareReceiverStopReady(group, 1); - PrepareReleaseHandler(group, 2); + PrepareReleaseHandler(group, 3); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, @@ -2248,9 +2354,9 @@ TEST_F(StateMachineTest, testDisableAndReleaseBidirectional) { EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); - EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); - EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(1); - EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(3); + EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); + EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); // Start the configuration and stream Media content @@ -2333,6 +2439,10 @@ TEST_F(StateMachineTest, testAseIdAssignmentCodecConfigured) { } TEST_F(StateMachineTest, testAseAutonomousRelease) { + /* Device is banded headphones with 2x snk + 1x src ase + * (1x bidirectional + 1xunidirectional CIS) + */ + additional_snk_ases = 1; const auto context_type = kContextTypeConversational; const int leaudio_group_id = 4; @@ -2342,13 +2452,13 @@ TEST_F(StateMachineTest, testAseAutonomousRelease) { /* Since we prepared device with Conversional context in mind, Sink and Source * ASEs should have been configured. */ - PrepareConfigureCodecHandler(group, 2); - PrepareConfigureQosHandler(group, 2); - PrepareEnableHandler(group, 2); - PrepareDisableHandler(group, 2); + PrepareConfigureCodecHandler(group, 3); + PrepareConfigureQosHandler(group, 3); + PrepareEnableHandler(group, 3); + PrepareDisableHandler(group, 3); PrepareReceiverStartReady(group, 1); PrepareReceiverStopReady(group, 1); - PrepareReleaseHandler(group, 2); + PrepareReleaseHandler(group, 3); InjectInitialIdleNotification(group); @@ -2370,7 +2480,7 @@ TEST_F(StateMachineTest, testAseAutonomousRelease) { .Times(AtLeast(1)); /* Single disconnect as it is bidirectional Cis*/ - EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1); + EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { @@ -2457,6 +2567,8 @@ TEST_F(StateMachineTest, testAseAutonomousRelease2Devices) { TEST_F(StateMachineTest, testStateTransitionTimeoutOnIdleState) { const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -2483,6 +2595,8 @@ TEST_F(StateMachineTest, testStateTransitionTimeoutOnIdleState) { TEST_F(StateMachineTest, testStateTransitionTimeout) { const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); @@ -2517,6 +2631,8 @@ MATCHER_P(dataPathIsEq, expected, "") { return (arg.data_path_id == expected); } TEST_F(StateMachineTest, testConfigureDataPathForHost) { const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; /* Should be called 3 times because * 1 - calling GetConfigurations just after connection @@ -2555,6 +2671,8 @@ TEST_F(StateMachineTest, testConfigureDataPathForHost) { TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel | + kLeAudioCodecLC3ChannelCountTwoChannel; /* Should be called 3 times because * 1 - calling GetConfigurations just after connection @@ -2959,7 +3077,8 @@ TEST_F(StateMachineTest, BoundedHeadphonesConversationalToMediaChannelCount_2) { sample_freq_ |= codec_specific::kCapSamplingFrequency48000Hz | codec_specific::kCapSamplingFrequency32000Hz; - additional_ases = 3; + additional_snk_ases = 3; + additional_src_ases = 1; ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid); ContentControlIdKeeper::GetInstance()->SetCcid(call_context, call_ccid); @@ -3039,5 +3158,97 @@ TEST_F(StateMachineTest, BoundedHeadphonesConversationalToMediaChannelCount_2) { testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); } + +TEST_F(StateMachineTest, BoundedHeadphonesConversationalToMediaChannelCount_1) { + const auto initial_context_type = kContextTypeConversational; + const auto new_context_type = kContextTypeMedia; + const auto leaudio_group_id = 6; + const auto num_devices = 1; + channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel; + + sample_freq_ |= codec_specific::kCapSamplingFrequency48000Hz | + codec_specific::kCapSamplingFrequency32000Hz; + additional_snk_ases = 3; + additional_src_ases = 1; + + ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid); + ContentControlIdKeeper::GetInstance()->SetCcid(call_context, call_ccid); + + // Prepare one fake connected devices in a group + auto* group = PrepareSingleTestDeviceGroup( + leaudio_group_id, initial_context_type, num_devices, + kContextTypeConversational | kContextTypeMedia); + ASSERT_EQ(group->Size(), num_devices); + + // Cannot verify here as we will change the number of ases on reconfigure + PrepareConfigureCodecHandler(group, 0, true); + PrepareConfigureQosHandler(group); + PrepareEnableHandler(group); + PrepareDisableHandler(group); + PrepareReleaseHandler(group); + PrepareReceiverStartReady(group); + + InjectInitialIdleNotification(group); + + auto* leAudioDevice = group->GetFirstDevice(); + auto expected_devices_written = 0; + while (leAudioDevice) { + /* 8 Writes: + * 1: Codec config (+1 after reconfig) + * 2: Codec QoS (+1 after reconfig) + * 3: Enabling (+1 after reconfig) + * 4: ReceiverStartReady (only for conversational) + * 5: Release + */ + EXPECT_CALL(gatt_queue, + WriteCharacteristic(leAudioDevice->conn_id_, + leAudioDevice->ctp_hdls_.val_hdl, _, + GATT_WRITE_NO_RSP, _, _)) + .Times(8); + expected_devices_written++; + leAudioDevice = group->GetNextDevice(leAudioDevice); + } + ASSERT_EQ(expected_devices_written, num_devices); + + // Validate GroupStreamStatus + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream( + group, initial_context_type, types::AudioContexts(initial_context_type)); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + + // Validate GroupStreamStatus + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::RELEASING)); + + EXPECT_CALL( + mock_callbacks_, + StatusReportCb( + leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS)); + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StopStream(group); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + + // Restart stream + EXPECT_CALL( + mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream( + group, new_context_type, types::AudioContexts(new_context_type)); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); +} } // namespace internal } // namespace le_audio diff --git a/system/bta/test/common/mock_csis_client.h b/system/bta/test/common/mock_csis_client.h index 33c106a36523523e6037490905c7bd8002dbcd8a..bd2823315730b775f71788bc37e0e81078237059 100644 --- a/system/bta/test/common/mock_csis_client.h +++ b/system/bta/test/common/mock_csis_client.h @@ -33,6 +33,7 @@ class MockCsisClient : public bluetooth::csis::CsisClient { (override)); MOCK_METHOD((std::vector<RawAddress>), GetDeviceList, (int group_id), (override)); + MOCK_METHOD((int), GetDesiredSize, (int group_id), (override)); /* Called from static methods */ MOCK_METHOD((void), Initialize,