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 <pw_async/dispatcher.h>
18 
19 #include <map>
20 #include <memory>
21 #include <unordered_map>
22 
23 #include "pw_bluetooth_sapphire/internal/host/att/att.h"
24 #include "pw_bluetooth_sapphire/internal/host/att/error.h"
25 #include "pw_bluetooth_sapphire/internal/host/att/packet.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
29 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
30 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
31 #include "pw_bluetooth_sapphire/internal/host/l2cap/scoped_channel.h"
32 
33 namespace bt::att {
34 
35 // Implements an ATT data bearer with the following features:
36 //
37 //   * This can be used over either a LE-U or an ACL-U logical link. No
38 //     assumptions are made on the logical transport of the underlying L2CAP
39 //     channel.
40 //   * Can simultaneously operate in both server and client roles of the
41 //     protocol.
42 //
43 // Deleting a Bearer closes the underlying channel. Unlike ShutDown(), this
44 // does NOT notify any callbacks to prevent them from running in destructors.
45 //
46 // THREAD-SAFETY:
47 //
48 // This class is intended to be created, accessed, and destroyed on the same
49 // thread. All callbacks will be invoked on a Bearer's creation thread.
50 class Bearer final {
51  public:
52   // Creates a new ATT Bearer. Returns nullptr if |chan| cannot be activated.
53   // This can happen if the link is closed.
54   static std::unique_ptr<Bearer> Create(l2cap::Channel::WeakPtr chan,
55                                         pw::async::Dispatcher& dispatcher);
56 
57   ~Bearer();
58 
59   // Returns true if the underlying channel is open.
is_open()60   bool is_open() const { return static_cast<bool>(chan_); }
61 
62   // The bi-directional (client + server) MTU currently in use. The default
63   // value is kLEMinMTU.
64   //
65   // NOTE: This is allowed to be initialized to something smaller than kLEMinMTU
66   // for unit tests.
mtu()67   uint16_t mtu() const { return mtu_; }
set_mtu(uint16_t value)68   void set_mtu(uint16_t value) {
69     bt_log(DEBUG, "att", "bearer: new MTU %u", value);
70     mtu_ = value;
71   }
72 
73   // The preferred MTU. This is initially assigned based on the MTU of the
74   // underlying L2CAP channel and will be used in future MTU Exchange
75   // procedures.
preferred_mtu()76   uint16_t preferred_mtu() const { return preferred_mtu_; }
set_preferred_mtu(uint16_t value)77   void set_preferred_mtu(uint16_t value) {
78     PW_DCHECK(value >= kLEMinMTU);
79     preferred_mtu_ = value;
80   }
81 
82   // Returns the correct minimum ATT_MTU based on the underlying link type.
min_mtu()83   uint16_t min_mtu() const { return min_mtu_; }
84 
85   // Returns the current link security properties.
security()86   sm::SecurityProperties security() const {
87     return chan_ ? chan_->security() : sm::SecurityProperties();
88   }
89 
90   // Sets a callback to be invoked invoked when the underlying channel has
91   // closed. |callback| should disconnect the underlying logical link.
set_closed_callback(fit::closure callback)92   void set_closed_callback(fit::closure callback) {
93     closed_cb_ = std::move(callback);
94   }
95 
96   // Closes the channel. This should be called when a protocol transaction
97   // warrants the link to be disconnected. Notifies any callback set via
98   // |set_closed_callback()| and notifies the error callback of all pending HCI
99   // transactions.
100   //
101   // Does nothing if the channel is not open.
102   //
103   // NOTE: Bearer internally shuts down the link on request timeouts and
104   // sequential protocol violations.
105   void ShutDown();
106 
107   // Initiates an asynchronous transaction and invokes |callback| on this
108   // Bearer's creation thread when the transaction completes. |pdu| must
109   // correspond to a request or indication.
110   //
111   // TransactionCallback is used to report the end of a request or indication
112   // transaction. |packet| will contain a matching response or confirmation PDU
113   // depending on the transaction in question.
114   //
115   // If the transaction ends with an error or cannot complete (e.g. due to a
116   // timeout), |error_callback| will be called instead of |callback| if
117   // provided.
118   //
119   // Returns false if |pdu| is empty, exceeds the current MTU, or does not
120   // correspond to a request or indication.
121   using TransactionResult = fit::result<std::pair<Error, Handle>, PacketReader>;
122   using TransactionCallback = fit::callback<void(TransactionResult)>;
123   void StartTransaction(ByteBufferPtr pdu, TransactionCallback callback);
124 
125   // Sends |pdu| without initiating a transaction. Used for command and
126   // notification PDUs which do not have flow control.
127   //
128   // Returns false if the packet is malformed or does not correspond to a
129   // command or notification.
130   [[nodiscard]] bool SendWithoutResponse(ByteBufferPtr pdu);
131 
132   // A Handler is a function that gets invoked when the Bearer receives a PDU
133   // that is not tied to a locally initiated transaction (see
134   // StartTransaction()).
135   //
136   //   * |tid| will be set to kInvalidTransactionId for command and notification
137   //     PDUs. These do not require a response and are not part of a
138   //     transaction.
139   //
140   //   * A valid |tid| will be provided for request and indication PDUs. These
141   //     require a response which can be sent by calling Reply().
142   using TransactionId = size_t;
143   static constexpr TransactionId kInvalidTransactionId = 0u;
144   using Handler =
145       fit::function<void(TransactionId tid, const PacketReader& packet)>;
146 
147   // Handler: called when |pdu| does not need flow control. This will be
148   // called for commands and notifications.
149   using HandlerId = size_t;
150   static constexpr HandlerId kInvalidHandlerId = 0u;
151   HandlerId RegisterHandler(OpCode opcode, Handler handler);
152 
153   // Unregisters a handler. |id| cannot be zero.
154   void UnregisterHandler(HandlerId id);
155 
156   // Ends a currently pending transaction with the given response or
157   // confirmation |pdu|. Returns false if |pdu| is malformed or if |id| and
158   // |pdu| do not match a pending transaction.
159   bool Reply(TransactionId tid, ByteBufferPtr pdu);
160 
161   // Ends a request transaction with an error response.
162   bool ReplyWithError(TransactionId id, Handle handle, ErrorCode error_code);
163 
164   using WeakPtr = WeakSelf<Bearer>::WeakPtr;
GetWeakPtr()165   WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
166 
167  private:
168   explicit Bearer(l2cap::Channel::WeakPtr chan,
169                   pw::async::Dispatcher& dispatcher);
170 
171   // Returns false if activation fails. This is called by the factory method.
172   bool Activate();
173 
174   // Represents a locally initiated pending request or indication transaction.
175   struct PendingTransaction {
176     PendingTransaction(OpCode opcode_in,
177                        TransactionCallback callback_in,
178                        ByteBufferPtr pdu_in);
179 
180     // Required fields
181     OpCode opcode;
182     TransactionCallback callback;
183 
184     // Holds the pdu while the transaction is in the send queue.
185     ByteBufferPtr pdu;
186 
187     // Contains the most recently requested security upgrade level under which
188     // this transaction has been retried following an ATT security error. The
189     // states look like the following:
190     //
191     //   - sm::SecurityLevel::kNoSecurity: The transaction has not been retried.
192     //   - sm::SecurityLevel::kEncrypted: The request has been queued following
193     //     a security upgrate to level "Encrypted".
194     //   - sm::SecurityLevel::kAuthenticated: The request has been queued
195     //     following a security upgrate to level "Authenticated".
196     //
197     // and so on.
198     sm::SecurityLevel security_retry_level;
199   };
200   using PendingTransactionPtr = std::unique_ptr<PendingTransaction>;
201 
202   // Represents a remote initiated pending request or indication transaction.
203   struct PendingRemoteTransaction {
204     PendingRemoteTransaction(TransactionId id_in, OpCode opcode_in);
205     PendingRemoteTransaction() = default;
206 
207     TransactionId id;
208     OpCode opcode;
209   };
210 
211   // Used the represent the state of active ATT protocol request and indication
212   // transactions.
213   class TransactionQueue {
214    public:
TransactionQueue(pw::async::Dispatcher & dispatcher)215     TransactionQueue(pw::async::Dispatcher& dispatcher)
216         : timeout_task_(dispatcher) {}
217     ~TransactionQueue() = default;
218 
219     TransactionQueue(TransactionQueue&& other);
220 
221     // Returns the transaction that has been sent to the peer and is currently
222     // pending completion.
current()223     inline PendingTransaction* current() const { return current_.get(); }
224 
225     // Clears the currently pending transaction data and cancels its transaction
226     // timeout task. Returns ownership of any pending transaction to the caller.
227     PendingTransactionPtr ClearCurrent();
228 
229     // Tries to initiate the next transaction. Sends the PDU over |chan| if
230     // successful.
231     void TrySendNext(const l2cap::Channel::WeakPtr& chan,
232                      pw::async::TaskFunction timeout_cb,
233                      pw::chrono::SystemClock::duration timeout);
234 
235     // Adds |next| to the transaction queue.
236     void Enqueue(PendingTransactionPtr transaction);
237 
238     // Resets the contents of this queue to their default state.
239     void Reset();
240 
241     // Invokes the error callbacks of all transactions with |status|.
242     void InvokeErrorAll(Error error);
243 
244    private:
245     std::queue<std::unique_ptr<PendingTransaction>> queue_;
246     std::unique_ptr<PendingTransaction> current_;
247     SmartTask timeout_task_;
248   };
249 
250   bool SendInternal(ByteBufferPtr pdu, TransactionCallback callback);
251 
252   // Shuts down the link.
253   void ShutDownInternal(bool due_to_timeout);
254 
255   // Returns false if |packet| is malformed.
256   bool IsPacketValid(const ByteBuffer& packet);
257 
258   // Tries to initiate the next transaction from the given |queue|.
259   void TryStartNextTransaction(TransactionQueue* tq);
260 
261   // Sends out an immediate error response.
262   void SendErrorResponse(OpCode request_opcode,
263                          Handle attribute_handle,
264                          ErrorCode error_code);
265 
266   // Called when the peer sends us a response or confirmation PDU.
267   void HandleEndTransaction(TransactionQueue* tq, const PacketReader& packet);
268 
269   // Returns the next handler ID and increments the counter.
270   HandlerId NextHandlerId();
271 
272   // Returns the next remote-initiated transaction ID.
273   TransactionId NextRemoteTransactionId();
274 
275   using RemoteTransaction = std::optional<PendingRemoteTransaction>;
276 
277   // Called when the peer initiates a request or indication transaction.
278   void HandleBeginTransaction(RemoteTransaction* currently_pending,
279                               const PacketReader& packet);
280 
281   // Returns any pending peer-initiated transaction that matches |id|. Returns
282   // nullptr otherwise.
283   RemoteTransaction* FindRemoteTransaction(TransactionId id);
284 
285   // Called when the peer sends us a PDU that has no flow control (i.e. command
286   // or notification). Routes the packet to the corresponding handler. Drops the
287   // packet if no handler exists.
288   void HandlePDUWithoutResponse(const PacketReader& packet);
289 
290   // l2cap::Channel callbacks:
291   void OnChannelClosed();
292   void OnRxBFrame(ByteBufferPtr sdu);
293 
294   pw::async::Dispatcher& dispatcher_;
295 
296   l2cap::ScopedChannel chan_;
297   uint16_t mtu_;
298   uint16_t preferred_mtu_;
299   uint16_t min_mtu_;
300 
301   // Set to true on the first call to ShutDownInternal.
302   bool shut_down_ = false;
303 
304   // Channel closed callback assigned to us via set_closed_callback().
305   fit::closure closed_cb_;
306 
307   // The state of outgoing ATT requests and indications
308   TransactionQueue request_queue_;
309   TransactionQueue indication_queue_;
310 
311   // The transaction identifier that will be assigned to the next
312   // remote-initiated request or indication transaction.
313   TransactionId next_remote_transaction_id_;
314 
315   // The next available remote-initiated PDU handler id.
316   HandlerId next_handler_id_;
317 
318   // Data about currently registered handlers.
319   std::unordered_map<HandlerId, OpCode> handler_id_map_;
320   std::unordered_map<OpCode, Handler> handlers_;
321 
322   // Remote-initiated transactions in progress.
323   RemoteTransaction remote_request_;
324   RemoteTransaction remote_indication_;
325 
326   WeakSelf<Bearer> weak_self_;
327 
328   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Bearer);
329 };
330 
331 }  // namespace bt::att
332