1 /*
2 * Copyright 2020 HIMSA II K/S - www.himsa.com.
3 * Represented by EHIMA - www.ehima.com
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "devices.h"
19
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22
23 #include "btm_api_mock.h"
24 #include "le_audio_set_configuration_provider.h"
25 #include "le_audio_types.h"
26 #include "mock_controller.h"
27 #include "mock_csis_client.h"
28 #include "os/log.h"
29 #include "stack/btm/btm_int_types.h"
30
btm_bda_to_acl(const RawAddress & bda,tBT_TRANSPORT transport)31 tACL_CONN* btm_bda_to_acl(const RawAddress& bda, tBT_TRANSPORT transport) {
32 return nullptr;
33 }
34
35 namespace bluetooth {
36 namespace le_audio {
37 namespace internal {
38 namespace {
39
40 using ::le_audio::DeviceConnectState;
41 using ::le_audio::LeAudioDevice;
42 using ::le_audio::LeAudioDeviceGroup;
43 using ::le_audio::LeAudioDevices;
44 using ::le_audio::types::AseState;
45 using ::le_audio::types::AudioContexts;
46 using ::le_audio::types::AudioLocations;
47 using ::le_audio::types::BidirectionalPair;
48 using ::le_audio::types::CisType;
49 using ::le_audio::types::LeAudioContextType;
50 using testing::_;
51 using testing::Invoke;
52 using testing::Return;
53 using testing::Test;
54
GetTestAddress(int index)55 RawAddress GetTestAddress(int index) {
56 CHECK_LT(index, UINT8_MAX);
57 RawAddress result = {
58 {0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
59 return result;
60 }
61
62 class LeAudioDevicesTest : public Test {
63 protected:
SetUp()64 void SetUp() override {
65 devices_ = new LeAudioDevices();
66 bluetooth::manager::SetMockBtmInterface(&btm_interface);
67 controller::SetMockControllerInterface(&controller_interface_);
68 }
69
TearDown()70 void TearDown() override {
71 controller::SetMockControllerInterface(nullptr);
72 bluetooth::manager::SetMockBtmInterface(nullptr);
73 delete devices_;
74 }
75
76 LeAudioDevices* devices_ = nullptr;
77 bluetooth::manager::MockBtmInterface btm_interface;
78 controller::MockControllerInterface controller_interface_;
79 };
80
TEST_F(LeAudioDevicesTest,test_add)81 TEST_F(LeAudioDevicesTest, test_add) {
82 RawAddress test_address_0 = GetTestAddress(0);
83 ASSERT_EQ((size_t)0, devices_->Size());
84 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
85 ASSERT_EQ((size_t)1, devices_->Size());
86 devices_->Add(GetTestAddress(1), DeviceConnectState::CONNECTING_BY_USER, 1);
87 ASSERT_EQ((size_t)2, devices_->Size());
88 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
89 ASSERT_EQ((size_t)2, devices_->Size());
90 devices_->Add(GetTestAddress(1), DeviceConnectState::CONNECTING_BY_USER, 2);
91 ASSERT_EQ((size_t)2, devices_->Size());
92 }
93
TEST_F(LeAudioDevicesTest,test_remove)94 TEST_F(LeAudioDevicesTest, test_remove) {
95 RawAddress test_address_0 = GetTestAddress(0);
96 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
97 RawAddress test_address_1 = GetTestAddress(1);
98 devices_->Add(test_address_1, DeviceConnectState::CONNECTING_BY_USER);
99 RawAddress test_address_2 = GetTestAddress(2);
100 devices_->Add(test_address_2, DeviceConnectState::CONNECTING_BY_USER);
101 ASSERT_EQ((size_t)3, devices_->Size());
102 devices_->Remove(test_address_0);
103 ASSERT_EQ((size_t)2, devices_->Size());
104 devices_->Remove(GetTestAddress(3));
105 ASSERT_EQ((size_t)2, devices_->Size());
106 devices_->Remove(test_address_0);
107 ASSERT_EQ((size_t)2, devices_->Size());
108 }
109
TEST_F(LeAudioDevicesTest,test_find_by_address_success)110 TEST_F(LeAudioDevicesTest, test_find_by_address_success) {
111 RawAddress test_address_0 = GetTestAddress(0);
112 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
113 RawAddress test_address_1 = GetTestAddress(1);
114 devices_->Add(test_address_1, DeviceConnectState::DISCONNECTED);
115 RawAddress test_address_2 = GetTestAddress(2);
116 devices_->Add(test_address_2, DeviceConnectState::CONNECTING_BY_USER);
117 LeAudioDevice* device = devices_->FindByAddress(test_address_1);
118 ASSERT_NE(nullptr, device);
119 ASSERT_EQ(test_address_1, device->address_);
120 }
121
TEST_F(LeAudioDevicesTest,test_find_by_address_failed)122 TEST_F(LeAudioDevicesTest, test_find_by_address_failed) {
123 RawAddress test_address_0 = GetTestAddress(0);
124 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
125 RawAddress test_address_2 = GetTestAddress(2);
126 devices_->Add(test_address_2, DeviceConnectState::CONNECTING_BY_USER);
127 LeAudioDevice* device = devices_->FindByAddress(GetTestAddress(1));
128 ASSERT_EQ(nullptr, device);
129 }
130
TEST_F(LeAudioDevicesTest,test_get_by_address_success)131 TEST_F(LeAudioDevicesTest, test_get_by_address_success) {
132 RawAddress test_address_0 = GetTestAddress(0);
133 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
134 RawAddress test_address_1 = GetTestAddress(1);
135 devices_->Add(test_address_1, DeviceConnectState::DISCONNECTED);
136 RawAddress test_address_2 = GetTestAddress(2);
137 devices_->Add(test_address_2, DeviceConnectState::CONNECTING_BY_USER);
138 std::shared_ptr<LeAudioDevice> device =
139 devices_->GetByAddress(test_address_1);
140 ASSERT_NE(nullptr, device);
141 ASSERT_EQ(test_address_1, device->address_);
142 }
143
TEST_F(LeAudioDevicesTest,test_get_by_address_failed)144 TEST_F(LeAudioDevicesTest, test_get_by_address_failed) {
145 RawAddress test_address_0 = GetTestAddress(0);
146 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
147 RawAddress test_address_2 = GetTestAddress(2);
148 devices_->Add(test_address_2, DeviceConnectState::CONNECTING_BY_USER);
149 std::shared_ptr<LeAudioDevice> device =
150 devices_->GetByAddress(GetTestAddress(1));
151 ASSERT_EQ(nullptr, device);
152 }
153
TEST_F(LeAudioDevicesTest,test_find_by_conn_id_success)154 TEST_F(LeAudioDevicesTest, test_find_by_conn_id_success) {
155 devices_->Add(GetTestAddress(1), DeviceConnectState::CONNECTING_BY_USER);
156 RawAddress test_address_0 = GetTestAddress(0);
157 devices_->Add(test_address_0, DeviceConnectState::CONNECTING_BY_USER);
158 devices_->Add(GetTestAddress(4), DeviceConnectState::CONNECTING_BY_USER);
159 LeAudioDevice* device = devices_->FindByAddress(test_address_0);
160 device->conn_id_ = 0x0005;
161 ASSERT_EQ(device, devices_->FindByConnId(0x0005));
162 }
163
TEST_F(LeAudioDevicesTest,test_find_by_conn_id_failed)164 TEST_F(LeAudioDevicesTest, test_find_by_conn_id_failed) {
165 devices_->Add(GetTestAddress(1), DeviceConnectState::CONNECTING_BY_USER);
166 devices_->Add(GetTestAddress(0), DeviceConnectState::CONNECTING_BY_USER);
167 devices_->Add(GetTestAddress(4), DeviceConnectState::CONNECTING_BY_USER);
168 ASSERT_EQ(nullptr, devices_->FindByConnId(0x0006));
169 }
170
171 /* TODO: Add FindByCisConnHdl test cases (ASE) */
172
173 } // namespace
174
175 namespace {
176 using namespace ::le_audio::codec_spec_caps;
177 using namespace ::le_audio::set_configurations;
178 using namespace ::le_audio::types;
179
180 static const hdl_pair hdl_pair_nil = hdl_pair(0x0000, 0x0000);
181
182 enum class Lc3SettingId {
183 _BEGIN,
184 LC3_8_1 = _BEGIN,
185 LC3_8_2,
186 LC3_16_1,
187 LC3_16_2,
188 LC3_24_1,
189 LC3_24_2,
190 LC3_32_1,
191 LC3_32_2,
192 LC3_441_1,
193 LC3_441_2,
194 LC3_48_1,
195 LC3_48_2,
196 LC3_48_3,
197 LC3_48_4,
198 LC3_48_5,
199 LC3_48_6,
200 LC3_VND_1,
201 _END,
202 UNSUPPORTED = _END,
203 };
204 static constexpr int Lc3SettingIdBegin = static_cast<int>(Lc3SettingId::_BEGIN);
205 static constexpr int Lc3SettingIdEnd = static_cast<int>(Lc3SettingId::_END);
206
IsLc3SettingSupported(LeAudioContextType context_type,Lc3SettingId id)207 bool IsLc3SettingSupported(LeAudioContextType context_type, Lc3SettingId id) {
208 /* Update those values, on any change of codec linked with content type */
209 switch (context_type) {
210 case LeAudioContextType::RINGTONE:
211 case LeAudioContextType::CONVERSATIONAL:
212 if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2 ||
213 id == Lc3SettingId::LC3_24_1 || id == Lc3SettingId::LC3_24_2 ||
214 id == Lc3SettingId::LC3_32_1 || id == Lc3SettingId::LC3_32_2 ||
215 id == Lc3SettingId::LC3_48_1 || id == Lc3SettingId::LC3_48_2 ||
216 id == Lc3SettingId::LC3_48_3 || id == Lc3SettingId::LC3_48_4 ||
217 id == Lc3SettingId::LC3_VND_1)
218 return true;
219
220 break;
221
222 case LeAudioContextType::MEDIA:
223 case LeAudioContextType::ALERTS:
224 case LeAudioContextType::INSTRUCTIONAL:
225 case LeAudioContextType::NOTIFICATIONS:
226 case LeAudioContextType::EMERGENCYALARM:
227 case LeAudioContextType::UNSPECIFIED:
228 if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2 ||
229 id == Lc3SettingId::LC3_48_4 || id == Lc3SettingId::LC3_48_1 ||
230 id == Lc3SettingId::LC3_48_2 || id == Lc3SettingId::LC3_VND_1 ||
231 id == Lc3SettingId::LC3_24_2)
232 return true;
233
234 break;
235
236 default:
237 if (id == Lc3SettingId::LC3_16_2) return true;
238
239 break;
240 };
241
242 return false;
243 }
244
245 static constexpr uint8_t kLeAudioSamplingFreqRfu = 0x0E;
GetSamplingFrequency(Lc3SettingId id)246 uint8_t GetSamplingFrequency(Lc3SettingId id) {
247 switch (id) {
248 case Lc3SettingId::LC3_8_1:
249 case Lc3SettingId::LC3_8_2:
250 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq8000Hz;
251 case Lc3SettingId::LC3_16_1:
252 case Lc3SettingId::LC3_16_2:
253 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq16000Hz;
254 case Lc3SettingId::LC3_24_1:
255 case Lc3SettingId::LC3_24_2:
256 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq24000Hz;
257 case Lc3SettingId::LC3_32_1:
258 case Lc3SettingId::LC3_32_2:
259 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq32000Hz;
260 case Lc3SettingId::LC3_441_1:
261 case Lc3SettingId::LC3_441_2:
262 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq44100Hz;
263 case Lc3SettingId::LC3_48_1:
264 case Lc3SettingId::LC3_48_2:
265 case Lc3SettingId::LC3_48_3:
266 case Lc3SettingId::LC3_48_4:
267 case Lc3SettingId::LC3_48_5:
268 case Lc3SettingId::LC3_48_6:
269 case Lc3SettingId::LC3_VND_1:
270 return ::le_audio::codec_spec_conf::kLeAudioSamplingFreq48000Hz;
271 case Lc3SettingId::UNSUPPORTED:
272 return kLeAudioSamplingFreqRfu;
273 }
274 }
275
276 static constexpr uint8_t kLeAudioCodecLC3FrameDurRfu = 0x02;
GetFrameDuration(Lc3SettingId id)277 uint8_t GetFrameDuration(Lc3SettingId id) {
278 switch (id) {
279 case Lc3SettingId::LC3_8_1:
280 case Lc3SettingId::LC3_16_1:
281 case Lc3SettingId::LC3_24_1:
282 case Lc3SettingId::LC3_32_1:
283 case Lc3SettingId::LC3_441_1:
284 case Lc3SettingId::LC3_48_1:
285 case Lc3SettingId::LC3_48_3:
286 case Lc3SettingId::LC3_48_5:
287 return ::le_audio::codec_spec_conf::kLeAudioCodecLC3FrameDur7500us;
288 case Lc3SettingId::LC3_8_2:
289 case Lc3SettingId::LC3_16_2:
290 case Lc3SettingId::LC3_24_2:
291 case Lc3SettingId::LC3_32_2:
292 case Lc3SettingId::LC3_441_2:
293 case Lc3SettingId::LC3_48_2:
294 case Lc3SettingId::LC3_48_4:
295 case Lc3SettingId::LC3_48_6:
296 case Lc3SettingId::LC3_VND_1:
297 return ::le_audio::codec_spec_conf::kLeAudioCodecLC3FrameDur10000us;
298 case Lc3SettingId::UNSUPPORTED:
299 return kLeAudioCodecLC3FrameDurRfu;
300 }
301 }
302
303 static constexpr uint8_t kLeAudioCodecLC3OctetsPerCodecFrameInvalid = 0;
GetOctetsPerCodecFrame(Lc3SettingId id)304 uint16_t GetOctetsPerCodecFrame(Lc3SettingId id) {
305 switch (id) {
306 case Lc3SettingId::LC3_8_1:
307 return 26;
308 case Lc3SettingId::LC3_8_2:
309 case Lc3SettingId::LC3_16_1:
310 return 30;
311 case Lc3SettingId::LC3_16_2:
312 return 40;
313 case Lc3SettingId::LC3_24_1:
314 return 45;
315 case Lc3SettingId::LC3_24_2:
316 case Lc3SettingId::LC3_32_1:
317 return 60;
318 case Lc3SettingId::LC3_32_2:
319 return 80;
320 case Lc3SettingId::LC3_441_1:
321 return 97;
322 case Lc3SettingId::LC3_441_2:
323 return 130;
324 case Lc3SettingId::LC3_48_1:
325 return 75;
326 case Lc3SettingId::LC3_48_2:
327 case Lc3SettingId::LC3_VND_1:
328 return 100;
329 case Lc3SettingId::LC3_48_3:
330 return 90;
331 case Lc3SettingId::LC3_48_4:
332 return 120;
333 case Lc3SettingId::LC3_48_5:
334 return 116;
335 case Lc3SettingId::LC3_48_6:
336 return 155;
337 case Lc3SettingId::UNSUPPORTED:
338 return kLeAudioCodecLC3OctetsPerCodecFrameInvalid;
339 }
340 }
341
342 class PublishedAudioCapabilitiesBuilder {
343 public:
PublishedAudioCapabilitiesBuilder()344 PublishedAudioCapabilitiesBuilder() {}
345
Add(LeAudioCodecId codec_id,uint8_t conf_sampling_frequency,uint8_t conf_frame_duration,uint8_t audio_channel_counts,uint16_t octets_per_frame,uint8_t codec_frames_per_sdu=0)346 void Add(LeAudioCodecId codec_id, uint8_t conf_sampling_frequency,
347 uint8_t conf_frame_duration, uint8_t audio_channel_counts,
348 uint16_t octets_per_frame, uint8_t codec_frames_per_sdu = 0) {
349 uint16_t sampling_frequencies =
350 SamplingFreqConfig2Capability(conf_sampling_frequency);
351 uint8_t frame_durations =
352 FrameDurationConfig2Capability(conf_frame_duration);
353 uint8_t max_codec_frames_per_sdu = codec_frames_per_sdu;
354 uint32_t octets_per_frame_range =
355 octets_per_frame | (octets_per_frame << 16);
356
357 pac_records_.push_back(
358 acs_ac_record({.codec_id = codec_id,
359 .codec_spec_caps = LeAudioLtvMap({
360 {kLeAudioCodecLC3TypeSamplingFreq,
361 UINT16_TO_VEC_UINT8(sampling_frequencies)},
362 {kLeAudioCodecLC3TypeFrameDuration,
363 UINT8_TO_VEC_UINT8(frame_durations)},
364 {kLeAudioCodecLC3TypeAudioChannelCounts,
365 UINT8_TO_VEC_UINT8(audio_channel_counts)},
366 {kLeAudioCodecLC3TypeOctetPerFrame,
367 UINT32_TO_VEC_UINT8(octets_per_frame_range)},
368 {kLeAudioCodecLC3TypeMaxCodecFramesPerSdu,
369 UINT8_TO_VEC_UINT8(max_codec_frames_per_sdu)},
370 }),
371 .metadata = std::vector<uint8_t>(0)}));
372 }
373
Add(LeAudioCodecId codec_id,uint16_t capa_sampling_frequency,uint8_t capa_frame_duration,uint8_t audio_channel_counts,uint16_t octets_per_frame_min,uint16_t ocets_per_frame_max,uint8_t codec_frames_per_sdu=1)374 void Add(LeAudioCodecId codec_id, uint16_t capa_sampling_frequency,
375 uint8_t capa_frame_duration, uint8_t audio_channel_counts,
376 uint16_t octets_per_frame_min, uint16_t ocets_per_frame_max,
377 uint8_t codec_frames_per_sdu = 1) {
378 uint32_t octets_per_frame_range =
379 octets_per_frame_min | (ocets_per_frame_max << 16);
380
381 pac_records_.push_back(
382 acs_ac_record({.codec_id = codec_id,
383 .codec_spec_caps = LeAudioLtvMap({
384 {kLeAudioCodecLC3TypeSamplingFreq,
385 UINT16_TO_VEC_UINT8(capa_sampling_frequency)},
386 {kLeAudioCodecLC3TypeFrameDuration,
387 UINT8_TO_VEC_UINT8(capa_frame_duration)},
388 {kLeAudioCodecLC3TypeAudioChannelCounts,
389 UINT8_TO_VEC_UINT8(audio_channel_counts)},
390 {kLeAudioCodecLC3TypeOctetPerFrame,
391 UINT32_TO_VEC_UINT8(octets_per_frame_range)},
392 {kLeAudioCodecLC3TypeMaxCodecFramesPerSdu,
393 UINT8_TO_VEC_UINT8(codec_frames_per_sdu)},
394 }),
395 .metadata = std::vector<uint8_t>(0)}));
396 }
397
Add(const CodecCapabilitySetting & setting,uint8_t audio_channel_counts)398 void Add(const CodecCapabilitySetting& setting,
399 uint8_t audio_channel_counts) {
400 if (setting.id != LeAudioCodecIdLc3) return;
401
402 const LeAudioLc3Config config = std::get<LeAudioLc3Config>(setting.config);
403
404 Add(setting.id, *config.sampling_frequency, *config.frame_duration,
405 audio_channel_counts, *config.octets_per_codec_frame);
406 }
407
Reset()408 void Reset() { pac_records_.clear(); }
409
Get()410 PublishedAudioCapabilities Get() {
411 return PublishedAudioCapabilities({{hdl_pair_nil, pac_records_}});
412 }
413
414 private:
415 std::vector<acs_ac_record> pac_records_;
416 };
417
418 struct TestGroupAseConfigurationData {
419 LeAudioDevice* device;
420 uint8_t audio_channel_counts_snk;
421 uint8_t audio_channel_counts_src;
422
423 /* Note, do not confuse ASEs with channels num. */
424 uint8_t expected_active_channel_num_snk;
425 uint8_t expected_active_channel_num_src;
426 };
427
428 class LeAudioAseConfigurationTest : public Test {
429 protected:
SetUp()430 void SetUp() override {
431 group_ = new LeAudioDeviceGroup(group_id_);
432 bluetooth::manager::SetMockBtmInterface(&btm_interface_);
433 controller::SetMockControllerInterface(&controller_interface_);
434 ::le_audio::AudioSetConfigurationProvider::Initialize();
435 MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_);
436 ON_CALL(mock_csis_client_module_, Get())
437 .WillByDefault(Return(&mock_csis_client_module_));
438 ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
439 .WillByDefault(Return(true));
440 ON_CALL(mock_csis_client_module_, GetDeviceList(_))
441 .WillByDefault(Invoke([this](int group_id) { return addresses_; }));
442 ON_CALL(mock_csis_client_module_, GetDesiredSize(_))
443 .WillByDefault(
444 Invoke([this](int group_id) { return (int)(addresses_.size()); }));
445 }
446
TearDown()447 void TearDown() override {
448 controller::SetMockControllerInterface(nullptr);
449 bluetooth::manager::SetMockBtmInterface(nullptr);
450 devices_.clear();
451 addresses_.clear();
452 delete group_;
453 ::le_audio::AudioSetConfigurationProvider::Cleanup();
454 }
455
AddTestDevice(int snk_ase_num,int src_ase_num,int snk_ase_num_cached=0,int src_ase_num_cached=0,bool invert_ases_emplacement=false,bool out_of_range_device=false)456 LeAudioDevice* AddTestDevice(int snk_ase_num, int src_ase_num,
457 int snk_ase_num_cached = 0,
458 int src_ase_num_cached = 0,
459 bool invert_ases_emplacement = false,
460 bool out_of_range_device = false) {
461 int index = group_->Size() + 1;
462 auto device = (std::make_shared<LeAudioDevice>(
463 GetTestAddress(index), DeviceConnectState::DISCONNECTED));
464 devices_.push_back(device);
465 LOG_INFO(" addresses %d", (int)(addresses_.size()));
466 addresses_.push_back(device->address_);
467 LOG_INFO(" Addresses %d", (int)(addresses_.size()));
468
469 if (out_of_range_device == false) {
470 group_->AddNode(device);
471 }
472
473 int ase_id = 1;
474 for (int i = 0; i < (invert_ases_emplacement ? snk_ase_num : src_ase_num);
475 i++) {
476 device->ases_.emplace_back(0x0000, 0x0000,
477 invert_ases_emplacement
478 ? kLeAudioDirectionSink
479 : kLeAudioDirectionSource,
480 ase_id++);
481 }
482
483 for (int i = 0; i < (invert_ases_emplacement ? src_ase_num : snk_ase_num);
484 i++) {
485 device->ases_.emplace_back(0x0000, 0x0000,
486 invert_ases_emplacement
487 ? kLeAudioDirectionSource
488 : kLeAudioDirectionSink,
489 ase_id++);
490 }
491
492 for (int i = 0; i < (invert_ases_emplacement ? snk_ase_num_cached
493 : src_ase_num_cached);
494 i++) {
495 struct ase ase(0x0000, 0x0000,
496 invert_ases_emplacement ? kLeAudioDirectionSink
497 : kLeAudioDirectionSource,
498 ase_id++);
499 ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
500 device->ases_.push_back(ase);
501 }
502
503 for (int i = 0; i < (invert_ases_emplacement ? src_ase_num_cached
504 : snk_ase_num_cached);
505 i++) {
506 struct ase ase(0x0000, 0x0000,
507 invert_ases_emplacement ? kLeAudioDirectionSource
508 : kLeAudioDirectionSink,
509 ase_id++);
510 ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
511 device->ases_.push_back(ase);
512 }
513
514 device->SetSupportedContexts(AudioContexts(kLeAudioContextAllTypes),
515 AudioContexts(kLeAudioContextAllTypes));
516 device->SetAvailableContexts(AudioContexts(kLeAudioContextAllTypes),
517 AudioContexts(kLeAudioContextAllTypes));
518 device->snk_audio_locations_ =
519 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft |
520 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
521 device->src_audio_locations_ =
522 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft |
523 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
524
525 device->conn_id_ = index;
526 device->SetConnectionState(out_of_range_device
527 ? DeviceConnectState::DISCONNECTED
528 : DeviceConnectState::CONNECTED);
529 group_->ReloadAudioDirections();
530 group_->ReloadAudioLocations();
531 return device.get();
532 }
533
TestGroupAseConfigurationVerdict(const TestGroupAseConfigurationData & data,uint8_t directions_to_verify)534 bool TestGroupAseConfigurationVerdict(
535 const TestGroupAseConfigurationData& data, uint8_t directions_to_verify) {
536 uint8_t active_channel_num_snk = 0;
537 uint8_t active_channel_num_src = 0;
538
539 if (directions_to_verify == 0) return false;
540 if (data.device->HaveActiveAse() == 0) return false;
541
542 for (ase* ase = data.device->GetFirstActiveAse(); ase;
543 ase = data.device->GetNextActiveAse(ase)) {
544 if (ase->direction == kLeAudioDirectionSink)
545 active_channel_num_snk +=
546 GetAudioChannelCounts(*ase->codec_config.audio_channel_allocation);
547 else
548 active_channel_num_src +=
549 GetAudioChannelCounts(*ase->codec_config.audio_channel_allocation);
550 }
551
552 bool result = true;
553 if (directions_to_verify & kLeAudioDirectionSink) {
554 result &=
555 (data.expected_active_channel_num_snk == active_channel_num_snk);
556 }
557 if (directions_to_verify & kLeAudioDirectionSource) {
558 result &=
559 (data.expected_active_channel_num_src == active_channel_num_src);
560 }
561
562 return result;
563 }
564
SetCisInformationToActiveAse(void)565 void SetCisInformationToActiveAse(void) {
566 uint8_t cis_id = 1;
567 uint16_t cis_conn_hdl = 0x0060;
568
569 for (auto& device : devices_) {
570 for (auto& ase : device->ases_) {
571 if (ase.active) {
572 ase.cis_id = cis_id++;
573 ase.cis_conn_hdl = cis_conn_hdl++;
574 }
575 }
576 }
577 }
578
TestSingleAseConfiguration(LeAudioContextType context_type,TestGroupAseConfigurationData * data,uint8_t data_size,const AudioSetConfiguration * audio_set_conf,uint8_t directions_to_verify)579 void TestSingleAseConfiguration(LeAudioContextType context_type,
580 TestGroupAseConfigurationData* data,
581 uint8_t data_size,
582 const AudioSetConfiguration* audio_set_conf,
583 uint8_t directions_to_verify) {
584 // the configuration should fail if there are no active ases expected
585 bool success_expected = data_size > 0;
586 uint8_t configuration_directions = 0;
587
588 for (int i = 0; i < data_size; i++) {
589 success_expected &= (data[i].expected_active_channel_num_snk +
590 data[i].expected_active_channel_num_src) > 0;
591
592 /* Prepare PAC's */
593 PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
594 for (const auto& entry : (*audio_set_conf).confs) {
595 if (entry.direction == kLeAudioDirectionSink) {
596 configuration_directions |= kLeAudioDirectionSink;
597 snk_pac_builder.Add(entry.codec, data[i].audio_channel_counts_snk);
598 } else {
599 configuration_directions |= kLeAudioDirectionSource;
600 src_pac_builder.Add(entry.codec, data[i].audio_channel_counts_src);
601 }
602 }
603
604 data[i].device->snk_pacs_ = snk_pac_builder.Get();
605 data[i].device->src_pacs_ = src_pac_builder.Get();
606 }
607
608 BidirectionalPair<AudioContexts> group_audio_locations = {
609 .sink = AudioContexts(context_type),
610 .source = AudioContexts(context_type)};
611
612 /* Stimulate update of available context map */
613 group_->UpdateAudioContextTypeAvailability(AudioContexts(context_type));
614 ASSERT_EQ(success_expected,
615 group_->Configure(context_type, group_audio_locations));
616
617 bool result = true;
618 for (int i = 0; i < data_size; i++) {
619 result &= TestGroupAseConfigurationVerdict(
620 data[i], directions_to_verify & configuration_directions);
621 }
622 ASSERT_TRUE(result);
623 }
624
getNumOfAses(LeAudioDevice * device,uint8_t direction)625 int getNumOfAses(LeAudioDevice* device, uint8_t direction) {
626 return std::count_if(
627 device->ases_.begin(), device->ases_.end(),
628 [direction](auto& a) { return a.direction == direction; });
629 }
630
TestGroupAseConfiguration(LeAudioContextType context_type,TestGroupAseConfigurationData * data,uint8_t data_size,uint8_t directions_to_verify=kLeAudioDirectionSink|kLeAudioDirectionSource)631 void TestGroupAseConfiguration(
632 LeAudioContextType context_type, TestGroupAseConfigurationData* data,
633 uint8_t data_size,
634 uint8_t directions_to_verify = kLeAudioDirectionSink |
635 kLeAudioDirectionSource) {
636 const auto* configurations =
637 ::le_audio::AudioSetConfigurationProvider::Get()->GetConfigurations(
638 context_type);
639
640 bool success_expected = directions_to_verify != 0;
641 int num_of_matching_configurations = 0;
642 for (const auto& audio_set_conf : *configurations) {
643 bool interesting_configuration = true;
644 uint8_t configuration_directions = 0;
645
646 // the configuration should fail if there are no active ases expected
647 PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
648 snk_pac_builder.Reset();
649 src_pac_builder.Reset();
650
651 /* Let's go thru devices in the group and configure them*/
652 for (int i = 0; i < data_size; i++) {
653 int num_of_ase_snk_per_dev = 0;
654 int num_of_ase_src_per_dev = 0;
655
656 /* Prepare PAC's for each device. Also make sure configuration is in our
657 * interest to test */
658 for (const auto& entry : (*audio_set_conf).confs) {
659 /* We are interested in the configurations which contains exact number
660 * of devices and number of ases is same the number of expected ases
661 * to active
662 */
663 if (entry.device_cnt != data_size) {
664 interesting_configuration = false;
665 }
666
667 /* Make sure the strategy is the expected one */
668 if (entry.direction == kLeAudioDirectionSink &&
669 group_->GetGroupStrategy(group_->Size()) != entry.strategy) {
670 interesting_configuration = false;
671 }
672
673 if (entry.direction == kLeAudioDirectionSink) {
674 configuration_directions |= kLeAudioDirectionSink;
675 num_of_ase_snk_per_dev = entry.ase_cnt / data_size;
676 snk_pac_builder.Add(entry.codec, data[i].audio_channel_counts_snk);
677 } else {
678 configuration_directions |= kLeAudioDirectionSource;
679 num_of_ase_src_per_dev = entry.ase_cnt / data_size;
680 src_pac_builder.Add(entry.codec, data[i].audio_channel_counts_src);
681 }
682
683 data[i].device->snk_pacs_ = snk_pac_builder.Get();
684 data[i].device->src_pacs_ = src_pac_builder.Get();
685 }
686
687 /* Make sure configuration can satisfy number of expected active ASEs*/
688 if (num_of_ase_snk_per_dev >
689 data[i].device->GetAseCount(kLeAudioDirectionSink)) {
690 interesting_configuration = false;
691 }
692
693 if (num_of_ase_src_per_dev >
694 data[i].device->GetAseCount(kLeAudioDirectionSource)) {
695 interesting_configuration = false;
696 }
697 }
698
699 BidirectionalPair<AudioContexts> group_audio_locations = {
700 .sink = AudioContexts(context_type),
701 .source = AudioContexts(context_type)};
702
703 /* Stimulate update of available context map */
704 group_->UpdateAudioContextTypeAvailability(AudioContexts(context_type));
705 auto configuration_result =
706 group_->Configure(context_type, group_audio_locations);
707
708 /* In case of configuration #ase is same as the one we expected to be
709 * activated verify, ASEs are actually active */
710 if (interesting_configuration &&
711 (directions_to_verify == configuration_directions)) {
712 ASSERT_TRUE(configuration_result);
713
714 bool matching_conf = true;
715 /* Check if each of the devices has activated ASEs as expected */
716 for (int i = 0; i < data_size; i++) {
717 matching_conf &= TestGroupAseConfigurationVerdict(
718 data[i], configuration_directions);
719 }
720
721 if (matching_conf) num_of_matching_configurations++;
722 }
723 group_->Deactivate();
724 TestAsesInactive();
725 }
726
727 if (success_expected) {
728 ASSERT_TRUE((num_of_matching_configurations > 0));
729 } else {
730 ASSERT_TRUE(num_of_matching_configurations == 0);
731 }
732 }
733
TestAsesActive(LeAudioCodecId codec_id,uint8_t sampling_frequency,uint8_t frame_duration,uint16_t octets_per_frame)734 void TestAsesActive(LeAudioCodecId codec_id, uint8_t sampling_frequency,
735 uint8_t frame_duration, uint16_t octets_per_frame) {
736 bool active_ase = false;
737
738 for (const auto& device : devices_) {
739 for (const auto& ase : device->ases_) {
740 if (!ase.active) continue;
741
742 /* Configure may request only partial ases to be activated */
743 if (!active_ase && ase.active) active_ase = true;
744
745 ASSERT_EQ(ase.codec_id, codec_id);
746
747 /* FIXME: Validate other codec parameters than LC3 if any */
748 ASSERT_EQ(ase.codec_id, LeAudioCodecIdLc3);
749 if (ase.codec_id == LeAudioCodecIdLc3) {
750 ASSERT_EQ(ase.codec_config.sampling_frequency, sampling_frequency);
751 ASSERT_EQ(ase.codec_config.frame_duration, frame_duration);
752 ASSERT_EQ(ase.codec_config.octets_per_codec_frame, octets_per_frame);
753 }
754 }
755 }
756
757 ASSERT_TRUE(active_ase);
758 }
759
TestActiveAses(void)760 void TestActiveAses(void) {
761 for (auto& device : devices_) {
762 for (const auto& ase : device->ases_) {
763 if (ase.active) {
764 ASSERT_FALSE(ase.cis_id == ::le_audio::kInvalidCisId);
765 }
766 }
767 }
768 }
769
TestAsesInactivated(const LeAudioDevice * device)770 void TestAsesInactivated(const LeAudioDevice* device) {
771 for (const auto& ase : device->ases_) {
772 ASSERT_FALSE(ase.active);
773 ASSERT_TRUE(ase.cis_id == ::le_audio::kInvalidCisId);
774 ASSERT_TRUE(ase.cis_conn_hdl == 0);
775 }
776 }
777
TestAsesInactive()778 void TestAsesInactive() {
779 for (const auto& device : devices_) {
780 for (const auto& ase : device->ases_) {
781 ASSERT_FALSE(ase.active);
782 }
783 }
784 }
785
TestLc3CodecConfig(LeAudioContextType context_type)786 void TestLc3CodecConfig(LeAudioContextType context_type) {
787 for (int i = Lc3SettingIdBegin; i < Lc3SettingIdEnd; i++) {
788 // test each configuration parameter against valid and invalid value
789 std::array<Lc3SettingId, 2> test_variants = {static_cast<Lc3SettingId>(i),
790 Lc3SettingId::UNSUPPORTED};
791
792 const bool is_lc3_setting_supported =
793 IsLc3SettingSupported(context_type, static_cast<Lc3SettingId>(i));
794
795 for (const auto sf_variant : test_variants) {
796 uint8_t sampling_frequency = GetSamplingFrequency(sf_variant);
797 for (const auto fd_variant : test_variants) {
798 uint8_t frame_duration = GetFrameDuration(fd_variant);
799 for (const auto opcf_variant : test_variants) {
800 uint16_t octets_per_frame = GetOctetsPerCodecFrame(opcf_variant);
801
802 PublishedAudioCapabilitiesBuilder pac_builder;
803 pac_builder.Add(LeAudioCodecIdLc3, sampling_frequency,
804 frame_duration,
805 kLeAudioCodecLC3ChannelCountSingleChannel |
806 kLeAudioCodecLC3ChannelCountTwoChannel,
807 octets_per_frame);
808 for (auto& device : devices_) {
809 /* For simplicity configure both PACs with the same
810 parameters*/
811 device->snk_pacs_ = pac_builder.Get();
812 device->src_pacs_ = pac_builder.Get();
813 }
814
815 bool success_expected = is_lc3_setting_supported;
816 if (is_lc3_setting_supported &&
817 (sf_variant == Lc3SettingId::UNSUPPORTED ||
818 fd_variant == Lc3SettingId::UNSUPPORTED ||
819 opcf_variant == Lc3SettingId::UNSUPPORTED)) {
820 success_expected = false;
821 }
822
823 /* Stimulate update of available context map */
824 group_->UpdateAudioContextTypeAvailability(
825 AudioContexts(context_type));
826 BidirectionalPair<AudioContexts> group_audio_locations = {
827 .sink = AudioContexts(context_type),
828 .source = AudioContexts(context_type)};
829 ASSERT_EQ(success_expected,
830 group_->Configure(context_type, group_audio_locations));
831 if (success_expected) {
832 TestAsesActive(LeAudioCodecIdLc3, sampling_frequency,
833 frame_duration, octets_per_frame);
834 group_->Deactivate();
835 }
836
837 TestAsesInactive();
838 }
839 }
840 }
841 }
842 }
843
SetAsesToCachedConfiguration(LeAudioDevice * device,LeAudioContextType context_type,uint8_t directions)844 void SetAsesToCachedConfiguration(LeAudioDevice* device,
845 LeAudioContextType context_type,
846 uint8_t directions) {
847 for (struct ase& ase : device->ases_) {
848 if (ase.direction & directions) {
849 ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
850 ase.active = false;
851 ase.configured_for_context_type = context_type;
852 }
853 }
854 }
855
856 const int group_id_ = 6;
857 std::vector<std::shared_ptr<LeAudioDevice>> devices_;
858 std::vector<RawAddress> addresses_;
859 LeAudioDeviceGroup* group_ = nullptr;
860 bluetooth::manager::MockBtmInterface btm_interface_;
861 controller::MockControllerInterface controller_interface_;
862 MockCsisClient mock_csis_client_module_;
863 };
864
TEST_F(LeAudioAseConfigurationTest,test_mono_speaker_ringtone)865 TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_ringtone) {
866 LeAudioDevice* mono_speaker = AddTestDevice(1, 0);
867 TestGroupAseConfigurationData data(
868 {mono_speaker, kLeAudioCodecLC3ChannelCountSingleChannel,
869 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0});
870
871 /* mono, change location as by default it is stereo */
872 mono_speaker->snk_audio_locations_ =
873 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
874 group_->ReloadAudioLocations();
875
876 uint8_t direction_to_verify = kLeAudioDirectionSink;
877 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1,
878 direction_to_verify);
879 }
880
TEST_F(LeAudioAseConfigurationTest,test_mono_speaker_conversational)881 TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_conversational) {
882 LeAudioDevice* mono_speaker = AddTestDevice(1, 0);
883 TestGroupAseConfigurationData data({mono_speaker,
884 kLeAudioCodecLC3ChannelCountSingleChannel,
885 kLeAudioCodecLC3ChannelCountNone, 1, 0});
886
887 /* mono, change location as by default it is stereo */
888 mono_speaker->snk_audio_locations_ =
889 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
890 group_->ReloadAudioLocations();
891
892 /* Microphone should be used on the phone */
893 uint8_t direction_to_verify = kLeAudioDirectionSink;
894 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1,
895 direction_to_verify);
896 }
897
TEST_F(LeAudioAseConfigurationTest,test_mono_speaker_media)898 TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_media) {
899 LeAudioDevice* mono_speaker = AddTestDevice(1, 0);
900 TestGroupAseConfigurationData data({mono_speaker,
901 kLeAudioCodecLC3ChannelCountSingleChannel,
902 kLeAudioCodecLC3ChannelCountNone, 1, 0});
903
904 /* mono, change location as by default it is stereo */
905 mono_speaker->snk_audio_locations_ =
906 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
907 group_->ReloadAudioLocations();
908
909 uint8_t direction_to_verify = kLeAudioDirectionSink;
910 TestGroupAseConfiguration(LeAudioContextType::MEDIA, &data, 1,
911 direction_to_verify);
912 }
913
TEST_F(LeAudioAseConfigurationTest,test_bounded_headphones_ringtone)914 TEST_F(LeAudioAseConfigurationTest, test_bounded_headphones_ringtone) {
915 LeAudioDevice* bounded_headphones = AddTestDevice(2, 0);
916 TestGroupAseConfigurationData data(
917 {bounded_headphones, kLeAudioCodecLC3ChannelCountTwoChannel,
918 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 0});
919
920 uint8_t direction_to_verify = kLeAudioDirectionSink;
921 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1,
922 direction_to_verify);
923 }
924
TEST_F(LeAudioAseConfigurationTest,test_bounded_headphones_conversional)925 TEST_F(LeAudioAseConfigurationTest, test_bounded_headphones_conversional) {
926 LeAudioDevice* bounded_headphones = AddTestDevice(2, 0);
927 TestGroupAseConfigurationData data({bounded_headphones,
928 kLeAudioCodecLC3ChannelCountTwoChannel,
929 kLeAudioCodecLC3ChannelCountNone, 2, 0});
930
931 uint8_t direction_to_verify = kLeAudioDirectionSink;
932 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1,
933 direction_to_verify);
934 }
935
TEST_F(LeAudioAseConfigurationTest,test_bounded_headphones_media)936 TEST_F(LeAudioAseConfigurationTest, test_bounded_headphones_media) {
937 LeAudioDevice* bounded_headphones = AddTestDevice(2, 0);
938 TestGroupAseConfigurationData data({bounded_headphones,
939 kLeAudioCodecLC3ChannelCountTwoChannel,
940 kLeAudioCodecLC3ChannelCountNone, 2, 0});
941
942 uint8_t direction_to_verify = kLeAudioDirectionSink;
943 TestGroupAseConfiguration(LeAudioContextType::MEDIA, &data, 1,
944 direction_to_verify);
945 }
946
TEST_F(LeAudioAseConfigurationTest,test_bounded_headset_ringtone_mono_microphone)947 TEST_F(LeAudioAseConfigurationTest,
948 test_bounded_headset_ringtone_mono_microphone) {
949 LeAudioDevice* bounded_headset = AddTestDevice(2, 1);
950 TestGroupAseConfigurationData data(
951 {bounded_headset, kLeAudioCodecLC3ChannelCountTwoChannel,
952 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
953
954 /* mono, change location as by default it is stereo */
955 bounded_headset->src_audio_locations_ =
956 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
957 group_->ReloadAudioLocations();
958
959 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
960 }
961
TEST_F(LeAudioAseConfigurationTest,test_bounded_headset_ringtone_stereo_microphone)962 TEST_F(LeAudioAseConfigurationTest,
963 test_bounded_headset_ringtone_stereo_microphone) {
964 LeAudioDevice* bounded_headset = AddTestDevice(2, 2);
965 TestGroupAseConfigurationData data(
966 {bounded_headset,
967 kLeAudioCodecLC3ChannelCountSingleChannel |
968 kLeAudioCodecLC3ChannelCountTwoChannel,
969 kLeAudioCodecLC3ChannelCountSingleChannel |
970 kLeAudioCodecLC3ChannelCountTwoChannel,
971 2, 2});
972
973 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
974 }
975
TEST_F(LeAudioAseConfigurationTest,test_bounded_headset_conversional)976 TEST_F(LeAudioAseConfigurationTest, test_bounded_headset_conversional) {
977 LeAudioDevice* bounded_headset = AddTestDevice(2, 1);
978 TestGroupAseConfigurationData data(
979 {bounded_headset, kLeAudioCodecLC3ChannelCountTwoChannel,
980 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
981
982 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1);
983 }
984
TEST_F(LeAudioAseConfigurationTest,test_bounded_headset_media)985 TEST_F(LeAudioAseConfigurationTest, test_bounded_headset_media) {
986 LeAudioDevice* bounded_headset = AddTestDevice(2, 1);
987 TestGroupAseConfigurationData data(
988 {bounded_headset, kLeAudioCodecLC3ChannelCountTwoChannel,
989 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 0});
990
991 uint8_t directions_to_verify = kLeAudioDirectionSink;
992 TestGroupAseConfiguration(LeAudioContextType::MEDIA, &data, 1,
993 directions_to_verify);
994 }
995
TEST_F(LeAudioAseConfigurationTest,test_earbuds_ringtone)996 TEST_F(LeAudioAseConfigurationTest, test_earbuds_ringtone) {
997 LeAudioDevice* left = AddTestDevice(1, 1);
998 LeAudioDevice* right = AddTestDevice(1, 1);
999 TestGroupAseConfigurationData data[] = {
1000 {left, kLeAudioCodecLC3ChannelCountSingleChannel,
1001 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1},
1002 {right, kLeAudioCodecLC3ChannelCountSingleChannel,
1003 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1}};
1004
1005 /* Change location as by default it is stereo */
1006 left->snk_audio_locations_ =
1007 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1008 left->src_audio_locations_ =
1009 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1010 right->snk_audio_locations_ =
1011 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1012 right->src_audio_locations_ =
1013 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1014 group_->ReloadAudioLocations();
1015
1016 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, data, 2);
1017 }
1018
TEST_F(LeAudioAseConfigurationTest,test_earbuds_conversional)1019 TEST_F(LeAudioAseConfigurationTest, test_earbuds_conversional) {
1020 LeAudioDevice* left = AddTestDevice(1, 1);
1021 LeAudioDevice* right = AddTestDevice(1, 1);
1022 TestGroupAseConfigurationData data[] = {
1023 {left, kLeAudioCodecLC3ChannelCountSingleChannel,
1024 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1},
1025 {right, kLeAudioCodecLC3ChannelCountSingleChannel,
1026 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1}};
1027
1028 /* Change location as by default it is stereo */
1029 left->snk_audio_locations_ =
1030 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1031 left->src_audio_locations_ =
1032 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1033 right->snk_audio_locations_ =
1034 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1035 right->src_audio_locations_ =
1036 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1037 group_->ReloadAudioLocations();
1038
1039 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, data, 2);
1040 }
1041
TEST_F(LeAudioAseConfigurationTest,test_earbuds_media)1042 TEST_F(LeAudioAseConfigurationTest, test_earbuds_media) {
1043 LeAudioDevice* left = AddTestDevice(1, 1);
1044 LeAudioDevice* right = AddTestDevice(1, 1);
1045 TestGroupAseConfigurationData data[] = {
1046 {left, kLeAudioCodecLC3ChannelCountSingleChannel,
1047 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0},
1048 {right, kLeAudioCodecLC3ChannelCountSingleChannel,
1049 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0}};
1050
1051 /* Change location as by default it is stereo */
1052 left->snk_audio_locations_ =
1053 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1054 left->src_audio_locations_ =
1055 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1056 right->snk_audio_locations_ =
1057 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1058 right->src_audio_locations_ =
1059 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1060 group_->ReloadAudioLocations();
1061
1062 uint8_t directions_to_verify = kLeAudioDirectionSink;
1063 TestGroupAseConfiguration(LeAudioContextType::MEDIA, data, 2,
1064 directions_to_verify);
1065 }
1066
TEST_F(LeAudioAseConfigurationTest,test_handsfree_mono_ringtone)1067 TEST_F(LeAudioAseConfigurationTest, test_handsfree_mono_ringtone) {
1068 LeAudioDevice* handsfree = AddTestDevice(1, 1);
1069 TestGroupAseConfigurationData data(
1070 {handsfree, kLeAudioCodecLC3ChannelCountSingleChannel,
1071 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1});
1072
1073 handsfree->snk_audio_locations_ =
1074 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1075 handsfree->src_audio_locations_ =
1076 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1077 group_->ReloadAudioLocations();
1078
1079 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
1080 }
1081
TEST_F(LeAudioAseConfigurationTest,test_handsfree_stereo_ringtone)1082 TEST_F(LeAudioAseConfigurationTest, test_handsfree_stereo_ringtone) {
1083 LeAudioDevice* handsfree = AddTestDevice(1, 1);
1084 TestGroupAseConfigurationData data(
1085 {handsfree,
1086 kLeAudioCodecLC3ChannelCountSingleChannel |
1087 kLeAudioCodecLC3ChannelCountTwoChannel,
1088 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
1089
1090 TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
1091 }
1092
TEST_F(LeAudioAseConfigurationTest,test_handsfree_mono_conversional)1093 TEST_F(LeAudioAseConfigurationTest, test_handsfree_mono_conversional) {
1094 LeAudioDevice* handsfree = AddTestDevice(1, 1);
1095 TestGroupAseConfigurationData data(
1096 {handsfree, kLeAudioCodecLC3ChannelCountSingleChannel,
1097 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 1});
1098
1099 handsfree->snk_audio_locations_ =
1100 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1101 handsfree->src_audio_locations_ =
1102 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1103 group_->ReloadAudioLocations();
1104
1105 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1);
1106 }
1107
TEST_F(LeAudioAseConfigurationTest,test_handsfree_stereo_conversional)1108 TEST_F(LeAudioAseConfigurationTest, test_handsfree_stereo_conversional) {
1109 LeAudioDevice* handsfree = AddTestDevice(1, 1);
1110 TestGroupAseConfigurationData data(
1111 {handsfree,
1112 kLeAudioCodecLC3ChannelCountSingleChannel |
1113 kLeAudioCodecLC3ChannelCountTwoChannel,
1114 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
1115
1116 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1);
1117 }
1118
TEST_F(LeAudioAseConfigurationTest,test_handsfree_full_cached_conversional)1119 TEST_F(LeAudioAseConfigurationTest, test_handsfree_full_cached_conversional) {
1120 LeAudioDevice* handsfree = AddTestDevice(0, 0, 1, 1);
1121 TestGroupAseConfigurationData data(
1122 {handsfree,
1123 kLeAudioCodecLC3ChannelCountSingleChannel |
1124 kLeAudioCodecLC3ChannelCountTwoChannel,
1125 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
1126
1127 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1);
1128 }
1129
TEST_F(LeAudioAseConfigurationTest,test_handsfree_partial_cached_conversional)1130 TEST_F(LeAudioAseConfigurationTest,
1131 test_handsfree_partial_cached_conversional) {
1132 LeAudioDevice* handsfree = AddTestDevice(1, 0, 0, 1);
1133 TestGroupAseConfigurationData data(
1134 {handsfree,
1135 kLeAudioCodecLC3ChannelCountSingleChannel |
1136 kLeAudioCodecLC3ChannelCountTwoChannel,
1137 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 1});
1138
1139 TestGroupAseConfiguration(LeAudioContextType::CONVERSATIONAL, &data, 1);
1140 }
1141
TEST_F(LeAudioAseConfigurationTest,test_handsfree_media_two_channels_allocation_stereo)1142 TEST_F(LeAudioAseConfigurationTest,
1143 test_handsfree_media_two_channels_allocation_stereo) {
1144 LeAudioDevice* handsfree = AddTestDevice(1, 1);
1145 TestGroupAseConfigurationData data(
1146 {handsfree,
1147 kLeAudioCodecLC3ChannelCountSingleChannel |
1148 kLeAudioCodecLC3ChannelCountTwoChannel,
1149 kLeAudioCodecLC3ChannelCountSingleChannel, 2, 0});
1150
1151 uint8_t directions_to_verify = kLeAudioDirectionSink;
1152 TestGroupAseConfiguration(LeAudioContextType::MEDIA, &data, 1,
1153 directions_to_verify);
1154 }
1155
TEST_F(LeAudioAseConfigurationTest,test_lc3_config_ringtone)1156 TEST_F(LeAudioAseConfigurationTest, test_lc3_config_ringtone) {
1157 AddTestDevice(1, 1);
1158
1159 TestLc3CodecConfig(LeAudioContextType::RINGTONE);
1160 }
1161
TEST_F(LeAudioAseConfigurationTest,test_lc3_config_conversional)1162 TEST_F(LeAudioAseConfigurationTest, test_lc3_config_conversional) {
1163 AddTestDevice(1, 1);
1164
1165 TestLc3CodecConfig(LeAudioContextType::CONVERSATIONAL);
1166 }
1167
TEST_F(LeAudioAseConfigurationTest,test_lc3_config_media)1168 TEST_F(LeAudioAseConfigurationTest, test_lc3_config_media) {
1169 AddTestDevice(1, 1);
1170
1171 TestLc3CodecConfig(LeAudioContextType::MEDIA);
1172 }
1173
TEST_F(LeAudioAseConfigurationTest,test_unsupported_codec)1174 TEST_F(LeAudioAseConfigurationTest, test_unsupported_codec) {
1175 const LeAudioCodecId UnsupportedCodecId = {
1176 .coding_format = kLeAudioCodingFormatVendorSpecific,
1177 .vendor_company_id = 0xBAD,
1178 .vendor_codec_id = 0xC0DE,
1179 };
1180
1181 LeAudioDevice* device = AddTestDevice(1, 0);
1182
1183 PublishedAudioCapabilitiesBuilder pac_builder;
1184 pac_builder.Add(UnsupportedCodecId,
1185 GetSamplingFrequency(Lc3SettingId::LC3_16_2),
1186 GetFrameDuration(Lc3SettingId::LC3_16_2),
1187 kLeAudioCodecLC3ChannelCountSingleChannel,
1188 GetOctetsPerCodecFrame(Lc3SettingId::LC3_16_2));
1189 device->snk_pacs_ = pac_builder.Get();
1190 device->src_pacs_ = pac_builder.Get();
1191
1192 ASSERT_FALSE(
1193 group_->Configure(LeAudioContextType::RINGTONE,
1194 {AudioContexts(LeAudioContextType::RINGTONE),
1195 AudioContexts(LeAudioContextType::RINGTONE)}));
1196 TestAsesInactive();
1197 }
1198
TEST_F(LeAudioAseConfigurationTest,test_reconnection_media)1199 TEST_F(LeAudioAseConfigurationTest, test_reconnection_media) {
1200 LeAudioDevice* left = AddTestDevice(2, 1);
1201 LeAudioDevice* right = AddTestDevice(2, 1);
1202
1203 /* Change location as by default it is stereo */
1204 left->snk_audio_locations_ =
1205 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1206 left->src_audio_locations_ =
1207 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1208 right->snk_audio_locations_ =
1209 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1210 right->src_audio_locations_ =
1211 ::le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
1212 group_->ReloadAudioLocations();
1213
1214 TestGroupAseConfigurationData data[] = {
1215 {left, kLeAudioCodecLC3ChannelCountSingleChannel,
1216 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0},
1217 {right, kLeAudioCodecLC3ChannelCountSingleChannel,
1218 kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0}};
1219
1220 auto all_configurations =
1221 ::le_audio::AudioSetConfigurationProvider::Get()->GetConfigurations(
1222 LeAudioContextType::MEDIA);
1223 ASSERT_NE(nullptr, all_configurations);
1224 ASSERT_NE(all_configurations->end(), all_configurations->begin());
1225 auto configuration = *all_configurations->begin();
1226
1227 uint8_t direction_to_verify = kLeAudioDirectionSink;
1228 TestSingleAseConfiguration(LeAudioContextType::MEDIA, data, 2, configuration,
1229 direction_to_verify);
1230
1231 /* Generate CISes, symulate CIG creation and assign cis handles to ASEs.*/
1232 group_->CigGenerateCisIds(LeAudioContextType::MEDIA);
1233 std::vector<uint16_t> handles = {0x0012, 0x0013};
1234 group_->CigAssignCisConnHandles(handles);
1235 group_->CigAssignCisIds(left);
1236 group_->CigAssignCisIds(right);
1237
1238 TestActiveAses();
1239 /* Left got disconnected */
1240 left->DeactivateAllAses();
1241
1242 /* Unassign from the group*/
1243 group_->CigUnassignCis(left);
1244
1245 TestAsesInactivated(left);
1246
1247 /* Prepare reconfiguration */
1248 uint8_t number_of_active_ases = 1; // Right one
1249 auto* ase = right->GetFirstActiveAseByDirection(kLeAudioDirectionSink);
1250 BidirectionalPair<AudioLocations> group_audio_locations = {
1251 .sink = *ase->codec_config.audio_channel_allocation,
1252 .source = *ase->codec_config.audio_channel_allocation};
1253
1254 /* Get entry for the sink direction and use it to set configuration */
1255 BidirectionalPair<std::vector<uint8_t>> ccid_lists = {{}, {}};
1256 BidirectionalPair<AudioContexts> audio_contexts = {AudioContexts(),
1257 AudioContexts()};
1258 for (auto& ent : configuration->confs) {
1259 if (ent.direction == ::le_audio::types::kLeAudioDirectionSink) {
1260 left->ConfigureAses(ent, group_->GetConfigurationContextType(),
1261 &number_of_active_ases, group_audio_locations,
1262 audio_contexts, ccid_lists, false);
1263 }
1264 }
1265
1266 ASSERT_TRUE(number_of_active_ases == 2);
1267 ASSERT_TRUE(group_audio_locations.sink == kChannelAllocationStereo);
1268
1269 uint8_t directions_to_verify = ::le_audio::types::kLeAudioDirectionSink;
1270 for (int i = 0; i < 2; i++) {
1271 TestGroupAseConfigurationVerdict(data[i], directions_to_verify);
1272 }
1273
1274 /* Before device is rejoining, and group already exist, cis handles are
1275 * assigned before sending codec config
1276 */
1277 group_->CigAssignCisIds(left);
1278 group_->CigAssignCisConnHandlesToAses(left);
1279
1280 TestActiveAses();
1281 }
1282
1283 /*
1284 * Failure happens when restarting conversational scenario and when
1285 * remote device uses caching.
1286 *
1287 * Failing scenario.
1288 * 1. Conversational scenario set up with
1289 * - ASE 1 and ASE 5 using bidirectional CIS 0
1290 * - ASE 2 being unidirectional on CIS 1
1291 * 2. Stop stream and go to CONFIGURED STATE.
1292 * 3. Trying to configure ASES again would end up in incorrectly assigned
1293 * CISes
1294 * - ASE 1 and ASE 5 set to CIS 0
1295 * - ASE 2 stay on CIS 1 but ASE 5 got reassigned to CIS 1 (error)
1296 *
1297 * The problem is finding matching_bidir_ase which shall not be just next
1298 * active ase with different direction, but it shall be also available (Cis
1299 * not assigned) or assigned to the same CIS ID as the opposite direction.
1300 */
TEST_F(LeAudioAseConfigurationTest,test_reactivation_conversational)1301 TEST_F(LeAudioAseConfigurationTest, test_reactivation_conversational) {
1302 LeAudioDevice* tws_headset = AddTestDevice(0, 0, 2, 1, true);
1303
1304 /* Change location as by default it is stereo */
1305 tws_headset->snk_audio_locations_ = kChannelAllocationStereo;
1306 tws_headset->src_audio_locations_ =
1307 ::le_audio::codec_spec_conf::kLeAudioLocationFrontLeft;
1308 group_->ReloadAudioLocations();
1309
1310 auto all_configurations =
1311 ::le_audio::AudioSetConfigurationProvider::Get()->GetConfigurations(
1312 LeAudioContextType::CONVERSATIONAL);
1313 ASSERT_NE(nullptr, all_configurations);
1314 ASSERT_NE(all_configurations->end(), all_configurations->begin());
1315
1316 /* Pick TWS configuration for conversational */
1317 auto iter = std::find_if(all_configurations->begin(),
1318 all_configurations->end(), [](auto& configuration) {
1319 return configuration->name ==
1320 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_"
1321 "16_2_Low_Latency";
1322 });
1323
1324 ASSERT_NE(iter, all_configurations->end());
1325
1326 auto conversational_configuration = *iter;
1327
1328 // Build PACs for device
1329 PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
1330 snk_pac_builder.Reset();
1331 src_pac_builder.Reset();
1332
1333 /* Create PACs for conversational scenario which covers also media. Single
1334 * PAC for each direction is enough.
1335 */
1336 for (const auto& entry : (*conversational_configuration).confs) {
1337 if (entry.direction == kLeAudioDirectionSink) {
1338 snk_pac_builder.Add(entry.codec, 1);
1339 } else {
1340 src_pac_builder.Add(entry.codec, 1);
1341 }
1342 }
1343
1344 tws_headset->snk_pacs_ = snk_pac_builder.Get();
1345 tws_headset->src_pacs_ = src_pac_builder.Get();
1346
1347 ::le_audio::types::AudioLocations group_snk_audio_locations = 0;
1348 ::le_audio::types::AudioLocations group_src_audio_locations = 0;
1349 uint8_t number_of_already_active_ases = 0;
1350
1351 BidirectionalPair<AudioLocations> group_audio_locations = {
1352 .sink = group_snk_audio_locations, .source = group_src_audio_locations};
1353
1354 /* Get entry for the sink direction and use it to set configuration */
1355 BidirectionalPair<std::vector<uint8_t>> ccid_lists = {{}, {}};
1356 BidirectionalPair<AudioContexts> audio_contexts = {AudioContexts(),
1357 AudioContexts()};
1358
1359 /* Get entry for the sink direction and use it to set configuration */
1360 for (auto& ent : conversational_configuration->confs) {
1361 tws_headset->ConfigureAses(ent, group_->GetConfigurationContextType(),
1362 &number_of_already_active_ases,
1363 group_audio_locations, audio_contexts,
1364 ccid_lists, false);
1365 }
1366
1367 /* Generate CISes, simulate CIG creation and assign cis handles to ASEs.*/
1368 std::vector<uint16_t> handles = {0x0012, 0x0013};
1369 group_->CigGenerateCisIds(LeAudioContextType::CONVERSATIONAL);
1370 group_->CigAssignCisConnHandles(handles);
1371 group_->CigAssignCisIds(tws_headset);
1372
1373 TestActiveAses();
1374
1375 /* Simulate stopping stream with caching codec configuration in ASEs */
1376 group_->CigUnassignCis(tws_headset);
1377 SetAsesToCachedConfiguration(tws_headset, LeAudioContextType::CONVERSATIONAL,
1378 kLeAudioDirectionSink | kLeAudioDirectionSource);
1379
1380 /* As context type is the same as previous and no changes were made in PACs
1381 * the same CIS ID can be used. This would lead to only activating group
1382 * without reconfiguring CIG.
1383 */
1384 group_->Activate(LeAudioContextType::CONVERSATIONAL);
1385
1386 TestActiveAses();
1387
1388 /* Verify ASEs assigned CISes by counting assigned to bi-directional CISes */
1389 int bi_dir_ases_count = std::count_if(
1390 tws_headset->ases_.begin(), tws_headset->ases_.end(), [=](auto& ase) {
1391 return this->group_->cises_[ase.cis_id].type ==
1392 CisType::CIS_TYPE_BIDIRECTIONAL;
1393 });
1394
1395 /* Only two ASEs can be bonded to one bi-directional CIS */
1396 ASSERT_EQ(bi_dir_ases_count, 2);
1397 }
1398
TEST_F(LeAudioAseConfigurationTest,test_num_of_connected)1399 TEST_F(LeAudioAseConfigurationTest, test_num_of_connected) {
1400 auto device1 = AddTestDevice(2, 1);
1401 auto device2 = AddTestDevice(2, 1);
1402 ASSERT_EQ(2, group_->NumOfConnected());
1403
1404 // Drop the ACL connection
1405 device1->conn_id_ = GATT_INVALID_CONN_ID;
1406 ASSERT_EQ(1, group_->NumOfConnected());
1407
1408 // Fully disconnect the other device
1409 device2->SetConnectionState(DeviceConnectState::DISCONNECTING);
1410 ASSERT_EQ(0, group_->NumOfConnected());
1411 }
1412
1413 /*
1414 * Failure happens when there is no matching single device scenario for dual
1415 * device scanario. Stereo location for single earbud seems to be invalid but
1416 * possible and stack should handle it.
1417 *
1418 * Failing scenario:
1419 * 1. Connect two - stereo location earbuds
1420 * 2. Disconnect one of earbud
1421 * 3. CIS generator will look for dual device scenario with matching strategy
1422 * 4. There is no dual device scenario with strategy stereo channels per device
1423 */
TEST_F(LeAudioAseConfigurationTest,test_getting_cis_count)1424 TEST_F(LeAudioAseConfigurationTest, test_getting_cis_count) {
1425 LeAudioDevice* left = AddTestDevice(2, 1);
1426 LeAudioDevice* right = AddTestDevice(0, 0, 0, 0, false, true);
1427
1428 /* Change location as by default it is stereo */
1429 left->snk_audio_locations_ = kChannelAllocationStereo;
1430 right->snk_audio_locations_ = kChannelAllocationStereo;
1431 group_->ReloadAudioLocations();
1432
1433 auto all_configurations =
1434 ::le_audio::AudioSetConfigurationProvider::Get()->GetConfigurations(
1435 LeAudioContextType::MEDIA);
1436 ASSERT_NE(nullptr, all_configurations);
1437 ASSERT_NE(all_configurations->end(), all_configurations->begin());
1438
1439 /* Pick configuration for media */
1440 auto iter = std::find_if(all_configurations->begin(),
1441 all_configurations->end(), [](auto& configuration) {
1442 return configuration->name ==
1443 "SingleDev_TwoChanStereoSnk_48_4_High_"
1444 "Reliability";
1445 });
1446
1447 ASSERT_NE(iter, all_configurations->end());
1448
1449 auto media_configuration = *iter;
1450
1451 // Build PACs for device
1452 PublishedAudioCapabilitiesBuilder snk_pac_builder;
1453 snk_pac_builder.Reset();
1454
1455 /* Create PACs for media. Single PAC for each direction is enough.
1456 */
1457 for (const auto& entry : (*media_configuration).confs) {
1458 if (entry.direction == kLeAudioDirectionSink) {
1459 snk_pac_builder.Add(LeAudioCodecIdLc3, 0x00b5, 0x03, 0x03, 0x001a, 0x00f0,
1460 2);
1461 }
1462 }
1463
1464 left->snk_pacs_ = snk_pac_builder.Get();
1465 left->snk_pacs_ = snk_pac_builder.Get();
1466
1467 ::le_audio::types::AudioLocations group_snk_audio_locations = 3;
1468 ::le_audio::types::AudioLocations group_src_audio_locations = 0;
1469 uint8_t number_of_already_active_ases = 0;
1470
1471 BidirectionalPair<AudioLocations> group_audio_locations = {
1472 .sink = group_snk_audio_locations, .source = group_src_audio_locations};
1473
1474 /* Get entry for the sink direction and use it to set configuration */
1475 BidirectionalPair<std::vector<uint8_t>> ccid_lists = {{}, {}};
1476 BidirectionalPair<AudioContexts> audio_contexts = {AudioContexts(),
1477 AudioContexts()};
1478
1479 /* Get entry for the sink direction and use it to set configuration */
1480 for (auto& ent : media_configuration->confs) {
1481 left->ConfigureAses(ent, group_->GetConfigurationContextType(),
1482 &number_of_already_active_ases, group_audio_locations,
1483 audio_contexts, ccid_lists, false);
1484 }
1485
1486 /* Generate CIS, simulate CIG creation and assign cis handles to ASEs.*/
1487 std::vector<uint16_t> handles = {0x0012};
1488 group_->CigGenerateCisIds(LeAudioContextType::MEDIA);
1489
1490 /* Verify prepared CISes by counting generated entries */
1491 int snk_cis_count = std::count_if(
1492 this->group_->cises_.begin(), this->group_->cises_.end(), [](auto& cis) {
1493 return cis.type == CisType::CIS_TYPE_UNIDIRECTIONAL_SINK;
1494 });
1495
1496 /* Two CIS should be prepared for dual dev expected set */
1497 ASSERT_EQ(snk_cis_count, 2);
1498 }
1499
1500 } // namespace
1501 } // namespace internal
1502 } // namespace le_audio
1503 } // namespace bluetooth
1504