| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_ADVERTISING_DATA_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_ADVERTISING_DATA_H_ |
| |
| #include <lib/fit/function.h> |
| #include <lib/fitx/result.h> |
| |
| #include <cstddef> |
| #include <limits> |
| #include <optional> |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/supplement_data.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uuid.h" |
| |
| namespace bt { |
| |
| // Potential values that can be provided in the "Flags" advertising data |
| // bitfield. |
| // clang-format off |
| enum AdvFlag : uint8_t { |
| // Octet 0 |
| kLELimitedDiscoverableMode = (1 << 0), |
| kLEGeneralDiscoverableMode = (1 << 1), |
| kBREDRNotSupported = (1 << 2), |
| kSimultaneousLEAndBREDRController = (1 << 3), |
| kSimultaneousLEAndBREDRHost = (1 << 4), |
| }; |
| // clang-format on |
| |
| // The Flags bitfield used in advertising data. |
| // Only the first octet (octet0) is represented in |AdvFlags|. |
| // |
| // See the Core Specification Supplement v9 for more information. |
| using AdvFlags = uint8_t; |
| |
| constexpr uint8_t kDefaultNoAdvFlags = 0; |
| |
| // The TLV size of the Flags datatype. |
| constexpr size_t kTLVFlagsSize = 3; |
| |
| // The TLV size of the TX power data type |
| constexpr size_t kTLVTxPowerLevelSize = 3; |
| |
| // The TLV size of the appearance data type |
| constexpr size_t kTLVAppearanceSize = 4; |
| |
| // Constants for the expected size (in octets) of an |
| // advertising/EIR/scan-response data field. |
| // |
| // * If a constant contains the word "Min", then it specifies a minimum |
| // expected length rather than an exact length. |
| // |
| // * If a constants contains the word "ElemSize", then the data field is |
| // expected to contain a contiguous array of elements of the specified size. |
| constexpr size_t kAppearanceSize = 2; |
| constexpr size_t kManufacturerIdSize = 2; |
| constexpr size_t kTxPowerLevelSize = 1; |
| |
| constexpr size_t kFlagsSizeMin = 1; |
| constexpr size_t kManufacturerSpecificDataSizeMin = kManufacturerIdSize; |
| |
| constexpr uint8_t kMaxUint8 = std::numeric_limits<uint8_t>::max(); |
| // The maximum length of a friendly name, derived from v5.2, Vol 4, Part E, 7.3.11 and Vol 3, Part |
| // C, 12.1 |
| constexpr uint8_t kMaxNameLength = 248; |
| |
| // The length of the entire manufacturer-specific data field must fit in a uint8_t, so the maximum |
| // data length is uint8_t::MAX - 1 byte for type - 2 bytes for manufacturer ID. |
| constexpr uint8_t kMaxManufacturerDataLength = kMaxUint8 - 3; |
| |
| // The length of the service data field must fit in a uint8_t, so uint8_t::MAX - 1 byte for type. |
| constexpr uint8_t kMaxEncodedServiceDataLength = kMaxUint8 - 1; |
| |
| // The length of an encoded URI together with its 1-byte type field must not exceed uint8_t limits |
| constexpr uint8_t kMaxEncodedUriLength = kMaxUint8 - 1; |
| |
| // "A packet or data block shall not contain more than one instance for each Service UUID data |
| // size." (Core Specification Supplement v9 Part A 1.1.1). For each UUID size, 1 (type byte) + # of |
| // UUIDs * UUID size = length of that size's encoded UUIDs. This length must fit in a uint8_t, hence |
| // there is a per-UUID-size limit on the # of UUIDs. |
| constexpr uint8_t kMax16BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k16Bit; |
| constexpr uint8_t kMax32BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k32Bit; |
| constexpr uint8_t kMax128BitUuids = (kMaxUint8 - 1) / UUIDElemSize::k128Bit; |
| |
| // A helper to build Adversiting Data, Scan Response Data, or Extended Inquiry |
| // Response Data fields. |
| // TODO(jamuraa): Add functionality for ACAD and OOB |
| // |
| // This can be viewed as a complex container type which has a specified byte |
| // view that is valid for: |
| // - Core Spec v5.0 Vol 3, Part C, Section 11 in the case of Advertising or |
| // Scan Response Data |
| // - Core Spec v5.0 Vol 3, Part C, Section 8 for Extended Inquiry Response data |
| // |
| // See those sections, and the Core Specification Supplement v7 for more |
| // information. |
| class AdvertisingData { |
| public: |
| // Possible failure modes for parsing an AdvertisingData from raw bytes. |
| enum class ParseError { |
| // The bytes provided are not a valid type-length-value container. |
| kInvalidTlvFormat, |
| // The length of a TxPowerLevel-type field does not match the TxPowerLevel value size. |
| kTxPowerLevelMalformed, |
| // The length of a LocalName-type field exceeds the length allowed by the spec (kMaxNameLength). |
| kLocalNameTooLong, |
| // A UUID-type field is malformed. |
| kUuidsMalformed, |
| // A ManufacturerSpecificData-type field is smaller than the minimum allowable length. |
| kManufacturerSpecificDataTooSmall, |
| // A ServiceData-type field is too small to fit its expected UUID size. |
| kServiceDataTooSmall, |
| // A UUID associated with a ServiceData-type field is malformed. |
| kServiceDataUuidMalformed, |
| // The length of an Appearance-type field does not match the Appearance value size. |
| kAppearanceMalformed, |
| }; |
| |
| // Creates an empty advertising data. |
| AdvertisingData() = default; |
| ~AdvertisingData() = default; |
| |
| // When these move operations return, `other` is specified to be an empty AdvertisingData - i.e. |
| // `other`'s state is as if `other = AdvertisingData()` was performed. |
| AdvertisingData(AdvertisingData&& other) noexcept; |
| AdvertisingData& operator=(AdvertisingData&& other) noexcept; |
| |
| // Construct from the raw Bluetooth field block |data|. Returns std::nullopt if |data| is not |
| // formatted correctly or on a parsing error. |
| using ParseResult = fitx::result<ParseError, AdvertisingData>; |
| static ParseResult FromBytes(const ByteBuffer& data); |
| static std::string ParseErrorToString(ParseError e); |
| |
| // Copies all of the data in this object to |out|, including making a copy of |
| // any data in manufacturing data or service data. |
| // Overwrites any data which is already in |out|. |
| void Copy(AdvertisingData* out) const; |
| |
| // Add a UUID to the set of services advertised. |
| // These service UUIDs will automatically be compressed to be represented in the smallest space |
| // possible. Returns true if the Service UUID was successfully added or already existed in the set |
| // of advertised services, or false if the UUID set was full and `uuid` could not be added. |
| [[nodiscard]] bool AddServiceUuid(const UUID& uuid); |
| |
| // Get the service UUIDs represented in this advertisement. |
| std::unordered_set<UUID> service_uuids() const; |
| |
| // Set service data for the service specified by |uuid|. Returns true if the data was set, false |
| // otherwise. Failure occurs if |uuid| + |data| exceed kMaxEncodedServiceDataLength when encoded. |
| [[nodiscard]] bool SetServiceData(const UUID& uuid, const ByteBuffer& data); |
| |
| // Get a set of which UUIDs have service data in this advertisement. |
| std::unordered_set<UUID> service_data_uuids() const; |
| |
| // View the currently set service data for |uuid|. |
| // This view is not stable; it should be used only ephemerally. |
| // Returns an empty BufferView if no service data is set for |uuid| |
| BufferView service_data(const UUID& uuid) const; |
| |
| // Set Manufacturer specific data for the company identified by |company_id|. Returns false & does |
| // not set the data if |data|.size() exceeds kMaxManufacturerDataLength, otherwise returns true. |
| [[nodiscard]] bool SetManufacturerData(uint16_t company_id, const BufferView& data); |
| |
| // Get a set of which IDs have manufacturer data in this advertisement. |
| std::unordered_set<uint16_t> manufacturer_data_ids() const; |
| |
| // View the currently set manufacturer data for the company |company_id|. |
| // Returns an empty BufferView if no manufacturer data is set for |
| // |company_id|. |
| // NOTE: it is valid to send a manufacturer data with no data. Check that one |
| // exists using manufacturer_data_ids() first. |
| // This view is not stable; it should be used only ephemerally. |
| BufferView manufacturer_data(uint16_t company_id) const; |
| |
| // Sets the local TX Power |
| // TODO(jamuraa): add documentation about where to get this number from |
| void SetTxPower(int8_t dbm); |
| |
| // Gets the TX power |
| std::optional<int8_t> tx_power() const; |
| |
| // Returns false if `name` is not set due to exceeding kMaxLocalName bytes, or true if it is set. |
| [[nodiscard]] bool SetLocalName(const std::string& name); |
| |
| // Gets the local name |
| std::optional<std::string> local_name() const; |
| |
| // Adds a URI to the set of URIs advertised. |
| // Does nothing if |uri| is empty or, when encoded, exceeds kMaxEncodedUriLength. |
| [[nodiscard]] bool AddUri(const std::string& uri); |
| |
| // Get the URIs in this advertisement |
| const std::unordered_set<std::string>& uris() const; |
| |
| // Sets the appearance |
| void SetAppearance(uint16_t appearance); |
| |
| // Get the appearance |
| std::optional<uint16_t> appearance() const; |
| |
| // Calculates the size of the current set of fields if they were to be written |
| // to a buffer using WriteBlock(). |
| // |
| // If |include_flags| is set, then the returned block size will include the |
| // expected size of writing advertising data flags. |
| size_t CalculateBlockSize(bool include_flags = false) const; |
| |
| // Writes the byte representation of this to |buffer| with the included |flags|. |
| // Returns false without modifying |buffer| if there is not enough space (i.e If |
| // the buffer size is less than CalculateBlockSize()). |
| // |
| // The responsibility is on the caller to provide a buffer that is large enough to |
| // encode the |AdvertisingData| and the optional flags. |
| bool WriteBlock(MutableByteBuffer* buffer, std::optional<AdvFlags> flags) const; |
| |
| // Relation operators |
| bool operator==(const AdvertisingData& other) const; |
| bool operator!=(const AdvertisingData& other) const; |
| |
| private: |
| // This class enforces that a set of UUIDs does not grow beyond its provided upper bound. |
| class BoundedUuids { |
| public: |
| // `bound` is the maximum number of UUIDs allowed in this set. |
| explicit BoundedUuids(uint8_t bound) : bound_(bound) {} |
| |
| // Adds a UUID to the set. Returns false if the UUID couldn't be added to the set due to the |
| // size bound, true otherwise. |
| bool AddUuid(UUID uuid); |
| |
| const std::unordered_set<UUID>& set() const { return set_; } |
| bool operator==(const BoundedUuids& other) const { |
| return bound_ == other.bound_ && set_ == other.set_; |
| } |
| bool operator!=(const BoundedUuids& other) const { return !(*this == other); } |
| |
| private: |
| std::unordered_set<UUID> set_ = std::unordered_set<UUID>{}; |
| uint8_t bound_; |
| }; |
| |
| // AD stores a map from service UUID size -> BoundedUuids. As the number of allowed UUID sizes is |
| // static, we define this default variable to represent the "empty" state of the UUID set. |
| const std::unordered_map<UUIDElemSize, BoundedUuids> kEmptyServiceUuidMap = { |
| {UUIDElemSize::k16Bit, BoundedUuids(kMax16BitUuids)}, |
| {UUIDElemSize::k32Bit, BoundedUuids(kMax32BitUuids)}, |
| {UUIDElemSize::k128Bit, BoundedUuids(kMax128BitUuids)}}; |
| // TODO(armansito): Consider storing the payload in its serialized form and |
| // have these point into the structure (see fxbug.dev/907). |
| std::optional<std::string> local_name_; |
| std::optional<int8_t> tx_power_; |
| std::optional<uint16_t> appearance_; |
| |
| // Each service UUID size is associated with a BoundedUuids. The BoundedUuids invariant that |
| // |bound| >= |set|.size() field is maintained by the AD class, not the BoundedUuids struct. |
| std::unordered_map<UUIDElemSize, BoundedUuids> service_uuids_ = kEmptyServiceUuidMap; |
| |
| // The length of each manufacturer data buffer is always <= kMaxManufacturerDataLength. |
| std::unordered_map<uint16_t, DynamicByteBuffer> manufacturer_data_; |
| |
| // For each element in `service_data_`, the compact size of the UUID + the buffer length is always |
| // <= kkMaxEncodedServiceDataLength |
| std::unordered_map<UUID, DynamicByteBuffer> service_data_; |
| |
| std::unordered_set<std::string> uris_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdvertisingData); |
| }; |
| |
| } // namespace bt |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_ADVERTISING_DATA_H_ |