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 <list> 17 #include <memory> 18 19 #include "pw_bluetooth_sapphire/internal/host/att/att.h" 20 #include "pw_bluetooth_sapphire/internal/host/att/attribute.h" 21 #include "pw_bluetooth_sapphire/internal/host/att/write_queue.h" 22 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 23 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 24 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h" 25 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h" 26 #include "pw_bluetooth_sapphire/internal/host/sm/types.h" 27 28 namespace bt::att { 29 30 // This class provides a simple attribute database abstraction. Attributes can 31 // be populated directly and queried to fulfill ATT protocol requests. 32 // 33 // Many Database instances can be created as long as care is taken that the 34 // referenced handle ranges are distinct. While this class is primarily intended 35 // to be used as a local ATT server database, it could also be used to represent 36 // a remote attribute cache. 37 class Database final : public WeakSelf<Database> { 38 using GroupingList = std::list<AttributeGrouping>; 39 40 public: 41 // This type allows iteration over the attributes in a database. An iterator 42 // is always initialzed with a handle range and options to skip attributes or 43 // groupings based on attribute type. An iterator always skips 44 // inactive/incomplete groupings. 45 // 46 // Modifying a database invalidates its iterators. 47 class Iterator final { 48 public: 49 // Returns the current attribute. Returns nullptr if the end of the handle 50 // range has been reached. 51 const Attribute* get() const; 52 53 // Advances the iterator forward. Skips over non-matching attributes if a 54 // type filter has been set. Has no effect if the end of the range was 55 // reached. 56 void Advance(); 57 58 // If set, |next()| will only return attributes with the given |type|. No 59 // filter is set by default. set_type_filter(const UUID & type)60 void set_type_filter(const UUID& type) { type_filter_ = type; } 61 62 // Returns true if the iterator cannot be advanced any further. AtEnd()63 inline bool AtEnd() const { return grp_iter_ == grp_end_; } 64 65 private: MarkEnd()66 inline void MarkEnd() { grp_iter_ = grp_end_; } 67 68 friend class Database; 69 Iterator(GroupingList* list, 70 Handle start, 71 Handle end, 72 const UUID* type, 73 bool groups_only); 74 75 Handle start_; 76 Handle end_; 77 bool grp_only_; 78 GroupingList::iterator grp_end_; 79 GroupingList::iterator grp_iter_; 80 uint16_t attr_offset_; 81 static_assert(std::numeric_limits<decltype(attr_offset_)>::max() >= 82 kHandleMax, 83 "attr_offset_ must be able to fit kMaxHandle!"); 84 std::optional<UUID> type_filter_; 85 }; 86 87 // Initializes this database to span the attribute handle range given by 88 // |range_start| and |range_end|. This allows the upper layer to segment the 89 // handle range into multiple contiguous regions by instantiating multiple 90 // Database objects. 91 // 92 // Note: This is to make it easy for the GATT layer to group service 93 // declarations with 16-bit UUIDs and 128-bit UUIDs separately as recommended 94 // by the GATT specification (see Vol 3, Part G, 3.1). 95 explicit Database(Handle range_start = kHandleMin, 96 Handle range_end = kHandleMax); 97 ~Database() = default; 98 99 // Returns an iterator that covers the handle range defined by |start| and 100 // |end| (inclusive). If |groups_only| is true, then the returned iterator 101 // will only return group declaration attributes (this allows quicker 102 // iteration over groupings while handling the ATT Read By Group Type 103 // request). 104 // 105 // If |type| is not a nullptr, it will be assigned as the iterator's type 106 // filter. 107 Iterator GetIterator(Handle start, 108 Handle end, 109 const UUID* type = nullptr, 110 bool groups_only = false); 111 112 // Creates a new attribute grouping with the given |type|. The grouping will 113 // be initialized to contain |attr_count| attributes (excluding the 114 // group declaration attribute) and |value| will be assigned as the group 115 // declaration attribute value. 116 // 117 // Returns a pointer to the new grouping, which can be used to populate 118 // attributes. Returns nullptr if the requested grouping could not be 119 // created due to insufficient handles. 120 // 121 // The returned pointer is owned and managed by this Database and should not 122 // be retained by the caller. Removing the grouping will invalidate the 123 // returned pointer. 124 AttributeGrouping* NewGrouping(const UUID& group_type, 125 size_t attr_count, 126 const ByteBuffer& decl_value); 127 128 // Removes the attribute grouping that has the given starting handle. Returns 129 // false if no such grouping was found. 130 bool RemoveGrouping(Handle start_handle); 131 groupings()132 const std::list<AttributeGrouping>& groupings() const { return groupings_; } 133 134 // Finds and returns the attribute with the given handle. Returns nullptr if 135 // the attribute cannot be found or is part of a grouping that is inactive 136 // or incomplete. 137 const Attribute* FindAttribute(Handle handle); 138 139 // Applies all write requests in |write_queue| and reports the result in 140 // |callback|. All requests will be delivered to the attribute write handlers 141 // at once, in order, without waiting for a response on individual writes. 142 // 143 // If one or more requests result in an application protocol error, 144 // |callback| will be invoked with the value of the first error received and 145 // all subsequent results will be ignored. Otherwise, |callback| will be 146 // called with a success status once a reply has been received for all 147 // requests in the queue. 148 // 149 // This function performs any security checks even though these are expected 150 // to be done during the "prepare" phase. This is because the attribute 151 // database can change from the time writes are queued until they are 152 // committed. |security| should match the security properties of the link 153 // under which the execute write request was initiatied. 154 // 155 // Attribute value validation (such as offset and length checks) are expected 156 // to be done by the application that has set up the attribute. This function 157 // does check for the validity of a given attribute handle and whether the 158 // attribute supports writes. 159 using WriteQueueResult = fit::result<std::tuple<Handle, ErrorCode>>; 160 using WriteCallback = fit::callback<void(WriteQueueResult)>; 161 void ExecuteWriteQueue(PeerId peer_id, 162 PrepareWriteQueue write_queue, 163 const sm::SecurityProperties& security, 164 WriteCallback callback); 165 166 private: 167 Handle range_start_; 168 Handle range_end_; 169 170 // The list of groupings is sorted by handle where each grouping maps to a 171 // non-overlapping handle range. Successive groupings don't necessarily 172 // represent contiguous handle ranges as any grouping can be removed. 173 GroupingList groupings_; 174 175 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Database); 176 }; 177 178 } // namespace bt::att 179