Coverage Report

Created: 2025-03-11 06:23

/proc/self/cwd/pw_bluetooth_sapphire/host/common/advertising_data.cc
Line
Count
Source (jump to first uncovered line)
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
35
529
DataType ServiceUuidTypeForUuidSize(UUIDElemSize size, bool complete) {
36
529
  switch (size) {
37
161
    case UUIDElemSize::k16Bit:
38
161
      return complete ? DataType::kComplete16BitServiceUuids
39
161
                      : DataType::kIncomplete16BitServiceUuids;
40
177
    case UUIDElemSize::k32Bit:
41
177
      return complete ? DataType::kComplete32BitServiceUuids
42
177
                      : DataType::kIncomplete32BitServiceUuids;
43
191
    case UUIDElemSize::k128Bit:
44
191
      return complete ? DataType::kComplete128BitServiceUuids
45
191
                      : DataType::kIncomplete128BitServiceUuids;
46
0
    default:
47
0
      PW_CRASH(
48
529
          "called ServiceUuidTypeForUuidSize with unknown UUIDElemSize %du",
49
529
          size);
50
529
  }
51
529
}
52
53
4.97k
DataType ServiceDataTypeForUuidSize(UUIDElemSize size) {
54
4.97k
  switch (size) {
55
2.15k
    case UUIDElemSize::k16Bit:
56
2.15k
      return DataType::kServiceData16Bit;
57
2.15k
    case UUIDElemSize::k32Bit:
58
2.15k
      return DataType::kServiceData32Bit;
59
658
    case UUIDElemSize::k128Bit:
60
658
      return DataType::kServiceData128Bit;
61
0
    default:
62
0
      PW_CRASH(
63
4.97k
          "called ServiceDataTypeForUuidSize with unknown UUIDElemSize %du",
64
4.97k
          size);
65
4.97k
  };
66
0
}
67
68
114
DataType SolicitationUuidTypeForUuidSize(UUIDElemSize size) {
69
114
  switch (size) {
70
71
    case UUIDElemSize::k16Bit:
71
71
      return DataType::kSolicitationUuid16Bit;
72
26
    case UUIDElemSize::k32Bit:
73
26
      return DataType::kSolicitationUuid32Bit;
74
17
    case UUIDElemSize::k128Bit:
75
17
      return DataType::kSolicitationUuid128Bit;
76
0
    default:
77
0
      PW_CRASH(
78
114
          "called SolicitationUuidTypeForUuidSize with unknown UUIDElemSize "
79
114
          "%du",
80
114
          size);
81
114
  };
82
0
}
83
84
41.0k
size_t EncodedServiceDataSize(const UUID& uuid, const BufferView data) {
85
41.0k
  return uuid.CompactSize() + data.size();
86
41.0k
}
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
117
67.8k
std::string EncodeUri(const std::string& uri) {
118
5.38M
  for (uint32_t i = 0; i < kUriSchemesSize; i++) {
119
5.36M
    const char* scheme = kUriSchemes[i];
120
5.36M
    size_t scheme_len = strlen(scheme);
121
5.36M
    if (std::strncmp(uri.c_str(), scheme, scheme_len) == 0) {
122
52.9k
      const pw::Result<pw::utf8::EncodedCodePoint> encoded_scheme =
123
52.9k
          pw::utf8::EncodeCodePoint(i + 2);
124
52.9k
      PW_DCHECK(encoded_scheme.ok());
125
52.9k
      return std::string(encoded_scheme->as_view()) + uri.substr(scheme_len);
126
52.9k
    }
127
5.36M
  }
128
  // First codepoint (U+0001) is for uncompressed schemes.
129
14.8k
  const pw::Result<pw::utf8::EncodedCodePoint> encoded_scheme =
130
14.8k
      pw::utf8::EncodeCodePoint(1u);
131
14.8k
  PW_DCHECK(encoded_scheme.ok());
132
14.8k
  return std::string(encoded_scheme->as_view()) + uri;
133
14.8k
}
134
135
const char kUndefinedScheme = 0x01;
136
137
48.0k
std::string DecodeUri(const std::string& uri) {
138
48.0k
  if (uri[0] == kUndefinedScheme) {
139
5.50k
    return uri.substr(1);
140
5.50k
  }
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
42.5k
  const auto result = pw::utf8::ReadCodePoint(uri);
146
42.5k
  if (!result.ok()) {
147
5.83k
    bt_log(INFO,
148
5.83k
           "gap-le",
149
5.83k
           "Attempted to decode malformed UTF-8 in AdvertisingData URI");
150
5.83k
    return "";
151
5.83k
  }
152
36.7k
  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
36.7k
  if (code_point >= kUriSchemesSize + 2 || code_point < 2) {
159
901
    bt_log(
160
901
        ERROR,
161
901
        "gap-le",
162
901
        "Failed to decode URI - supplied UTF-8 encoding scheme codepoint %u "
163
901
        "must be in the "
164
901
        "range 2-kUriSchemesSize + 1 (2-%zu) to correspond to a URI encoding",
165
901
        code_point,
166
901
        kUriSchemesSize + 1);
167
901
    return "";
168
901
  }
169
35.8k
  return kUriSchemes[code_point - 2] + uri.substr(result->size());
170
36.7k
}
171
172
template <typename T>
173
2.18k
inline size_t BufferWrite(MutableByteBuffer* buffer, size_t pos, const T& var) {
174
2.18k
  buffer->Write((const uint8_t*)(uintptr_t)(&var), sizeof(T), pos);
175
2.18k
  return sizeof(T);
176
2.18k
}
177
178
}  // namespace
179
180
8.72k
AdvertisingData::AdvertisingData(AdvertisingData&& other) noexcept {
181
8.72k
  *this = std::move(other);
182
8.72k
}
183
184
8.72k
AdvertisingData& AdvertisingData::operator=(AdvertisingData&& other) noexcept {
185
  // Reset `other`'s state to that of a fresh, empty AdvertisingData
186
8.72k
  local_name_ = std::exchange(other.local_name_, {});
187
8.72k
  tx_power_ = std::exchange(other.tx_power_, {});
188
8.72k
  appearance_ = std::exchange(other.appearance_, {});
189
8.72k
  service_uuids_ = std::exchange(other.service_uuids_, kEmptyServiceUuidMap);
190
8.72k
  solicitation_uuids_ =
191
8.72k
      std::exchange(other.solicitation_uuids_, kEmptyServiceUuidMap);
192
8.72k
  manufacturer_data_ = std::exchange(other.manufacturer_data_, {});
193
8.72k
  service_data_ = std::exchange(other.service_data_, {});
194
8.72k
  uris_ = std::exchange(other.uris_, {});
195
8.72k
  flags_ = std::exchange(other.flags_, {});
196
8.72k
  resolvable_set_identifier_ =
197
8.72k
      std::exchange(other.resolvable_set_identifier_, {});
198
8.72k
  broadcast_name_ = std::exchange(other.broadcast_name_, {});
199
8.72k
  return *this;
200
8.72k
}
201
202
34
std::string AdvertisingData::ParseErrorToString(ParseError e) {
203
34
  switch (e) {
204
2
    case ParseError::kInvalidTlvFormat:
205
2
      return "provided bytes are not a valid type-length-value container";
206
2
    case ParseError::kTxPowerLevelMalformed:
207
2
      return "malformed tx power level";
208
0
    case ParseError::kLocalNameTooLong:
209
0
      return "local name exceeds max length (248)";
210
26
    case ParseError::kUuidsMalformed:
211
26
      return "malformed UUIDs list";
212
0
    case ParseError::kManufacturerSpecificDataTooSmall:
213
0
      return "manufacturer specific data too small";
214
0
    case ParseError::kServiceDataTooSmall:
215
0
      return "service data too small to fit UUIDs";
216
0
    case ParseError::kServiceDataUuidMalformed:
217
0
      return "UUIDs associated with service data are malformed";
218
2
    case ParseError::kAppearanceMalformed:
219
2
      return "malformed appearance field";
220
0
    case ParseError::kMissing:
221
0
      return "data missing";
222
0
    case ParseError::kResolvableSetIdentifierSize:
223
0
      return "resolvable set identifier is wrong size";
224
2
    case ParseError::kBroadcastNameTooShort:
225
2
      return "broadcast name is too short";
226
0
    case ParseError::kBroadcastNameTooLong:
227
0
      return "broadcast name is too long";
228
34
  }
229
34
}
230
231
0
std::string AdvFlagsToString(const std::optional<AdvFlags>& flags_opt) {
232
0
  std::string result = "Flags: {";
233
234
0
  if (!flags_opt.has_value()) {
235
0
    return result += "} ";
236
0
  }
237
238
0
  const AdvFlags& flags = flags_opt.value();
239
240
0
  if (flags & kLELimitedDiscoverableMode) {
241
0
    result += " LE Limited Discoverable Mode,";
242
0
  }
243
0
  if (flags & kLEGeneralDiscoverableMode) {
244
0
    result += " LE General Discoverable Mode,";
245
0
  }
246
0
  if (flags & kBREDRNotSupported) {
247
0
    result += " BR/EDR Not Supported,";
248
0
  }
249
0
  if (flags & kSimultaneousLEAndBREDRController) {
250
0
    result += " Simultaneous LE And BR/EDR Controller,";
251
0
  }
252
0
  if (flags & kSimultaneousLEAndBREDRHost) {
253
0
    result += " Simultaneous LE And BR/EDR Host,";
254
0
  }
255
0
  return result += " }, ";
256
0
}
257
258
0
std::string AdvertisingData::ToString() const {
259
0
  std::string result = "Advertising Data { ";
260
261
0
  if (local_name_) {
262
0
    bt_lib_cpp_string::StringAppendf(
263
0
        &result,
264
0
        "%s Name: %s, ",
265
0
        (local_name_->is_complete ? "Complete" : "Short"),
266
0
        local_name_->name.c_str());
267
0
  }
268
269
0
  if (tx_power_) {
270
0
    bt_lib_cpp_string::StringAppendf(&result, "TX Power: %hhd, ", *tx_power_);
271
0
  }
272
273
0
  if (appearance_) {
274
0
    bt_lib_cpp_string::StringAppendf(
275
0
        &result, "Appearance: 0x%04x, ", *appearance_);
276
0
  }
277
278
0
  if (!uris_.empty()) {
279
0
    result += "URIs: { ";
280
0
    for (const auto& uri : uris_) {
281
0
      bt_lib_cpp_string::StringAppendf(&result, "%s, ", uri.c_str());
282
0
    }
283
0
    result += "}, ";
284
0
  }
285
286
0
  if (flags_.has_value()) {
287
0
    result += AdvFlagsToString(flags_);
288
0
  }
289
290
0
  bool has_service_uuids = false;
291
0
  for (const auto& [_, bounded_uuids] : service_uuids_) {
292
0
    if (!bounded_uuids.set().empty()) {
293
0
      has_service_uuids = true;
294
0
      break;
295
0
    }
296
0
  }
297
298
0
  if (has_service_uuids) {
299
0
    result += "Service UUIDs: { ";
300
0
    for (const auto& [_, bounded_uuids] : service_uuids_) {
301
0
      for (const auto& uuid : bounded_uuids.set()) {
302
0
        bt_lib_cpp_string::StringAppendf(&result, "%s, ", bt_str(uuid));
303
0
      }
304
0
    }
305
0
    result += "}, ";
306
0
  }
307
308
0
  if (!service_data_.empty()) {
309
0
    result += "Service Data: { ";
310
0
    for (const auto& [uuid, data_buffer] : service_data_) {
311
0
      bt_lib_cpp_string::StringAppendf(
312
0
          &result,
313
0
          "{ UUID:%s, Data: {%s} }, ",
314
0
          bt_str(uuid),
315
0
          data_buffer.ToString(/*as_hex*/ true).c_str());
316
0
    }
317
0
    result += "}, ";
318
0
  }
319
320
0
  bool has_solicitation_uuids = false;
321
0
  for (const auto& [_, bounded_uuids] : solicitation_uuids_) {
322
0
    if (!bounded_uuids.set().empty()) {
323
0
      has_solicitation_uuids = true;
324
0
      break;
325
0
    }
326
0
  }
327
328
0
  if (has_solicitation_uuids) {
329
0
    result += "Solicitation UUIDs: { ";
330
0
    for (const auto& [_, bounded_uuids] : solicitation_uuids_) {
331
0
      for (const auto& uuid : bounded_uuids.set()) {
332
0
        bt_lib_cpp_string::StringAppendf(&result, "%s, ", bt_str(uuid));
333
0
      }
334
0
    }
335
0
    result += "}, ";
336
0
  }
337
338
0
  if (!manufacturer_data_.empty()) {
339
0
    result += "Manufacturer Data: { ";
340
0
    for (const auto& [company_id, data_buffer] : manufacturer_data_) {
341
0
      bt_lib_cpp_string::StringAppendf(
342
0
          &result,
343
0
          "{ Company ID: 0x%04x, Data: {%s} }, ",
344
0
          company_id,
345
0
          data_buffer.ToString(/*as_hex*/ true).c_str());
346
0
    }
347
0
    result += "}, ";
348
0
  }
349
350
0
  if (resolvable_set_identifier_.has_value()) {
351
0
    result += "Resolvable Set Idenfidier: { ";
352
0
    BufferView view(resolvable_set_identifier_->data(),
353
0
                    resolvable_set_identifier_->size());
354
0
    result += view.ToString(/*as_hex=*/true);
355
0
    result += "}, ";
356
0
  }
357
358
0
  if (broadcast_name_) {
359
0
    bt_lib_cpp_string::StringAppendf(
360
0
        &result, "Broadcast Name: %s, ", broadcast_name_->c_str());
361
0
  }
362
0
  result += "}";
363
0
  return result;
364
0
}
365
366
AdvertisingData::ParseResult AdvertisingData::FromBytes(
367
4.48k
    const ByteBuffer& data) {
368
4.48k
  if (data.size() == 0) {
369
4
    return fit::error(ParseError::kMissing);
370
4
  }
371
4.48k
  SupplementDataReader reader(data);
372
4.48k
  if (!reader.is_valid()) {
373
25
    return fit::error(ParseError::kInvalidTlvFormat);
374
25
  }
375
376
4.45k
  AdvertisingData out_ad;
377
4.45k
  DataType type;
378
4.45k
  BufferView field;
379
164k
  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
159k
    PW_DCHECK(field.size() <= std::numeric_limits<uint8_t>::max());
384
159k
    PW_MODIFY_DIAGNOSTICS_PUSH();
385
159k
    PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
386
159k
    switch (type) {
387
221
      case DataType::kTxPowerLevel: {
388
221
        if (field.size() != kTxPowerLevelSize) {
389
15
          return fit::error(ParseError::kTxPowerLevelMalformed);
390
15
        }
391
392
206
        out_ad.SetTxPower(static_cast<int8_t>(field[0]));
393
206
        break;
394
221
      }
395
2.24k
      case DataType::kShortenedLocalName: {
396
2.24k
        if (field.ToString().size() > kMaxNameLength) {
397
2
          return fit::error(ParseError::kLocalNameTooLong);
398
2
        }
399
400
2.24k
        (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/false);
401
2.24k
        break;
402
2.24k
      }
403
18.4k
      case DataType::kCompleteLocalName: {
404
18.4k
        if (field.ToString().size() > kMaxNameLength) {
405
1
          return fit::error(ParseError::kLocalNameTooLong);
406
1
        }
407
408
18.4k
        (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/true);
409
18.4k
        break;
410
18.4k
      }
411
886
      case DataType::kIncomplete16BitServiceUuids:
412
1.99k
      case DataType::kComplete16BitServiceUuids:
413
3.55k
      case DataType::kIncomplete32BitServiceUuids:
414
11.2k
      case DataType::kComplete32BitServiceUuids:
415
12.9k
      case DataType::kIncomplete128BitServiceUuids:
416
13.9k
      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
13.9k
        if (!ParseUuids(
426
13.9k
                field,
427
13.9k
                SizeForType(type),
428
13.9k
                fit::bind_member<&AdvertisingData::AddServiceUuid>(&out_ad))) {
429
239
          return fit::error(ParseError::kUuidsMalformed);
430
239
        }
431
13.7k
        break;
432
13.9k
      }
433
13.7k
      case DataType::kSolicitationUuid16Bit:
434
655
      case DataType::kSolicitationUuid32Bit:
435
944
      case DataType::kSolicitationUuid128Bit: {
436
944
        if (!ParseUuids(field,
437
944
                        SizeForType(type),
438
944
                        fit::bind_member<&AdvertisingData::AddSolicitationUuid>(
439
944
                            &out_ad))) {
440
12
          return fit::error(ParseError::kUuidsMalformed);
441
12
        }
442
932
        break;
443
944
      }
444
12.0k
      case DataType::kManufacturerSpecificData: {
445
12.0k
        if (field.size() < kManufacturerSpecificDataSizeMin) {
446
12
          return fit::error(ParseError::kManufacturerSpecificDataTooSmall);
447
12
        }
448
449
12.0k
        uint16_t id = static_cast<uint16_t>(pw::bytes::ConvertOrderFrom(
450
12.0k
            cpp20::endian::little,
451
12.0k
            *reinterpret_cast<const uint16_t*>(field.data())));
452
12.0k
        const BufferView manuf_data(field.data() + kManufacturerIdSize,
453
12.0k
                                    field.size() - kManufacturerIdSize);
454
455
12.0k
        PW_CHECK(out_ad.SetManufacturerData(id, manuf_data));
456
12.0k
        break;
457
12.0k
      }
458
12.0k
      case DataType::kServiceData16Bit:
459
9.39k
      case DataType::kServiceData32Bit:
460
36.1k
      case DataType::kServiceData128Bit: {
461
36.1k
        UUID uuid;
462
36.1k
        size_t uuid_size = SizeForType(type);
463
36.1k
        if (field.size() < uuid_size) {
464
28
          return fit::error(ParseError::kServiceDataTooSmall);
465
28
        }
466
36.0k
        const BufferView uuid_bytes(field.data(), uuid_size);
467
36.0k
        if (!UUID::FromBytes(uuid_bytes, &uuid)) {
468
0
          return fit::error(ParseError::kServiceDataUuidMalformed);
469
0
        }
470
36.0k
        const BufferView service_data(field.data() + uuid_size,
471
36.0k
                                      field.size() - uuid_size);
472
36.0k
        PW_CHECK(out_ad.SetServiceData(uuid, service_data));
473
36.0k
        break;
474
36.0k
      }
475
36.0k
      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
593
        if (field.size() != kAppearanceSize) {
480
16
          return fit::error(ParseError::kAppearanceMalformed);
481
16
        }
482
483
577
        out_ad.SetAppearance(pw::bytes::ConvertOrderFrom(cpp20::endian::little,
484
577
                                                         field.To<uint16_t>()));
485
577
        break;
486
593
      }
