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