1 /*
2 *
3 * Copyright 2022 The Android Open Source Project
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
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21
22 #include <algorithm>
23 #include <map>
24 #include <memory>
25
26 #include "btif/include/core_callbacks.h"
27 #include "btif/include/stack_manager_t.h"
28 #include "stack/btm/btm_sco.h"
29 #include "stack/include/hfp_lc3_decoder.h"
30 #include "stack/include/hfp_lc3_encoder.h"
31 #include "stack/include/hfp_msbc_decoder.h"
32 #include "stack/include/hfp_msbc_encoder.h"
33 #include "stack/test/btm/btm_test_fixtures.h"
34 #include "test/common/mock_functions.h"
35
36 extern bluetooth::core::CoreInterface* GetInterfaceToProfiles();
37
38 namespace {
39
40 using testing::AllOf;
41 using testing::Ge;
42 using testing::Le;
43 using testing::Test;
44
45 const std::vector<uint8_t> msbc_zero_packet{
46 0x01, 0x08, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb,
47 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
48 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb,
49 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00};
50
51 const std::vector<uint8_t> lc3_zero_packet{
52 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x24, 0xf9, 0x4a, 0x0d, 0x00, 0x00, 0x03};
56
57 // Maps irregular packet size to expected decode buffer size.
58 // See |btm_wbs_supported_pkt_size| and |btm_wbs_msbc_buffer_size|.
59 const std::map<size_t, size_t> irregular_packet_to_buffer_size{
60 {72, 360},
61 {24, 120},
62 };
63
64 // The encoded packet size is 60 regardless of the codec.
65 const int ENCODED_PACKET_SIZE = 60;
66
67 struct MsbcCodecInterface : bluetooth::core::CodecInterface {
MsbcCodecInterface__anonb28ba4f50111::MsbcCodecInterface68 MsbcCodecInterface() : bluetooth::core::CodecInterface() {}
69
initialize__anonb28ba4f50111::MsbcCodecInterface70 void initialize() override {
71 hfp_msbc_decoder_init();
72 hfp_msbc_encoder_init();
73 }
74
cleanup__anonb28ba4f50111::MsbcCodecInterface75 void cleanup() override {
76 hfp_msbc_decoder_cleanup();
77 hfp_msbc_encoder_cleanup();
78 }
79
encodePacket__anonb28ba4f50111::MsbcCodecInterface80 uint32_t encodePacket(int16_t* input, uint8_t* output) {
81 return hfp_msbc_encode_frames(input, output);
82 }
83
decodePacket__anonb28ba4f50111::MsbcCodecInterface84 bool decodePacket(const uint8_t* i_buf, int16_t* o_buf, size_t out_len) {
85 return hfp_msbc_decoder_decode_packet(i_buf, o_buf, out_len);
86 }
87 };
88
89 struct Lc3CodecInterface : bluetooth::core::CodecInterface {
Lc3CodecInterface__anonb28ba4f50111::Lc3CodecInterface90 Lc3CodecInterface() : bluetooth::core::CodecInterface() {}
91
initialize__anonb28ba4f50111::Lc3CodecInterface92 void initialize() override {
93 hfp_lc3_decoder_init();
94 hfp_lc3_encoder_init();
95 }
96
cleanup__anonb28ba4f50111::Lc3CodecInterface97 void cleanup() override {
98 hfp_lc3_decoder_cleanup();
99 hfp_lc3_encoder_cleanup();
100 }
101
encodePacket__anonb28ba4f50111::Lc3CodecInterface102 uint32_t encodePacket(int16_t* input, uint8_t* output) {
103 return hfp_lc3_encode_frames(input, output);
104 }
105
decodePacket__anonb28ba4f50111::Lc3CodecInterface106 bool decodePacket(const uint8_t* i_buf, int16_t* o_buf, size_t out_len) {
107 return hfp_lc3_decoder_decode_packet(i_buf, o_buf, out_len);
108 }
109 };
110
111 class ScoHciTest : public BtmWithMocksTest {
112 public:
113 protected:
SetUp()114 void SetUp() override {
115 BtmWithMocksTest::SetUp();
116
117 static auto msbc_codec = MsbcCodecInterface{};
118 static auto lc3_codec = Lc3CodecInterface{};
119 GetInterfaceToProfiles()->msbcCodec = &msbc_codec;
120 GetInterfaceToProfiles()->lc3Codec = &lc3_codec;
121 }
TearDown()122 void TearDown() override { BtmWithMocksTest::TearDown(); }
123 };
124
125 class ScoHciWithOpenCleanTest : public ScoHciTest {
126 public:
127 protected:
SetUp()128 void SetUp() override {
129 ScoHciTest::SetUp();
130 bluetooth::audio::sco::open();
131 }
TearDown()132 void TearDown() override { bluetooth::audio::sco::cleanup(); }
133 };
134
135 class ScoHciWbsTest : public ScoHciTest {};
136 class ScoHciSwbTest : public ScoHciTest {};
137
138 class ScoHciWbsWithInitCleanTest : public ScoHciTest {
139 public:
140 protected:
SetUp()141 void SetUp() override {
142 ScoHciTest::SetUp();
143 bluetooth::audio::sco::wbs::init(60);
144 }
TearDown()145 void TearDown() override { bluetooth::audio::sco::wbs::cleanup(); }
146 };
147
148 class ScoHciSwbWithInitCleanTest : public ScoHciTest {
149 public:
150 protected:
SetUp()151 void SetUp() override {
152 ScoHciTest::SetUp();
153 bluetooth::audio::sco::swb::init(60);
154 }
TearDown()155 void TearDown() override { bluetooth::audio::sco::swb::cleanup(); }
156 };
157
TEST_F(ScoHciWbsTest,WbsInit)158 TEST_F(ScoHciWbsTest, WbsInit) {
159 ASSERT_EQ(bluetooth::audio::sco::wbs::init(60), size_t(60));
160 ASSERT_EQ(bluetooth::audio::sco::wbs::init(72), size_t(72));
161 // Fallback to 60 if the packet size is not supported
162 ASSERT_EQ(bluetooth::audio::sco::wbs::init(48), size_t(60));
163 bluetooth::audio::sco::wbs::cleanup();
164 }
165
TEST_F(ScoHciSwbTest,SwbInit)166 TEST_F(ScoHciSwbTest, SwbInit) {
167 ASSERT_EQ(bluetooth::audio::sco::swb::init(60), size_t(60));
168 ASSERT_EQ(bluetooth::audio::sco::swb::init(72), size_t(72));
169 // Fallback to 60 if the packet size is not supported
170 ASSERT_EQ(bluetooth::audio::sco::swb::init(48), size_t(60));
171 bluetooth::audio::sco::swb::cleanup();
172 }
173
TEST_F(ScoHciWbsTest,WbsEnqueuePacketWithoutInit)174 TEST_F(ScoHciWbsTest, WbsEnqueuePacketWithoutInit) {
175 std::vector<uint8_t> payload{60, 0};
176 // Return 0 if buffer is uninitialized
177 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false), false);
178 }
179
TEST_F(ScoHciSwbTest,SwbEnqueuePacketWithoutInit)180 TEST_F(ScoHciSwbTest, SwbEnqueuePacketWithoutInit) {
181 std::vector<uint8_t> payload{60, 0};
182 // Return 0 if buffer is uninitialized
183 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false), false);
184 }
185
TEST_F(ScoHciWbsWithInitCleanTest,WbsEnqueuePacket)186 TEST_F(ScoHciWbsWithInitCleanTest, WbsEnqueuePacket) {
187 std::vector<uint8_t> payload;
188 for (size_t i = 0; i < 60; i++) {
189 payload.push_back(0);
190 }
191 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false), true);
192 // Return 0 if buffer is full
193 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false), false);
194 }
195
TEST_F(ScoHciSwbWithInitCleanTest,SwbEnqueuePacket)196 TEST_F(ScoHciSwbWithInitCleanTest, SwbEnqueuePacket) {
197 std::vector<uint8_t> payload;
198 for (size_t i = 0; i < 60; i++) {
199 payload.push_back(0);
200 }
201 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false), true);
202 // Return 0 if buffer is full
203 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false), false);
204 }
205
TEST_F(ScoHciWbsTest,WbsDecodeWithoutInit)206 TEST_F(ScoHciWbsTest, WbsDecodeWithoutInit) {
207 const uint8_t* decoded = nullptr;
208 // Return 0 if buffer is uninitialized
209 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
210 ASSERT_EQ(decoded, nullptr);
211 }
212
TEST_F(ScoHciSwbTest,SwbDecodeWithoutInit)213 TEST_F(ScoHciSwbTest, SwbDecodeWithoutInit) {
214 const uint8_t* decoded = nullptr;
215 // Return 0 if buffer is uninitialized
216 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(0));
217 ASSERT_EQ(decoded, nullptr);
218 }
219
TEST_F(ScoHciWbsWithInitCleanTest,WbsDecode)220 TEST_F(ScoHciWbsWithInitCleanTest, WbsDecode) {
221 const uint8_t* decoded = nullptr;
222 std::vector<uint8_t> payload;
223 for (size_t i = 0; i < 60; i++) {
224 payload.push_back(0);
225 }
226
227 // No data to decode
228 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
229 ASSERT_EQ(decoded, nullptr);
230 // Fill in invalid packet, all zeros.
231 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false), true);
232
233 // Return all zero frames when there comes an invalid packet.
234 // This is expected even with PLC as there is no history in the PLC buffer.
235 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
236 ASSERT_NE(decoded, nullptr);
237 for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
238 ASSERT_EQ(decoded[i], 0);
239 }
240
241 decoded = nullptr;
242 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(msbc_zero_packet, false), true);
243 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
244 ASSERT_NE(decoded, nullptr);
245 for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
246 ASSERT_EQ(decoded[i], 0);
247 }
248
249 decoded = nullptr;
250 // No remaining data to decode
251 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
252 ASSERT_EQ(decoded, nullptr);
253 }
254
TEST_F(ScoHciSwbWithInitCleanTest,SwbDecode)255 TEST_F(ScoHciSwbWithInitCleanTest, SwbDecode) {
256 const uint8_t* decoded = nullptr;
257 std::vector<uint8_t> payload;
258 for (size_t i = 0; i < 60; i++) {
259 payload.push_back(0);
260 }
261
262 // No data to decode
263 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(0));
264 ASSERT_EQ(decoded, nullptr);
265 // Fill in invalid packet, all zeros.
266 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false), true);
267
268 // Return all zero frames when there comes an invalid packet.
269 // This is expected even with PLC as there is no history in the PLC buffer.
270 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
271 ASSERT_NE(decoded, nullptr);
272 for (size_t i = 0; i < BTM_LC3_CODE_SIZE; i++) {
273 ASSERT_EQ(decoded[i], 0);
274 }
275
276 decoded = nullptr;
277 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(lc3_zero_packet, false), true);
278 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
279 ASSERT_NE(decoded, nullptr);
280 for (size_t i = 0; i < BTM_LC3_CODE_SIZE; i++) {
281 ASSERT_EQ(decoded[i], 0);
282 }
283
284 decoded = nullptr;
285 // No remaining data to decode
286 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(0));
287 ASSERT_EQ(decoded, nullptr);
288 }
289
TEST_F(ScoHciWbsTest,WbsDecodeWithIrregularOffset)290 TEST_F(ScoHciWbsTest, WbsDecodeWithIrregularOffset) {
291 for (auto [pkt_size, buf_size] : irregular_packet_to_buffer_size) {
292 ASSERT_EQ(buf_size % pkt_size, 0u);
293
294 bluetooth::audio::sco::wbs::init(pkt_size);
295
296 const uint8_t* decoded = nullptr;
297
298 // No data to decode
299 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
300 ASSERT_EQ(decoded, nullptr);
301
302 // Start the payload with an irregular offset that misaligns with the
303 // packet size.
304 std::vector<uint8_t> payload = std::vector<uint8_t>(1, 0);
305 while (payload.size() <= pkt_size) {
306 payload.insert(payload.end(), msbc_zero_packet.begin(), msbc_zero_packet.end());
307 }
308 size_t packet_offset = msbc_zero_packet.size() - (payload.size() - pkt_size);
309 payload.resize(pkt_size);
310
311 // Try to decode as many packets as to hit the boundary.
312 for (size_t iter = 0, decodable = 0; iter < 2 * buf_size / pkt_size; ++iter) {
313 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, false), true);
314 decodable += payload.size() - !iter; // compensate for the first offset
315
316 while (decodable >= ENCODED_PACKET_SIZE) {
317 decoded = nullptr;
318 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
319 ASSERT_NE(decoded, nullptr);
320 for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
321 ASSERT_EQ(decoded[i], 0);
322 }
323 decodable -= ENCODED_PACKET_SIZE;
324 }
325
326 payload = std::vector<uint8_t>(msbc_zero_packet.begin() + packet_offset,
327 msbc_zero_packet.end());
328 while (payload.size() < pkt_size) {
329 payload.insert(payload.end(), msbc_zero_packet.begin(), msbc_zero_packet.end());
330 }
331 packet_offset += msbc_zero_packet.size() - packet_offset;
332 packet_offset +=
333 msbc_zero_packet.size() - (payload.size() - pkt_size) % msbc_zero_packet.size();
334 packet_offset %= msbc_zero_packet.size();
335 payload.resize(pkt_size);
336 }
337
338 bluetooth::audio::sco::wbs::cleanup();
339 }
340 }
341
TEST_F(ScoHciSwbTest,SwbDecodeWithIrregularOffset)342 TEST_F(ScoHciSwbTest, SwbDecodeWithIrregularOffset) {
343 for (auto [pkt_size, buf_size] : irregular_packet_to_buffer_size) {
344 ASSERT_EQ(buf_size % pkt_size, 0u);
345
346 bluetooth::audio::sco::swb::init(pkt_size);
347
348 const uint8_t* decoded = nullptr;
349
350 // No data to decode
351 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(0));
352 ASSERT_EQ(decoded, nullptr);
353
354 // Start the payload with an irregular offset that misaligns with the
355 // packet size.
356 std::vector<uint8_t> payload = std::vector<uint8_t>(1, 0);
357 while (payload.size() <= pkt_size) {
358 payload.insert(payload.end(), lc3_zero_packet.begin(), lc3_zero_packet.end());
359 }
360 size_t packet_offset = lc3_zero_packet.size() - (payload.size() - pkt_size);
361 payload.resize(pkt_size);
362
363 // Try to decode as many packets as to hit the boundary.
364 for (size_t iter = 0, decodable = 0; iter < 2 * buf_size / pkt_size; ++iter) {
365 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(payload, false), true);
366 decodable += payload.size() - !iter; // compensate for the first offset
367
368 while (decodable >= ENCODED_PACKET_SIZE) {
369 decoded = nullptr;
370 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
371 ASSERT_NE(decoded, nullptr);
372 for (size_t i = 0; i < BTM_LC3_CODE_SIZE; i++) {
373 ASSERT_EQ(decoded[i], 0);
374 }
375 decodable -= ENCODED_PACKET_SIZE;
376 }
377
378 payload =
379 std::vector<uint8_t>(lc3_zero_packet.begin() + packet_offset, lc3_zero_packet.end());
380 while (payload.size() < pkt_size) {
381 payload.insert(payload.end(), lc3_zero_packet.begin(), lc3_zero_packet.end());
382 }
383 packet_offset += lc3_zero_packet.size() - packet_offset;
384 packet_offset +=
385 lc3_zero_packet.size() - (payload.size() - pkt_size) % lc3_zero_packet.size();
386 packet_offset %= lc3_zero_packet.size();
387 payload.resize(pkt_size);
388 }
389
390 bluetooth::audio::sco::swb::cleanup();
391 }
392 }
393
TEST_F(ScoHciWbsTest,WbsEncodeWithoutInit)394 TEST_F(ScoHciWbsTest, WbsEncodeWithoutInit) {
395 int16_t data[120] = {0};
396 // Return 0 if buffer is uninitialized
397 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), size_t(0));
398 }
399
TEST_F(ScoHciSwbTest,SwbEncodeWithoutInit)400 TEST_F(ScoHciSwbTest, SwbEncodeWithoutInit) {
401 int16_t data[BTM_LC3_CODE_SIZE / 2] = {0};
402 // Return 0 if buffer is uninitialized
403 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), size_t(0));
404 }
405
TEST_F(ScoHciWbsWithInitCleanTest,WbsEncode)406 TEST_F(ScoHciWbsWithInitCleanTest, WbsEncode) {
407 int16_t data[120] = {0};
408
409 // Return 0 if data is invalid
410 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(nullptr, sizeof(data)), size_t(0));
411 // Return 0 if data length is insufficient
412 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data) - 1), size_t(0));
413 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), sizeof(data));
414
415 // Return 0 if the packet buffer is full
416 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), size_t(0));
417 }
418
TEST_F(ScoHciSwbWithInitCleanTest,SwbEncode)419 TEST_F(ScoHciSwbWithInitCleanTest, SwbEncode) {
420 int16_t data[BTM_LC3_CODE_SIZE / 2] = {0};
421
422 // Return 0 if data is invalid
423 ASSERT_EQ(bluetooth::audio::sco::swb::encode(nullptr, sizeof(data)), size_t(0));
424 // Return 0 if data length is insufficient
425 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data) - 1), size_t(0));
426 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), sizeof(data));
427
428 // Return 0 if the packet buffer is full
429 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), size_t(0));
430 }
431
TEST_F(ScoHciWbsTest,WbsDequeuePacketWithoutInit)432 TEST_F(ScoHciWbsTest, WbsDequeuePacketWithoutInit) {
433 const uint8_t* encoded = nullptr;
434 // Return 0 if buffer is uninitialized
435 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(0));
436 ASSERT_EQ(encoded, nullptr);
437 }
438
TEST_F(ScoHciSwbTest,SwbDequeuePacketWithoutInit)439 TEST_F(ScoHciSwbTest, SwbDequeuePacketWithoutInit) {
440 const uint8_t* encoded = nullptr;
441 // Return 0 if buffer is uninitialized
442 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(0));
443 ASSERT_EQ(encoded, nullptr);
444 }
445
TEST_F(ScoHciWbsWithInitCleanTest,WbsDequeuePacket)446 TEST_F(ScoHciWbsWithInitCleanTest, WbsDequeuePacket) {
447 const uint8_t* encoded = nullptr;
448 // Return 0 if output pointer is invalid
449 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(nullptr), size_t(0));
450 ASSERT_EQ(encoded, nullptr);
451
452 // Return 0 if there is insufficient data to dequeue
453 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(0));
454 ASSERT_EQ(encoded, nullptr);
455 }
456
TEST_F(ScoHciSwbWithInitCleanTest,SwbDequeuePacket)457 TEST_F(ScoHciSwbWithInitCleanTest, SwbDequeuePacket) {
458 const uint8_t* encoded = nullptr;
459 // Return 0 if output pointer is invalid
460 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(nullptr), size_t(0));
461 ASSERT_EQ(encoded, nullptr);
462
463 // Return 0 if there is insufficient data to dequeue
464 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(0));
465 ASSERT_EQ(encoded, nullptr);
466 }
467
TEST_F(ScoHciWbsWithInitCleanTest,WbsEncodeDequeuePackets)468 TEST_F(ScoHciWbsWithInitCleanTest, WbsEncodeDequeuePackets) {
469 uint8_t h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8};
470 int16_t data[120] = {0};
471 const uint8_t* encoded = nullptr;
472
473 for (size_t i = 0; i < 5; i++) {
474 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), sizeof(data));
475 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(60));
476 ASSERT_NE(encoded, nullptr);
477 for (size_t j = 0; j < 60; j++) {
478 ASSERT_EQ(encoded[j], j == 1 ? h2_header_frames_count[i % 4] : msbc_zero_packet[j]);
479 }
480 }
481 }
482
TEST_F(ScoHciSwbWithInitCleanTest,SwbEncodeDequeuePackets)483 TEST_F(ScoHciSwbWithInitCleanTest, SwbEncodeDequeuePackets) {
484 uint8_t h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8};
485 int16_t data[BTM_LC3_CODE_SIZE / 2] = {0};
486 const uint8_t* encoded = nullptr;
487
488 for (size_t i = 0; i < 5; i++) {
489 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), sizeof(data));
490 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(60));
491 ASSERT_NE(encoded, nullptr);
492 for (size_t j = 0; j < 60; j++) {
493 ASSERT_EQ(encoded[j], j == 1 ? h2_header_frames_count[i % 4] : lc3_zero_packet[j]);
494 }
495 }
496 }
497
TEST_F(ScoHciWbsWithInitCleanTest,WbsPlc)498 TEST_F(ScoHciWbsWithInitCleanTest, WbsPlc) {
499 int16_t triangle[16] = {0, 100, 200, 300, 400, 300, 200, 100,
500 0, -100, -200, -300, -400, -300, -200, -100};
501 int16_t data[120];
502 int16_t expect_data[120];
503 std::vector<uint8_t> encoded_vec;
504 for (size_t i = 0; i < 60; i++) {
505 encoded_vec.push_back(0);
506 }
507 const uint8_t* encoded = nullptr;
508 const uint8_t* decoded = nullptr;
509 size_t lost_pkt_idx = 17;
510
511 // Simulate a run without any packet loss
512 for (size_t i = 0, sample_idx = 0; i <= lost_pkt_idx; i++) {
513 // Input data is a 1000Hz triangle wave
514 for (size_t j = 0; j < 120; j++, sample_idx++) {
515 data[j] = triangle[sample_idx % 16];
516 }
517 // Build the packet
518 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), sizeof(data));
519 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(60));
520 ASSERT_NE(encoded, nullptr);
521
522 // Simulate the reception of the packet
523 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
524 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(encoded_vec, false), true);
525 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
526 ASSERT_NE(decoded, nullptr);
527 }
528 // Store the decoded data we expect to get
529 std::copy((const int16_t*)decoded, (const int16_t*)(decoded + BTM_MSBC_CODE_SIZE), expect_data);
530 // Start with the fresh WBS buffer
531 bluetooth::audio::sco::wbs::cleanup();
532 bluetooth::audio::sco::wbs::init(60);
533
534 // check PLC returns gracefully with invalid parameters
535 ASSERT_EQ(bluetooth::audio::sco::wbs::fill_plc_stats(nullptr, nullptr), false);
536
537 int num_decoded_frames;
538 double packet_loss_ratio;
539 // check PLC returns gracefully when there hasn't been decoded frames
540 ASSERT_EQ(bluetooth::audio::sco::wbs::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
541 false);
542
543 int decode_count = 0;
544 for (size_t i = 0, sample_idx = 0; i <= lost_pkt_idx; i++) {
545 // Data is a 1000Hz triangle wave
546 for (size_t j = 0; j < 120; j++, sample_idx++) {
547 data[j] = triangle[sample_idx % 16];
548 }
549 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), sizeof(data));
550 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(60));
551 ASSERT_NE(encoded, nullptr);
552
553 // Substitute to invalid packet to simulate packet loss.
554 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
555 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(
556 i != lost_pkt_idx ? encoded_vec : std::vector<uint8_t>(60, 0), false),
557 true);
558 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
559 decode_count++;
560 ASSERT_NE(decoded, nullptr);
561 }
562
563 ASSERT_EQ(bluetooth::audio::sco::wbs::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
564 true);
565 ASSERT_EQ(num_decoded_frames, decode_count);
566 ASSERT_EQ(packet_loss_ratio, (double)1 / decode_count);
567
568 int16_t* ptr = (int16_t*)decoded;
569 for (size_t i = 0; i < 120; i++) {
570 // The frames generated by PLC won't be perfect due to:
571 // 1. mSBC decoder is statefull
572 // 2. We apply overlap-add to glue the frames when packet loss happens
573 ASSERT_THAT(ptr[i] - expect_data[i], AllOf(Ge(-3), Le(3)))
574 << "PLC data " << ptr[i] << " deviates from expected " << expect_data[i] << " at index "
575 << i;
576 }
577
578 size_t corrupted_pkt_idx = lost_pkt_idx;
579 // Start with the fresh WBS buffer
580 decode_count = 0;
581 bluetooth::audio::sco::wbs::cleanup();
582 bluetooth::audio::sco::wbs::init(60);
583 for (size_t i = 0, sample_idx = 0; i <= corrupted_pkt_idx; i++) {
584 // Data is a 1000Hz triangle wave
585 for (size_t j = 0; j < 120; j++, sample_idx++) {
586 data[j] = triangle[sample_idx % 16];
587 }
588 ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), sizeof(data));
589 ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(60));
590 ASSERT_NE(encoded, nullptr);
591
592 // Substitute to report packet corrupted to simulate packet loss.
593 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
594 ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(encoded_vec, i == corrupted_pkt_idx),
595 true);
596 ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(BTM_MSBC_CODE_SIZE));
597 decode_count++;
598 ASSERT_NE(decoded, nullptr);
599 }
600
601 ASSERT_EQ(bluetooth::audio::sco::wbs::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
602 true);
603 ASSERT_EQ(num_decoded_frames, decode_count);
604 ASSERT_EQ(packet_loss_ratio, (double)1 / decode_count);
605
606 ptr = (int16_t*)decoded;
607 for (size_t i = 0; i < 120; i++) {
608 // The frames generated by PLC won't be perfect due to:
609 // 1. mSBC decoder is statefull
610 // 2. We apply overlap-add to glue the frames when packet loss happens
611 ASSERT_THAT(ptr[i] - expect_data[i], AllOf(Ge(-3), Le(3)))
612 << "PLC data " << ptr[i] << " deviates from expected " << expect_data[i] << " at index "
613 << i;
614 }
615 }
616
617 // TODO(b/269970706): implement PLC validation with
618 // github.com/google/liblc3/issues/16 in mind.
TEST_F(ScoHciSwbWithInitCleanTest,SwbPlc)619 TEST_F(ScoHciSwbWithInitCleanTest, SwbPlc) {
620 int16_t triangle[16] = {0, 100, 200, 300, 400, 300, 200, 100,
621 0, -100, -200, -300, -400, -300, -200, -100};
622 int16_t data[BTM_LC3_CODE_SIZE / 2];
623 int16_t expect_data[BTM_LC3_CODE_SIZE / 2];
624 std::vector<uint8_t> encoded_vec;
625 for (size_t i = 0; i < 60; i++) {
626 encoded_vec.push_back(0);
627 }
628 const uint8_t* encoded = nullptr;
629 const uint8_t* decoded = nullptr;
630 size_t lost_pkt_idx = 17;
631
632 // Simulate a run without any packet loss
633 for (size_t i = 0, sample_idx = 0; i <= lost_pkt_idx; i++) {
634 // Input data is a 1000Hz triangle wave
635 for (size_t j = 0; j < BTM_LC3_CODE_SIZE / 2; j++, sample_idx++) {
636 data[j] = triangle[sample_idx % 16];
637 }
638 // Build the packet
639 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), sizeof(data));
640 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(60));
641 ASSERT_NE(encoded, nullptr);
642
643 // Simulate the reception of the packet
644 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
645 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(encoded_vec, false), true);
646 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
647 ASSERT_NE(decoded, nullptr);
648 }
649 // Store the decoded data we expect to get
650 std::copy((const int16_t*)decoded, (const int16_t*)(decoded + BTM_LC3_CODE_SIZE), expect_data);
651 // Start with the fresh SWB buffer
652 bluetooth::audio::sco::swb::cleanup();
653 bluetooth::audio::sco::swb::init(60);
654
655 // check PLC returns gracefully with invalid parameters
656 ASSERT_EQ(bluetooth::audio::sco::swb::fill_plc_stats(nullptr, nullptr), false);
657
658 int num_decoded_frames;
659 double packet_loss_ratio;
660 // check PLC returns gracefully when there hasn't been decoded frames
661 ASSERT_EQ(bluetooth::audio::sco::swb::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
662 false);
663
664 int decode_count = 0;
665 for (size_t i = 0, sample_idx = 0; i <= lost_pkt_idx; i++) {
666 // Data is a 1000Hz triangle wave
667 for (size_t j = 0; j < BTM_LC3_CODE_SIZE / 2; j++, sample_idx++) {
668 data[j] = triangle[sample_idx % 16];
669 }
670 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), sizeof(data));
671 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(60));
672 ASSERT_NE(encoded, nullptr);
673
674 // Substitute to invalid packet to simulate packet loss.
675 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
676 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(
677 i != lost_pkt_idx ? encoded_vec : std::vector<uint8_t>(60, 0), false),
678 true);
679 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
680 decode_count++;
681 ASSERT_NE(decoded, nullptr);
682 }
683
684 ASSERT_EQ(bluetooth::audio::sco::swb::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
685 true);
686 ASSERT_EQ(num_decoded_frames, decode_count);
687 ASSERT_EQ(packet_loss_ratio, (double)1 / decode_count);
688
689 size_t corrupted_pkt_idx = lost_pkt_idx;
690 // Start with the fresh SWB buffer
691 decode_count = 0;
692 bluetooth::audio::sco::swb::cleanup();
693 bluetooth::audio::sco::swb::init(60);
694 for (size_t i = 0, sample_idx = 0; i <= corrupted_pkt_idx; i++) {
695 // Data is a 1000Hz triangle wave
696 for (size_t j = 0; j < BTM_LC3_CODE_SIZE / 2; j++, sample_idx++) {
697 data[j] = triangle[sample_idx % 16];
698 }
699 ASSERT_EQ(bluetooth::audio::sco::swb::encode(data, sizeof(data)), sizeof(data));
700 ASSERT_EQ(bluetooth::audio::sco::swb::dequeue_packet(&encoded), size_t(60));
701 ASSERT_NE(encoded, nullptr);
702
703 // Substitute to report packet corrupted to simulate packet loss.
704 std::copy(encoded, encoded + size_t(60), encoded_vec.data());
705 ASSERT_EQ(bluetooth::audio::sco::swb::enqueue_packet(encoded_vec, i == corrupted_pkt_idx),
706 true);
707 ASSERT_EQ(bluetooth::audio::sco::swb::decode(&decoded), size_t(BTM_LC3_CODE_SIZE));
708 decode_count++;
709 ASSERT_NE(decoded, nullptr);
710 }
711
712 ASSERT_EQ(bluetooth::audio::sco::swb::fill_plc_stats(&num_decoded_frames, &packet_loss_ratio),
713 true);
714 ASSERT_EQ(num_decoded_frames, decode_count);
715 ASSERT_EQ(packet_loss_ratio, (double)1 / decode_count);
716 }
717 } // namespace
718