487
48.0k
      case DataType::kURI: {
488
        // Assertion is safe as AddUri only fails when field size > uint8_t,
489
        // which is impossible.
490
48.0k
        PW_CHECK(out_ad.AddUri(DecodeUri(field.ToString())));
491
48.0k
        break;
492
48.0k
      }
493
48.0k
      case DataType::kFlags: {
494
        // Flags field may be zero or more octets long but we only store the
495
        // first octet.
496
7.92k
        if (field.size() > 0) {
497
4.48k
          out_ad.SetFlags(field[0]);
498
4.48k
        } else {
499
3.43k
          out_ad.SetFlags(0);
500
3.43k
        }
501
7.92k
        break;
502
48.0k
      }
503
413
      case DataType::kResolvableSetIdentifier: {
504
413
        if (field.size() != kResolvableSetIdentifierSize) {
505
13
          return fit::error(ParseError::kResolvableSetIdentifierSize);
506
13
        }
507
400
        std::array<uint8_t, kResolvableSetIdentifierSize> ident{
508
400
            field[0], field[1], field[2], field[3], field[4], field[5]};
509
400
        out_ad.SetResolvableSetIdentifier(ident);
510
400
        break;
511
413
      }
512
821
      case DataType::kBroadcastName: {
513
821
        if (field.size() < kMinBroadcastNameBytes) {
514
10
          return fit::error(ParseError::kBroadcastNameTooShort);
515
10
        }
516
811
        if (field.size() > kMaxBroadcastNameBytes) {
517
2
          return fit::error(ParseError::kBroadcastNameTooLong);
518
2
        }
519
809
        std::string name = field.ToString();
520
809
        out_ad.SetBroadcastName(name);
521
809
        break;
522
811
      }
