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