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