523
18.1k
      default:
524
18.1k
        bt_log(DEBUG,
525
18.1k
               "gap",
526
18.1k
               "ignored advertising field (type %#.2x)",
527
18.1k
               static_cast<unsigned int>(type));
528
18.1k
        break;
529
159k
    }
530
159k
    PW_MODIFY_DIAGNOSTICS_POP();
531
159k
  }
532
533
4.10k
  return fit::ok(std::move(out_ad));
534
4.45k
}
535
536
0
void AdvertisingData::Copy(AdvertisingData* out) const {
537
0
  *out = AdvertisingData();
538
539
0
  if (local_name_) {
540
0
    PW_CHECK(out->SetLocalName(*local_name_));
541
0
  }
542
543
0
  if (tx_power_) {
544
0
    out->SetTxPower(*tx_power_);
545
0
  }
546
547
0
  if (appearance_) {
548
0
    out->SetAppearance(*appearance_);
549
0
  }
550
551
0
  out->service_uuids_ = service_uuids_;
552
0
  out->solicitation_uuids_ = solicitation_uuids_;
553
0
  out->resolvable_set_identifier_ = resolvable_set_identifier_;
554
0
  out->broadcast_name_ = broadcast_name_;
555
556
0
  for (const auto& it : manufacturer_data_) {
557
0
    PW_CHECK(out->SetManufacturerData(it.first, it.second.view()));
558
0
  }
559
560
0
  for (const auto& it : service_data_) {
561
0
    PW_CHECK(out->SetServiceData(it.first, it.second.view()));
562
0
  }
563
564
0
  for (const auto& it : uris_) {
565
0
    PW_CHECK(out->AddUri(it), "Copying invalid AD with too-long URI");
566
0
  }
567
0
}
568
569
27.4k
[[nodiscard]] bool AdvertisingData::AddServiceUuid(const UUID& uuid) {
570
27.4k
  auto iter = service_uuids_.find(uuid.CompactSize());
571
27.4k
  PW_CHECK(iter != service_uuids_.end());
572
27.4k
  BoundedUuids& uuids = iter->second;
573
27.4k
  return uuids.AddUuid(uuid);
574
27.4k
}
575
576
0
std::unordered_set<UUID> AdvertisingData::service_uuids() const {
577
0
  std::unordered_set<UUID> out;
578
0
  for (auto& [_elemsize, uuids] : service_uuids_) {
579
0
    out.insert(uuids.set().begin(), uuids.set().end());
580
0
  }
581
0
  return out;
582
0
}
583
584
[[nodiscard]] bool AdvertisingData::SetServiceData(const UUID& uuid,
585
36.0k
                                                   const ByteBuffer& data) {
586
36.0k
  size_t encoded_size = EncodedServiceDataSize(uuid, data.view());
587
36.0k
  if (encoded_size > kMaxEncodedServiceDataLength) {
588
0
    bt_log(WARN,
589
0
           "gap-le",
590
0
           "SetServiceData for UUID %s failed: (UUID+data) size %zu > maximum "
591
0
           "allowed size %du",
592
0
           bt_str(uuid),
593
0
           encoded_size,
594
0
           kMaxEncodedServiceDataLength);
595
0
    return false;
596
0
  }
597
36.0k
  service_data_[uuid] = DynamicByteBuffer(data);
598
36.0k
  return true;
599
36.0k
}
600
601
0
std::unordered_set<UUID> AdvertisingData::service_data_uuids() const {
602
0
  std::unordered_set<UUID> uuids;
603
0
  for (const auto& it : service_data_) {
604
0
    uuids.emplace(it.first);
605
0
  }
606
0
  return uuids;
607
0
}
608
609
0
BufferView AdvertisingData::service_data(const UUID& uuid) const {
610
0
  auto iter = service_data_.find(uuid);
611
0
  if (iter == service_data_.end())
612
0
    return BufferView();
613
0
  return BufferView(iter->second);
614
0
}
615
616
2.93k
[[nodiscard]] bool AdvertisingData::AddSolicitationUuid(const UUID& uuid) {
617
2.93k
  auto iter = solicitation_uuids_.find(uuid.CompactSize());
618
2.93k
  PW_CHECK(iter != solicitation_uuids_.end());
619
2.93k
  BoundedUuids& uuids = iter->second;
620
2.93k
  return uuids.AddUuid(uuid);
621
2.93k
}
622
623
0
std::unordered_set<UUID> AdvertisingData::solicitation_uuids() const {
624
0
  std::unordered_set<UUID> out;
625
0
  for (auto& [_elemsize, uuids] : solicitation_uuids_) {
626
0
    out.insert(uuids.set().begin(), uuids.set().end());
627
0
  }
628
0
  return out;
629
0
}
630
631
[[nodiscard]] bool AdvertisingData::SetManufacturerData(
632
12.0k
    const uint16_t company_id, const BufferView& data) {
633
12.0k
  size_t field_size = data.size();
634
12.0k
  if (field_size > kMaxManufacturerDataLength) {
635
0
    bt_log(WARN,
636
0
           "gap-le",
637
0
           "SetManufacturerData for company id %#.4x failed: (UUID+data) size "
638
0
           "%zu > maximum allowed "
639
0
           "size %hhu",
640
0
           company_id,
641
0
           field_size,
642
0
           kMaxManufacturerDataLength);
643
0
    return false;
644
0
  }
645
12.0k
  manufacturer_data_[company_id] = DynamicByteBuffer(data);
646
12.0k
  return true;
647
12.0k
}
648
649
0
std::unordered_set<uint16_t> AdvertisingData::manufacturer_data_ids() const {
650
0
  std::unordered_set<uint16_t> manuf_ids;
651
0
  for (const auto& it : manufacturer_data_) {
652
0
    manuf_ids.emplace(it.first);
653
0
  }
654
0
  return manuf_ids;
655
0
}
656
657
0
BufferView AdvertisingData::manufacturer_data(const uint16_t company_id) const {
658
0
  auto iter = manufacturer_data_.find(company_id);
659
0
  if (iter == manufacturer_data_.end())
660
0
    return BufferView();
661
0
  return BufferView(iter->second);
662
0
}
663
664
206
void AdvertisingData::SetTxPower(int8_t dbm) { tx_power_ = dbm; }
665
666
0
std::optional<int8_t> AdvertisingData::tx_power() const { return tx_power_; }
667
668
20.7k
bool AdvertisingData::SetLocalName(const LocalName& local_name) {
669
20.7k
  if (local_name.name.size() > kMaxNameLength) {
670
0
    return false;
671
0
  }
672
20.7k
  if (local_name_.has_value() && local_name_->is_complete &&
673
20.7k
      !local_name.is_complete) {
674
1.44k
    return false;
675
1.44k
  }
676
19.2k
  local_name_ = local_name;
677
19.2k
  return true;
678
20.7k
}
679
680
516
std::optional<AdvertisingData::LocalName> AdvertisingData::local_name() const {
681
516
  return local_name_;
682
516
}
683
684
void AdvertisingData::SetResolvableSetIdentifier(
685
400
    std::array<uint8_t, kResolvableSetIdentifierSize> identifier) {
686
400
  resolvable_set_identifier_ = identifier;
687
400
}
688
689
const std::optional<std::array<uint8_t, kResolvableSetIdentifierSize>>&
690
0
AdvertisingData::resolvable_set_identifier() const {
691
0
  return resolvable_set_identifier_;
692
0
}
693
694
809
void AdvertisingData::SetBroadcastName(const std::string& name) {
695
809
  broadcast_name_ = name;
696
809
}
697
698
0
const std::optional<std::string>& AdvertisingData::broadcast_name() const {
699
0
  return broadcast_name_;
700
0
}
701
702
48.0k
[[nodiscard]] bool AdvertisingData::AddUri(const std::string& uri) {
703
48.0k
  if (EncodeUri(uri).size() > kMaxEncodedUriLength) {
704
0
    bt_log(WARN,
705
0
           "gap-le",
706
0
           "not inserting uri %s as it exceeds the max URI size for AD",
707
0
           uri.c_str());
708
0
    return false;
709
0
  }
710
48.0k
  if (uri.empty()) {
711
7.27k
    bt_log(WARN, "gap-le", "skipping insertion of empty uri to AD");
712
7.27k
    return true;
713
7.27k
  }
714
40.7k
  uris_.insert(uri);
715
40.7k
  return true;
716
48.0k
}
717
718
0
const std::unordered_set<std::string>& AdvertisingData::uris() const {
719
0
  return uris_;
720
0
}
721
722
577
void AdvertisingData::SetAppearance(uint16_t appearance) {
723
577
  appearance_ = appearance;
724
577
}
725
726
0
std::optional<uint16_t> AdvertisingData::appearance() const {
727
0
  return appearance_;
728
0
}
729
730
7.92k
void AdvertisingData::SetFlags(AdvFlags flags) { flags_ = flags; }
731
732
0
std::optional<AdvFlags> AdvertisingData::flags() const { return flags_; }
733
734
3.59k
size_t AdvertisingData::CalculateBlockSize(bool include_flags) const {
735
3.59k
  size_t len = 0;
736
3.59k
  if (include_flags) {
737
1.50k
    len += kTLVFlagsSize;
738
1.50k
  }
739
740
3.59k
  if (tx_power_) {
741
13
    len += kTLVTxPowerLevelSize;
742
13
  }
743
744
3.59k
  if (appearance_) {
745
9
    len += kTLVAppearanceSize;
746
9
  }
747
748
3.59k
  if (local_name_) {
749
159
    len += 2 + local_name_->name.size();
750
159
  }
751
752
3.59k
  for (const auto& manuf_pair : manufacturer_data_) {
753
2.95k
    len += 2 + 2 + manuf_pair.second.size();
754
2.95k
  }
755
756
9.70k
  for (const auto& service_data_pair : service_data_) {
757
9.70k
    len += 2 + service_data_pair.first.CompactSize() +
758
9.70k
           service_data_pair.second.size();
759
9.70k
  }
760
761
15.2k
  for (const auto& uri : uris_) {
762
15.2k
    len += 2 + EncodeUri(uri).size();
763
15.2k
  }
764
765
10.7k
  for (const auto& [uuid_size, bounded_uuids] : service_uuids_) {
766
10.7k
    if (bounded_uuids.set().empty()) {
767
9.90k
      continue;
768
9.90k
    }
769
870
    len += 2;  // 1 byte for # of UUIDs and 1 for UUID type
770
870
    len += uuid_size * bounded_uuids.set().size();
771
870
  }
772
773
10.7k
  for (const auto& [uuid_size, bounded_uuids] : solicitation_uuids_) {
774
10.7k
    if (bounded_uuids.set().empty()) {
775
10.6k
      continue;
776
10.6k
    }
777
158
    len += 2;  // 1 byte for # of UUIDs and 1 for UUID type
778
158
    len += uuid_size * bounded_uuids.set().size();
779
158
  }
780
781
3.59k
  if (resolvable_set_identifier_.has_value()) {
782
13
    len += kTLVResolvableSetIdentifierSize;
783
13
  }
784
785
3.59k
  if (broadcast_name_) {
786
56
    len += 2 + broadcast_name_->size();
787
56
  }
788
789
3.59k
  return len;
790
3.59k
}
791
792
bool AdvertisingData::WriteBlock(MutableByteBuffer* buffer,
793
3.59k
                                 std::optional<AdvFlags> flags) const {
794
3.59k
  PW_DCHECK(buffer);
795
796
3.59k
  size_t min_buf_size = CalculateBlockSize(flags.has_value());
797
3.59k
  if (buffer->size() < min_buf_size) {
798
770
    return false;
799
770
  }
800
801
2.82k
  size_t pos = 0;
802
2.82k
  if (flags) {
803
1.26k
    (*buffer)[pos++] =
804
1.26k
        kTLVFlagsSize - 1;  // size variable includes current field, subtract 1
805
1.26k
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kFlags);
806
1.26k
    (*buffer)[pos++] = static_cast<uint8_t>(flags.value());
807
1.26k
  }
