blob: b5f92e83d7b7e418e4b0abb89d5e9958f423a89d [file] [log] [blame]
// 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_