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