808
809
2.82k
  if (tx_power_) {
810
11
    (*buffer)[pos++] = kTLVTxPowerLevelSize -
811
11
                       1;  // size variable includes current field, subtract 1
812
11
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kTxPowerLevel);
813
11
    (*buffer)[pos++] = static_cast<uint8_t>(tx_power_.value());
814
11
  }
815
816
2.82k
  if (appearance_) {
817
8
    (*buffer)[pos++] = kTLVAppearanceSize -
818
8
                       1;  // size variable includes current field, subtract 1
819
8
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kAppearance);
820
8
    pos += BufferWrite(buffer, pos, appearance_.value());
821
8
  }
822
823
2.82k
  if (local_name_) {
824
118
    PW_CHECK(local_name_->name.size() <= kMaxNameLength);
825
118
    (*buffer)[pos++] =
826
118
        static_cast<uint8_t>(local_name_->name.size()) + 1;  // 1 for null char
827
118
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kCompleteLocalName);
828
118
    buffer->Write(reinterpret_cast<const uint8_t*>(local_name_->name.c_str()),
829
118
                  local_name_->name.size(),
830
118
                  pos);
831
118
    pos += local_name_->name.size();
832
118
  }
833
834
2.82k
  for (const auto& manuf_pair : manufacturer_data_) {
835
2.17k
    size_t data_size = manuf_pair.second.size();
836
2.17k
    PW_CHECK(data_size <= kMaxManufacturerDataLength);
837
2.17k
    (*buffer)[pos++] =
838
2.17k
        1 + 2 +
839
2.17k
        static_cast<uint8_t>(data_size);  // 1 for type, 2 for Manuf. Code
840
2.17k
    (*buffer)[pos++] =
841
2.17k
        static_cast<uint8_t>(DataType::kManufacturerSpecificData);
842
2.17k
    pos += BufferWrite(buffer, pos, manuf_pair.first);
843
2.17k
    buffer->Write(manuf_pair.second, pos);
844
2.17k
    pos += data_size;
845
2.17k
  }
