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