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