846
847
4.97k
  for (const auto& service_data_pair : service_data_) {
848
4.97k
    UUID uuid = service_data_pair.first;
849
4.97k
    size_t encoded_service_data_size =
850
4.97k
        EncodedServiceDataSize(uuid, service_data_pair.second.view());
851
4.97k
    PW_CHECK(encoded_service_data_size <= kMaxEncodedServiceDataLength);
852
4.97k
    (*buffer)[pos++] =
853
4.97k
        1 + static_cast<uint8_t>(encoded_service_data_size);  // 1 for type
854
4.97k
    (*buffer)[pos++] =
855
4.97k
        static_cast<uint8_t>(ServiceDataTypeForUuidSize(uuid.CompactSize()));
856
4.97k
    auto target = buffer->mutable_view(pos);
857
4.97k
    pos += service_data_pair.first.ToBytes(&target);
858
4.97k
    buffer->Write(service_data_pair.second, pos);
859
4.97k
    pos += service_data_pair.second.size();
860
4.97k
  }
861
862
4.47k
  for (const auto& uri : uris_) {
863
4.47k
    std::string s = EncodeUri(uri);
864
4.47k
    PW_CHECK(s.size() <= kMaxEncodedUriLength);
865
4.47k
    (*buffer)[pos++] = 1 + static_cast<uint8_t>(s.size());  // 1 for type
866
4.47k
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kURI);
867
4.47k
    buffer->Write(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), pos);
