1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
16
17 #include <cpp-string/string_printf.h>
18
19 #include <limits>
20 #include <variant>
21
22 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
26 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
27 #include "pw_unit_test/framework.h"
28
29 namespace bt {
30 namespace {
31
32 constexpr uint16_t kGattUuid = 0x1801;
33 constexpr uint16_t kEddystoneUuid = 0xFEAA;
34 constexpr uint16_t kHeartRateServiceUuid = 0x180D;
35
36 constexpr uint16_t kId1As16 = 0x0212;
37 constexpr uint16_t kId2As16 = 0x1122;
38
39 constexpr size_t kRandomDataSize = 100;
40
TEST(AdvertisingDataTest,MakeEmpty)41 TEST(AdvertisingDataTest, MakeEmpty) {
42 AdvertisingData data;
43
44 EXPECT_EQ(0u, data.CalculateBlockSize());
45 }
46
TEST(AdvertisingDataTest,CopyLeavesNoRemnants)47 TEST(AdvertisingDataTest, CopyLeavesNoRemnants) {
48 AdvertisingData data;
49 data.SetFlags(0x4);
50 data.SetTxPower(4);
51 data.SetAppearance(0x4567);
52 EXPECT_TRUE(data.SetLocalName("fuchsia"));
53 EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));
54
55 StaticByteBuffer bytes(0x01, 0x02, 0x03);
56 EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));
57
58 auto service_uuid = UUID(kId1As16);
59 EXPECT_TRUE(data.AddServiceUuid(service_uuid));
60
61 StaticByteBuffer service_bytes(0x01, 0x02);
62 EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));
63
64 AdvertisingData new_data;
65 new_data.SetTxPower(1);
66 new_data.Copy(&data);
67
68 EXPECT_EQ(1, data.tx_power().value());
69 EXPECT_FALSE(data.appearance().has_value());
70 EXPECT_FALSE(data.local_name().has_value());
71 EXPECT_FALSE(data.flags().has_value());
72 EXPECT_EQ(0u, data.uris().size());
73 EXPECT_EQ(0u, data.manufacturer_data_ids().size());
74 EXPECT_EQ(0u, data.service_uuids().size());
75 EXPECT_EQ(0u, data.service_data_uuids().size());
76 }
77
TEST(AdvertisingDataTest,EncodeKnownURI)78 TEST(AdvertisingDataTest, EncodeKnownURI) {
79 AdvertisingData data;
80 EXPECT_TRUE(data.AddUri("https://abc.xyz"));
81
82 StaticByteBuffer bytes(
83 0x0B, 0x24, 0x17, '/', '/', 'a', 'b', 'c', '.', 'x', 'y', 'z');
84
85 EXPECT_EQ(bytes.size(), data.CalculateBlockSize());
86 DynamicByteBuffer block(data.CalculateBlockSize());
87 data.WriteBlock(&block, std::nullopt);
88 EXPECT_TRUE(ContainersEqual(bytes, block));
89 }
90
TEST(AdvertisingDataTest,EncodeUnknownURI)91 TEST(AdvertisingDataTest, EncodeUnknownURI) {
92 AdvertisingData data;
93 EXPECT_TRUE(data.AddUri("flubs:xyz"));
94
95 StaticByteBuffer bytes(
96 0x0B, 0x24, 0x01, 'f', 'l', 'u', 'b', 's', ':', 'x', 'y', 'z');
97
98 size_t block_size = data.CalculateBlockSize();
99 EXPECT_EQ(bytes.size(), block_size);
100 DynamicByteBuffer block(block_size);
101 data.WriteBlock(&block, std::nullopt);
102 EXPECT_TRUE(ContainersEqual(bytes, block));
103 }
104
TEST(AdvertisingDataTest,CompressServiceUUIDs)105 TEST(AdvertisingDataTest, CompressServiceUUIDs) {
106 AdvertisingData data;
107 std::unordered_set<UUID> uuids{UUID(kId1As16), UUID(kId2As16)};
108 for (auto& uuid : uuids) {
109 SCOPED_TRACE(bt_str(uuid));
110 EXPECT_TRUE(data.AddServiceUuid(uuid));
111 }
112
113 uint8_t expected_block_size = 1 // length byte
114 + 1 // type byte
115 + (sizeof(uint16_t) * 2); // 2 16-bit UUIDs
116 EXPECT_EQ(expected_block_size, data.CalculateBlockSize());
117
118 StaticByteBuffer expected_header{expected_block_size - 1,
119 DataType::kIncomplete16BitServiceUuids};
120
121 DynamicByteBuffer block(expected_block_size);
122 data.WriteBlock(&block, std::nullopt);
123
124 EXPECT_TRUE(
125 ContainersEqual(expected_header, block.view(/*pos=*/0, /*size=*/2)));
126 auto to_uuid = [](const ByteBuffer& b, size_t pos) {
127 return UUID(b.view(pos, /*size=*/2).To<uint16_t>());
128 };
129 EXPECT_TRUE(uuids.find(to_uuid(block, 2)) != uuids.end());
130 EXPECT_TRUE(uuids.find(to_uuid(block, 4)) != uuids.end());
131 }
132
TEST(AdvertisingDataTest,ParseBlock)133 TEST(AdvertisingDataTest, ParseBlock) {
134 StaticByteBuffer bytes(
135 // Complete 16-bit UUIDs
136 0x05,
137 0x03,
138 0x12,
139 0x02,
140 0x22,
141 0x11,
142 // Incomplete list of 32-bit UUIDs
143 0x05,
144 0x04,
145 0x34,
146 0x12,
147 0x34,
148 0x12,
149 // Local name
150 0x09,
151 0x09,
152 'T',
153 'e',
154 's',
155 't',
156 0xF0,
157 0x9F,
158 0x92,
159 0x96,
160 // TX Power
161 0x02,
162 0x0A,
163 0x8F);
164
165 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
166 ASSERT_EQ(fit::ok(), data);
167
168 EXPECT_EQ(3u, data->service_uuids().size());
169 EXPECT_TRUE(data->local_name());
170 EXPECT_EQ("Test", data->local_name()->name);
171 EXPECT_TRUE(data->tx_power());
172 EXPECT_EQ(-113, *(data->tx_power()));
173 }
174
TEST(AdvertisingDataTest,ParseBlockUnknownDataType)175 TEST(AdvertisingDataTest, ParseBlockUnknownDataType) {
176 AdvertisingData expected_ad;
177 constexpr uint8_t lower_byte = 0x12, upper_byte = 0x22;
178 constexpr uint16_t uuid_value = (upper_byte << 8) + lower_byte;
179 // The only field present in the expected AD is one complete 16-bit UUID.
180 EXPECT_TRUE(expected_ad.AddServiceUuid(UUID(uuid_value)));
181
182 StaticByteBuffer bytes{// Complete 16-bit UUIDs
183 0x03,
184 0x03,
185 lower_byte,
186 upper_byte,
187 // 0x40, the second octet, is not a recognized DataType
188 // (see common/supplement_data.h).
189 0x05,
190 0x40,
191 0x34,
192 0x12,
193 0x34,
194 0x12};
195 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
196 ASSERT_EQ(fit::ok(), data);
197
198 // The second field of `bytes` was valid (in that its length byte matched its
199 // length), but its Data Type was unknown, so it should be ignored (i.e. the
200 // only field in the `data` should be the single 16-bit UUID, matching
201 // expected AD).
202 EXPECT_EQ(expected_ad, *data);
203 }
204
TEST(AdvertisingDataTest,ParseBlockNameTooLong)205 TEST(AdvertisingDataTest, ParseBlockNameTooLong) {
206 // A block with a name of exactly kMaxNameLength (==248) bytes should be
207 // parsed correctly.
208 {
209 StaticByteBuffer<2> leading_bytes{kMaxNameLength + 1,
210 DataType::kCompleteLocalName};
211 auto bytes = DynamicByteBuffer(kMaxNameLength + 2);
212 bytes.Write(leading_bytes);
213 DynamicByteBuffer name(kMaxNameLength);
214 name.Fill('a');
215 bytes.Write(name, /*pos=*/2);
216 AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
217 ASSERT_EQ(fit::ok(), result);
218 EXPECT_EQ(result->local_name()->name, std::string(kMaxNameLength, 'a'));
219 }
220 // Repeat previous test with shortened name.
221 {
222 auto leading_bytes =
223 StaticByteBuffer<2>{kMaxNameLength + 1, DataType::kShortenedLocalName};
224 auto bytes = DynamicByteBuffer(kMaxNameLength + 2);
225 bytes.Write(leading_bytes);
226 DynamicByteBuffer name(kMaxNameLength);
227 name.Fill('a');
228 bytes.Write(name, /*pos=*/2);
229 AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
230 ASSERT_EQ(fit::ok(), result);
231 EXPECT_EQ(result->local_name()->name, std::string(kMaxNameLength, 'a'));
232 }
233 // A block with a name of kMaxNameLength+1 (==249) bytes should be rejected.
234 {
235 StaticByteBuffer<2> leading_bytes{kMaxNameLength + 2,
236 DataType::kCompleteLocalName};
237 auto bytes = DynamicByteBuffer(kMaxNameLength + 3);
238 bytes.Write(leading_bytes);
239 DynamicByteBuffer name(kMaxNameLength + 1);
240 name.Fill('a');
241 bytes.Write(name, /*pos=*/2);
242 AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
243 ASSERT_TRUE(result.is_error());
244 EXPECT_EQ(AdvertisingData::ParseError::kLocalNameTooLong,
245 result.error_value());
246 }
247 // Repeat previous test with shortened name.
248 {
249 auto leading_bytes =
250 StaticByteBuffer<2>{kMaxNameLength + 2, DataType::kShortenedLocalName};
251 auto bytes = DynamicByteBuffer(kMaxNameLength + 3);
252 bytes.Write(leading_bytes);
253 DynamicByteBuffer name(kMaxNameLength + 1);
254 name.Fill('a');
255 bytes.Write(name, /*pos=*/2);
256 AdvertisingData::ParseResult result = AdvertisingData::FromBytes(bytes);
257 ASSERT_TRUE(result.is_error());
258 EXPECT_EQ(AdvertisingData::ParseError::kLocalNameTooLong,
259 result.error_value());
260 }
261 }
262
TEST(AdvertisingDataTest,ManufacturerZeroLength)263 TEST(AdvertisingDataTest, ManufacturerZeroLength) {
264 StaticByteBuffer bytes(
265 // Complete 16-bit UUIDs
266 0x05,
267 0x03,
268 0x12,
269 0x02,
270 0x22,
271 0x11,
272 // Manufacturer Data with no data
273 0x03,
274 0xFF,
275 0x34,
276 0x12);
277
278 EXPECT_EQ(0u, AdvertisingData().manufacturer_data_ids().size());
279
280 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
281 ASSERT_EQ(fit::ok(), data);
282
283 EXPECT_EQ(1u, data->manufacturer_data_ids().count(0x1234));
284 EXPECT_EQ(0u, data->manufacturer_data(0x1234).size());
285 }
286
TEST(AdvertisingDataTest,ServiceData)287 TEST(AdvertisingDataTest, ServiceData) {
288 // A typical Eddystone-URL beacon advertisement
289 // to "https://fuchsia.cl"
290 StaticByteBuffer bytes(
291 // Complete 16-bit UUIDs, 0xFEAA
292 0x03,
293 0x03,
294 0xAA,
295 0xFE,
296 // Eddystone Service (0xFEAA) Data:
297 0x10,
298 0x16,
299 0xAA,
300 0xFE,
301 0x10, // Eddystone-Uri type
302 0xEE, // TX Power level -18dBm
303 0x03, // "https://"
304 'f',
305 'u',
306 'c',
307 'h',
308 's',
309 'i',
310 'a',
311 '.',
312 'c',
313 'l');
314
315 EXPECT_EQ(0u, AdvertisingData().service_data_uuids().size());
316
317 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
318 ASSERT_EQ(fit::ok(), data);
319
320 UUID eddystone(uint16_t{0xFEAA});
321
322 EXPECT_EQ(1u, data->service_data_uuids().size());
323 EXPECT_EQ(13u, data->service_data(eddystone).size());
324
325 EXPECT_TRUE(ContainersEqual(bytes.view(8), data->service_data(eddystone)));
326 }
327
328 // Per CSS v9 Part A 1.1.1, "A packet or data block shall not contain more than
329 // one instance for each Service UUID data size". We enforce this by failing to
330 // parse AdvertisingData with UUIDs of a particular size which exceed the amount
331 // that can fit in one TLV field.
TEST(AdvertisingDataTest,TooManyUuidsOfSizeRejected)332 TEST(AdvertisingDataTest, TooManyUuidsOfSizeRejected) {
333 // Space for the maximum # of 16 bit UUIDs + length + type fields.
334 const uint64_t kMaxAllowed16BitUuidsSize =
335 (2 + kMax16BitUuids * UUIDElemSize::k16Bit);
336 // Space for one more UUID + type and length fields
337 const uint64_t kExpectedBuffSize =
338 kMaxAllowed16BitUuidsSize + (2 + UUIDElemSize::k16Bit);
339
340 DynamicByteBuffer bytes(kExpectedBuffSize);
341 uint64_t offset = 0;
342 // Write first TLV field with maximum # of UUIDs
343 bytes.Write(StaticByteBuffer{
344 kMax16BitUuids * UUIDElemSize::k16Bit + 1, // Size byte
345 static_cast<uint8_t>(DataType::kComplete16BitServiceUuids) // Type byte
346 });
347 offset += 2;
348 for (uint16_t i = 0; i < kMax16BitUuids; ++i) {
349 UUID uuid(static_cast<uint16_t>(i + 'a'));
350 bytes.Write(uuid.CompactView(), offset);
351 offset += uuid.CompactSize();
352 }
353 // Verify that we successfully parse an AD with the maximum amount of 16 bit
354 // UUIDs
355 AdvertisingData::ParseResult adv_result = AdvertisingData::FromBytes(
356 bytes.view(/*pos=*/0, /*size=*/kMaxAllowed16BitUuidsSize));
357 ASSERT_EQ(fit::ok(), adv_result);
358 EXPECT_EQ(kMax16BitUuids, adv_result->service_uuids().size());
359 // Write second Complete 16 bit Service UUIDs TLV field with one more UUID
360 bytes.Write(
361 StaticByteBuffer{
362 UUIDElemSize::k16Bit + 1, // Size byte
363 static_cast<uint8_t>(
364 DataType::kComplete16BitServiceUuids) // Type byte
365 },
366 offset);
367 offset += 2;
368 UUID uuid(static_cast<uint16_t>(kMax16BitUuids + 'a'));
369 bytes.Write(uuid.CompactView(), offset);
370
371 adv_result = AdvertisingData::FromBytes(bytes);
372 ASSERT_TRUE(adv_result.is_error());
373 EXPECT_EQ(AdvertisingData::ParseError::kUuidsMalformed,
374 adv_result.error_value());
375 }
376
TEST(AdvertisingDataTest,Missing)377 TEST(AdvertisingDataTest, Missing) {
378 AdvertisingData::ParseResult result =
379 AdvertisingData::FromBytes(DynamicByteBuffer());
380 ASSERT_TRUE(result.is_error());
381 EXPECT_EQ(AdvertisingData::ParseError::kMissing, result.error_value());
382 }
383
TEST(AdvertisingDataTest,InvalidTlvFormat)384 TEST(AdvertisingDataTest, InvalidTlvFormat) {
385 AdvertisingData::ParseResult result =
386 AdvertisingData::FromBytes(StaticByteBuffer(0x03));
387 ASSERT_TRUE(result.is_error());
388 EXPECT_EQ(AdvertisingData::ParseError::kInvalidTlvFormat,
389 result.error_value());
390 }
391
TEST(AdvertisingDataTest,TxPowerLevelMalformed)392 TEST(AdvertisingDataTest, TxPowerLevelMalformed) {
393 StaticByteBuffer service_data{/*length=*/0x01,
394 static_cast<uint8_t>(DataType::kTxPowerLevel)};
395 AdvertisingData::ParseResult result =
396 AdvertisingData::FromBytes(service_data);
397 ASSERT_TRUE(result.is_error());
398 EXPECT_EQ(AdvertisingData::ParseError::kTxPowerLevelMalformed,
399 result.error_value());
400 }
401
TEST(AdvertisingDataTest,UuidsMalformed)402 TEST(AdvertisingDataTest, UuidsMalformed) {
403 StaticByteBuffer service_data{
404 0x02, // Length
405 static_cast<uint8_t>(DataType::kComplete16BitServiceUuids),
406 0x12 // The length of a valid 16-bit UUID byte array be a multiple of 2
407 // (and 1 % 2 == 1).
408 };
409 AdvertisingData::ParseResult result =
410 AdvertisingData::FromBytes(service_data);
411 ASSERT_TRUE(result.is_error());
412 EXPECT_EQ(AdvertisingData::ParseError::kUuidsMalformed, result.error_value());
413 }
414
TEST(AdvertisingDataTest,ManufacturerSpecificDataTooSmall)415 TEST(AdvertisingDataTest, ManufacturerSpecificDataTooSmall) {
416 StaticByteBuffer service_data{
417 0x02, // Length
418 static_cast<uint8_t>(DataType::kManufacturerSpecificData),
419 0x12 // Manufacturer-specific data must be at least 2 bytes
420 };
421 AdvertisingData::ParseResult result =
422 AdvertisingData::FromBytes(service_data);
423 ASSERT_TRUE(result.is_error());
424 EXPECT_EQ(AdvertisingData::ParseError::kManufacturerSpecificDataTooSmall,
425 result.error_value());
426 }
427
TEST(AdvertisingDataTest,DecodeServiceDataWithIncompleteUuid)428 TEST(AdvertisingDataTest, DecodeServiceDataWithIncompleteUuid) {
429 StaticByteBuffer service_data(
430 0x02, // Length
431 static_cast<uint8_t>(DataType::kServiceData16Bit), // Data type
432 0xAA // First byte of incomplete UUID
433 );
434
435 AdvertisingData::ParseResult result =
436 AdvertisingData::FromBytes(service_data);
437 ASSERT_TRUE(result.is_error());
438 EXPECT_EQ(AdvertisingData::ParseError::kServiceDataTooSmall,
439 result.error_value());
440 }
441
TEST(AdvertisingDataTest,AppearanceMalformed)442 TEST(AdvertisingDataTest, AppearanceMalformed) {
443 StaticByteBuffer service_data{
444 0x02, // Length
445 static_cast<uint8_t>(DataType::kAppearance),
446 0x12 // Appearance is supposed to be 2 bytes
447 };
448 AdvertisingData::ParseResult result =
449 AdvertisingData::FromBytes(service_data);
450 ASSERT_TRUE(result.is_error());
451 EXPECT_EQ(AdvertisingData::ParseError::kAppearanceMalformed,
452 result.error_value());
453 }
TEST(AdvertisingDataTest,Equality)454 TEST(AdvertisingDataTest, Equality) {
455 AdvertisingData one, two;
456
457 UUID gatt(kGattUuid);
458 UUID eddy(kEddystoneUuid);
459
460 // Service UUIDs
461 EXPECT_EQ(two, one);
462 EXPECT_TRUE(one.AddServiceUuid(gatt));
463 EXPECT_NE(two, one);
464 EXPECT_TRUE(two.AddServiceUuid(gatt));
465 EXPECT_EQ(two, one);
466
467 // Even when the bytes are the same but from different places
468 StaticByteBuffer bytes(0x01, 0x02, 0x03, 0x04);
469 StaticByteBuffer same(0x01, 0x02, 0x03, 0x04);
470 EXPECT_TRUE(two.SetManufacturerData(0x0123, bytes.view()));
471 EXPECT_NE(two, one);
472 EXPECT_TRUE(one.SetManufacturerData(0x0123, same.view()));
473 EXPECT_EQ(two, one);
474
475 // When TX Power is different
476 two.SetTxPower(-34);
477 EXPECT_NE(two, one);
478 one.SetTxPower(-30);
479 EXPECT_NE(two, one);
480 one.SetTxPower(-34);
481 EXPECT_EQ(two, one);
482
483 // Even if the fields were added in different orders
484 AdvertisingData three, four;
485 EXPECT_TRUE(three.AddServiceUuid(eddy));
486 EXPECT_TRUE(three.AddServiceUuid(gatt));
487 EXPECT_NE(three, four);
488
489 EXPECT_TRUE(four.AddServiceUuid(gatt));
490 EXPECT_TRUE(four.AddServiceUuid(eddy));
491 EXPECT_EQ(three, four);
492 }
493
TEST(AdvertisingDataTest,Copy)494 TEST(AdvertisingDataTest, Copy) {
495 UUID gatt(kGattUuid);
496 UUID eddy(kEddystoneUuid);
497 StaticByteBuffer<kRandomDataSize> rand_data;
498 random_generator()->Get(rand_data.mutable_subspan());
499
500 AdvertisingData source;
501 EXPECT_TRUE(source.AddUri("http://fuchsia.cl"));
502 EXPECT_TRUE(source.AddUri("https://ru.st"));
503 EXPECT_TRUE(source.SetManufacturerData(0x0123, rand_data.view()));
504 EXPECT_TRUE(source.AddServiceUuid(gatt));
505 EXPECT_TRUE(source.AddServiceUuid(eddy));
506
507 AdvertisingData dest;
508 source.Copy(&dest);
509
510 EXPECT_EQ(source, dest);
511
512 // Modifying the source shouldn't mess with the copy
513 EXPECT_TRUE(source.SetLocalName("fuchsia"));
514 EXPECT_FALSE(dest.local_name());
515
516 StaticByteBuffer bytes(0x01, 0x02, 0x03);
517 EXPECT_TRUE(source.SetManufacturerData(0x0123, bytes.view()));
518 EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
519 }
520
TEST(AdvertisingDataTest,Move)521 TEST(AdvertisingDataTest, Move) {
522 UUID gatt(kGattUuid);
523 UUID eddy(kEddystoneUuid);
524 StaticByteBuffer<kRandomDataSize> rand_data;
525 random_generator()->Get(rand_data.mutable_subspan());
526
527 UUID heart_rate_uuid(kHeartRateServiceUuid);
528 int8_t tx_power = 18; // arbitrary TX power
529 uint16_t appearance = 0x4567; // arbitrary appearance value
530 uint8_t flags = 0x48; // arbitrary flags value
531 AdvertisingData source;
532 EXPECT_TRUE(source.SetLocalName("test"));
533 source.SetFlags(flags);
534 source.SetTxPower(tx_power);
535 source.SetAppearance(appearance);
536 EXPECT_TRUE(source.AddUri("http://fuchsia.cl"));
537 EXPECT_TRUE(source.AddUri("https://ru.st"));
538 EXPECT_TRUE(source.SetManufacturerData(0x0123, rand_data.view()));
539 EXPECT_TRUE(source.AddServiceUuid(gatt));
540 EXPECT_TRUE(source.AddServiceUuid(eddy));
541 EXPECT_TRUE(source.SetServiceData(heart_rate_uuid, rand_data.view()));
542
543 auto verify_advertising_data = [&](const AdvertisingData& dest,
544 const char* type) {
545 SCOPED_TRACE(type);
546 // Dest should have the data we set.
547 EXPECT_EQ("test", dest.local_name()->name);
548 EXPECT_EQ(tx_power, dest.tx_power().value());
549 EXPECT_EQ(appearance, dest.appearance().value());
550 EXPECT_EQ(
551 std::unordered_set<std::string>({"http://fuchsia.cl", "https://ru.st"}),
552 dest.uris());
553 EXPECT_TRUE(ContainersEqual(rand_data, dest.manufacturer_data(0x0123)));
554 EXPECT_EQ(std::unordered_set<UUID>({gatt, eddy}), dest.service_uuids());
555 EXPECT_TRUE(ContainersEqual(rand_data, dest.service_data(heart_rate_uuid)));
556 EXPECT_EQ(flags, dest.flags().value());
557 };
558
559 AdvertisingData move_constructed(std::move(source));
560
561 // source should be empty.
562 EXPECT_EQ(AdvertisingData(), source);
563 verify_advertising_data(move_constructed, "move_constructed");
564
565 AdvertisingData move_assigned{};
566 move_assigned = std::move(move_constructed);
567 EXPECT_EQ(AdvertisingData(), move_constructed);
568 verify_advertising_data(move_assigned, "move_assigned");
569 }
570
TEST(AdvertisingDataTest,Flags)571 TEST(AdvertisingDataTest, Flags) {
572 // A zero-byte flags is allowed, and sets the flags field to zeroes.
573 StaticByteBuffer flags_empty(0x01, DataType::kFlags);
574 // Extra bytes are accepted but ignored.
575 StaticByteBuffer flags_extra(0x04, DataType::kFlags, 0x03, 0x42, 0x49);
576
577 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(flags_empty);
578 ASSERT_EQ(fit::ok(), data);
579 ASSERT_TRUE(data->flags().has_value());
580 ASSERT_EQ(0x00, data->flags().value());
581
582 data = AdvertisingData::FromBytes(flags_extra);
583 ASSERT_EQ(fit::ok(), data);
584 ASSERT_TRUE(data->flags().has_value());
585 ASSERT_EQ(0x03, data->flags().value());
586 }
587
TEST(AdvertisingDataTest,Uris)588 TEST(AdvertisingDataTest, Uris) {
589 // The encoding scheme is represented by the first UTF-8 code-point in the URI
590 // string. Per
591 // https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping/,
592 // 0xBA is the highest code point corresponding to an encoding scheme.
593 // However, 0xBA > 0x7F, so representing too-large encoding schemes (i.e.
594 // code-points > 0xBA) in UTF-8 requires two bytes.
595 const uint8_t kLargestKnownSchemeByte1 = 0xC2,
596 kLargestKnownSchemeByte2 = 0xBA;
597 // These bytes represent the (valid) UTF-8 code point for the (unknown
598 // encoding scheme) U+00BB.
599 const uint8_t kUnknownSchemeByte1 = 0xC2, kUnknownSchemeByte2 = 0xBB;
600 StaticByteBuffer bytes(
601 // Uri: "https://abc.xyz"
602 0x0B,
603 DataType::kURI,
604 0x17,
605 '/',
606 '/',
607 'a',
608 'b',
609 'c',
610 '.',
611 'x',
612 'y',
613 'z',
614 // Empty URI should be ignored:
615 0x01,
616 DataType::kURI,
617 // Uri: "flubs:abc"
618 0x0B,
619 DataType::kURI,
620 0x01,
621 'f',
622 'l',
623 'u',
624 'b',
625 's',
626 ':',
627 'a',
628 'b',
629 'c',
630 // Uri: "ms-settings-cloudstorage:flub"
631 0x07,
632 DataType::kURI,
633 kLargestKnownSchemeByte1,
634 kLargestKnownSchemeByte2,
635 'f',
636 'l',
637 'u',
638 'b',
639 // Invalid URI should be ignored - UTF-8 U+00BB doesn't correspond to an
640 // encoding scheme.
641 0x07,
642 DataType::kURI,
643 kUnknownSchemeByte1,
644 kUnknownSchemeByte2,
645 'f',
646 'l',
647 'u',
648 'b',
649 // Invalid URI should be ignored - UTF-8 U+0000 doesn't correspond to an
650 // encoding scheme.
651 0x03,
652 DataType::kURI,
653 0x00,
654 0x00);
655
656 AdvertisingData::ParseResult data = AdvertisingData::FromBytes(bytes);
657 ASSERT_EQ(fit::ok(), data);
658
659 auto uris = data->uris();
660 EXPECT_EQ(3u, uris.size());
661
662 EXPECT_TRUE(std::find(uris.begin(), uris.end(), "https://abc.xyz") !=
663 uris.end());
664 EXPECT_TRUE(std::find(uris.begin(), uris.end(), "flubs:abc") != uris.end());
665 EXPECT_TRUE(std::find(uris.begin(),
666 uris.end(),
667 "ms-settings-cloudstorage:flub") != uris.end());
668 }
669
670 // Tests writing a fully populated |AdvertisingData| to
671 // an output buffer succeeds.
TEST(AdvertisingDataTest,WriteBlockSuccess)672 TEST(AdvertisingDataTest, WriteBlockSuccess) {
673 AdvertisingData data;
674
675 data.SetTxPower(4);
676 data.SetAppearance(0x4567);
677 EXPECT_TRUE(data.SetLocalName("fuchsia"));
678
679 StaticByteBuffer bytes(0x01, 0x02, 0x03);
680 EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));
681
682 auto service_uuid = UUID(kId1As16);
683 StaticByteBuffer service_bytes(0x01, 0x02);
684 EXPECT_TRUE(data.AddServiceUuid(service_uuid));
685 EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));
686
687 EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));
688
689 DynamicByteBuffer write_buf(data.CalculateBlockSize());
690 EXPECT_TRUE(data.WriteBlock(&write_buf, std::nullopt));
691
692 StaticByteBuffer expected_buf(0x02,
693 0x0a,
694 0x04, // tx_power_level_: 4
695 0x03,
696 0x19,
697 0x67,
698 0x45, // appearance_: 0x4567
699 0x08,
700 0x09,
701 0x66,
702 0x75,
703 0x63,
704 0x68,
705 0x73,
706 0x69,
707 0x61, // local_name_: "fuchsia"
708 0x06,
709 0xff,
710 0x23,
711 0x01,
712 0x01,
713 0x02,
714 0x03, // manufacturer_data_
715 0x05,
716 0x16,
717 0x12,
718 0x02,
719 0x01,
720 0x02, // service_data_
721 0x0e,
722 0x24,
723 0x16,
724 0x2f,
725 0x2f,
726 0x66,
727 0x75,
728 0x63,
729 0x68,
730 0x73,
731 0x69,
732 0x61,
733 0x2e,
734 0x63,
735 0x6c,
736 0x03,
737 0x02,
738 0x12,
739 0x02); // uris_
740 EXPECT_TRUE(ContainersEqual(expected_buf, write_buf));
741 }
742
743 // Tests writing |AdvertisingData| to an output buffer that
744 // is too small fails gracefully and returns early.
TEST(AdvertisingDataTest,WriteBlockSmallBufError)745 TEST(AdvertisingDataTest, WriteBlockSmallBufError) {
746 AdvertisingData data;
747
748 data.SetTxPower(4);
749 data.SetAppearance(0x4567);
750 EXPECT_TRUE(data.SetLocalName("fuchsia"));
751
752 DynamicByteBuffer write_buf(data.CalculateBlockSize() - 1);
753 // The buffer is too small. No write should occur, and should return false.
754 EXPECT_FALSE(data.WriteBlock(&write_buf, std::nullopt));
755 }
756
757 // Tests writing a fully populated |AdvertisingData| with provided flags to
758 // an output buffer succeeds.
TEST(AdvertisingDataTest,WriteBlockWithFlagsSuccess)759 TEST(AdvertisingDataTest, WriteBlockWithFlagsSuccess) {
760 AdvertisingData data;
761
762 data.SetTxPower(4);
763 data.SetAppearance(0x4567);
764 EXPECT_TRUE(data.SetLocalName("fuchsia"));
765
766 StaticByteBuffer bytes(0x01, 0x02, 0x03);
767 EXPECT_TRUE(data.SetManufacturerData(0x0123, bytes.view()));
768
769 auto service_uuid = UUID(kId1As16);
770 StaticByteBuffer service_bytes(0x01, 0x02);
771 EXPECT_TRUE(data.AddServiceUuid(service_uuid));
772 EXPECT_TRUE(data.SetServiceData(service_uuid, service_bytes.view()));
773
774 EXPECT_TRUE(data.AddUri("http://fuchsia.cl"));
775
776 DynamicByteBuffer write_buf(data.CalculateBlockSize(/*include_flags=*/true));
777 EXPECT_TRUE(data.WriteBlock(&write_buf, AdvFlag::kLEGeneralDiscoverableMode));
778
779 StaticByteBuffer expected_buf(0x02,
780 0x01,
781 0x02, // flags: 2
782 0x02,
783 0x0a,
784 0x04, // tx_power_level_: 4
785 0x03,
786 0x19,
787 0x67,
788 0x45, // appearance_: 0x4567
789 0x08,
790 0x09,
791 0x66,
792 0x75,
793 0x63,
794 0x68,
795 0x73,
796 0x69,
797 0x61, // local_name_: "fuchsia"
798 0x06,
799 0xff,
800 0x23,
801 0x01,
802 0x01,
803 0x02,
804 0x03, // manufacturer_data_
805 0x05,
806 0x16,
807 0x12,
808 0x02,
809 0x01,
810 0x02, // service_data_
811 0x0e,
812 0x24,
813 0x16,
814 0x2f,
815 0x2f,
816 0x66,
817 0x75,
818 0x63,
819 0x68,
820 0x73,
821 0x69,
822 0x61,
823 0x2e,
824 0x63,
825 0x6c,
826 0x03,
827 0x02,
828 0x12,
829 0x02); // uris_
830 EXPECT_TRUE(ContainersEqual(expected_buf, write_buf));
831 }
832
TEST(AdvertisingDataTest,WriteBlockWithFlagsBufError)833 TEST(AdvertisingDataTest, WriteBlockWithFlagsBufError) {
834 AdvertisingData data;
835
836 data.SetTxPower(6);
837 EXPECT_TRUE(data.SetLocalName("Fuchsia"));
838 data.SetAppearance(0x1234);
839
840 DynamicByteBuffer write_buf(data.CalculateBlockSize(/*include_flags=*/true) -
841 1);
842 EXPECT_FALSE(
843 data.WriteBlock(&write_buf, AdvFlag::kLEGeneralDiscoverableMode));
844 }
845
846 // Adds `n_(consecutively_increasing)_uuids` to `input` and returns the "next"
847 // UUID in the sequence. UUIDs may wrap around - this is OK, as we only care
848 // that they are all distinct.
AddNDistinctUuids(AdvertisingData & input,std::variant<uint16_t,uint32_t,UInt128> starting_uuid,uint8_t n_uuids)849 UUID AddNDistinctUuids(AdvertisingData& input,
850 std::variant<uint16_t, uint32_t, UInt128> starting_uuid,
851 uint8_t n_uuids) {
852 UUID next;
853 for (uint8_t i = 0; true; ++i) {
854 std::visit(
855 [&](auto arg) {
856 using T = std::decay_t<decltype(arg)>;
857 if constexpr (std::is_same_v<T, UInt128>) {
858 arg[0] += i;
859 next = UUID(arg);
860 } else {
861 next = UUID(static_cast<T>(arg + i));
862 }
863 },
864 starting_uuid);
865 SCOPED_TRACE(
866 bt_lib_cpp_string::StringPrintf("i: %du UUID: %s", i, bt_str(next)));
867 if (i >= n_uuids) {
868 return next;
869 }
870 EXPECT_TRUE(input.AddServiceUuid(next));
871 }
872 }
873
TEST(AdvertisingDataTest,SetFieldsWithTooLongParameters)874 TEST(AdvertisingDataTest, SetFieldsWithTooLongParameters) {
875 AdvertisingData data;
876 {
877 // Use the https URI encoding scheme. This prefix will be compressed to one
878 // byte when encoded.
879 std::string uri = "https:";
880 uri += std::string(kMaxEncodedUriLength - 1, '.');
881 EXPECT_TRUE(data.AddUri(uri));
882 uri += '.';
883 EXPECT_FALSE(data.AddUri(uri));
884 }
885 // Attempt to set slightly too long service data.
886 {
887 UUID two_byte_uuid{kHeartRateServiceUuid};
888 DynamicByteBuffer long_data(kMaxEncodedServiceDataLength - 1);
889 long_data.Fill(0xAB);
890 EXPECT_FALSE(data.SetServiceData(two_byte_uuid, long_data));
891 // An empty DynamicByteBuffer represents unset service data per the header.
892 EXPECT_TRUE(
893 ContainersEqual(DynamicByteBuffer(), data.service_data(two_byte_uuid)));
894 // Now use a view that is just small enough to fit when encoded
895 BufferView view = long_data.view(/*pos=*/0, /*size=*/long_data.size() - 1);
896 EXPECT_TRUE(data.SetServiceData(two_byte_uuid, view));
897 EXPECT_TRUE(ContainersEqual(view, data.service_data(two_byte_uuid)));
898 }
899 // Attempt to set slightly too long manufacturer data.
900 {
901 uint16_t manufacturer_id{0xABBA};
902 DynamicByteBuffer long_data(kMaxManufacturerDataLength + 1);
903 long_data.Fill(0xAB);
904 EXPECT_FALSE(data.SetManufacturerData(manufacturer_id, long_data.view()));
905 // An empty DynamicByteBuffer represents unset service data per the header.
906 EXPECT_TRUE(ContainersEqual(DynamicByteBuffer(),
907 data.manufacturer_data(manufacturer_id)));
908 // Now use a view that is just small enough to fit when encoded
909 BufferView view = long_data.view(/*pos=*/0, /*size=*/long_data.size() - 1);
910 EXPECT_TRUE(data.SetManufacturerData(manufacturer_id, view));
911 EXPECT_TRUE(ContainersEqual(view, data.manufacturer_data(manufacturer_id)));
912 }
913 // Ensure that service UUIDs are truncated when they do not fit.
914 {
915 uint16_t starting_16bit_uuid = 0x0001;
916 UUID should_fail = AddNDistinctUuids(
917 data,
918 std::variant<uint16_t, uint32_t, UInt128>{starting_16bit_uuid},
919 kMax16BitUuids);
920 EXPECT_FALSE(data.AddServiceUuid(should_fail));
921 EXPECT_TRUE(data.service_uuids().find(should_fail) ==
922 data.service_uuids().end());
923
924 // This value must not fit in a 16 bit number in order to count as a "32
925 // bit" UUID
926 uint32_t starting_32bit_uuid = std::numeric_limits<uint16_t>::max() + 1;
927 should_fail = AddNDistinctUuids(
928 data,
929 std::variant<uint16_t, uint32_t, UInt128>{starting_32bit_uuid},
930 kMax32BitUuids);
931 EXPECT_FALSE(data.AddServiceUuid(should_fail));
932 EXPECT_TRUE(data.service_uuids().find(should_fail) ==
933 data.service_uuids().end());
934
935 UInt128 starting_128bit_uuid = {0xAB,
936 0xAB,
937 0xAB,
938 0xAB,
939 0xAB,
940 0xAB,
941 0xAB,
942 0xAB,
943 0xAB,
944 0xAB,
945 0xAB,
946 0xAB,
947 0xAB,
948 0xAB,
949 0xAB,
950 0xAB};
951 should_fail = AddNDistinctUuids(
952 data,
953 std::variant<uint16_t, uint32_t, UInt128>{starting_128bit_uuid},
954 kMax128BitUuids);
955 EXPECT_FALSE(data.AddServiceUuid(should_fail));
956 EXPECT_TRUE(data.service_uuids().find(should_fail) ==
957 data.service_uuids().end());
958 }
959 // Ensures names exceeding kMaxNameLength are rejected.
960 {
961 std::string name_that_fits(kMaxNameLength, 'a');
962 std::string too_long_name(kMaxNameLength + 1, 'b');
963 EXPECT_TRUE(data.SetLocalName(name_that_fits));
964 EXPECT_EQ(name_that_fits, data.local_name()->name);
965 EXPECT_FALSE(data.SetLocalName(too_long_name));
966 EXPECT_EQ(name_that_fits, data.local_name()->name);
967 }
968 // Write the data out to ensure no assertions are triggered
969 DynamicByteBuffer block(data.CalculateBlockSize());
970 EXPECT_TRUE(data.WriteBlock(&block, std::nullopt));
971 }
972
973 // Tests that setting a complete local name overwrites an existing shortened
974 // local name and that setting a shortened local name has no effect if a
975 // complete local name is currently stored.
TEST(AdvertisingDataTest,CompleteLocalNameFavored)976 TEST(AdvertisingDataTest, CompleteLocalNameFavored) {
977 AdvertisingData data;
978 std::string short_name = "short";
979 std::string complete_name = "complete";
980
981 EXPECT_TRUE(data.SetLocalName(short_name, /*is_complete=*/false));
982 EXPECT_EQ(short_name, data.local_name()->name);
983 EXPECT_TRUE(data.SetLocalName(complete_name, /*is_complete=*/true));
984 EXPECT_EQ(complete_name, data.local_name()->name);
985
986 EXPECT_FALSE(data.SetLocalName(short_name, /*is_complete=*/false));
987 EXPECT_EQ(complete_name, data.local_name()->name);
988 }
989
990 // Tests that even when the maximum number of distinct UUIDs for a certain size
991 // have been added to an AD, we do not reject additional UUIDs that are
992 // duplicates of already-added UUIDs.
TEST(AdvertisingDataTest,AddDuplicateServiceUuidsWhenFullSucceeds)993 TEST(AdvertisingDataTest, AddDuplicateServiceUuidsWhenFullSucceeds) {
994 AdvertisingData data;
995 uint16_t starting_16bit_uuid = 0x0001;
996 UUID should_fail = AddNDistinctUuids(
997 data,
998 std::variant<uint16_t, uint32_t, UInt128>{starting_16bit_uuid},
999 kMax16BitUuids);
1000 // Verify that adding another distinct UUID fails - i.e. we are at the limit.
1001 EXPECT_FALSE(data.AddServiceUuid(should_fail));
1002 EXPECT_TRUE(data.service_uuids().find(should_fail) ==
1003 data.service_uuids().end());
1004 // Verify that we are notified of success when adding an existing UUID
1005 EXPECT_TRUE(data.AddServiceUuid(UUID(starting_16bit_uuid)));
1006 }
1007 } // namespace
1008 } // namespace bt
1009