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 <memory> 20 #include <vector> 21 22 #include "pw_bluetooth_sapphire/internal/host/att/att.h" 23 #include "pw_bluetooth_sapphire/internal/host/common/assert.h" 24 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 25 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h" 26 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 27 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h" 28 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h" 29 30 namespace bt::att { 31 32 // Defines the read or write access permissions for an attribute. 33 class AccessRequirements final { 34 public: 35 // The default access permission prevents the attribute from being accessed. 36 AccessRequirements(); 37 38 // Enables access permission with the given requirements. 39 // |min_enc_key_size| is the required minimum size of the key encrypting the 40 // connection in order to access an attribute where |encryption| is true. 41 // 42 // The default value of |min_enc_key_size| enforces maximum security, but 43 // could prevent devices that don't support |kMaxEncryptionKeySize| from 44 // accessing attributes that require encryption. In practice, it seems that 45 // most devices that access such attributes support |kMaxEncryptionKeySize|. 46 AccessRequirements(bool encryption, 47 bool authentication, 48 bool authorization, 49 uint8_t min_enc_key_size = sm::kMaxEncryptionKeySize); 50 51 // Returns true if this attribute can be accessed at all. allowed()52 inline bool allowed() const { 53 return value_ & kAttributePermissionBitAllowed; 54 } 55 56 // Returns true if no security is required. allowed_without_security()57 inline bool allowed_without_security() const { 58 return value_ == kAttributePermissionBitAllowed; 59 } 60 61 // The following getters return the security requirements of this attribute: 62 encryption_required()63 inline bool encryption_required() const { 64 return value_ & kAttributePermissionBitEncryptionRequired; 65 } 66 authentication_required()67 inline bool authentication_required() const { 68 return value_ & kAttributePermissionBitAuthenticationRequired; 69 } 70 authorization_required()71 inline bool authorization_required() const { 72 return value_ & kAttributePermissionBitAuthorizationRequired; 73 } 74 min_enc_key_size()75 inline uint8_t min_enc_key_size() const { return min_enc_key_size_; } 76 77 inline bool operator==(const AccessRequirements& other) const { 78 return value_ == other.value_ && 79 min_enc_key_size_ == other.min_enc_key_size_; 80 } 81 82 private: 83 uint8_t value_; 84 uint8_t min_enc_key_size_; 85 }; 86 87 class AttributeGrouping; 88 89 // Represents an attribute. Each attribute is assigned a handle (unique within 90 // the scope of an Adapter) and a UUID that identifies its type. The type of an 91 // attribute dictates how to interpret the attribute value. 92 // 93 // Each attribute has a value of up to 512 octets. An Attribute can be 94 // configured to have a static value. In such a case the value can be directly 95 // obtained by calling |value()|. Such attributes cannot be written to. 96 // 97 // Otherwise, an Attribute is considered dynamic and its value must be accessed 98 // asynchronously by calling ReadAsync()/WriteAsync(). 99 // 100 // Instances cannot be constructed directly and must be obtained from an 101 // AttributeGrouping. 102 // 103 // THREAD-SAFETY: 104 // 105 // This class is not thread-safe. The constructor/destructor and all public 106 // methods must be called on the same thread. 107 class Attribute final { 108 public: 109 // Allow move construction and assignment. 110 Attribute(Attribute&& other) = default; 111 Attribute& operator=(Attribute&& other) = default; 112 is_initialized()113 bool is_initialized() const { return handle_ != kInvalidHandle; } 114 handle()115 Handle handle() const { return handle_; } type()116 const UUID& type() const { return type_; } 117 118 // The grouping that this attribute belongs to. group()119 const AttributeGrouping& group() const { return *group_; } 120 121 // Returns the current attribute value. Returns nullptr if the attribute has a 122 // dynamic value. value()123 const ByteBuffer* value() const { return value_.size() ? &value_ : nullptr; } 124 125 // The read/write permissions of this attribute. read_reqs()126 const AccessRequirements& read_reqs() const { return read_reqs_; } write_reqs()127 const AccessRequirements& write_reqs() const { return write_reqs_; } 128 129 // Sets |value| as the static attribute value. Once a value is assigned it 130 // cannot be overwritten. A static value cannot be assigned to an attribute 131 // that permits writes as attribute writes need to propagate to the service 132 // layer. 133 void SetValue(const ByteBuffer& value); 134 135 // Handlers for reading and writing and attribute value asynchronously. A 136 // handler must call the provided the |result_callback| to signal the end of 137 // the operation. 138 using ReadResultCallback = fit::callback<void(fit::result<ErrorCode> status, 139 const ByteBuffer& value)>; 140 using ReadHandler = fit::function<void(PeerId peer_id, 141 Handle handle, 142 uint16_t offset, 143 ReadResultCallback result_callback)>; set_read_handler(ReadHandler read_handler)144 void set_read_handler(ReadHandler read_handler) { 145 read_handler_ = std::move(read_handler); 146 } 147 148 // An "ATT Write Command" will trigger WriteHandler with 149 // a null |result_callback| 150 using WriteResultCallback = 151 fit::callback<void(fit::result<ErrorCode> status)>; 152 using WriteHandler = fit::function<void(PeerId peer_id, 153 Handle handle, 154 uint16_t offset, 155 const ByteBuffer& value, 156 WriteResultCallback result_callback)>; set_write_handler(WriteHandler write_handler)157 void set_write_handler(WriteHandler write_handler) { 158 write_handler_ = std::move(write_handler); 159 } 160 161 // Initiates an asynchronous read of the attribute value. Returns false if 162 // this attribute is not dynamic. 163 bool ReadAsync(PeerId peer_id, 164 uint16_t offset, 165 ReadResultCallback result_callback) const; 166 167 // Initiates an asynchronous write of the attribute value. Returns false if 168 // this attribute is not dynamic. 169 bool WriteAsync(PeerId peer_id, 170 uint16_t offset, 171 const ByteBuffer& value, 172 WriteResultCallback result_callback) const; 173 174 private: 175 // Only an AttributeGrouping can construct this. 176 friend class AttributeGrouping; 177 178 // The default constructor will construct this attribute as uninitialized. 179 // This is intended for STL containers. 180 Attribute(); 181 Attribute(AttributeGrouping* group, 182 Handle handle, 183 const UUID& type, 184 const AccessRequirements& read_reqs, 185 const AccessRequirements& write_reqs); 186 187 AttributeGrouping* group_; // The group that owns this Attribute. 188 Handle handle_; 189 UUID type_; 190 AccessRequirements read_reqs_; 191 AccessRequirements write_reqs_; 192 193 ReadHandler read_handler_; 194 WriteHandler write_handler_; 195 196 DynamicByteBuffer value_; 197 198 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Attribute); 199 }; 200 201 // Represents a grouping of attributes (see Vol 3, Part F, 3.2.3). Each grouping 202 // contains at least one leading attribute that contains the group declaration. 203 // The type of this attribute dictates the type of the grouping. 204 // 205 // Each grouping covers a contiguous range of handle numbers. The size of the 206 // range is determined by the |attr_count| constructor argument which defines 207 // the number of attributes in the grouping following the declaration attribute. 208 // Once constructed, a grouping is not considered complete until available 209 // handles within the range have been populated. 210 class AttributeGrouping final { 211 public: 212 // Initializes this attribute grouping with a group declaration attribute and 213 // enough storage for |attr_count| additional attributes. |decl_value| is 214 // assigned as the read-only value of the declaration attribute. 215 // 216 // Note: |attr_count| should not cause the group end handle to exceed 217 // att::kHandleMax. 218 AttributeGrouping(const UUID& group_type, 219 Handle start_handle, 220 size_t attr_count, 221 const ByteBuffer& decl_value); 222 223 // Inserts a new attribute into this grouping using the given parameters and 224 // returns a pointer to it. Returns nullptr if the grouping is out of handles 225 // to allocate. 226 // 227 // The caller should not hold on to the returned pointer as the Attribute 228 // object is owned and managed by this AttributeGrouping. 229 Attribute* AddAttribute( 230 const UUID& type, 231 const AccessRequirements& read_reqs = AccessRequirements(), 232 const AccessRequirements& write_reqs = AccessRequirements()); 233 234 // Returns true if all attributes of this grouping have been populated. complete()235 bool complete() const { 236 return attributes_.size() == (end_handle_ - start_handle_ + 1); 237 } 238 group_type()239 const UUID& group_type() const { 240 PW_DCHECK(!attributes_.empty()); 241 return attributes_[0].type(); 242 } 243 244 // Value of the group declaration attribute. decl_value()245 BufferView decl_value() const { 246 PW_DCHECK(!attributes_.empty()); 247 PW_DCHECK(attributes_[0].value()); 248 return attributes_[0].value()->view(); 249 } 250 251 // The start and end handles of this grouping (inclusive). start_handle()252 Handle start_handle() const { return start_handle_; } end_handle()253 Handle end_handle() const { return end_handle_; } 254 active()255 bool active() const { return active_; } set_active(bool active)256 void set_active(bool active) { 257 PW_DCHECK(complete(), "set_active() called on incomplete grouping!"); 258 active_ = active; 259 } 260 attributes()261 const std::vector<Attribute>& attributes() const { return attributes_; } 262 263 private: 264 Handle start_handle_; 265 Handle end_handle_; 266 267 // Only groupings that are active are considered when responding to ATT 268 // requests. 269 bool active_; 270 271 // The Attributes in this grouping, including the declaration attribute. Space 272 // is reserved for all attributes upon construction. The number of elements in 273 // |attributes_| reflects how many of the attributes have been initialized. 274 std::vector<Attribute> attributes_; 275 276 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AttributeGrouping); 277 }; 278 279 } // namespace bt::att 280