868
4.47k
    pos += s.size();
869
4.47k
  }
870
871
8.46k
  for (const auto& [uuid_width, bounded_uuids] : service_uuids_) {
872
8.46k
    if (bounded_uuids.set().empty()) {
873
7.93k
      continue;
874
7.93k
    }
875
876
    // 1 for type
877
529
    PW_CHECK(1 + uuid_width * bounded_uuids.set().size() <=
878
529
             std::numeric_limits<uint8_t>::max());
879
529
    (*buffer)[pos++] =
880
529
        1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
881
529
    (*buffer)[pos++] = static_cast<uint8_t>(
882
529
        ServiceUuidTypeForUuidSize(uuid_width, /*complete=*/false));
883
4.32k
    for (const auto& uuid : bounded_uuids.set()) {
884
4.32k
      PW_CHECK(uuid.CompactSize() == uuid_width,
885
4.32k
               "UUID: %s - Expected Width: %d",
886
4.32k
               bt_str(uuid),
887
4.32k
               uuid_width);
888
4.32k
      auto target = buffer->mutable_view(pos);
889
4.32k
      pos += uuid.ToBytes(&target);
890
4.32k
    }
891
529
  }
892
893
8.46k
  for (const auto& [uuid_width, bounded_uuids] : solicitation_uuids_) {
894
8.46k
    if (bounded_uuids.set().empty()) {
895
8.34k
      continue;
896
8.34k
    }
897
898
    // 1 for type
899
114
    PW_CHECK(1 + uuid_width * bounded_uuids.set().size() <=
900
114
             std::numeric_limits<uint8_t>::max());
901
114
    (*buffer)[pos++] =
902
114
        1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
903
114
    (*buffer)[pos++] =
904
114
        static_cast<uint8_t>(SolicitationUuidTypeForUuidSize(uuid_width));
905
1.27k
    for (const auto& uuid : bounded_uuids.set()) {
906
1.27k
      PW_CHECK(uuid.CompactSize() == uuid_width,
907
1.27k
               "UUID: %s - Expected Width: %d",
908
1.27k
               bt_str(uuid),
909
1.27k
               uuid_width);
910
1.27k
      auto target = buffer->mutable_view(pos);
911
1.27k
      pos += uuid.ToBytes(&target);
912
1.27k
    }
913
114
  }
