leaudio: Fix improper cis calculations for TWS style earbuds With this patch code takes number of devices in the group in order to choose best configuration when calculating required CISes. Also it fixes how strategy is taken into account - as for now we want to check only strategy for sink direction as this is defines group strategy. Also fixes unit tests and improves some important logs Bug: 261624834 Test: atest BluetoothInstrumentationTests Test: atest bluetooth_le_audio_test bluetooth_le_audio_client_test Tag: #feature Merged-In: I00a0cc426d193a78938609fce0e620a55d9102e1 Change-Id: I00a0cc426d193a78938609fce0e620a55d9102e1 (cherry picked from commit 8d3aeb573bf8984944652dfce1e609698f548041)
diff --git a/system/bta/Android.bp b/system/bta/Android.bp index 62620a0..c33a732 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp
@@ -606,6 +606,7 @@ "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 a210dc4..4ef353f 100644 --- a/system/bta/csis/csis_client.cc +++ b/system/bta/csis/csis_client.cc
@@ -525,6 +525,16 @@ } } + 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 466a1d4..75783a4 100644 --- a/system/bta/csis/csis_types.h +++ b/system/bta/csis/csis_types.h
@@ -64,7 +64,7 @@ /* 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 fe864f5..44dca04 100644 --- a/system/bta/include/bta_csis_api.h +++ b/system/bta/include/bta_csis_api.h
@@ -47,6 +47,7 @@ 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 4a53440..14143c0 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" @@ -967,10 +968,16 @@ 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) { @@ -980,7 +987,6 @@ .type = CisType::CIS_TYPE_BIDIRECTIONAL, .conn_handle = 0, }; - cises_.push_back(cis_entry); cis_count_bidir--; idx++; @@ -1045,18 +1051,21 @@ 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_; + + 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; } - 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_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) { @@ -1267,7 +1276,7 @@ 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; @@ -1535,12 +1544,12 @@ 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); } @@ -1575,9 +1584,9 @@ 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 = @@ -1585,10 +1594,11 @@ 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; @@ -1621,14 +1631,14 @@ 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; @@ -1904,21 +1914,22 @@ 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 8dbedf6..a7a3cda 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::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 @@ 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 @@ 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 @@ 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 4f5590e..ee4c51c 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -3358,6 +3358,9 @@ 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); @@ -3428,6 +3431,9 @@ 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); @@ -3514,6 +3520,9 @@ 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); @@ -3563,8 +3572,10 @@ 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, @@ -3575,6 +3586,9 @@ 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_); @@ -3587,7 +3601,6 @@ 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, @@ -3625,6 +3638,9 @@ 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); @@ -3693,6 +3709,9 @@ 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 c0d6ed5..29d69ec 100644 --- a/system/bta/le_audio/le_audio_types.cc +++ b/system/bta/le_audio/le_audio_types.cc
@@ -70,124 +70,154 @@ 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); +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()); - 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; + /* 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; - 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; - } + 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); + + /* 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; + } + + /* 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; + } + + /* 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; + } + + /* 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; } + } - if (group_ase_src_count == 0 && snk_src_pair.second > 0) { - LOG_DEBUG("Group does not have source ASEs"); - continue; - } - - /* 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 - */ - - 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; - } - - 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); - } - - *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 67dbeb4..b5935a5 100644 --- a/system/bta/le_audio/le_audio_types.h +++ b/system/bta/le_audio/le_audio_types.h
@@ -751,11 +751,12 @@ 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 da84810..f9fcb7c 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 @@ 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 @@ 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 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 @@ 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 @@ } le_audio_devices_.push_back(leAudioDevice); + addresses_.push_back(leAudioDevice->address_); return std::move(leAudioDevice); } @@ -511,10 +529,9 @@ 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 @@ }); } - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ }; } + MockCsisClient mock_csis_client_module_; NiceMock<controller::MockControllerInterface> mock_controller_; NiceMock<bluetooth::manager::MockBtmInterface> btm_interface; gatt::MockBtaGattInterface gatt_interface; @@ -1163,6 +1181,7 @@ 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, 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, 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 @@ * 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, 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 @@ * 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, 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, 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 @@ 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, 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 @@ 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, 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 @@ 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, 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 @@ /* 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 @@ 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 @@ // 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, 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 @@ 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 @@ 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, 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 @@ /* 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 @@ 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, 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 @@ /* 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 @@ 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, 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 @@ /* 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 @@ .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, 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, 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 @@ 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, 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 @@ 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 @@ 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 33c106a..bd28233 100644 --- a/system/bta/test/common/mock_csis_client.h +++ b/system/bta/test/common/mock_csis_client.h
@@ -33,6 +33,7 @@ (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,