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 #pragma once
16 #include <lib/fit/function.h>
17 #include <lib/fit/result.h>
18 
19 #include <cstddef>
20 #include <limits>
21 #include <optional>
22 #include <unordered_map>
23 #include <unordered_set>
24 
25 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/supplement_data.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
28 
29 namespace bt {
30 
31 // Potential values that can be provided in the "Flags" advertising data
32 // bitfield.
33 // clang-format off
34 enum AdvFlag : uint8_t {
35   // Octet 0
36   kLELimitedDiscoverableMode        = (1 << 0),
37   kLEGeneralDiscoverableMode        = (1 << 1),
38   kBREDRNotSupported                = (1 << 2),
39   kSimultaneousLEAndBREDRController = (1 << 3),
40   kSimultaneousLEAndBREDRHost       = (1 << 4),
41 };
42 // clang-format on
43 
44 // The Flags bitfield used in advertising data.
45 // Only the first octet (octet0) is represented in |AdvFlags|.
46 //
47 // See the Core Specification Supplement v9 for more information.
48 using AdvFlags = uint8_t;
49 
50 constexpr uint8_t kDefaultNoAdvFlags = 0;
51 
52 // The TLV size of the Flags datatype.
53 constexpr size_t kTLVFlagsSize = 3;
54 
55 // The TLV size of the TX power data type
56 constexpr size_t kTLVTxPowerLevelSize = 3;
57 
58 // The TLV size of the appearance data type
59 constexpr size_t kTLVAppearanceSize = 4;
60 
61 // The TLV side of the resolvable set identifier data type
62 constexpr size_t kTLVResolvableSetIdentifierSize = 8;
63 
64 // Constants for the expected size (in octets) of an
65 // advertising/EIR/scan-response data field.
66 //
67 //  * If a constant contains the word "Min", then it specifies a minimum
68 //    expected length rather than an exact length.
69 //
70 //  * If a constants contains the word "ElemSize", then the data field is
71 //    expected to contain a contiguous array of elements of the specified size.
72 constexpr size_t kAppearanceSize = 2;
73 constexpr size_t kManufacturerIdSize = 2;
74 constexpr size_t kTxPowerLevelSize = 1;
75 constexpr size_t kResolvableSetIdentifierSize = 6;
76 
77 constexpr size_t kFlagsSizeMin = 1;
78 constexpr size_t kManufacturerSpecificDataSizeMin = kManufacturerIdSize;
79 
80 constexpr uint8_t kMaxUint8 = std::numeric_limits<uint8_t>::max();
81 // The maximum length of a friendly name, derived from v5.2, Vol 4, Part
82 // E, 7.3.11 and Vol 3, Part C, 12.1
83 constexpr uint8_t kMaxNameLength = 248;
84 
85 // The minimum length of a Broadcast Name, as defined by Public Broadcast
86 // Profile, in bytes.  Defined as 4 UTF-8 characters
87 constexpr uint8_t kMinBroadcastNameBytes = 4;
88 // The maximum length of a Broadcast Name, as defined by Public Broadcast
89 // Profile, in bytes.  Defined as 32 UTF-8 characters
90 constexpr uint8_t kMaxBroadcastNameBytes = 128;
91 
92 // The length of the entire manufacturer-specific data field must fit in a
93 // uint8_t, so the maximum data length is uint8_t::MAX - 1 byte for type - 2
94 // bytes for manufacturer ID.
95 constexpr uint8_t kMaxManufacturerDataLength = kMaxUint8 - 3;
96 
97 // The length of the service data field must fit in a uint8_t, so uint8_t::MAX -
98 // 1 byte for type.
99 constexpr uint8_t kMaxEncodedServiceDataLength = kMaxUint8 - 1;
100 
101 // The length of an encoded URI together with its 1-byte type field must not
102 // exceed uint8_t limits
103 constexpr uint8_t kMaxEncodedUriLength = kMaxUint8 - 1;
104 
105 // "A packet or data block shall not contain more than one instance for each
106 // Service UUID data size." (Core Specification Supplement v9 Part A 1.1.1). For
107 // each UUID size, 1 (type byte) + # of UUIDs * UUID size = length of that
108 // size's encoded UUIDs. This length must fit in a uint8_t, hence there is a
109 // per-UUID-size limit on the # of UUIDs.
110 constexpr uint8_t kMax16BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k16Bit;
111 constexpr uint8_t kMax32BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k32Bit;
112 constexpr uint8_t kMax128BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k128Bit;
113 
114 // A helper to build Adversiting Data, Scan Response Data, or Extended Inquiry
115 // Response Data fields.
116 // TODO(jamuraa): Add functionality for ACAD and OOB
117 //
118 // This can be viewed as a complex container type which has a specified byte
119 // view that is valid for:
120 //  - Core Spec v5.0 Vol 3, Part C, Section 11 in the case of Advertising or
121 //    Scan Response Data
122 //  - Core Spec v5.0 Vol 3, Part C, Section 8 for Extended Inquiry Response data
123 //
124 // See those sections, and the Core Specification Supplement v7 for more
125 // information.
126 class AdvertisingData {
127  public:
128   // Possible failure modes for parsing an AdvertisingData from raw bytes.
129   enum class ParseError {
130     // The bytes provided are not a valid type-length-value container.
131     kInvalidTlvFormat,
132     // The length of a TxPowerLevel-type field does not match the TxPowerLevel
133     // value size.
134     kTxPowerLevelMalformed,
135     // The length of a LocalName-type field exceeds the length allowed by the
136     // spec (kMaxNameLength).
137     kLocalNameTooLong,
138     // A UUID-type field is malformed.
139     kUuidsMalformed,
140     // A ManufacturerSpecificData-type field is smaller than the minimum
141     // allowable length.
142     kManufacturerSpecificDataTooSmall,
143     // A ServiceData-type field is too small to fit its expected UUID size.
144     kServiceDataTooSmall,
145     // A UUID associated with a ServiceData-type field is malformed.
146     kServiceDataUuidMalformed,
147     // The length of an Appearance-type field does not match the Appearance
148     // value size.
149     kAppearanceMalformed,
150     // Advertising Data missing
151     kMissing,
152     // Resolvable Set Identifier is the wrong size
153     kResolvableSetIdentifierSize,
154     // Broadcast name is too short
155     kBroadcastNameTooShort,
156     // Broadcast name is too long
157     kBroadcastNameTooLong,
158   };
159 
160   // Both complete and shortened forms of the local name can be advertised.
161   struct LocalName {
162     std::string name;
163     bool is_complete;
164 
165     bool operator==(const LocalName& other) const {
166       return (name == other.name) && (is_complete == other.is_complete);
167     }
168     bool operator!=(const LocalName& other) const { return !(*this == other); }
169   };
170 
171   // Creates an empty advertising data.
172   AdvertisingData() = default;
173   ~AdvertisingData() = default;
174 
175   // When these move operations return, `other` is specified to be an empty
176   // AdvertisingData - i.e. `other`'s state is as if `other = AdvertisingData()`
177   // was performed.
178   AdvertisingData(AdvertisingData&& other) noexcept;
179   AdvertisingData& operator=(AdvertisingData&& other) noexcept;
180 
181   // Construct from the raw Bluetooth field block |data|. Returns std::nullopt
182   // if |data| is not formatted correctly or on a parsing error.
183   using ParseResult = fit::result<ParseError, AdvertisingData>;
184   static ParseResult FromBytes(const ByteBuffer& data);
185   static std::string ParseErrorToString(ParseError e);
186 
187   // Copies all of the data in this object to |out|, including making a copy of
188   // any data in manufacturing data or service data. The result is |out| being
189   // an exact copy of this object.
190   void Copy(AdvertisingData* out) const;
191 
192   // Add a UUID to the set of services advertised.
193   // These service UUIDs will automatically be compressed to be represented in
194   // the smallest space possible. Returns true if the Service UUID was
195   // successfully added or already existed in the set of advertised services, or
196   // false if the UUID set was full and `uuid` could not be added.
197   [[nodiscard]] bool AddServiceUuid(const UUID& uuid);
198 
199   // Get the service UUIDs represented in this advertisement.
200   std::unordered_set<UUID> service_uuids() const;
201 
202   // Set service data for the service specified by |uuid|. Returns true if the
203   // data was set, false otherwise. Failure occurs if |uuid| + |data| exceed
204   // kMaxEncodedServiceDataLength when encoded.
205   [[nodiscard]] bool SetServiceData(const UUID& uuid, const ByteBuffer& data);
206 
207   // Get a set of which UUIDs have service data in this advertisement.
208   std::unordered_set<UUID> service_data_uuids() const;
209 
210   // View the currently set service data for |uuid|.
211   // This view is not stable; it should be used only ephemerally.
212   // Returns an empty BufferView if no service data is set for |uuid|
213   BufferView service_data(const UUID& uuid) const;
214 
215   // Set Manufacturer specific data for the company identified by |company_id|.
216   // Returns false & does not set the data if |data|.size() exceeds
217   // kMaxManufacturerDataLength, otherwise returns true.
218   [[nodiscard]] bool SetManufacturerData(uint16_t company_id,
219                                          const BufferView& data);
220 
221   // Get a set of which IDs have manufacturer data in this advertisement.
222   std::unordered_set<uint16_t> manufacturer_data_ids() const;
223 
224   // View the currently set manufacturer data for the company |company_id|.
225   // Returns an empty BufferView if no manufacturer data is set for
226   // |company_id|.
227   // NOTE: it is valid to send a manufacturer data with no data. Check that one
228   // exists using manufacturer_data_ids() first.
229   // This view is not stable; it should be used only ephemerally.
230   BufferView manufacturer_data(uint16_t company_id) const;
231 
232   // Sets the local TX Power
233   // TODO(jamuraa): add documentation about where to get this number from
234   void SetTxPower(int8_t dbm);
235 
236   // Gets the TX power
237   std::optional<int8_t> tx_power() const;
238 
239   // Returns false if `name` is not set, which happens if `name` is shortened
240   // and a complete name is currently set, or if `name` exceeds kMaxLocalName
241   // bytes. Returns true if `name` is set.
242   [[nodiscard]] bool SetLocalName(const LocalName& local_name);
243   [[nodiscard]] bool SetLocalName(const std::string& name,
244                                   bool is_complete = true) {
245     return SetLocalName(LocalName{name, is_complete});
246   }
247 
248   // Gets the local name
249   std::optional<LocalName> local_name() const;
250 
251   // Sets the resolvable set identifier
252   void SetResolvableSetIdentifier(
253       std::array<uint8_t, kResolvableSetIdentifierSize> identifier);
254 
255   // Gets the resolvable set identifier
256   const std::optional<std::array<uint8_t, kResolvableSetIdentifierSize>>&
257   resolvable_set_identifier() const;
258 
259   // Sets the broadcast name
260   void SetBroadcastName(const std::string& name);
261   // Gets the broadcast name
262   const std::optional<std::string>& broadcast_name() const;
263 
264   // Adds a URI to the set of URIs advertised.
265   // Does nothing if |uri| is empty or, when encoded, exceeds
266   // kMaxEncodedUriLength.
267   [[nodiscard]] bool AddUri(const std::string& uri);
268 
269   // Get the URIs in this advertisement
270   const std::unordered_set<std::string>& uris() const;
271 
272   // Sets the appearance
273   void SetAppearance(uint16_t appearance);
274 
275   // Get the appearance
276   std::optional<uint16_t> appearance() const;
277 
278   // Sets the Advertising Flags
279   void SetFlags(AdvFlags flags);
280 
281   // Get the currently-set flags.
282   std::optional<AdvFlags> flags() const;
283 
284   // Converts the AdvertisingData class attributes to a single string.
285   std::string ToString() const;
286 
287   // Calculates the size of the current set of fields if they were to be written
288   // to a buffer using WriteBlock().
289   //
290   // If |include_flags| is set, then the returned block size will include the
291   // expected size of writing advertising data flags.
292   size_t CalculateBlockSize(bool include_flags = false) const;
293 
294   // Writes the byte representation of this to |buffer| with the included
295   // |flags|. Returns false without modifying |buffer| if there is not enough
296   // space (i.e If the buffer size is less than CalculateBlockSize()).
297   //
298   // The responsibility is on the caller to provide a buffer that is large
299   // enough to encode the |AdvertisingData| and the optional flags.
300   bool WriteBlock(MutableByteBuffer* buffer,
301                   std::optional<AdvFlags> flags) const;
302 
303   // Relation operators
304   bool operator==(const AdvertisingData& other) const;
305   bool operator!=(const AdvertisingData& other) const;
306 
307  private:
308   // This class enforces that a set of UUIDs does not grow beyond its provided
309   // upper bound.
310   class BoundedUuids {
311    public:
312     // `bound` is the maximum number of UUIDs allowed in this set.
BoundedUuids(uint8_t bound)313     explicit BoundedUuids(uint8_t bound) : bound_(bound) {}
314 
315     // Adds a UUID to the set. Returns false if the UUID couldn't be added to
316     // the set due to the size bound, true otherwise.
317     bool AddUuid(UUID uuid);
318 
set()319     const std::unordered_set<UUID>& set() const { return set_; }
320     bool operator==(const BoundedUuids& other) const {
321       return bound_ == other.bound_ && set_ == other.set_;
322     }
323     bool operator!=(const BoundedUuids& other) const {
324       return !(*this == other);
325     }
326 
327    private:
328     std::unordered_set<UUID> set_ = std::unordered_set<UUID>{};
329     uint8_t bound_;
330   };
331 
332   // AD stores a map from service UUID size -> BoundedUuids. As the number of
333   // allowed UUID sizes is static, we define this default variable to represent
334   // the "empty" state of the UUID set.
335   const std::unordered_map<UUIDElemSize, BoundedUuids> kEmptyServiceUuidMap = {
336       {UUIDElemSize::k16Bit, BoundedUuids(kMax16BitUuids)},
337       {UUIDElemSize::k32Bit, BoundedUuids(kMax32BitUuids)},
338       {UUIDElemSize::k128Bit, BoundedUuids(kMax128BitUuids)}};
339   // TODO(armansito): Consider storing the payload in its serialized form and
340   // have these point into the structure (see fxbug.dev/42172180).
341   std::optional<LocalName> local_name_;
342   std::optional<int8_t> tx_power_;
343   std::optional<uint16_t> appearance_;
344 
345   // Each service UUID size is associated with a BoundedUuids. The BoundedUuids
346   // invariant that |bound| >= |set|.size() field is maintained by the AD class,
347   // not the BoundedUuids struct.
348   std::unordered_map<UUIDElemSize, BoundedUuids> service_uuids_ =
349       kEmptyServiceUuidMap;
350 
351   // The length of each manufacturer data buffer is always <=
352   // kMaxManufacturerDataLength.
353   std::unordered_map<uint16_t, DynamicByteBuffer> manufacturer_data_;
354 
355   // For each element in `service_data_`, the compact size of the UUID + the
356   // buffer length is always
357   // <= kkMaxEncodedServiceDataLength
358   std::unordered_map<UUID, DynamicByteBuffer> service_data_;
359 
360   std::unordered_set<std::string> uris_;
361 
362   std::optional<std::array<uint8_t, kResolvableSetIdentifierSize>>
363       resolvable_set_identifier_;
364 
365   std::optional<std::string> broadcast_name_;
366 
367   // Flags, if they have been parsed or set.
368   // Note: When using `WriteBlock`, the passed flags override these.
369   std::optional<AdvFlags> flags_;
370 
371   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdvertisingData);
372 };
373 
374 }  // namespace bt
375