914
915
2.82k
  if (resolvable_set_identifier_) {
916
11
    (*buffer)[pos++] =
917
11
        1 +
918
11
        static_cast<uint8_t>(resolvable_set_identifier_->size());  // 1 for type
919
11
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kResolvableSetIdentifier);
920
11
    buffer->Write(resolvable_set_identifier_->data(),
921
11
                  resolvable_set_identifier_->size(),
922
11
                  pos);
923
11
    pos += resolvable_set_identifier_->size();
924
11
  }
925
926
2.82k
  if (broadcast_name_) {
927
36
    (*buffer)[pos++] =
928
36
        1 + static_cast<uint8_t>(broadcast_name_->size());  // 1 for type
929
36
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kBroadcastName);
930
36
    buffer->Write(reinterpret_cast<const uint8_t*>(broadcast_name_->c_str()),
931
36
                  broadcast_name_->size(),
932
36
                  pos);
933
36
    pos += broadcast_name_->size();
934
36
  }
935
936
2.82k
  return true;
937
2.82k
}
938
939
0
bool AdvertisingData::operator==(const AdvertisingData& other) const {
940
0
  if ((local_name_ != other.local_name_) || (tx_power_ != other.tx_power_) ||
941
0
      (appearance_ != other.appearance_) ||
942
0
      (service_uuids_ != other.service_uuids_) ||
943
0
      (solicitation_uuids_ != other.solicitation_uuids_) ||
944
0
      (uris_ != other.uris_) || (flags_ != other.flags_) ||
945
0
      (resolvable_set_identifier_ != other.resolvable_set_identifier_) ||
946
0
      (broadcast_name_ != other.broadcast_name_)) {
947
0
    return false;
948
0
  }
949
950
0
  if (manufacturer_data_.size() != other.manufacturer_data_.size()) {
951
0
    return false;
952
0
  }
953
954
0
  for (const auto& it : manufacturer_data_) {
955
0
    auto that = other.manufacturer_data_.find(it.first);
956
0
    if (that == other.manufacturer_data_.end()) {
957
0
      return false;
958
0
    }
959
0
    size_t bytes = it.second.size();
960
0
    if (bytes != that->second.size()) {
961
0
      return false;
962
0
    }
963
0
    if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
964
0
      return false;
965
0
    }
966
0
  }
