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/utf_codecs.h>
18 #include <endian.h>
19
20 #include <type_traits>
21
22 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
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/uuid.h"
26
27 #pragma clang diagnostic ignored "-Wswitch-enum"
28
29 namespace bt {
30
31 namespace {
32
ServiceUuidTypeForUuidSize(UUIDElemSize size,bool complete)33 DataType ServiceUuidTypeForUuidSize(UUIDElemSize size, bool complete) {
34 switch (size) {
35 case UUIDElemSize::k16Bit:
36 return complete ? DataType::kComplete16BitServiceUuids
37 : DataType::kIncomplete16BitServiceUuids;
38 case UUIDElemSize::k32Bit:
39 return complete ? DataType::kComplete32BitServiceUuids
40 : DataType::kIncomplete32BitServiceUuids;
41 case UUIDElemSize::k128Bit:
42 return complete ? DataType::kComplete128BitServiceUuids
43 : DataType::kIncomplete128BitServiceUuids;
44 default:
45 BT_PANIC(
46 "called ServiceUuidTypeForUuidSize with unknown UUIDElemSize %du",
47 size);
48 }
49 }
50
ServiceDataTypeForUuidSize(UUIDElemSize size)51 DataType ServiceDataTypeForUuidSize(UUIDElemSize size) {
52 switch (size) {
53 case UUIDElemSize::k16Bit:
54 return DataType::kServiceData16Bit;
55 case UUIDElemSize::k32Bit:
56 return DataType::kServiceData32Bit;
57 case UUIDElemSize::k128Bit:
58 return DataType::kServiceData128Bit;
59 default:
60 BT_PANIC(
61 "called ServiceDataTypeForUuidSize with unknown UUIDElemSize %du",
62 size);
63 };
64 }
65
EncodedServiceDataSize(const UUID & uuid,const BufferView data)66 size_t EncodedServiceDataSize(const UUID& uuid, const BufferView data) {
67 return uuid.CompactSize() + data.size();
68 }
69
70 // clang-format off
71 // https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping
72 const char* kUriSchemes[] = {"aaa:", "aaas:", "about:", "acap:", "acct:", "cap:", "cid:",
73 "coap:", "coaps:", "crid:", "data:", "dav:", "dict:", "dns:", "file:", "ftp:", "geo:",
74 "go:", "gopher:", "h323:", "http:", "https:", "iax:", "icap:", "im:", "imap:", "info:",
75 "ipp:", "ipps:", "iris:", "iris.beep:", "iris.xpc:", "iris.xpcs:", "iris.lwz:", "jabber:",
76 "ldap:", "mailto:", "mid:", "msrp:", "msrps:", "mtqp:", "mupdate:", "news:", "nfs:", "ni:",
77 "nih:", "nntp:", "opaquelocktoken:", "pop:", "pres:", "reload:", "rtsp:", "rtsps:", "rtspu:",
78 "service:", "session:", "shttp:", "sieve:", "sip:", "sips:", "sms:", "snmp:", "soap.beep:",
79 "soap.beeps:", "stun:", "stuns:", "tag:", "tel:", "telnet:", "tftp:", "thismessage:",
80 "tn3270:", "tip:", "turn:", "turns:", "tv:", "urn:", "vemmi:", "ws:", "wss:", "xcon:",
81 "xcon-userid:", "xmlrpc.beep:", "xmlrpc.beeps:", "xmpp:", "z39.50r:", "z39.50s:", "acr:",
82 "adiumxtra:", "afp:", "afs:", "aim:", "apt:", "attachment:", "aw:", "barion:", "beshare:",
83 "bitcoin:", "bolo:", "callto:", "chrome:", "chrome-extension:", "com-eventbrite-attendee:",
84 "content:", "cvs:", "dlna-playsingle:", "dlna-playcontainer:", "dtn:", "dvb:", "ed2k:",
85 "facetime:", "feed:", "feedready:", "finger:", "fish:", "gg:", "git:", "gizmoproject:",
86 "gtalk:", "ham:", "hcp:", "icon:", "ipn:", "irc:", "irc6:", "ircs:", "itms:", "jar:",
87 "jms:", "keyparc:", "lastfm:", "ldaps:", "magnet:", "maps:", "market:", "message:", "mms:",
88 "ms-help:", "ms-settings-power:", "msnim:", "mumble:", "mvn:", "notes:", "oid:", "palm:",
89 "paparazzi:", "pkcs11:", "platform:", "proxy:", "psyc:", "query:", "res:", "resource:",
90 "rmi:", "rsync:", "rtmfp:", "rtmp:", "secondlife:", "sftp:", "sgn:", "skype:", "smb:",
91 "smtp:", "soldat:", "spotify:", "ssh:", "steam:", "submit:", "svn:", "teamspeak:",
92 "teliaeid:", "things:", "udp:", "unreal:", "ut2004:", "ventrilo:", "view-source:",
93 "webcal:", "wtai:", "wyciwyg:", "xfire:", "xri:", "ymsgr:", "example:",
94 "ms-settings-cloudstorage:"};
95 // clang-format on
96
97 const size_t kUriSchemesSize = std::extent<decltype(kUriSchemes)>::value;
98
EncodeUri(const std::string & uri)99 std::string EncodeUri(const std::string& uri) {
100 std::string encoded_scheme;
101 for (uint32_t i = 0; i < kUriSchemesSize; i++) {
102 const char* scheme = kUriSchemes[i];
103 size_t scheme_len = strlen(scheme);
104 if (std::equal(scheme, scheme + scheme_len, uri.begin())) {
105 bt_lib_cpp_string::WriteUnicodeCharacter(i + 2, &encoded_scheme);
106 return encoded_scheme + uri.substr(scheme_len);
107 }
108 }
109 // First codepoint (U+0001) is for uncompressed schemes.
110 bt_lib_cpp_string::WriteUnicodeCharacter(1, &encoded_scheme);
111 return encoded_scheme + uri;
112 }
113
114 const char kUndefinedScheme = 0x01;
115
DecodeUri(const std::string & uri)116 std::string DecodeUri(const std::string& uri) {
117 if (uri[0] == kUndefinedScheme) {
118 return uri.substr(1);
119 }
120 uint32_t code_point = 0;
121 size_t index = 0;
122
123 // NOTE: as we are reading UTF-8 from `uri`, it is possible that `code_point`
124 // corresponds to > 1 byte of `uri` (even for valid URI encoding schemes, as
125 // U+00(>7F) encodes to 2 bytes).
126 if (!bt_lib_cpp_string::ReadUnicodeCharacter(
127 uri.c_str(), uri.size(), &index, &code_point)) {
128 bt_log(INFO,
129 "gap-le",
130 "Attempted to decode malformed UTF-8 in AdvertisingData URI");
131 return "";
132 }
133 // `uri` is not a c-string, so URIs that start with '\0' after c_str
134 // conversion (i.e. both empty URIs and URIs with leading null bytes '\0') are
135 // caught by the code_point < 2 check. We check
136 // "< 2" instead of "== 0" for redundancy (extra safety!) with the
137 // kUndefinedScheme check above.
138 if (code_point >= kUriSchemesSize + 2 || code_point < 2) {
139 bt_log(
140 ERROR,
141 "gap-le",
142 "Failed to decode URI - supplied UTF-8 encoding scheme codepoint %u "
143 "must be in the "
144 "range 2-kUriSchemesSize + 1 (2-%lu) to correspond to a URI encoding",
145 code_point,
146 kUriSchemesSize + 1);
147 return "";
148 }
149 return kUriSchemes[code_point - 2] + uri.substr(index + 1);
150 }
151
152 template <typename T>
BufferWrite(MutableByteBuffer * buffer,size_t pos,const T & var)153 inline size_t BufferWrite(MutableByteBuffer* buffer, size_t pos, const T& var) {
154 buffer->Write((const uint8_t*)(uintptr_t)(&var), sizeof(T), pos);
155 return sizeof(T);
156 }
157
158 } // namespace
159
AdvertisingData(AdvertisingData && other)160 AdvertisingData::AdvertisingData(AdvertisingData&& other) noexcept {
161 *this = std::move(other);
162 }
163
operator =(AdvertisingData && other)164 AdvertisingData& AdvertisingData::operator=(AdvertisingData&& other) noexcept {
165 // Reset `other`'s state to that of a fresh, empty AdvertisingData
166 local_name_ = std::exchange(other.local_name_, {});
167 tx_power_ = std::exchange(other.tx_power_, {});
168 appearance_ = std::exchange(other.appearance_, {});
169 service_uuids_ = std::exchange(other.service_uuids_, kEmptyServiceUuidMap);
170 manufacturer_data_ = std::exchange(other.manufacturer_data_, {});
171 service_data_ = std::exchange(other.service_data_, {});
172 uris_ = std::exchange(other.uris_, {});
173 flags_ = std::exchange(other.flags_, {});
174 return *this;
175 }
176
ParseErrorToString(ParseError e)177 std::string AdvertisingData::ParseErrorToString(ParseError e) {
178 switch (e) {
179 case ParseError::kInvalidTlvFormat:
180 return "provided bytes are not a valid type-length-value container";
181 case ParseError::kTxPowerLevelMalformed:
182 return "malformed tx power level";
183 case ParseError::kLocalNameTooLong:
184 return "local name exceeds max length (248)";
185 case ParseError::kUuidsMalformed:
186 return "malformed service UUIDs list";
187 case ParseError::kManufacturerSpecificDataTooSmall:
188 return "manufacturer specific data too small";
189 case ParseError::kServiceDataTooSmall:
190 return "service data too small to fit UUIDs";
191 case ParseError::kServiceDataUuidMalformed:
192 return "UUIDs associated with service data are malformed";
193 case ParseError::kAppearanceMalformed:
194 return "malformed appearance field";
195 case ParseError::kMissing:
196 return "data missing";
197 }
198 }
199
FromBytes(const ByteBuffer & data)200 AdvertisingData::ParseResult AdvertisingData::FromBytes(
201 const ByteBuffer& data) {
202 if (data.size() == 0) {
203 return fit::error(ParseError::kMissing);
204 }
205 SupplementDataReader reader(data);
206 if (!reader.is_valid()) {
207 return fit::error(ParseError::kInvalidTlvFormat);
208 }
209
210 AdvertisingData out_ad;
211 DataType type;
212 BufferView field;
213 while (reader.GetNextField(&type, &field)) {
214 // While parsing through the advertising data fields, we do not need to
215 // validate that per-field sizes do not overflow a uint8_t because they, by
216 // construction, are obtained from a uint8_t.
217 BT_DEBUG_ASSERT(field.size() <= std::numeric_limits<uint8_t>::max());
218 switch (type) {
219 case DataType::kTxPowerLevel: {
220 if (field.size() != kTxPowerLevelSize) {
221 return fit::error(ParseError::kTxPowerLevelMalformed);
222 }
223
224 out_ad.SetTxPower(static_cast<int8_t>(field[0]));
225 break;
226 }
227 case DataType::kShortenedLocalName: {
228 if (field.ToString().size() > kMaxNameLength) {
229 return fit::error(ParseError::kLocalNameTooLong);
230 }
231
232 (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/false);
233 break;
234 }
235 case DataType::kCompleteLocalName: {
236 if (field.ToString().size() > kMaxNameLength) {
237 return fit::error(ParseError::kLocalNameTooLong);
238 }
239
240 (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/true);
241 break;
242 }
243 case DataType::kIncomplete16BitServiceUuids:
244 case DataType::kComplete16BitServiceUuids:
245 case DataType::kIncomplete32BitServiceUuids:
246 case DataType::kComplete32BitServiceUuids:
247 case DataType::kIncomplete128BitServiceUuids:
248 case DataType::kComplete128BitServiceUuids: {
249 // AddServiceUuid fails when the number of N bit UUIDs exceed the
250 // kMaxNBitUuids bounds. These bounds are based on the number of UUIDs
251 // that fit in the wire (byte) representation of an AdvertisingData, so
252 // for valid AdvertisingData packets, the number of N bit service UUIDs
253 // cannot exceed the bounds limits. However, because invalid packets may
254 // provide multiple DataType fields for the same UUID (not allowed by
255 // CSS v9 Part A 1.1.1), this limit may be exceeded, in which case we
256 // reject the packet.
257 if (!ParseUuids(
258 field,
259 SizeForType(type),
260 fit::bind_member<&AdvertisingData::AddServiceUuid>(&out_ad))) {
261 return fit::error(ParseError::kUuidsMalformed);
262 }
263 break;
264 }
265 case DataType::kManufacturerSpecificData: {
266 if (field.size() < kManufacturerSpecificDataSizeMin) {
267 return fit::error(ParseError::kManufacturerSpecificDataTooSmall);
268 }
269
270 uint16_t id = le16toh(*reinterpret_cast<const uint16_t*>(field.data()));
271 const BufferView manuf_data(field.data() + kManufacturerIdSize,
272 field.size() - kManufacturerIdSize);
273
274 BT_ASSERT(out_ad.SetManufacturerData(id, manuf_data));
275 break;
276 }
277 case DataType::kServiceData16Bit:
278 case DataType::kServiceData32Bit:
279 case DataType::kServiceData128Bit: {
280 UUID uuid;
281 size_t uuid_size = SizeForType(type);
282 if (field.size() < uuid_size) {
283 return fit::error(ParseError::kServiceDataTooSmall);
284 }
285 const BufferView uuid_bytes(field.data(), uuid_size);
286 if (!UUID::FromBytes(uuid_bytes, &uuid)) {
287 // This is impossible given that uuid_bytes.size() is guaranteed to be
288 // a valid UUID size, and the current UUID::FromBytes implementation
289 // only fails if given an invalid size. We leave it in anyway in case
290 // this implementation changes in the future.
291 return fit::error(ParseError::kServiceDataUuidMalformed);
292 }
293 const BufferView service_data(field.data() + uuid_size,
294 field.size() - uuid_size);
295 BT_ASSERT(out_ad.SetServiceData(uuid, service_data));
296 break;
297 }
298 case DataType::kAppearance: {
299 // TODO(armansito): Peer should have a function to return the
300 // device appearance, as it can be obtained either from advertising data
301 // or via GATT.
302 if (field.size() != kAppearanceSize) {
303 return fit::error(ParseError::kAppearanceMalformed);
304 }
305
306 out_ad.SetAppearance(le16toh(field.To<uint16_t>()));
307 break;
308 }
309 case DataType::kURI: {
310 // Assertion is safe as AddUri only fails when field size > uint8_t,
311 // which is impossible.
312 BT_ASSERT(out_ad.AddUri(DecodeUri(field.ToString())));
313 break;
314 }
315 case DataType::kFlags: {
316 // Flags field may be zero or more octets long but we only store the
317 // first octet.
318 if (field.size() > 0) {
319 out_ad.SetFlags(field[0]);
320 } else {
321 out_ad.SetFlags(0);
322 }
323 break;
324 }
325 default:
326 bt_log(DEBUG,
327 "gap",
328 "ignored advertising field (type %#.2x)",
329 static_cast<unsigned int>(type));
330 break;
331 }
332 }
333
334 return fit::ok(std::move(out_ad));
335 }
336
Copy(AdvertisingData * out) const337 void AdvertisingData::Copy(AdvertisingData* out) const {
338 *out = AdvertisingData();
339
340 if (local_name_) {
341 BT_ASSERT(out->SetLocalName(*local_name_));
342 }
343
344 if (tx_power_) {
345 out->SetTxPower(*tx_power_);
346 }
347
348 if (appearance_) {
349 out->SetAppearance(*appearance_);
350 }
351
352 out->service_uuids_ = service_uuids_;
353
354 for (const auto& it : manufacturer_data_) {
355 BT_ASSERT(out->SetManufacturerData(it.first, it.second.view()));
356 }
357
358 for (const auto& it : service_data_) {
359 BT_ASSERT(out->SetServiceData(it.first, it.second.view()));
360 }
361
362 for (const auto& it : uris_) {
363 BT_ASSERT_MSG(out->AddUri(it), "Copying invalid AD with too-long URI");
364 }
365 }
366
AddServiceUuid(const UUID & uuid)367 [[nodiscard]] bool AdvertisingData::AddServiceUuid(const UUID& uuid) {
368 auto iter = service_uuids_.find(uuid.CompactSize());
369 BT_ASSERT(iter != service_uuids_.end());
370 BoundedUuids& uuids = iter->second;
371 return uuids.AddUuid(uuid);
372 }
373
service_uuids() const374 std::unordered_set<UUID> AdvertisingData::service_uuids() const {
375 std::unordered_set<UUID> out;
376 for (auto& [_elemsize, uuids] : service_uuids_) {
377 out.insert(uuids.set().begin(), uuids.set().end());
378 }
379 return out;
380 }
381
SetServiceData(const UUID & uuid,const ByteBuffer & data)382 [[nodiscard]] bool AdvertisingData::SetServiceData(const UUID& uuid,
383 const ByteBuffer& data) {
384 size_t encoded_size = EncodedServiceDataSize(uuid, data.view());
385 if (encoded_size > kMaxEncodedServiceDataLength) {
386 bt_log(WARN,
387 "gap-le",
388 "SetServiceData for UUID %s failed: (UUID+data) size %zu > maximum "
389 "allowed size %du",
390 bt_str(uuid),
391 encoded_size,
392 kMaxEncodedServiceDataLength);
393 return false;
394 }
395 service_data_[uuid] = DynamicByteBuffer(data);
396 return true;
397 }
398
service_data_uuids() const399 std::unordered_set<UUID> AdvertisingData::service_data_uuids() const {
400 std::unordered_set<UUID> uuids;
401 for (const auto& it : service_data_) {
402 uuids.emplace(it.first);
403 }
404 return uuids;
405 }
406
service_data(const UUID & uuid) const407 BufferView AdvertisingData::service_data(const UUID& uuid) const {
408 auto iter = service_data_.find(uuid);
409 if (iter == service_data_.end())
410 return BufferView();
411 return BufferView(iter->second);
412 }
413
SetManufacturerData(const uint16_t company_id,const BufferView & data)414 [[nodiscard]] bool AdvertisingData::SetManufacturerData(
415 const uint16_t company_id, const BufferView& data) {
416 size_t field_size = data.size();
417 if (field_size > kMaxManufacturerDataLength) {
418 bt_log(WARN,
419 "gap-le",
420 "SetManufacturerData for company id %#.4x failed: (UUID+data) size "
421 "%zu > maximum allowed "
422 "size %hhu",
423 company_id,
424 field_size,
425 kMaxManufacturerDataLength);
426 return false;
427 }
428 manufacturer_data_[company_id] = DynamicByteBuffer(data);
429 return true;
430 }
431
manufacturer_data_ids() const432 std::unordered_set<uint16_t> AdvertisingData::manufacturer_data_ids() const {
433 std::unordered_set<uint16_t> manuf_ids;
434 for (const auto& it : manufacturer_data_) {
435 manuf_ids.emplace(it.first);
436 }
437 return manuf_ids;
438 }
439
manufacturer_data(const uint16_t company_id) const440 BufferView AdvertisingData::manufacturer_data(const uint16_t company_id) const {
441 auto iter = manufacturer_data_.find(company_id);
442 if (iter == manufacturer_data_.end())
443 return BufferView();
444 return BufferView(iter->second);
445 }
446
SetTxPower(int8_t dbm)447 void AdvertisingData::SetTxPower(int8_t dbm) { tx_power_ = dbm; }
448
tx_power() const449 std::optional<int8_t> AdvertisingData::tx_power() const { return tx_power_; }
450
SetLocalName(const LocalName & local_name)451 bool AdvertisingData::SetLocalName(const LocalName& local_name) {
452 if (local_name.name.size() > kMaxNameLength) {
453 return false;
454 }
455 if (local_name_.has_value() && local_name_->is_complete &&
456 !local_name.is_complete) {
457 return false;
458 }
459 local_name_ = local_name;
460 return true;
461 }
462
local_name() const463 std::optional<AdvertisingData::LocalName> AdvertisingData::local_name() const {
464 return local_name_;
465 }
466
AddUri(const std::string & uri)467 [[nodiscard]] bool AdvertisingData::AddUri(const std::string& uri) {
468 if (EncodeUri(uri).size() > kMaxEncodedUriLength) {
469 bt_log(WARN,
470 "gap-le",
471 "not inserting uri %s as it exceeds the max URI size for AD",
472 uri.c_str());
473 return false;
474 }
475 if (uri.empty()) {
476 bt_log(WARN, "gap-le", "skipping insertion of empty uri to AD");
477 return true;
478 }
479 uris_.insert(uri);
480 return true;
481 }
482
uris() const483 const std::unordered_set<std::string>& AdvertisingData::uris() const {
484 return uris_;
485 }
486
SetAppearance(uint16_t appearance)487 void AdvertisingData::SetAppearance(uint16_t appearance) {
488 appearance_ = appearance;
489 }
490
appearance() const491 std::optional<uint16_t> AdvertisingData::appearance() const {
492 return appearance_;
493 }
494
SetFlags(AdvFlags flags)495 void AdvertisingData::SetFlags(AdvFlags flags) { flags_ = flags; }
496
flags() const497 std::optional<AdvFlags> AdvertisingData::flags() const { return flags_; }
498
CalculateBlockSize(bool include_flags) const499 size_t AdvertisingData::CalculateBlockSize(bool include_flags) const {
500 size_t len = 0;
501 if (include_flags) {
502 len += kTLVFlagsSize;
503 }
504
505 if (tx_power_) {
506 len += kTLVTxPowerLevelSize;
507 }
508
509 if (appearance_) {
510 len += kTLVAppearanceSize;
511 }
512
513 if (local_name_) {
514 len += 2 + local_name_->name.size();
515 }
516
517 for (const auto& manuf_pair : manufacturer_data_) {
518 len += 2 + 2 + manuf_pair.second.size();
519 }
520
521 for (const auto& service_data_pair : service_data_) {
522 len += 2 + service_data_pair.first.CompactSize() +
523 service_data_pair.second.size();
524 }
525
526 for (const auto& uri : uris_) {
527 len += 2 + EncodeUri(uri).size();
528 }
529
530 for (const auto& [uuid_size, bounded_uuids] : service_uuids_) {
531 if (bounded_uuids.set().empty()) {
532 continue;
533 }
534 len += 2; // 1 byte for # of UUIDs and 1 for UUID type
535 len += uuid_size * bounded_uuids.set().size();
536 }
537
538 return len;
539 }
540
WriteBlock(MutableByteBuffer * buffer,std::optional<AdvFlags> flags) const541 bool AdvertisingData::WriteBlock(MutableByteBuffer* buffer,
542 std::optional<AdvFlags> flags) const {
543 BT_DEBUG_ASSERT(buffer);
544
545 size_t min_buf_size = CalculateBlockSize(flags.has_value());
546 if (buffer->size() < min_buf_size) {
547 return false;
548 }
549
550 size_t pos = 0;
551 if (flags) {
552 (*buffer)[pos++] =
553 kTLVFlagsSize - 1; // size variable includes current field, subtract 1
554 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kFlags);
555 (*buffer)[pos++] = static_cast<uint8_t>(flags.value());
556 }
557
558 if (tx_power_) {
559 (*buffer)[pos++] = kTLVTxPowerLevelSize -
560 1; // size variable includes current field, subtract 1
561 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kTxPowerLevel);
562 (*buffer)[pos++] = static_cast<uint8_t>(tx_power_.value());
563 }
564
565 if (appearance_) {
566 (*buffer)[pos++] = kTLVAppearanceSize -
567 1; // size variable includes current field, subtract 1
568 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kAppearance);
569 pos += BufferWrite(buffer, pos, appearance_.value());
570 }
571
572 if (local_name_) {
573 BT_ASSERT(local_name_->name.size() <= kMaxNameLength);
574 (*buffer)[pos++] =
575 static_cast<uint8_t>(local_name_->name.size()) + 1; // 1 for null char
576 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kCompleteLocalName);
577 buffer->Write(reinterpret_cast<const uint8_t*>(local_name_->name.c_str()),
578 local_name_->name.size(),
579 pos);
580 pos += local_name_->name.size();
581 }
582
583 for (const auto& manuf_pair : manufacturer_data_) {
584 size_t data_size = manuf_pair.second.size();
585 BT_ASSERT(data_size <= kMaxManufacturerDataLength);
586 (*buffer)[pos++] =
587 1 + 2 +
588 static_cast<uint8_t>(data_size); // 1 for type, 2 for Manuf. Code
589 (*buffer)[pos++] =
590 static_cast<uint8_t>(DataType::kManufacturerSpecificData);
591 pos += BufferWrite(buffer, pos, manuf_pair.first);
592 buffer->Write(manuf_pair.second, pos);
593 pos += data_size;
594 }
595
596 for (const auto& service_data_pair : service_data_) {
597 UUID uuid = service_data_pair.first;
598 size_t encoded_service_data_size =
599 EncodedServiceDataSize(uuid, service_data_pair.second.view());
600 BT_ASSERT(encoded_service_data_size <= kMaxEncodedServiceDataLength);
601 (*buffer)[pos++] =
602 1 + static_cast<uint8_t>(encoded_service_data_size); // 1 for type
603 (*buffer)[pos++] =
604 static_cast<uint8_t>(ServiceDataTypeForUuidSize(uuid.CompactSize()));
605 auto target = buffer->mutable_view(pos);
606 pos += service_data_pair.first.ToBytes(&target);
607 buffer->Write(service_data_pair.second, pos);
608 pos += service_data_pair.second.size();
609 }
610
611 for (const auto& uri : uris_) {
612 std::string s = EncodeUri(uri);
613 BT_ASSERT(s.size() <= kMaxEncodedUriLength);
614 (*buffer)[pos++] = 1 + static_cast<uint8_t>(s.size()); // 1 for type
615 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kURI);
616 buffer->Write(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), pos);
617 pos += s.size();
618 }
619
620 for (const auto& [uuid_width, bounded_uuids] : service_uuids_) {
621 if (bounded_uuids.set().empty()) {
622 continue;
623 }
624
625 // 1 for type
626 BT_ASSERT(1 + uuid_width * bounded_uuids.set().size() <=
627 std::numeric_limits<uint8_t>::max());
628 (*buffer)[pos++] =
629 1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
630 (*buffer)[pos++] = static_cast<uint8_t>(
631 ServiceUuidTypeForUuidSize(uuid_width, /*complete=*/false));
632 for (const auto& uuid : bounded_uuids.set()) {
633 BT_ASSERT_MSG(uuid.CompactSize() == uuid_width,
634 "UUID: %s - Expected Width: %d",
635 bt_str(uuid),
636 uuid_width);
637 auto target = buffer->mutable_view(pos);
638 pos += uuid.ToBytes(&target);
639 }
640 }
641
642 return true;
643 }
644
operator ==(const AdvertisingData & other) const645 bool AdvertisingData::operator==(const AdvertisingData& other) const {
646 if ((local_name_ != other.local_name_) || (tx_power_ != other.tx_power_) ||
647 (appearance_ != other.appearance_) ||
648 (service_uuids_ != other.service_uuids_) || (uris_ != other.uris_) ||
649 (flags_ != other.flags_)) {
650 return false;
651 }
652
653 if (manufacturer_data_.size() != other.manufacturer_data_.size()) {
654 return false;
655 }
656
657 for (const auto& it : manufacturer_data_) {
658 auto that = other.manufacturer_data_.find(it.first);
659 if (that == other.manufacturer_data_.end()) {
660 return false;
661 }
662 size_t bytes = it.second.size();
663 if (bytes != that->second.size()) {
664 return false;
665 }
666 if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
667 return false;
668 }
669 }
670
671 if (service_data_.size() != other.service_data_.size()) {
672 return false;
673 }
674
675 for (const auto& it : service_data_) {
676 auto that = other.service_data_.find(it.first);
677 if (that == other.service_data_.end()) {
678 return false;
679 }
680 size_t bytes = it.second.size();
681 if (bytes != that->second.size()) {
682 return false;
683 }
684 if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
685 return false;
686 }
687 }
688
689 return true;
690 }
691
operator !=(const AdvertisingData & other) const692 bool AdvertisingData::operator!=(const AdvertisingData& other) const {
693 return !(*this == other);
694 }
695
AddUuid(UUID uuid)696 bool AdvertisingData::BoundedUuids::AddUuid(UUID uuid) {
697 BT_ASSERT(set_.size() <= bound_);
698 if (set_.size() < bound_) {
699 if (!set_.insert(uuid).second) {
700 bt_log(INFO,
701 "gap-le",
702 "Skipping addition of duplicate UUID %s to AD",
703 bt_str(uuid));
704 }
705 return true;
706 }
707 if (set_.find(uuid) != set_.end()) {
708 bt_log(INFO,
709 "gap-le",
710 "Skipping addition of duplicate UUID %s to AD",
711 bt_str(uuid));
712 return true;
713 }
714 bt_log(WARN,
715 "gap-le",
716 "Failed to add service UUID %s to AD - no space left",
717 bt_str(uuid));
718 return false;
719 }
720 } // namespace bt
721