• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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