967
968
0
  if (service_data_.size() != other.service_data_.size()) {
969
0
    return false;
970
0
  }
971
972
0
  for (const auto& it : service_data_) {
973
0
    auto that = other.service_data_.find(it.first);
974
0
    if (that == other.service_data_.end()) {
975
0
      return false;
976
0
    }
977
0
    size_t bytes = it.second.size();
978
0
    if (bytes != that->second.size()) {
979
0
      return false;
980
0
    }
981
0
    if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
982
0
      return false;
983
0
    }
984
0
  }
985
986
0
  return true;
987
0
}
988
989
0
bool AdvertisingData::operator!=(const AdvertisingData& other) const {
990
0
  return !(*this == other);
991
0
}
992
993
30.3k
bool AdvertisingData::BoundedUuids::AddUuid(UUID uuid) {
994
30.3k
  PW_CHECK(set_.size() <= bound_);
995
30.3k
  if (set_.size() < bound_) {
996
28.6k
    if (!set_.insert(uuid).second) {
997
14.4k
      bt_log(INFO,
998
14.4k
             "gap-le",
999
14.4k
             "Skipping addition of duplicate UUID %s to AD",
1000
14.4k
             bt_str(uuid));
1001
14.4k
    }
1002
28.6k
    return true;
1003
28.6k
  }
1004
1.72k
  if (set_.find(uuid) != set_.end()) {
1005
1.51k
    bt_log(INFO,
1006
1.51k
           "gap-le",
1007
1.51k
           "Skipping addition of duplicate UUID %s to AD",
1008
1.51k
           bt_str(uuid));
1009
1.51k
    return true;
1010
1.51k
  }
1011
1.72k
  bt_log(WARN,
1012
206
         "gap-le",
1013
206
         "Failed to add UUID %s to AD - no space left",
1014
206
         bt_str(uuid));
1015
206
  return false;
1016
1.72k
}
1017
}  // namespace bt