• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #include <optional>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/bounded_inspect_list_node.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/expiring_set.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/metrics.h"
22 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection.h"
23 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection_request.h"
24 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
25 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
26 #include "pw_bluetooth_sapphire/internal/host/gap/types.h"
27 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
28 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
29 #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection_request.h"
30 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
31 #include "pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h"
32 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
33 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
34 #include "pw_bluetooth_sapphire/internal/host/sdp/service_discoverer.h"
35 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
36 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
37 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
38 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
39 
40 namespace bt {
41 
42 namespace hci {
43 class SequentialCommandRunner;
44 }  // namespace hci
45 
46 namespace gap {
47 
48 class PairingDelegate;
49 class PeerCache;
50 
51 enum class DisconnectReason : uint8_t {
52   // A FIDL method explicitly requested this disconnect
53   kApiRequest,
54   // The interrogation procedure for this peer failed
55   kInterrogationFailed,
56   // The connection encountered an error during Pairing
57   kPairingFailed,
58   // An error was encountered on the ACL link
59   kAclLinkError,
60   // Peer Disconnected
61   kPeerDisconnection,
62 };
63 
64 // Manages all activity related to connections in the BR/EDR section of the
65 // controller, including whether the peer can be connected to, incoming
66 // connections, and initiating connections.
67 //
68 // There are two flows for destroying connections: explicit local
69 // disconnections, and peer disconnections. When the connection is disconnected
70 // explicitly with |Disconnect()|, the connection is immediately cleaned up and
71 // removed from the internal |connections_| map and owned by itself until the
72 // HCI Disconnection Complete event is received by the underlying
73 // hci::Connection object. When the peer disconnects, the |OnPeerDisconnect()|
74 // callback is called by the underlying hci::Connection object and the
75 // connection is cleaned up and removed from the internal |connections_| map.
76 class BrEdrConnectionManager final {
77  public:
78   BrEdrConnectionManager(
79       hci::Transport::WeakPtr hci,
80       PeerCache* peer_cache,
81       DeviceAddress local_address,
82       hci::LocalAddressDelegate* low_energy_address_delegate,
83       l2cap::ChannelManager* l2cap,
84       bool use_interlaced_scan,
85       bool local_secure_connections_supported,
86       bool legacy_pairing_enabled,
87       bool controller_remote_public_key_validation_supported,
88       sm::BrEdrSecurityManagerFactory security_manager_factory,
89       pw::async::Dispatcher& dispatcher);
90   ~BrEdrConnectionManager();
91 
92   // Set whether this host is connectable
93   void SetConnectable(bool connectable, hci::ResultFunction<> status_cb);
94 
95   // Returns the PairingDelegate currently assigned to this connection manager.
pairing_delegate()96   const PairingDelegate::WeakPtr& pairing_delegate() const {
97     return pairing_delegate_;
98   }
99 
100   // Assigns a new PairingDelegate to handle BR/EDR authentication challenges.
101   // Replacing an existing pairing delegate cancels all ongoing pairing
102   // procedures. If a delegate is not set then all pairing requests will be
103   // rejected.
104   void SetPairingDelegate(PairingDelegate::WeakPtr delegate);
105 
106   // Retrieves the peer id that is connected to the connection |handle|.
107   // Returns kInvalidPeerId if no such peer exists.
108   PeerId GetPeerId(hci_spec::ConnectionHandle handle) const;
109 
110   // Opens a new L2CAP channel to service |psm| on |peer_id| using the preferred
111   // parameters |params|. If the current connection doesn't meet
112   // |security_requirements|, attempt to upgrade the link key and report an
113   // error via |cb| if the upgrade fails.
114   //
115   // |cb| will be called with the channel created to the peer, or nullptr if the
116   // channel creation resulted in an error.
117   void OpenL2capChannel(PeerId peer_id,
118                         l2cap::Psm psm,
119                         BrEdrSecurityRequirements security_requirements,
120                         l2cap::ChannelParameters params,
121                         l2cap::ChannelCallback cb);
122 
123   // See ScoConnectionManager for documentation. If a BR/EDR connection with
124   // the peer does not exist, returns nullopt.
125   using ScoRequestHandle = BrEdrConnection::ScoRequestHandle;
126   std::optional<ScoRequestHandle> OpenScoConnection(
127       PeerId peer_id,
128       bt::StaticPacket<
129           pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
130           parameters,
131       sco::ScoConnectionManager::OpenConnectionCallback callback);
132   std::optional<ScoRequestHandle> AcceptScoConnection(
133       PeerId peer_id,
134       std::vector<bt::StaticPacket<
135           pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
136           parameters,
137       sco::ScoConnectionManager::AcceptConnectionCallback callback);
138 
139   // Add a service search to be performed on new connected remote peers.
140   // This search will happen on every peer connection.
141   // |callback| will be called with the |attributes| that exist in the service
142   // entry on the peer's SDP server. If |attributes| is empty, all attributes on
143   // the server will be returned. Returns a SearchId which can be used to remove
144   // the search later. Identical searches will perform the same search for each
145   // search added. Results of added service searches will be added to each
146   // Peer's BrEdrData.
147   // TODO(fxbug.dev/42086791): Make identical searches just search once
148   using SearchCallback = sdp::ServiceDiscoverer::ResultCallback;
149   using SearchId = sdp::ServiceDiscoverer::SearchId;
150   SearchId AddServiceSearch(const UUID& uuid,
151                             std::unordered_set<sdp::AttributeId> attributes,
152                             SearchCallback callback);
153 
154   // Remove a search previously added with AddServiceSearch()
155   // Returns true if a search was removed.
156   // This function is idempotent.
157   bool RemoveServiceSearch(SearchId id);
158 
159   using ConnectResultCallback =
160       fit::function<void(hci::Result<>, BrEdrConnection*)>;
161 
162   // Initiates an outgoing Create Connection Request to attempt to connect to
163   // the peer identified by |peer_id|. Returns false if the connection
164   // request was invalid, otherwise returns true and |callback| will be called
165   // with the result of the procedure, whether successful or not
166   // TODO(fxbug.dev/42087181) - implement a timeout
167   [[nodiscard]] bool Connect(PeerId peer_id, ConnectResultCallback callback);
168 
169   // Initiate pairing to the peer with |peer_id| using the bondable preference.
170   // Pairing will only be initiated if the current link key does not meet the
171   // |security| requirements. |callback| will be called with the result of the
172   // procedure, successful or not.
173   void Pair(PeerId peer_id,
174             BrEdrSecurityRequirements security,
175             hci::ResultFunction<> callback);
176 
177   // Disconnects any existing BR/EDR connection to |peer_id|. Returns true if
178   // the peer is disconnected, false if the peer can not be disconnected.
179   bool Disconnect(PeerId peer_id, DisconnectReason reason);
180 
181   // Sets the BR/EDR security mode of the local device (Core Spec v5.4, Vol 3,
182   // Part C, 5.2.2). If set to SecureConnectionsOnly, any currently encrypted
183   // links not meeting the requirements of Security Mode 4 Level 4 will be
184   // disconnected.
185   void SetSecurityMode(BrEdrSecurityMode mode);
186 
security_mode()187   BrEdrSecurityMode security_mode() { return *security_mode_; }
188 
189   // If `reason` is DisconnectReason::kApiRequest, then incoming connections
190   // from `peer_id` are rejected for kLocalDisconnectCooldownDuration
191   static constexpr pw::chrono::SystemClock::duration
192       kLocalDisconnectCooldownDuration = std::chrono::seconds(30);
193 
194   // Attach Inspect node as child of |parent| named |name|.
195   // Only connections established after a call to AttachInspect are tracked.
196   void AttachInspect(inspect::Node& parent, std::string name);
197 
198  private:
199   using ConnectionMap =
200       std::unordered_map<hci_spec::ConnectionHandle, BrEdrConnection>;
201 
202   // Callback for hci::Connection. Called when the peer disconnects.
203   void OnPeerDisconnect(const hci::Connection* connection);
204 
205   // Called to cancel an outgoing connection request
206   void SendCreateConnectionCancelCommand(DeviceAddress addr);
207 
208   // Attempt to complete the active connection request for the peer |peer_id|
209   // with status |status|
210   void CompleteRequest(PeerId peer_id,
211                        DeviceAddress address,
212                        hci::Result<> status,
213                        hci_spec::ConnectionHandle handle);
214 
215   // Is there a current incoming connection request in progress for the given
216   // address
217   bool ExistsIncomingRequest(PeerId id);
218 
219   // Writes page timeout duration to the controller.
220   // |page_timeout| must be in the range [PageTimeout::MIN (0x0001),
221   // PageTimeout::MAX (0xFFFF)] |cb| will be called with the resulting return
222   // parameter status.
223   void WritePageTimeout(pw::chrono::SystemClock::duration page_timeout,
224                         hci::ResultFunction<> cb);
225 
226   // Reads the controller page scan settings.
227   void ReadPageScanSettings();
228 
229   // Writes page scan parameters to the controller.
230   // If |interlaced| is true, and the controller does not support interlaced
231   // page scan mode, standard mode is used.
232   void WritePageScanSettings(uint16_t interval,
233                              uint16_t window,
234                              bool interlaced,
235                              hci::ResultFunction<> cb);
236 
237   // Write PIN type used for legacy pairing to the controller.
238   void WritePinType(pw::bluetooth::emboss::PinType pin_type);
239 
240   // Helper to register an event handler to run.
241   hci::CommandChannel::EventHandlerId AddEventHandler(
242       const hci_spec::EventCode& code, hci::CommandChannel::EventCallback cb);
243 
244   // Find the outstanding connection request object for a connection request
245   // to/from |peer_id|. Returns nullopt if no request for |peer_id| exists.
246   std::optional<BrEdrConnectionRequest*> FindConnectionRequestById(
247       PeerId peer_id);
248 
249   // Find the handle for a connection to |peer_id|. Returns nullopt if no BR/EDR
250   // |peer_id| is connected.
251   std::optional<std::pair<hci_spec::ConnectionHandle, BrEdrConnection*>>
252   FindConnectionById(PeerId peer_id);
253 
254   // Find the handle for a connection to |bd_addr|. Returns nullopt if no BR/EDR
255   // |bd_addr| is connected.
256   std::optional<std::pair<hci_spec::ConnectionHandle, BrEdrConnection*>>
257   FindConnectionByAddress(const DeviceAddressBytes& bd_addr);
258 
259   // Find a peer with |addr| or create one if not found.
260   Peer* FindOrInitPeer(DeviceAddress addr);
261 
262   // Initialize ACL connection state from |connection_handle| obtained from the
263   // controller and begin interrogation. The connection will be initialized with
264   // the role |role|.
265   void InitializeConnection(DeviceAddress addr,
266                             hci_spec::ConnectionHandle connection_handle,
267                             pw::bluetooth::emboss::ConnectionRole role);
268 
269   // Called once interrogation completes to make connection identified by
270   // |handle| available to upper layers and begin new connection procedures.
271   void CompleteConnectionSetup(Peer::WeakPtr peer,
272                                hci_spec::ConnectionHandle handle);
273 
274   // Callbacks for registered events
275   hci::CommandChannel::EventCallbackResult OnAuthenticationComplete(
276       const hci::EventPacket& event);
277   hci::CommandChannel::EventCallbackResult OnConnectionRequest(
278       const hci::EventPacket& event);
279   hci::CommandChannel::EventCallbackResult OnConnectionComplete(
280       const hci::EventPacket& event);
281   hci::CommandChannel::EventCallbackResult OnIoCapabilityRequest(
282       const hci::EventPacket& event);
283   hci::CommandChannel::EventCallbackResult OnIoCapabilityResponse(
284       const hci::EventPacket& event);
285   hci::CommandChannel::EventCallbackResult OnLinkKeyRequest(
286       const hci::EventPacket& event);
287   hci::CommandChannel::EventCallbackResult OnLinkKeyNotification(
288       const hci::EventPacket& event);
289   hci::CommandChannel::EventCallbackResult OnSimplePairingComplete(
290       const hci::EventPacket& event_packet);
291   hci::CommandChannel::EventCallbackResult OnUserConfirmationRequest(
292       const hci::EventPacket& event_packet);
293   hci::CommandChannel::EventCallbackResult OnUserPasskeyRequest(
294       const hci::EventPacket& event_packet);
295   hci::CommandChannel::EventCallbackResult OnUserPasskeyNotification(
296       const hci::EventPacket& event_packet);
297   hci::CommandChannel::EventCallbackResult OnRoleChange(
298       const hci::EventPacket& event);
299   hci::CommandChannel::EventCallbackResult OnPinCodeRequest(
300       const hci::EventPacket& event);
301 
302   void HandleNonAclConnectionRequest(const DeviceAddress& addr,
303                                      pw::bluetooth::emboss::LinkType link_type);
304 
305   // Called when we complete a pending request. Initiates a new connection
306   // attempt for the next peer in the pending list, if any.
307   void TryCreateNextConnection();
308 
309   // Called when a request times out waiting for a connection complete packet,
310   // *after* the command status was received. This is responsible for canceling
311   // the request and initiating the next one in the queue
312   void OnRequestTimeout();
313 
314   // Clean up |conn| after it has been deliberately disconnected or after its
315   // link closed. Unregisters the connection from the data domain and marks the
316   // peer's BR/EDR cache state as disconnected. Takes ownership of |conn| and
317   // destroys it.
318   void CleanUpConnection(hci_spec::ConnectionHandle handle,
319                          BrEdrConnection conn,
320                          DisconnectReason reason);
321 
322   // Helpers for sending commands on the command channel for this controller.
323   // All callbacks will run on |dispatcher_|.
324   void SendAuthenticationRequested(hci_spec::ConnectionHandle handle,
325                                    hci::ResultFunction<> cb);
326   void SendIoCapabilityRequestReply(
327       DeviceAddressBytes bd_addr,
328       pw::bluetooth::emboss::IoCapability io_capability,
329       pw::bluetooth::emboss::OobDataPresent oob_data_present,
330       pw::bluetooth::emboss::AuthenticationRequirements auth_requirements,
331       hci::ResultFunction<> cb = nullptr);
332   void SendIoCapabilityRequestNegativeReply(
333       DeviceAddressBytes bd_addr,
334       pw::bluetooth::emboss::StatusCode reason,
335       hci::ResultFunction<> cb = nullptr);
336   void SendUserConfirmationRequestReply(DeviceAddressBytes bd_addr,
337                                         hci::ResultFunction<> cb = nullptr);
338   void SendUserConfirmationRequestNegativeReply(
339       DeviceAddressBytes bd_addr, hci::ResultFunction<> cb = nullptr);
340   void SendUserPasskeyRequestReply(DeviceAddressBytes bd_addr,
341                                    uint32_t numeric_value,
342                                    hci::ResultFunction<> cb = nullptr);
343   void SendUserPasskeyRequestNegativeReply(DeviceAddressBytes bd_addr,
344                                            hci::ResultFunction<> cb = nullptr);
345   void SendLinkKeyRequestNegativeReply(DeviceAddressBytes bd_addr,
346                                        hci::ResultFunction<> cb = nullptr);
347   void SendLinkKeyRequestReply(DeviceAddressBytes bd_addr,
348                                hci_spec::LinkKey link_key,
349                                hci::ResultFunction<> cb = nullptr);
350   void SendAcceptConnectionRequest(DeviceAddressBytes addr,
351                                    hci::ResultFunction<> cb = nullptr);
352   void SendRejectConnectionRequest(DeviceAddress addr,
353                                    pw::bluetooth::emboss::StatusCode reason,
354                                    hci::ResultFunction<> cb = nullptr);
355   void SendRejectSynchronousRequest(DeviceAddress addr,
356                                     pw::bluetooth::emboss::StatusCode reason,
357                                     hci::ResultFunction<> cb = nullptr);
358   void SendPinCodeRequestReply(DeviceAddressBytes bd_addr,
359                                uint16_t pin_code,
360                                hci::ResultFunction<> cb = nullptr);
361   void SendPinCodeRequestNegativeReply(DeviceAddressBytes bd_addr,
362                                        hci::ResultFunction<> cb = nullptr);
363 
364   // Send the HCI command encoded in |command_packet|. If |cb| is not nullptr,
365   // the event returned will be decoded for its status, which is passed to |cb|.
366   template <typename T>
367   void SendCommandWithStatusCallback(T command_packet,
368                                      hci::ResultFunction<> cb);
369 
370   // Record a disconnection in Inspect's list of disconnections.
371   void RecordDisconnectInspect(const BrEdrConnection& conn,
372                                DisconnectReason reason);
373 
374   hci::Transport::WeakPtr hci_;
375   std::unique_ptr<hci::SequentialCommandRunner> hci_cmd_runner_;
376 
377   // The pairing delegate used for authentication challenges. If nullptr, all
378   // pairing requests will be rejected.
379   PairingDelegate::WeakPtr pairing_delegate_;
380 
381   // Peer cache is used to look up parameters for connecting to peers and
382   // update the state of connected peers as well as introduce unknown peers.
383   // This object must outlive this instance.
384   PeerCache* cache_;
385 
386   const DeviceAddress local_address_;
387 
388   l2cap::ChannelManager* l2cap_;
389 
390   // Discoverer for SDP services
391   sdp::ServiceDiscoverer discoverer_;
392 
393   // Holds the connections that are active.
394   ConnectionMap connections_;
395 
396   // Current security mode
397   StringInspectable<BrEdrSecurityMode> security_mode_{
398       BrEdrSecurityMode::Mode4,
399       /*convert=*/[](auto mode) { return BrEdrSecurityModeToString(mode); }};
400 
401   // Holds a denylist with cooldowns for locally requested disconnects.
402   ExpiringSet<DeviceAddress> deny_incoming_;
403 
404   // Handler IDs for registered events
405   std::vector<hci::CommandChannel::EventHandlerId> event_handler_ids_;
406 
407   // The current page scan parameters of the controller.
408   // Set to 0 when non-connectable.
409   uint16_t page_scan_interval_;
410   uint16_t page_scan_window_;
411   pw::bluetooth::emboss::PageScanType page_scan_type_;
412   bool use_interlaced_scan_;
413 
414   // True when local Host and Controller support BR/EDR Secure Connections
415   bool local_secure_connections_supported_;
416 
417   // When True, BR/EDR pairing may attempt to use legacy pairing if the peer
418   // does not support SSP.
419   bool legacy_pairing_enabled_;
420 
421   bool controller_remote_public_key_validation_supported_;
422 
423   sm::BrEdrSecurityManagerFactory security_manager_factory_;
424 
425   hci::LocalAddressDelegate* low_energy_address_delegate_;
426 
427   // Outstanding incoming and outgoing connection requests from remote peer with
428   // |PeerId|.
429   std::unordered_map<PeerId, BrEdrConnectionRequest> connection_requests_;
430 
431   // Represents an outgoing HCI_Connection_Request that has not been fulfilled.
432   // There may only be one outstanding request at any given time.
433   //
434   // This request is fulfilled on either an HCI_Connection_Complete event from
435   // the peer this request was sent to or a failure during the connection
436   // process. This request might not complete if the connection closes or if the
437   // request times out.
438   std::unique_ptr<hci::BrEdrConnectionRequest> pending_request_;
439 
440   struct InspectProperties {
441     BoundedInspectListNode last_disconnected_list =
442         BoundedInspectListNode(/*capacity=*/5);
443     inspect::Node connections_node_;
444     inspect::Node requests_node_;
445     struct DirectionalCounts {
446       inspect::Node node_;
447       UintMetricCounter connection_attempts_;
448       UintMetricCounter successful_connections_;
449       UintMetricCounter failed_connections_;
450     };
451 
452     // These stats only represent HCI connections.
453     // Connections may be closed if we fail to initialize, interrogate, or pair
454     DirectionalCounts outgoing_;
455     DirectionalCounts incoming_;
456 
457     // Successfully initialized connections
458     UintMetricCounter interrogation_complete_count_;
459 
460     UintMetricCounter disconnect_local_api_request_count_;
461     UintMetricCounter disconnect_interrogation_failed_count_;
462     UintMetricCounter disconnect_pairing_failed_count_;
463     UintMetricCounter disconnect_acl_link_error_count_;
464     UintMetricCounter disconnect_peer_disconnection_count_;
465   };
466   InspectProperties inspect_properties_;
467   inspect::Node inspect_node_;
468 
469   pw::async::Dispatcher& dispatcher_;
470 
471   // Keep this as the last member to make sure that all weak pointers are
472   // invalidated before other members get destroyed.
473   WeakSelf<BrEdrConnectionManager> weak_self_;
474 
475   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrConnectionManager);
476 };
477 
478 }  // namespace gap
479 }  // namespace bt
480