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