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 #include <pw_assert/check.h>
19 #include <pw_bytes/endian.h>
20 #include <pw_preprocessor/compiler.h>
21 #include <pw_string/utf_codecs.h>
22
23 #include <string>
24 #include <type_traits>
25
26 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/to_string.h"
29 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
30
31 namespace bt {
32
33 namespace {
34
ServiceUuidTypeForUuidSize(UUIDElemSize size,bool complete)35 DataType ServiceUuidTypeForUuidSize(UUIDElemSize size, bool complete) {
36 switch (size) {
37 case UUIDElemSize::k16Bit:
38 return complete ? DataType::kComplete16BitServiceUuids
39 : DataType::kIncomplete16BitServiceUuids;
40 case UUIDElemSize::k32Bit:
41 return complete ? DataType::kComplete32BitServiceUuids
42 : DataType::kIncomplete32BitServiceUuids;
43 case UUIDElemSize::k128Bit:
44 return complete ? DataType::kComplete128BitServiceUuids
45 : DataType::kIncomplete128BitServiceUuids;
46 default:
47 PW_CRASH(
48 "called ServiceUuidTypeForUuidSize with unknown UUIDElemSize %du",
49 size);
50 }
51 }
52
ServiceDataTypeForUuidSize(UUIDElemSize size)53 DataType ServiceDataTypeForUuidSize(UUIDElemSize size) {
54 switch (size) {
55 case UUIDElemSize::k16Bit:
56 return DataType::kServiceData16Bit;
57 case UUIDElemSize::k32Bit:
58 return DataType::kServiceData32Bit;
59 case UUIDElemSize::k128Bit:
60 return DataType::kServiceData128Bit;
61 default:
62 PW_CRASH(
63 "called ServiceDataTypeForUuidSize with unknown UUIDElemSize %du",
64 size);
65 };
66 }
67
SolicitationUuidTypeForUuidSize(UUIDElemSize size)68 DataType SolicitationUuidTypeForUuidSize(UUIDElemSize size) {
69 switch (size) {
70 case UUIDElemSize::k16Bit:
71 return DataType::kSolicitationUuid16Bit;
72 case UUIDElemSize::k32Bit:
73 return DataType::kSolicitationUuid32Bit;
74 case UUIDElemSize::k128Bit:
75 return DataType::kSolicitationUuid128Bit;
76 default:
77 PW_CRASH(
78 "called SolicitationUuidTypeForUuidSize with unknown UUIDElemSize "
79 "%du",
80 size);
81 };
82 }
83
EncodedServiceDataSize(const UUID & uuid,const BufferView data)84 size_t EncodedServiceDataSize(const UUID& uuid, const BufferView data) {
85 return uuid.CompactSize() + data.size();
86 }
87
88 // clang-format off
89 // https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping
90 constexpr const char* kUriSchemes[] = {"aaa:", "aaas:", "about:", "acap:", "acct:", "cap:", "cid:",
91 "coap:", "coaps:", "crid:", "data:", "dav:", "dict:", "dns:", "file:", "ftp:", "geo:",
92 "go:", "gopher:", "h323:", "http:", "https:", "iax:", "icap:", "im:", "imap:", "info:",
93 "ipp:", "ipps:", "iris:", "iris.beep:", "iris.xpc:", "iris.xpcs:", "iris.lwz:", "jabber:",
94 "ldap:", "mailto:", "mid:", "msrp:", "msrps:", "mtqp:", "mupdate:", "news:", "nfs:", "ni:",
95 "nih:", "nntp:", "opaquelocktoken:", "pop:", "pres:", "reload:", "rtsp:", "rtsps:", "rtspu:",
96 "service:", "session:", "shttp:", "sieve:", "sip:", "sips:", "sms:", "snmp:", "soap.beep:",
97 "soap.beeps:", "stun:", "stuns:", "tag:", "tel:", "telnet:", "tftp:", "thismessage:",
98 "tn3270:", "tip:", "turn:", "turns:", "tv:", "urn:", "vemmi:", "ws:", "wss:", "xcon:",
99 "xcon-userid:", "xmlrpc.beep:", "xmlrpc.beeps:", "xmpp:", "z39.50r:", "z39.50s:", "acr:",
100 "adiumxtra:", "afp:", "afs:", "aim:", "apt:", "attachment:", "aw:", "barion:", "beshare:",
101 "bitcoin:", "bolo:", "callto:", "chrome:", "chrome-extension:", "com-eventbrite-attendee:",
102 "content:", "cvs:", "dlna-playsingle:", "dlna-playcontainer:", "dtn:", "dvb:", "ed2k:",
103 "facetime:", "feed:", "feedready:", "finger:", "fish:", "gg:", "git:", "gizmoproject:",
104 "gtalk:", "ham:", "hcp:", "icon:", "ipn:", "irc:", "irc6:", "ircs:", "itms:", "jar:",
105 "jms:", "keyparc:", "lastfm:", "ldaps:", "magnet:", "maps:", "market:", "message:", "mms:",
106 "ms-help:", "ms-settings-power:", "msnim:", "mumble:", "mvn:", "notes:", "oid:", "palm:",
107 "paparazzi:", "pkcs11:", "platform:", "proxy:", "psyc:", "query:", "res:", "resource:",
108 "rmi:", "rsync:", "rtmfp:", "rtmp:", "secondlife:", "sftp:", "sgn:", "skype:", "smb:",
109 "smtp:", "soldat:", "spotify:", "ssh:", "steam:", "submit:", "svn:", "teamspeak:",
110 "teliaeid:", "things:", "udp:", "unreal:", "ut2004:", "ventrilo:", "view-source:",
111 "webcal:", "wtai:", "wyciwyg:", "xfire:", "xri:", "ymsgr:", "example:",
112 "ms-settings-cloudstorage:"};
113 // clang-format on
114
115 const size_t kUriSchemesSize = std::extent<decltype(kUriSchemes)>::value;
116
EncodeUri(const std::string & uri)117 std::string EncodeUri(const std::string& uri) {
118 for (uint32_t i = 0; i < kUriSchemesSize; i++) {
119 const char* scheme = kUriSchemes[i];
120 size_t scheme_len = strlen(scheme);
121 if (std::strncmp(uri.c_str(), scheme, scheme_len) == 0) {
122 const pw::Result<pw::utf8::EncodedCodePoint> encoded_scheme =
123 pw::utf8::EncodeCodePoint(i + 2);
124 PW_DCHECK(encoded_scheme.ok());
125 return std::string(encoded_scheme->as_view()) + uri.substr(scheme_len);
126 }
127 }
128 // First codepoint (U+0001) is for uncompressed schemes.
129 const pw::Result<pw::utf8::EncodedCodePoint> encoded_scheme =
130 pw::utf8::EncodeCodePoint(1u);
131 PW_DCHECK(encoded_scheme.ok());
132 return std::string(encoded_scheme->as_view()) + uri;
133 }
134
135 const char kUndefinedScheme = 0x01;
136
DecodeUri(const std::string & uri)137 std::string DecodeUri(const std::string& uri) {
138 if (uri[0] == kUndefinedScheme) {
139 return uri.substr(1);
140 }
141
142 // NOTE: as we are reading UTF-8 from `uri`, it is possible that `code_point`
143 // corresponds to > 1 byte of `uri` (even for valid URI encoding schemes, as
144 // U+00(>7F) encodes to 2 bytes).
145 const auto result = pw::utf8::ReadCodePoint(uri);
146 if (!result.ok()) {
147 bt_log(INFO,
148 "gap-le",
149 "Attempted to decode malformed UTF-8 in AdvertisingData URI");
150 return "";
151 }
152 const uint32_t code_point = result->code_point();
153 // `uri` is not a c-string, so URIs that start with '\0' after c_str
154 // conversion (i.e. both empty URIs and URIs with leading null bytes '\0') are
155 // caught by the code_point < 2 check. We check
156 // "< 2" instead of "== 0" for redundancy (extra safety!) with the
157 // kUndefinedScheme check above.
158 if (code_point >= kUriSchemesSize + 2 || code_point < 2) {
159 bt_log(
160 ERROR,
161 "gap-le",
162 "Failed to decode URI - supplied UTF-8 encoding scheme codepoint %u "
163 "must be in the "
164 "range 2-kUriSchemesSize + 1 (2-%zu) to correspond to a URI encoding",
165 code_point,
166 kUriSchemesSize + 1);
167 return "";
168 }
169 return kUriSchemes[code_point - 2] + uri.substr(result->size());
170 }
171
172 template <typename T>
BufferWrite(MutableByteBuffer * buffer,size_t pos,const T & var)173 inline size_t BufferWrite(MutableByteBuffer* buffer, size_t pos, const T& var) {
174 buffer->Write((const uint8_t*)(uintptr_t)(&var), sizeof(T), pos);
175 return sizeof(T);
176 }
177
178 } // namespace
179
AdvertisingData(AdvertisingData && other)180 AdvertisingData::AdvertisingData(AdvertisingData&& other) noexcept {
181 *this = std::move(other);
182 }
183
operator =(AdvertisingData && other)184 AdvertisingData& AdvertisingData::operator=(AdvertisingData&& other) noexcept {
185 // Reset `other`'s state to that of a fresh, empty AdvertisingData
186 local_name_ = std::exchange(other.local_name_, {});
187 tx_power_ = std::exchange(other.tx_power_, {});
188 appearance_ = std::exchange(other.appearance_, {});
189 service_uuids_ = std::exchange(other.service_uuids_, kEmptyServiceUuidMap);
190 solicitation_uuids_ =
191 std::exchange(other.solicitation_uuids_, kEmptyServiceUuidMap);
192 manufacturer_data_ = std::exchange(other.manufacturer_data_, {});
193 service_data_ = std::exchange(other.service_data_, {});
194 uris_ = std::exchange(other.uris_, {});
195 flags_ = std::exchange(other.flags_, {});
196 resolvable_set_identifier_ =
197 std::exchange(other.resolvable_set_identifier_, {});
198 broadcast_name_ = std::exchange(other.broadcast_name_, {});
199 return *this;
200 }
201
ParseErrorToString(ParseError e)202 std::string AdvertisingData::ParseErrorToString(ParseError e) {
203 switch (e) {
204 case ParseError::kInvalidTlvFormat:
205 return "provided bytes are not a valid type-length-value container";
206 case ParseError::kTxPowerLevelMalformed:
207 return "malformed tx power level";
208 case ParseError::kLocalNameTooLong:
209 return "local name exceeds max length (248)";
210 case ParseError::kUuidsMalformed:
211 return "malformed UUIDs list";
212 case ParseError::kManufacturerSpecificDataTooSmall:
213 return "manufacturer specific data too small";
214 case ParseError::kServiceDataTooSmall:
215 return "service data too small to fit UUIDs";
216 case ParseError::kServiceDataUuidMalformed:
217 return "UUIDs associated with service data are malformed";
218 case ParseError::kAppearanceMalformed:
219 return "malformed appearance field";
220 case ParseError::kMissing:
221 return "data missing";
222 case ParseError::kResolvableSetIdentifierSize:
223 return "resolvable set identifier is wrong size";
224 case ParseError::kBroadcastNameTooShort:
225 return "broadcast name is too short";
226 case ParseError::kBroadcastNameTooLong:
227 return "broadcast name is too long";
228 }
229 }
230
AdvFlagsToString(const std::optional<AdvFlags> & flags_opt)231 std::string AdvFlagsToString(const std::optional<AdvFlags>& flags_opt) {
232 std::string result = "Flags: {";
233
234 if (!flags_opt.has_value()) {
235 return result += "} ";
236 }
237
238 const AdvFlags& flags = flags_opt.value();
239
240 if (flags & kLELimitedDiscoverableMode) {
241 result += " LE Limited Discoverable Mode,";
242 }
243 if (flags & kLEGeneralDiscoverableMode) {
244 result += " LE General Discoverable Mode,";
245 }
246 if (flags & kBREDRNotSupported) {
247 result += " BR/EDR Not Supported,";
248 }
249 if (flags & kSimultaneousLEAndBREDRController) {
250 result += " Simultaneous LE And BR/EDR Controller,";
251 }
252 if (flags & kSimultaneousLEAndBREDRHost) {
253 result += " Simultaneous LE And BR/EDR Host,";
254 }
255 return result += " }, ";
256 }
257
ToString() const258 std::string AdvertisingData::ToString() const {
259 std::string result = "Advertising Data { ";
260
261 if (local_name_) {
262 bt_lib_cpp_string::StringAppendf(
263 &result,
264 "%s Name: %s, ",
265 (local_name_->is_complete ? "Complete" : "Short"),
266 local_name_->name.c_str());
267 }
268
269 if (tx_power_) {
270 bt_lib_cpp_string::StringAppendf(&result, "TX Power: %hhd, ", *tx_power_);
271 }
272
273 if (appearance_) {
274 bt_lib_cpp_string::StringAppendf(
275 &result, "Appearance: 0x%04x, ", *appearance_);
276 }
277
278 if (!uris_.empty()) {
279 result += "URIs: { ";
280 for (const auto& uri : uris_) {
281 bt_lib_cpp_string::StringAppendf(&result, "%s, ", uri.c_str());
282 }
283 result += "}, ";
284 }
285
286 if (flags_.has_value()) {
287 result += AdvFlagsToString(flags_);
288 }
289
290 bool has_service_uuids = false;
291 for (const auto& [_, bounded_uuids] : service_uuids_) {
292 if (!bounded_uuids.set().empty()) {
293 has_service_uuids = true;
294 break;
295 }
296 }
297
298 if (has_service_uuids) {
299 result += "Service UUIDs: { ";
300 for (const auto& [_, bounded_uuids] : service_uuids_) {
301 for (const auto& uuid : bounded_uuids.set()) {
302 bt_lib_cpp_string::StringAppendf(&result, "%s, ", bt_str(uuid));
303 }
304 }
305 result += "}, ";
306 }
307
308 if (!service_data_.empty()) {
309 result += "Service Data: { ";
310 for (const auto& [uuid, data_buffer] : service_data_) {
311 bt_lib_cpp_string::StringAppendf(
312 &result,
313 "{ UUID:%s, Data: {%s} }, ",
314 bt_str(uuid),
315 data_buffer.ToString(/*as_hex*/ true).c_str());
316 }
317 result += "}, ";
318 }
319
320 bool has_solicitation_uuids = false;
321 for (const auto& [_, bounded_uuids] : solicitation_uuids_) {
322 if (!bounded_uuids.set().empty()) {
323 has_solicitation_uuids = true;
324 break;
325 }
326 }
327
328 if (has_solicitation_uuids) {
329 result += "Solicitation UUIDs: { ";
330 for (const auto& [_, bounded_uuids] : solicitation_uuids_) {
331 for (const auto& uuid : bounded_uuids.set()) {
332 bt_lib_cpp_string::StringAppendf(&result, "%s, ", bt_str(uuid));
333 }
334 }
335 result += "}, ";
336 }
337
338 if (!manufacturer_data_.empty()) {
339 result += "Manufacturer Data: { ";
340 for (const auto& [company_id, data_buffer] : manufacturer_data_) {
341 bt_lib_cpp_string::StringAppendf(
342 &result,
343 "{ Company ID: 0x%04x, Data: {%s} }, ",
344 company_id,
345 data_buffer.ToString(/*as_hex*/ true).c_str());
346 }
347 result += "}, ";
348 }
349
350 if (resolvable_set_identifier_.has_value()) {
351 result += "Resolvable Set Idenfidier: { ";
352 BufferView view(resolvable_set_identifier_->data(),
353 resolvable_set_identifier_->size());
354 result += view.ToString(/*as_hex=*/true);
355 result += "}, ";
356 }
357
358 if (broadcast_name_) {
359 bt_lib_cpp_string::StringAppendf(
360 &result, "Broadcast Name: %s, ", broadcast_name_->c_str());
361 }
362 result += "}";
363 return result;
364 }
365
FromBytes(const ByteBuffer & data)366 AdvertisingData::ParseResult AdvertisingData::FromBytes(
367 const ByteBuffer& data) {
368 if (data.size() == 0) {
369 return fit::error(ParseError::kMissing);
370 }
371 SupplementDataReader reader(data);
372 if (!reader.is_valid()) {
373 return fit::error(ParseError::kInvalidTlvFormat);
374 }
375
376 AdvertisingData out_ad;
377 DataType type;
378 BufferView field;
379 while (reader.GetNextField(&type, &field)) {
380 // While parsing through the advertising data fields, we do not need to
381 // validate that per-field sizes do not overflow a uint8_t because they, by
382 // construction, are obtained from a uint8_t.
383 PW_DCHECK(field.size() <= std::numeric_limits<uint8_t>::max());
384 PW_MODIFY_DIAGNOSTICS_PUSH();
385 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
386 switch (type) {
387 case DataType::kTxPowerLevel: {
388 if (field.size() != kTxPowerLevelSize) {
389 return fit::error(ParseError::kTxPowerLevelMalformed);
390 }
391
392 out_ad.SetTxPower(static_cast<int8_t>(field[0]));
393 break;
394 }
395 case DataType::kShortenedLocalName: {
396 if (field.ToString().size() > kMaxNameLength) {
397 return fit::error(ParseError::kLocalNameTooLong);
398 }
399
400 (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/false);
401 break;
402 }
403 case DataType::kCompleteLocalName: {
404 if (field.ToString().size() > kMaxNameLength) {
405 return fit::error(ParseError::kLocalNameTooLong);
406 }
407
408 (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/true);
409 break;
410 }
411 case DataType::kIncomplete16BitServiceUuids:
412 case DataType::kComplete16BitServiceUuids:
413 case DataType::kIncomplete32BitServiceUuids:
414 case DataType::kComplete32BitServiceUuids:
415 case DataType::kIncomplete128BitServiceUuids:
416 case DataType::kComplete128BitServiceUuids: {
417 // AddServiceUuid fails when the number of N bit UUIDs exceed the
418 // kMaxNBitUuids bounds. These bounds are based on the number of UUIDs
419 // that fit in the wire (byte) representation of an AdvertisingData, so
420 // for valid AdvertisingData packets, the number of N bit service UUIDs
421 // cannot exceed the bounds limits. However, because invalid packets may
422 // provide multiple DataType fields for the same UUID (not allowed by
423 // CSS v9 Part A 1.1.1), this limit may be exceeded, in which case we
424 // reject the packet.
425 if (!ParseUuids(
426 field,
427 SizeForType(type),
428 fit::bind_member<&AdvertisingData::AddServiceUuid>(&out_ad))) {
429 return fit::error(ParseError::kUuidsMalformed);
430 }
431 break;
432 }
433 case DataType::kSolicitationUuid16Bit:
434 case DataType::kSolicitationUuid32Bit:
435 case DataType::kSolicitationUuid128Bit: {
436 if (!ParseUuids(field,
437 SizeForType(type),
438 fit::bind_member<&AdvertisingData::AddSolicitationUuid>(
439 &out_ad))) {
440 return fit::error(ParseError::kUuidsMalformed);
441 }
442 break;
443 }
444 case DataType::kManufacturerSpecificData: {
445 if (field.size() < kManufacturerSpecificDataSizeMin) {
446 return fit::error(ParseError::kManufacturerSpecificDataTooSmall);
447 }
448
449 uint16_t id = static_cast<uint16_t>(pw::bytes::ConvertOrderFrom(
450 cpp20::endian::little,
451 *reinterpret_cast<const uint16_t*>(field.data())));
452 const BufferView manuf_data(field.data() + kManufacturerIdSize,
453 field.size() - kManufacturerIdSize);
454
455 PW_CHECK(out_ad.SetManufacturerData(id, manuf_data));
456 break;
457 }
458 case DataType::kServiceData16Bit:
459 case DataType::kServiceData32Bit:
460 case DataType::kServiceData128Bit: {
461 UUID uuid;
462 size_t uuid_size = SizeForType(type);
463 if (field.size() < uuid_size) {
464 return fit::error(ParseError::kServiceDataTooSmall);
465 }
466 const BufferView uuid_bytes(field.data(), uuid_size);
467 if (!UUID::FromBytes(uuid_bytes, &uuid)) {
468 return fit::error(ParseError::kServiceDataUuidMalformed);
469 }
470 const BufferView service_data(field.data() + uuid_size,
471 field.size() - uuid_size);
472 PW_CHECK(out_ad.SetServiceData(uuid, service_data));
473 break;
474 }
475 case DataType::kAppearance: {
476 // TODO(armansito): Peer should have a function to return the
477 // device appearance, as it can be obtained either from advertising data
478 // or via GATT.
479 if (field.size() != kAppearanceSize) {
480 return fit::error(ParseError::kAppearanceMalformed);
481 }
482
483 out_ad.SetAppearance(pw::bytes::ConvertOrderFrom(cpp20::endian::little,
484 field.To<uint16_t>()));
485 break;
486 }
487 case DataType::kURI: {
488 // Assertion is safe as AddUri only fails when field size > uint8_t,
489 // which is impossible.
490 PW_CHECK(out_ad.AddUri(DecodeUri(field.ToString())));
491 break;
492 }
493 case DataType::kFlags: {
494 // Flags field may be zero or more octets long but we only store the
495 // first octet.
496 if (field.size() > 0) {
497 out_ad.SetFlags(field[0]);
498 } else {
499 out_ad.SetFlags(0);
500 }
501 break;
502 }
503 case DataType::kResolvableSetIdentifier: {
504 if (field.size() != kResolvableSetIdentifierSize) {
505 return fit::error(ParseError::kResolvableSetIdentifierSize);
506 }
507 std::array<uint8_t, kResolvableSetIdentifierSize> ident{
508 field[0], field[1], field[2], field[3], field[4], field[5]};
509 out_ad.SetResolvableSetIdentifier(ident);
510 break;
511 }
512 case DataType::kBroadcastName: {
513 if (field.size() < kMinBroadcastNameBytes) {
514 return fit::error(ParseError::kBroadcastNameTooShort);
515 }
516 if (field.size() > kMaxBroadcastNameBytes) {
517 return fit::error(ParseError::kBroadcastNameTooLong);
518 }
519 std::string name = field.ToString();
520 out_ad.SetBroadcastName(name);
521 break;
522 }
523 default:
524 bt_log(DEBUG,
525 "gap",
526 "ignored advertising field (type %#.2x)",
527 static_cast<unsigned int>(type));
528 break;
529 }
530 PW_MODIFY_DIAGNOSTICS_POP();
531 }
532
533 return fit::ok(std::move(out_ad));
534 }
535
Copy(AdvertisingData * out) const536 void AdvertisingData::Copy(AdvertisingData* out) const {
537 *out = AdvertisingData();
538
539 if (local_name_) {
540 PW_CHECK(out->SetLocalName(*local_name_));
541 }
542
543 if (tx_power_) {
544 out->SetTxPower(*tx_power_);
545 }
546
547 if (appearance_) {
548 out->SetAppearance(*appearance_);
549 }
550
551 out->service_uuids_ = service_uuids_;
552 out->solicitation_uuids_ = solicitation_uuids_;
553 out->resolvable_set_identifier_ = resolvable_set_identifier_;
554 out->broadcast_name_ = broadcast_name_;
555
556 for (const auto& it : manufacturer_data_) {
557 PW_CHECK(out->SetManufacturerData(it.first, it.second.view()));
558 }
559
560 for (const auto& it : service_data_) {
561 PW_CHECK(out->SetServiceData(it.first, it.second.view()));
562 }
563
564 for (const auto& it : uris_) {
565 PW_CHECK(out->AddUri(it), "Copying invalid AD with too-long URI");
566 }
567 }
568
AddServiceUuid(const UUID & uuid)569 [[nodiscard]] bool AdvertisingData::AddServiceUuid(const UUID& uuid) {
570 auto iter = service_uuids_.find(uuid.CompactSize());
571 PW_CHECK(iter != service_uuids_.end());
572 BoundedUuids& uuids = iter->second;
573 return uuids.AddUuid(uuid);
574 }
575
service_uuids() const576 std::unordered_set<UUID> AdvertisingData::service_uuids() const {
577 std::unordered_set<UUID> out;
578 for (auto& [_elemsize, uuids] : service_uuids_) {
579 out.insert(uuids.set().begin(), uuids.set().end());
580 }
581 return out;
582 }
583
SetServiceData(const UUID & uuid,const ByteBuffer & data)584 [[nodiscard]] bool AdvertisingData::SetServiceData(const UUID& uuid,
585 const ByteBuffer& data) {
586 size_t encoded_size = EncodedServiceDataSize(uuid, data.view());
587 if (encoded_size > kMaxEncodedServiceDataLength) {
588 bt_log(WARN,
589 "gap-le",
590 "SetServiceData for UUID %s failed: (UUID+data) size %zu > maximum "
591 "allowed size %du",
592 bt_str(uuid),
593 encoded_size,
594 kMaxEncodedServiceDataLength);
595 return false;
596 }
597 service_data_[uuid] = DynamicByteBuffer(data);
598 return true;
599 }
600
service_data_uuids() const601 std::unordered_set<UUID> AdvertisingData::service_data_uuids() const {
602 std::unordered_set<UUID> uuids;
603 for (const auto& it : service_data_) {
604 uuids.emplace(it.first);
605 }
606 return uuids;
607 }
608
service_data(const UUID & uuid) const609 BufferView AdvertisingData::service_data(const UUID& uuid) const {
610 auto iter = service_data_.find(uuid);
611 if (iter == service_data_.end())
612 return BufferView();
613 return BufferView(iter->second);
614 }
615
AddSolicitationUuid(const UUID & uuid)616 [[nodiscard]] bool AdvertisingData::AddSolicitationUuid(const UUID& uuid) {
617 auto iter = solicitation_uuids_.find(uuid.CompactSize());
618 PW_CHECK(iter != solicitation_uuids_.end());
619 BoundedUuids& uuids = iter->second;
620 return uuids.AddUuid(uuid);
621 }
622
solicitation_uuids() const623 std::unordered_set<UUID> AdvertisingData::solicitation_uuids() const {
624 std::unordered_set<UUID> out;
625 for (auto& [_elemsize, uuids] : solicitation_uuids_) {
626 out.insert(uuids.set().begin(), uuids.set().end());
627 }
628 return out;
629 }
630
SetManufacturerData(const uint16_t company_id,const BufferView & data)631 [[nodiscard]] bool AdvertisingData::SetManufacturerData(
632 const uint16_t company_id, const BufferView& data) {
633 size_t field_size = data.size();
634 if (field_size > kMaxManufacturerDataLength) {
635 bt_log(WARN,
636 "gap-le",
637 "SetManufacturerData for company id %#.4x failed: (UUID+data) size "
638 "%zu > maximum allowed "
639 "size %hhu",
640 company_id,
641 field_size,
642 kMaxManufacturerDataLength);
643 return false;
644 }
645 manufacturer_data_[company_id] = DynamicByteBuffer(data);
646 return true;
647 }
648
manufacturer_data_ids() const649 std::unordered_set<uint16_t> AdvertisingData::manufacturer_data_ids() const {
650 std::unordered_set<uint16_t> manuf_ids;
651 for (const auto& it : manufacturer_data_) {
652 manuf_ids.emplace(it.first);
653 }
654 return manuf_ids;
655 }
656
manufacturer_data(const uint16_t company_id) const657 BufferView AdvertisingData::manufacturer_data(const uint16_t company_id) const {
658 auto iter = manufacturer_data_.find(company_id);
659 if (iter == manufacturer_data_.end())
660 return BufferView();
661 return BufferView(iter->second);
662 }
663
SetTxPower(int8_t dbm)664 void AdvertisingData::SetTxPower(int8_t dbm) { tx_power_ = dbm; }
665
tx_power() const666 std::optional<int8_t> AdvertisingData::tx_power() const { return tx_power_; }
667
SetLocalName(const LocalName & local_name)668 bool AdvertisingData::SetLocalName(const LocalName& local_name) {
669 if (local_name.name.size() > kMaxNameLength) {
670 return false;
671 }
672 if (local_name_.has_value() && local_name_->is_complete &&
673 !local_name.is_complete) {
674 return false;
675 }
676 local_name_ = local_name;
677 return true;
678 }
679
local_name() const680 std::optional<AdvertisingData::LocalName> AdvertisingData::local_name() const {
681 return local_name_;
682 }
683
SetResolvableSetIdentifier(std::array<uint8_t,kResolvableSetIdentifierSize> identifier)684 void AdvertisingData::SetResolvableSetIdentifier(
685 std::array<uint8_t, kResolvableSetIdentifierSize> identifier) {
686 resolvable_set_identifier_ = identifier;
687 }
688
689 const std::optional<std::array<uint8_t, kResolvableSetIdentifierSize>>&
resolvable_set_identifier() const690 AdvertisingData::resolvable_set_identifier() const {
691 return resolvable_set_identifier_;
692 }
693
SetBroadcastName(const std::string & name)694 void AdvertisingData::SetBroadcastName(const std::string& name) {
695 broadcast_name_ = name;
696 }
697
broadcast_name() const698 const std::optional<std::string>& AdvertisingData::broadcast_name() const {
699 return broadcast_name_;
700 }
701
AddUri(const std::string & uri)702 [[nodiscard]] bool AdvertisingData::AddUri(const std::string& uri) {
703 if (EncodeUri(uri).size() > kMaxEncodedUriLength) {
704 bt_log(WARN,
705 "gap-le",
706 "not inserting uri %s as it exceeds the max URI size for AD",
707 uri.c_str());
708 return false;
709 }
710 if (uri.empty()) {
711 bt_log(WARN, "gap-le", "skipping insertion of empty uri to AD");
712 return true;
713 }
714 uris_.insert(uri);
715 return true;
716 }
717
uris() const718 const std::unordered_set<std::string>& AdvertisingData::uris() const {
719 return uris_;
720 }
721
SetAppearance(uint16_t appearance)722 void AdvertisingData::SetAppearance(uint16_t appearance) {
723 appearance_ = appearance;
724 }
725
appearance() const726 std::optional<uint16_t> AdvertisingData::appearance() const {
727 return appearance_;
728 }
729
SetFlags(AdvFlags flags)730 void AdvertisingData::SetFlags(AdvFlags flags) { flags_ = flags; }
731
flags() const732 std::optional<AdvFlags> AdvertisingData::flags() const { return flags_; }
733
CalculateBlockSize(bool include_flags) const734 size_t AdvertisingData::CalculateBlockSize(bool include_flags) const {
735 size_t len = 0;
736 if (include_flags) {
737 len += kTLVFlagsSize;
738 }
739
740 if (tx_power_) {
741 len += kTLVTxPowerLevelSize;
742 }
743
744 if (appearance_) {
745 len += kTLVAppearanceSize;
746 }
747
748 if (local_name_) {
749 len += 2 + local_name_->name.size();
750 }
751
752 for (const auto& manuf_pair : manufacturer_data_) {
753 len += 2 + 2 + manuf_pair.second.size();
754 }
755
756 for (const auto& service_data_pair : service_data_) {
757 len += 2 + service_data_pair.first.CompactSize() +
758 service_data_pair.second.size();
759 }
760
761 for (const auto& uri : uris_) {
762 len += 2 + EncodeUri(uri).size();
763 }
764
765 for (const auto& [uuid_size, bounded_uuids] : service_uuids_) {
766 if (bounded_uuids.set().empty()) {
767 continue;
768 }
769 len += 2; // 1 byte for # of UUIDs and 1 for UUID type
770 len += uuid_size * bounded_uuids.set().size();
771 }
772
773 for (const auto& [uuid_size, bounded_uuids] : solicitation_uuids_) {
774 if (bounded_uuids.set().empty()) {
775 continue;
776 }
777 len += 2; // 1 byte for # of UUIDs and 1 for UUID type
778 len += uuid_size * bounded_uuids.set().size();
779 }
780
781 if (resolvable_set_identifier_.has_value()) {
782 len += kTLVResolvableSetIdentifierSize;
783 }
784
785 if (broadcast_name_) {
786 len += 2 + broadcast_name_->size();
787 }
788
789 return len;
790 }
791
WriteBlock(MutableByteBuffer * buffer,std::optional<AdvFlags> flags) const792 bool AdvertisingData::WriteBlock(MutableByteBuffer* buffer,
793 std::optional<AdvFlags> flags) const {
794 PW_DCHECK(buffer);
795
796 size_t min_buf_size = CalculateBlockSize(flags.has_value());
797 if (buffer->size() < min_buf_size) {
798 return false;
799 }
800
801 size_t pos = 0;
802 if (flags) {
803 (*buffer)[pos++] =
804 kTLVFlagsSize - 1; // size variable includes current field, subtract 1
805 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kFlags);
806 (*buffer)[pos++] = static_cast<uint8_t>(flags.value());
807 }
808
809 if (tx_power_) {
810 (*buffer)[pos++] = kTLVTxPowerLevelSize -
811 1; // size variable includes current field, subtract 1
812 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kTxPowerLevel);
813 (*buffer)[pos++] = static_cast<uint8_t>(tx_power_.value());
814 }
815
816 if (appearance_) {
817 (*buffer)[pos++] = kTLVAppearanceSize -
818 1; // size variable includes current field, subtract 1
819 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kAppearance);
820 pos += BufferWrite(buffer, pos, appearance_.value());
821 }
822
823 if (local_name_) {
824 PW_CHECK(local_name_->name.size() <= kMaxNameLength);
825 (*buffer)[pos++] =
826 static_cast<uint8_t>(local_name_->name.size()) + 1; // 1 for null char
827 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kCompleteLocalName);
828 buffer->Write(reinterpret_cast<const uint8_t*>(local_name_->name.c_str()),
829 local_name_->name.size(),
830 pos);
831 pos += local_name_->name.size();
832 }
833
834 for (const auto& manuf_pair : manufacturer_data_) {
835 size_t data_size = manuf_pair.second.size();
836 PW_CHECK(data_size <= kMaxManufacturerDataLength);
837 (*buffer)[pos++] =
838 1 + 2 +
839 static_cast<uint8_t>(data_size); // 1 for type, 2 for Manuf. Code
840 (*buffer)[pos++] =
841 static_cast<uint8_t>(DataType::kManufacturerSpecificData);
842 pos += BufferWrite(buffer, pos, manuf_pair.first);
843 buffer->Write(manuf_pair.second, pos);
844 pos += data_size;
845 }
846
847 for (const auto& service_data_pair : service_data_) {
848 UUID uuid = service_data_pair.first;
849 size_t encoded_service_data_size =
850 EncodedServiceDataSize(uuid, service_data_pair.second.view());
851 PW_CHECK(encoded_service_data_size <= kMaxEncodedServiceDataLength);
852 (*buffer)[pos++] =
853 1 + static_cast<uint8_t>(encoded_service_data_size); // 1 for type
854 (*buffer)[pos++] =
855 static_cast<uint8_t>(ServiceDataTypeForUuidSize(uuid.CompactSize()));
856 auto target = buffer->mutable_view(pos);
857 pos += service_data_pair.first.ToBytes(&target);
858 buffer->Write(service_data_pair.second, pos);
859 pos += service_data_pair.second.size();
860 }
861
862 for (const auto& uri : uris_) {
863 std::string s = EncodeUri(uri);
864 PW_CHECK(s.size() <= kMaxEncodedUriLength);
865 (*buffer)[pos++] = 1 + static_cast<uint8_t>(s.size()); // 1 for type
866 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kURI);
867 buffer->Write(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), pos);
868 pos += s.size();
869 }
870
871 for (const auto& [uuid_width, bounded_uuids] : service_uuids_) {
872 if (bounded_uuids.set().empty()) {
873 continue;
874 }
875
876 // 1 for type
877 PW_CHECK(1 + uuid_width * bounded_uuids.set().size() <=
878 std::numeric_limits<uint8_t>::max());
879 (*buffer)[pos++] =
880 1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
881 (*buffer)[pos++] = static_cast<uint8_t>(
882 ServiceUuidTypeForUuidSize(uuid_width, /*complete=*/false));
883 for (const auto& uuid : bounded_uuids.set()) {
884 PW_CHECK(uuid.CompactSize() == uuid_width,
885 "UUID: %s - Expected Width: %d",
886 bt_str(uuid),
887 uuid_width);
888 auto target = buffer->mutable_view(pos);
889 pos += uuid.ToBytes(&target);
890 }
891 }
892
893 for (const auto& [uuid_width, bounded_uuids] : solicitation_uuids_) {
894 if (bounded_uuids.set().empty()) {
895 continue;
896 }
897
898 // 1 for type
899 PW_CHECK(1 + uuid_width * bounded_uuids.set().size() <=
900 std::numeric_limits<uint8_t>::max());
901 (*buffer)[pos++] =
902 1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
903 (*buffer)[pos++] =
904 static_cast<uint8_t>(SolicitationUuidTypeForUuidSize(uuid_width));
905 for (const auto& uuid : bounded_uuids.set()) {
906 PW_CHECK(uuid.CompactSize() == uuid_width,
907 "UUID: %s - Expected Width: %d",
908 bt_str(uuid),
909 uuid_width);
910 auto target = buffer->mutable_view(pos);
911 pos += uuid.ToBytes(&target);
912 }
913 }
914
915 if (resolvable_set_identifier_) {
916 (*buffer)[pos++] =
917 1 +
918 static_cast<uint8_t>(resolvable_set_identifier_->size()); // 1 for type
919 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kResolvableSetIdentifier);
920 buffer->Write(resolvable_set_identifier_->data(),
921 resolvable_set_identifier_->size(),
922 pos);
923 pos += resolvable_set_identifier_->size();
924 }
925
926 if (broadcast_name_) {
927 (*buffer)[pos++] =
928 1 + static_cast<uint8_t>(broadcast_name_->size()); // 1 for type
929 (*buffer)[pos++] = static_cast<uint8_t>(DataType::kBroadcastName);
930 buffer->Write(reinterpret_cast<const uint8_t*>(broadcast_name_->c_str()),
931 broadcast_name_->size(),
932 pos);
933 pos += broadcast_name_->size();
934 }
935
936 return true;
937 }
938
operator ==(const AdvertisingData & other) const939 bool AdvertisingData::operator==(const AdvertisingData& other) const {
940 if ((local_name_ != other.local_name_) || (tx_power_ != other.tx_power_) ||
941 (appearance_ != other.appearance_) ||
942 (service_uuids_ != other.service_uuids_) ||
943 (solicitation_uuids_ != other.solicitation_uuids_) ||
944 (uris_ != other.uris_) || (flags_ != other.flags_) ||
945 (resolvable_set_identifier_ != other.resolvable_set_identifier_) ||
946 (broadcast_name_ != other.broadcast_name_)) {
947 return false;
948 }
949
950 if (manufacturer_data_.size() != other.manufacturer_data_.size()) {
951 return false;
952 }
953
954 for (const auto& it : manufacturer_data_) {
955 auto that = other.manufacturer_data_.find(it.first);
956 if (that == other.manufacturer_data_.end()) {
957 return false;
958 }
959 size_t bytes = it.second.size();
960 if (bytes != that->second.size()) {
961 return false;
962 }
963 if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
964 return false;
965 }
966 }
967
968 if (service_data_.size() != other.service_data_.size()) {
969 return false;
970 }
971
972 for (const auto& it : service_data_) {
973 auto that = other.service_data_.find(it.first);
974 if (that == other.service_data_.end()) {
975 return false;
976 }
977 size_t bytes = it.second.size();
978 if (bytes != that->second.size()) {
979 return false;
980 }
981 if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
982 return false;
983 }
984 }
985
986 return true;
987 }
988
operator !=(const AdvertisingData & other) const989 bool AdvertisingData::operator!=(const AdvertisingData& other) const {
990 return !(*this == other);
991 }
992
AddUuid(UUID uuid)993 bool AdvertisingData::BoundedUuids::AddUuid(UUID uuid) {
994 PW_CHECK(set_.size() <= bound_);
995 if (set_.size() < bound_) {
996 if (!set_.insert(uuid).second) {
997 bt_log(INFO,
998 "gap-le",
999 "Skipping addition of duplicate UUID %s to AD",
1000 bt_str(uuid));
1001 }
1002 return true;
1003 }
1004 if (set_.find(uuid) != set_.end()) {
1005 bt_log(INFO,
1006 "gap-le",
1007 "Skipping addition of duplicate UUID %s to AD",
1008 bt_str(uuid));
1009 return true;
1010 }
1011 bt_log(WARN,
1012 "gap-le",
1013 "Failed to add UUID %s to AD - no space left",
1014 bt_str(uuid));
1015 return false;
1016 }
1017 } // namespace bt
1018