| // 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_ATT_ATTRIBUTE_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_ATT_ATTRIBUTE_H_ |
| |
| #include <lib/fit/function.h> |
| #include <lib/fitx/result.h> |
| #include <zircon/assert.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include <fbl/macros.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/att/att.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/identifier.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uuid.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/smp.h" |
| |
| namespace bt::att { |
| |
| // Defines the read or write access permissions for an attribute. |
| class AccessRequirements final { |
| public: |
| // The default access permission prevents the attribute from being accessed. |
| AccessRequirements(); |
| |
| // Enables access permission with the given requirements. |
| // |min_enc_key_size| is the required minimum size of the key encrypting the connection in order |
| // to access an attribute where |encryption| is true. |
| // |
| // The default value of |min_enc_key_size| enforces maximum security, but could prevent devices |
| // that don't support |kMaxEncryptionKeySize| from accessing attributes that require encryption. |
| // In practice, it seems that most devices that access such attributes support |
| // |kMaxEncryptionKeySize|. |
| AccessRequirements(bool encryption, bool authentication, bool authorization, |
| uint8_t min_enc_key_size = sm::kMaxEncryptionKeySize); |
| |
| // Returns true if this attribute can be accessed at all. |
| inline bool allowed() const { return value_ & kAttributePermissionBitAllowed; } |
| |
| // Returns true if no security is required. |
| inline bool allowed_without_security() const { return value_ == kAttributePermissionBitAllowed; } |
| |
| // The following getters return the security requirements of this attribute: |
| |
| inline bool encryption_required() const { |
| return value_ & kAttributePermissionBitEncryptionRequired; |
| } |
| |
| inline bool authentication_required() const { |
| return value_ & kAttributePermissionBitAuthenticationRequired; |
| } |
| |
| inline bool authorization_required() const { |
| return value_ & kAttributePermissionBitAuthorizationRequired; |
| } |
| |
| inline uint8_t min_enc_key_size() const { return min_enc_key_size_; } |
| |
| inline bool operator==(const AccessRequirements& other) const { |
| return value_ == other.value_ && min_enc_key_size_ == other.min_enc_key_size_; |
| } |
| |
| private: |
| uint8_t value_; |
| uint8_t min_enc_key_size_; |
| }; |
| |
| class AttributeGrouping; |
| |
| // Represents an attribute. Each attribute is assigned a handle (unique within |
| // the scope of an Adapter) and a UUID that identifies its type. The type of an |
| // attribute dictates how to interpret the attribute value. |
| // |
| // Each attribute has a value of up to 512 octets. An Attribute can be |
| // configured to have a static value. In such a case the value can be directly |
| // obtained by calling |value()|. Such attributes cannot be written to. |
| // |
| // Otherwise, an Attribute is considered dynamic and its value must be accessed |
| // asynchronously by calling ReadAsync()/WriteAsync(). |
| // |
| // Instances cannot be constructed directly and must be obtained from an |
| // AttributeGrouping. |
| // |
| // THREAD-SAFETY: |
| // |
| // This class is not thread-safe. The constructor/destructor and all public |
| // methods must be called on the same thread. |
| class Attribute final { |
| public: |
| // Allow move construction and assignment. |
| Attribute(Attribute&& other) = default; |
| Attribute& operator=(Attribute&& other) = default; |
| |
| bool is_initialized() const { return handle_ != kInvalidHandle; } |
| |
| Handle handle() const { return handle_; } |
| const UUID& type() const { return type_; } |
| |
| // The grouping that this attribute belongs to. |
| const AttributeGrouping& group() const { return *group_; } |
| |
| // Returns the current attribute value. Returns nullptr if the attribute has a |
| // dynamic value. |
| const ByteBuffer* value() const { return value_.size() ? &value_ : nullptr; } |
| |
| // The read/write permissions of this attribute. |
| const AccessRequirements& read_reqs() const { return read_reqs_; } |
| const AccessRequirements& write_reqs() const { return write_reqs_; } |
| |
| // Sets |value| as the static attribute value. Once a value is assigned it |
| // cannot be overwritten. A static value cannot be assigned to an attribute |
| // that permits writes as attribute writes need to propagate to the service |
| // layer. |
| void SetValue(const ByteBuffer& value); |
| |
| // Handlers for reading and writing and attribute value asynchronously. A |
| // handler must call the provided the |result_callback| to signal the end of |
| // the operation. |
| using ReadResultCallback = |
| fit::callback<void(fitx::result<ErrorCode> status, const ByteBuffer& value)>; |
| using ReadHandler = fit::function<void(PeerId peer_id, Handle handle, uint16_t offset, |
| ReadResultCallback result_callback)>; |
| void set_read_handler(ReadHandler read_handler) { read_handler_ = std::move(read_handler); } |
| |
| // An "ATT Write Command" will trigger WriteHandler with |
| // a null |result_callback| |
| using WriteResultCallback = fit::callback<void(fitx::result<ErrorCode> status)>; |
| using WriteHandler = |
| fit::function<void(PeerId peer_id, Handle handle, uint16_t offset, const ByteBuffer& value, |
| WriteResultCallback result_callback)>; |
| void set_write_handler(WriteHandler write_handler) { write_handler_ = std::move(write_handler); } |
| |
| // Initiates an asynchronous read of the attribute value. Returns false if |
| // this attribute is not dynamic. |
| bool ReadAsync(PeerId peer_id, uint16_t offset, ReadResultCallback result_callback) const; |
| |
| // Initiates an asynchronous write of the attribute value. Returns false if |
| // this attribute is not dynamic. |
| bool WriteAsync(PeerId peer_id, uint16_t offset, const ByteBuffer& value, |
| WriteResultCallback result_callback) const; |
| |
| private: |
| // Only an AttributeGrouping can construct this. |
| friend class AttributeGrouping; |
| |
| // The default constructor will construct this attribute as uninitialized. |
| // This is intended for STL containers. |
| Attribute(); |
| Attribute(AttributeGrouping* group, Handle handle, const UUID& type, |
| const AccessRequirements& read_reqs, const AccessRequirements& write_reqs); |
| |
| AttributeGrouping* group_; // The group that owns this Attribute. |
| Handle handle_; |
| UUID type_; |
| AccessRequirements read_reqs_; |
| AccessRequirements write_reqs_; |
| |
| ReadHandler read_handler_; |
| WriteHandler write_handler_; |
| |
| DynamicByteBuffer value_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Attribute); |
| }; |
| |
| // Represents a grouping of attributes (see Vol 3, Part F, 3.2.3). Each grouping |
| // contains at least one leading attribute that contains the group declaration. |
| // The type of this attribute dictates the type of the grouping. |
| // |
| // Each grouping covers a contiguous range of handle numbers. The size of the |
| // range is determined by the |attr_count| constructor argument which defines |
| // the number of attributes in the grouping following the declaration attribute. |
| // Once constructed, a grouping is not considered complete until available |
| // handles within the range have been populated. |
| class AttributeGrouping final { |
| public: |
| // Initializes this attribute grouping with a group declaration attribute and |
| // enough storage for |attr_count| additional attributes. |decl_value| is |
| // assigned as the read-only value of the declaration attribute. |
| // |
| // Note: |attr_count| should not cause the group end handle to exceed |
| // att::kHandleMax. |
| AttributeGrouping(const UUID& group_type, Handle start_handle, size_t attr_count, |
| const ByteBuffer& decl_value); |
| |
| // Inserts a new attribute into this grouping using the given parameters and |
| // returns a pointer to it. Returns nullptr if the grouping is out of handles |
| // to allocate. |
| // |
| // The caller should not hold on to the returned pointer as the Attribute |
| // object is owned and managed by this AttributeGrouping. |
| Attribute* AddAttribute(const UUID& type, |
| const AccessRequirements& read_reqs = AccessRequirements(), |
| const AccessRequirements& write_reqs = AccessRequirements()); |
| |
| // Returns true if all attributes of this grouping have been populated. |
| bool complete() const { return attributes_.size() == (end_handle_ - start_handle_ + 1); } |
| |
| const UUID& group_type() const { |
| ZX_DEBUG_ASSERT(!attributes_.empty()); |
| return attributes_[0].type(); |
| } |
| |
| // Value of the group declaration attribute. |
| BufferView decl_value() const { |
| ZX_DEBUG_ASSERT(!attributes_.empty()); |
| ZX_DEBUG_ASSERT(attributes_[0].value()); |
| return attributes_[0].value()->view(); |
| } |
| |
| // The start and end handles of this grouping (inclusive). |
| Handle start_handle() const { return start_handle_; } |
| Handle end_handle() const { return end_handle_; } |
| |
| bool active() const { return active_; } |
| void set_active(bool active) { |
| ZX_DEBUG_ASSERT_MSG(complete(), "set_active() called on incomplete grouping!"); |
| active_ = active; |
| } |
| |
| const std::vector<Attribute>& attributes() const { return attributes_; } |
| |
| private: |
| Handle start_handle_; |
| Handle end_handle_; |
| |
| // Only groupings that are active are considered when responding to ATT |
| // requests. |
| bool active_; |
| |
| // The Attributes in this grouping, including the declaration attribute. Space |
| // is reserved for all attributes upon construction. The number of elements in |
| // |attributes_| reflects how many of the attributes have been initialized. |
| std::vector<Attribute> attributes_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AttributeGrouping); |
| }; |
| |
| } // namespace bt::att |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_ATT_ATTRIBUTE_H_ |