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 
18 #include <list>
19 #include <memory>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include "lib/fit/result.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/error.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/metrics.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/windowed_inspect_numeric_property.h"
29 #include "pw_bluetooth_sapphire/internal/host/gap/adapter_state.h"
30 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
31 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_request.h"
32 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connector.h"
33 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_discovery_manager.h"
34 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h"
35 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h"
36 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connector.h"
37 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
38 #include "pw_bluetooth_sapphire/internal/host/sm/error.h"
39 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
40 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
41 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
42 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
43 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
44 
45 namespace bt {
46 
47 namespace sm {
48 using SecurityManagerFactory = std::function<std::unique_ptr<SecurityManager>(
49     hci::LowEnergyConnection::WeakPtr,
50     l2cap::Channel::WeakPtr,
51     IOCapability,
52     Delegate::WeakPtr,
53     BondableMode,
54     gap::LESecurityMode,
55     pw::async::Dispatcher&)>;
56 }  // namespace sm
57 
58 namespace hci {
59 class LocalAddressDelegate;
60 }  // namespace hci
61 
62 namespace gap {
63 
64 namespace internal {
65 class LowEnergyConnection;
66 }  // namespace internal
67 
68 // TODO(armansito): Document the usage pattern.
69 
70 class LowEnergyConnectionManager;
71 class PairingDelegate;
72 class Peer;
73 class PeerCache;
74 
75 enum class LowEnergyDisconnectReason : uint8_t {
76   // Explicit disconnect request
77   kApiRequest,
78   // An internal error was encountered
79   kError,
80 };
81 
82 // LowEnergyConnectionManager is responsible for connecting and initializing new
83 // connections, interrogating connections, intiating pairing, and disconnecting
84 // connections.
85 class LowEnergyConnectionManager final {
86  public:
87   // Duration after which connection failures are removed from Inspect.
88   static constexpr pw::chrono::SystemClock::duration
89       kInspectRecentConnectionFailuresExpiryDuration = std::chrono::minutes(10);
90 
91   // |hci|: The HCI transport used to track link layer connection events from
92   //        the controller.
93   // |addr_delegate|: Used to obtain local identity information during pairing
94   //                  procedures.
95   // |connector|: Adapter object for initiating link layer connections. This
96   //              object abstracts the legacy and extended HCI command sets.
97   // |peer_cache|: The cache that stores peer peer data. The connection
98   //                 manager stores and retrieves pairing data and connection
99   //                 parameters to/from the cache. It also updates the
100   //                 connection and bonding state of a peer via the cache.
101   // |l2cap|: Used to interact with the L2CAP layer.
102   // |gatt|: Used to interact with the GATT profile layer.
103   // |adapter_state|: Provides information on controller capabilities.
104   LowEnergyConnectionManager(
105       hci::Transport::WeakPtr hci,
106       hci::LocalAddressDelegate* addr_delegate,
107       hci::LowEnergyConnector* connector,
108       PeerCache* peer_cache,
109       l2cap::ChannelManager* l2cap,
110       gatt::GATT::WeakPtr gatt,
111       LowEnergyDiscoveryManager::WeakPtr discovery_manager,
112       sm::SecurityManagerFactory sm_creator,
113       const AdapterState& adapter_state,
114       pw::async::Dispatcher& dispatcher);
115   ~LowEnergyConnectionManager();
116 
117   // Allows a caller to claim shared ownership over a connection to the
118   // requested remote LE peer identified by |peer_id|.
119   //   * If |peer_id| is not recognized, |callback| is called with an error.
120   //
121   //   * If the requested peer is already connected, |callback| is called with a
122   //     LowEnergyConnectionHandle immediately.
123   //     This is done for both local and remote initiated connections (i.e. the
124   //     local adapter can either be in the LE central or peripheral roles).
125   //
126   //   * If the requested peer is NOT connected, then this method initiates a
127   //     connection to the requested peer using the
128   //     internal::LowEnergyConnector. See that class's documentation for a more
129   //     detailed overview of the Connection process. A
130   //     LowEnergyConnectionHandle is asynchronously returned to the caller once
131   //     the connection has been set up.
132   //
133   // The status of the procedure is reported in |callback| in the case of an
134   // error.
135   using ConnectionResult =
136       fit::result<HostError, std::unique_ptr<LowEnergyConnectionHandle>>;
137   using ConnectionResultCallback = fit::function<void(ConnectionResult)>;
138   void Connect(PeerId peer_id,
139                ConnectionResultCallback callback,
140                LowEnergyConnectionOptions connection_options);
141 
local_address_delegate()142   hci::LocalAddressDelegate* local_address_delegate() const {
143     return local_address_delegate_;
144   }
145 
146   // Disconnects any existing or pending LE connection to |peer_id|,
147   // invalidating all active LowEnergyConnectionHandles. Returns false if the
148   // peer can not be disconnected.
149   bool Disconnect(PeerId peer_id,
150                   LowEnergyDisconnectReason reason =
151                       LowEnergyDisconnectReason::kApiRequest);
152 
153   // Initializes a new connection over the given |link| and asynchronously
154   // returns a connection reference.
155   //
156   // |link| must be the result of a remote initiated connection.
157   //
158   // |callback| will be called with a connection status and connection
159   // reference. The connection reference will be nullptr if the connection was
160   // rejected (as indicated by a failure status).
161   //
162   // TODO(armansito): Add an |own_address| parameter for the locally advertised
163   // address that was connected to.
164   //
165   // A link with the given handle should not have been previously registered.
166   void RegisterRemoteInitiatedLink(
167       std::unique_ptr<hci::LowEnergyConnection> link,
168       sm::BondableMode bondable_mode,
169       ConnectionResultCallback callback);
170 
171   // Returns the PairingDelegate currently assigned to this connection manager.
pairing_delegate()172   const PairingDelegate::WeakPtr& pairing_delegate() const {
173     return pairing_delegate_;
174   }
175 
176   // Assigns a new PairingDelegate to handle LE authentication challenges.
177   // Replacing an existing pairing delegate cancels all ongoing pairing
178   // procedures. If a delegate is not set then all pairing requests will be
179   // rejected.
180   void SetPairingDelegate(const PairingDelegate::WeakPtr& delegate);
181 
182   // Opens a new L2CAP channel to service |psm| on |peer_id| using the preferred
183   // parameters |params|.
184   //
185   // |cb| will be called with the channel created to the peer, or nullptr if the
186   // channel creation resulted in an error.
187   void OpenL2capChannel(PeerId peer_id,
188                         l2cap::Psm psm,
189                         l2cap::ChannelParameters params,
190                         sm::SecurityLevel security_level,
191                         l2cap::ChannelCallback cb);
192 
193   // TODO(armansito): Add a PeerCache::Observer interface and move these
194   // callbacks there.
195 
196   // Called when a link with the given handle gets disconnected. This event is
197   // guaranteed to be called before invalidating connection references.
198   // |callback| is run on the creation thread.
199   //
200   // NOTE: This is intended ONLY for unit tests. Clients should watch for
201   // disconnection events using LowEnergyConnectionHandle::set_closed_callback()
202   // instead. DO NOT use outside of tests.
203   using DisconnectCallback = fit::function<void(hci_spec::ConnectionHandle)>;
204   void SetDisconnectCallbackForTesting(DisconnectCallback callback);
205 
206   // Sets the timeout interval to be used on future connect requests. The
207   // default value is kLECreateConnectionTimeout.
set_request_timeout_for_testing(pw::chrono::SystemClock::duration value)208   void set_request_timeout_for_testing(
209       pw::chrono::SystemClock::duration value) {
210     request_timeout_ = value;
211   }
212 
213   // Callback for hci::Connection, called when the peer disconnects.
214   // |reason| is used to control retry logic.
215   void OnPeerDisconnect(const hci::Connection* connection,
216                         pw::bluetooth::emboss::StatusCode reason);
217 
218   // Initiates the pairing process. Expected to only be called during
219   // higher-level testing.
220   //   |peer_id|: the peer to pair to - if the peer is not connected, |cb| is
221   //   called with an error. |pairing_level|: determines the security level of
222   //   the pairing. **Note**: If the security
223   //                    level of the link is already >= |pairing level|, no
224   //                    pairing takes place.
225   //   |bondable_mode|: sets the bonding mode of this connection. A device in
226   //   bondable mode forms a
227   //                    bond to the peer upon pairing, assuming the peer is also
228   //                    in bondable mode. A device in non-bondable mode will not
229   //                    allow pairing that forms a bond.
230   //   |cb|: callback called upon completion of this function, whether pairing
231   //   takes place or not.
232   void Pair(PeerId peer_id,
233             sm::SecurityLevel pairing_level,
234             sm::BondableMode bondable_mode,
235             sm::ResultFunction<> cb);
236 
237   // Sets the LE security mode of the local device (see v5.2 Vol. 3 Part C
238   // Section 10.2). If set to SecureConnectionsOnly, any currently encrypted
239   // links not meeting the requirements of Security Mode 1 Level 4 will be
240   // disconnected.
241   void SetSecurityMode(LESecurityMode mode);
242 
243   // Attach manager inspect node as a child node of |parent|.
244   void AttachInspect(inspect::Node& parent, std::string name);
245 
security_mode()246   LESecurityMode security_mode() const { return security_mode_; }
sm_factory_func()247   sm::SecurityManagerFactory sm_factory_func() const {
248     return sm_factory_func_;
249   }
250 
251   using WeakPtr = WeakSelf<LowEnergyConnectionManager>::WeakPtr;
252 
253  private:
254   friend class internal::LowEnergyConnection;
255 
256   // Mapping from peer identifiers to open LE connections.
257   using ConnectionMap =
258       std::unordered_map<PeerId,
259                          std::unique_ptr<internal::LowEnergyConnection>>;
260 
261   // Called by LowEnergyConnectionHandle::Release().
262   void ReleaseReference(LowEnergyConnectionHandle* handle);
263 
264   // Initiates a new connection attempt for the next peer in the pending list,
265   // if any.
266   void TryCreateNextConnection();
267 
268   // Called by internal::LowEnergyConnector to indicate the result of a local
269   // connect request.
270   void OnLocalInitiatedConnectResult(
271       hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result);
272 
273   // Called by internal::LowEnergyConnector to indicate the result of a remote
274   // connect request.
275   void OnRemoteInitiatedConnectResult(
276       PeerId peer_id,
277       hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result);
278 
279   // Either report an error to clients or initialize the connection and report
280   // success to clients.
281   void ProcessConnectResult(
282       hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result,
283       internal::LowEnergyConnectionRequest request);
284 
285   // Finish setting up connection, adding to |connections_| map, and notifying
286   // clients.
287   bool InitializeConnection(
288       std::unique_ptr<internal::LowEnergyConnection> connection,
289       internal::LowEnergyConnectionRequest request);
290 
291   // Cleans up a connection state. This results in a HCI_Disconnect command if
292   // the connection has not already been disconnected, and notifies any
293   // referenced LowEnergyConnectionHandles of the disconnection. Marks the
294   // corresponding PeerCache entry as disconnected and cleans up all data
295   // bearers.
296   //
297   // |conn_state| will have been removed from the underlying map at the time of
298   // a call. Its ownership is passed to the method for disposal.
299   //
300   // This is also responsible for unregistering the link from managed subsystems
301   // (e.g. L2CAP).
302   void CleanUpConnection(std::unique_ptr<internal::LowEnergyConnection> conn);
303 
304   // Updates |peer_cache_| with the given |link| and returns the corresponding
305   // Peer.
306   //
307   // Creates a new Peer if |link| matches a peer that did not
308   // previously exist in the cache. Otherwise this updates and returns an
309   // existing Peer.
310   //
311   // The returned peer is marked as non-temporary and its connection
312   // parameters are updated.
313   //
314   // Called by RegisterRemoteInitiatedLink() and RegisterLocalInitiatedLink().
315   Peer* UpdatePeerWithLink(const hci::LowEnergyConnection& link);
316 
317   // Called when the peer disconnects with a "Connection Failed to be
318   // Established" error. Cleans up the existing connection and adds the
319   // connection request back to the queue for a retry.
320   void CleanUpAndRetryConnection(
321       std::unique_ptr<internal::LowEnergyConnection> connection);
322 
323   // Returns an iterator into |connections_| if a connection is found that
324   // matches the given logical link |handle|. Otherwise, returns an iterator
325   // that is equal to |connections_.end()|.
326   //
327   // The general rules of validity around std::unordered_map::iterator apply to
328   // the returned value.
329   ConnectionMap::iterator FindConnection(hci_spec::ConnectionHandle handle);
330 
331   pw::async::Dispatcher& dispatcher_;
332 
333   hci::Transport::WeakPtr hci_;
334 
335   // The pairing delegate used for authentication challenges. If nullptr, all
336   // pairing requests will be rejected.
337   PairingDelegate::WeakPtr pairing_delegate_;
338 
339   // The GAP LE security mode of the device (v5.2 Vol. 3 Part C 10.2).
340   LESecurityMode security_mode_;
341 
342   // The function used to create each channel's SecurityManager implementation.
343   sm::SecurityManagerFactory sm_factory_func_;
344 
345   // Time after which a connection attempt is considered to have timed out. This
346   // is configurable to allow unit tests to set a shorter value.
347   pw::chrono::SystemClock::duration request_timeout_;
348 
349   // The peer cache is used to look up and persist remote peer data that is
350   // relevant during connection establishment (such as the address, preferred
351   // connection parameters, etc). Expected to outlive this instance.
352   PeerCache* peer_cache_;  // weak
353 
354   // The reference to L2CAP, used to interact with the L2CAP layer to
355   // manage LE logical links, fixed channels, and LE-specific L2CAP signaling
356   // events (e.g. connection parameter update).
357   l2cap::ChannelManager* l2cap_;
358 
359   // The GATT layer reference, used to add and remove ATT data bearers and
360   // service discovery.
361   gatt::GATT::WeakPtr gatt_;
362 
363   // Provides us with information on the capabilities of our controller
364   AdapterState adapter_state_;
365 
366   // Local GATT service registry.
367   std::unique_ptr<gatt::LocalServiceManager> gatt_registry_;
368 
369   LowEnergyDiscoveryManager::WeakPtr discovery_manager_;
370 
371   // Callbacks used by unit tests to observe connection state events.
372   DisconnectCallback test_disconn_cb_;
373 
374   // Outstanding connection requests based on remote peer ID.
375   std::unordered_map<PeerId, internal::LowEnergyConnectionRequest>
376       pending_requests_;
377 
378   // Mapping from peer identifiers to currently open LE connections.
379   ConnectionMap connections_;
380 
381   struct RequestAndConnector {
382     internal::LowEnergyConnectionRequest request;
383     std::unique_ptr<internal::LowEnergyConnector> connector;
384   };
385   // The in-progress locally initiated connection request, if any.
386   std::optional<RequestAndConnector> current_request_;
387 
388   // Active connectors for remote connection requests.
389   std::unordered_map<PeerId, RequestAndConnector> remote_connectors_;
390 
391   // For passing to internal::LowEnergyConnector. |hci_connector_| must
392   // out-live this connection manager.
393   hci::LowEnergyConnector* hci_connector_;  // weak
394 
395   // Address manager is used to obtain local identity information during pairing
396   // procedures. Expected to outlive this instance.
397   hci::LocalAddressDelegate* local_address_delegate_;  // weak
398 
399   // True if the connection manager is performing a scan for a peer before
400   // connecting.
401   bool scanning_ = false;
402 
403   struct InspectProperties {
404     // Count of connection failures in the past 10 minutes.
InspectPropertiesInspectProperties405     explicit InspectProperties(pw::async::Dispatcher& pw_dispatcher)
406         : recent_connection_failures(
407               pw_dispatcher, kInspectRecentConnectionFailuresExpiryDuration) {}
408     WindowedInspectIntProperty recent_connection_failures;
409 
410     UintMetricCounter outgoing_connection_success_count_;
411     UintMetricCounter outgoing_connection_failure_count_;
412     UintMetricCounter incoming_connection_success_count_;
413     UintMetricCounter incoming_connection_failure_count_;
414 
415     UintMetricCounter disconnect_explicit_disconnect_count_;
416     UintMetricCounter disconnect_link_error_count_;
417     UintMetricCounter disconnect_zero_ref_count_;
418     UintMetricCounter disconnect_remote_disconnection_count_;
419   };
420   InspectProperties inspect_properties_{dispatcher_};
421   inspect::Node inspect_node_;
422   // Container node for pending request nodes.
423   inspect::Node inspect_pending_requests_node_;
424   // container node for connection nodes.
425   inspect::Node inspect_connections_node_;
426 
427   // Keep this as the last member to make sure that all weak pointers are
428   // invalidated before other members get destroyed.
429   WeakSelf<LowEnergyConnectionManager> weak_self_;
430 
431   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnectionManager);
432 };
433 
434 }  // namespace gap
435 }  // namespace bt
436