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 <pw_assert/check.h> 17 18 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h" 19 #include "pw_bluetooth_sapphire/internal/host/common/inspectable.h" 20 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h" 21 #include "pw_bluetooth_sapphire/internal/host/gap/generic_access_client.h" 22 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_handle.h" 23 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_request.h" 24 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h" 25 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 26 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h" 27 #include "pw_bluetooth_sapphire/internal/host/iso/iso_stream_manager.h" 28 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h" 29 #include "pw_bluetooth_sapphire/internal/host/sm/delegate.h" 30 #include "pw_bluetooth_sapphire/internal/host/sm/types.h" 31 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h" 32 33 namespace bt::sm { 34 class SecurityManager; 35 } 36 37 namespace bt::gap { 38 39 namespace internal { 40 41 // LowEnergyConnector constructs LowEnergyConnection instances immediately upon 42 // successful completion of the link layer connection procedure (to hook up HCI 43 // event callbacks). However, LowEnergyConnections aren't exposed to the rest of 44 // the stack (including the LowEnergyConnectionManager) until fully 45 // interrogated, as completion of the link-layer connection process is 46 // insufficient to guarantee a working connection. Thus this class represents 47 // the state of an active *AND* (outside of LowEnergyConnector) known-functional 48 // connection. 49 // 50 // Instances are kept alive as long as there is at least one 51 // LowEnergyConnectionHandle that references them. Instances are expected to be 52 // destroyed immediately after a peer disconnect event is received (as indicated 53 // by peer_disconnect_cb). 54 class LowEnergyConnection final : public sm::Delegate { 55 public: 56 // |peer| is the peer that this connection is connected to. 57 // |link| is the underlying LE HCI connection that this connection corresponds 58 // to. |peer_disconnect_cb| will be called when the peer disconnects. It will 59 // not be called before this method returns. |error_cb| will be called when a 60 // fatal connection error occurs and the connection should be closed (e.g. 61 // when L2CAP reports an error). It will not be called before this method 62 // returns. |conn_mgr| is the LowEnergyConnectionManager that owns this 63 // connection. |l2cap|, |gatt|, and |hci| are pointers to the interfaces of 64 // the corresponding layers. Returns nullptr if connection initialization 65 // fails. 66 using PeerDisconnectCallback = 67 fit::callback<void(pw::bluetooth::emboss::StatusCode)>; 68 using ErrorCallback = fit::callback<void()>; 69 static std::unique_ptr<LowEnergyConnection> Create( 70 Peer::WeakPtr peer, 71 std::unique_ptr<hci::LowEnergyConnection> link, 72 LowEnergyConnectionOptions connection_options, 73 PeerDisconnectCallback peer_disconnect_cb, 74 ErrorCallback error_cb, 75 WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr, 76 l2cap::ChannelManager* l2cap, 77 gatt::GATT::WeakPtr gatt, 78 hci::Transport::WeakPtr hci, 79 pw::async::Dispatcher& dispatcher); 80 81 // Notifies request callbacks and connection refs of the disconnection. 82 ~LowEnergyConnection() override; 83 84 // Create a reference to this connection. When the last reference is dropped, 85 // this connection will be disconnected. 86 std::unique_ptr<LowEnergyConnectionHandle> AddRef(); 87 88 // Decrements the ref count. Must be called when a LowEnergyConnectionHandle 89 // is released/destroyed. 90 void DropRef(LowEnergyConnectionHandle* ref); 91 92 // Used to respond to protocol/service requests for increased security. 93 void OnSecurityRequest(sm::SecurityLevel level, sm::ResultFunction<> cb); 94 95 // Handles a pairing request (i.e. security upgrade) received from "higher 96 // levels", likely initiated from GAP. This will only be used by pairing 97 // requests that are initiated in the context of testing. May only be called 98 // on an already-established connection. 99 void UpgradeSecurity(sm::SecurityLevel level, 100 sm::BondableMode bondable_mode, 101 sm::ResultFunction<> cb); 102 103 // Cancels any on-going pairing procedures and sets up SMP to use the provided 104 // new I/O capabilities for future pairing procedures. 105 void ResetSecurityManager(sm::IOCapability ioc); 106 107 // Must be called when interrogation has completed. May update connection 108 // parameters if all initialization procedures have completed. 109 void OnInterrogationComplete(); 110 111 // Opens an L2CAP channel using the parameters |params|. Otherwise, calls |cb| 112 // with a nullptr. 113 void OpenL2capChannel(l2cap::Psm psm, 114 l2cap::ChannelParameters params, 115 l2cap::ChannelCallback cb); 116 117 // Accept a future incoming request to establish an Isochronous stream on this 118 // LE connection. |id| specifies the CIG/CIS pair that identify the stream. 119 // |cb| will be called after the request is received to indicate success of 120 // establishing a stream, and the associated parameters. 121 iso::AcceptCisStatus AcceptCis(iso::CigCisIdentifier id, 122 iso::CisEstablishedCallback cb); 123 124 // Attach connection as child node of |parent| with specified |name|. 125 void AttachInspect(inspect::Node& parent, std::string name); 126 127 void set_security_mode(LESecurityMode mode); 128 129 // Sets a callback that will be called when the peer disconnects. set_peer_disconnect_callback(PeerDisconnectCallback cb)130 void set_peer_disconnect_callback(PeerDisconnectCallback cb) { 131 PW_CHECK(cb); 132 peer_disconnect_callback_ = std::move(cb); 133 } 134 135 // |peer_conn_token| is a token generated by the connected Peer, and is used 136 // to synchronize connection state. set_peer_conn_token(Peer::ConnectionToken peer_conn_token)137 void set_peer_conn_token(Peer::ConnectionToken peer_conn_token) { 138 PW_CHECK(interrogation_completed_); 139 PW_CHECK(!peer_conn_token_); 140 peer_conn_token_ = std::move(peer_conn_token); 141 } 142 143 // Sets a callback that will be called when a fatal connection error occurs. set_error_callback(ErrorCallback cb)144 void set_error_callback(ErrorCallback cb) { 145 PW_CHECK(cb); 146 error_callback_ = std::move(cb); 147 } 148 ref_count()149 size_t ref_count() const { return refs_->size(); } 150 peer_id()151 PeerId peer_id() const { return peer_->identifier(); } handle()152 hci_spec::ConnectionHandle handle() const { return link_->handle(); } link()153 hci::LowEnergyConnection* link() const { return link_.get(); } 154 sm::BondableMode bondable_mode() const; 155 156 sm::SecurityProperties security() const; 157 role()158 pw::bluetooth::emboss::ConnectionRole role() const { return link()->role(); } 159 160 using WeakPtr = WeakSelf<LowEnergyConnection>::WeakPtr; GetWeakPtr()161 LowEnergyConnection::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); } 162 163 private: 164 LowEnergyConnection(Peer::WeakPtr peer, 165 std::unique_ptr<hci::LowEnergyConnection> link, 166 LowEnergyConnectionOptions connection_options, 167 PeerDisconnectCallback peer_disconnect_cb, 168 ErrorCallback error_cb, 169 WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr, 170 std::unique_ptr<iso::IsoStreamManager> iso_mgr, 171 l2cap::ChannelManager* l2cap, 172 gatt::GATT::WeakPtr gatt, 173 hci::Transport::WeakPtr hci, 174 pw::async::Dispatcher& dispatcher); 175 176 // Registers this connection with L2CAP and initializes the fixed channel 177 // protocols. Return true on success, false on failure. 178 [[nodiscard]] bool InitializeFixedChannels(); 179 180 // Register handlers for HCI events that correspond to this connection. 181 void RegisterEventHandlers(); 182 183 // Start kLEConnectionPauseCentral/Peripheral timeout that will update 184 // connection parameters. Should be called as soon as this GAP connection is 185 // established. 186 void StartConnectionPauseTimeout(); 187 188 // Start kLEConnectionPausePeripheral timeout that will send a connection 189 // parameter update request. Should be called as soon as connection is 190 // established. 191 void StartConnectionPausePeripheralTimeout(); 192 193 // Start kLEConnectionPauseCentral timeout that will update connection 194 // parameters. Should be called as soon as connection is established. 195 void StartConnectionPauseCentralTimeout(); 196 197 // Initializes SecurityManager and GATT. 198 // Called by the L2CAP layer once the link has been registered and the fixed 199 // channels have been opened. Returns false if GATT initialization fails. 200 [[nodiscard]] bool OnL2capFixedChannelsOpened( 201 l2cap::Channel::WeakPtr att, 202 l2cap::Channel::WeakPtr smp, 203 LowEnergyConnectionOptions connection_options); 204 205 // Called when the preferred connection parameters have been received for a LE 206 // peripheral. This can happen in the form of: 207 // 208 // 1. <<Peripheral Connection Interval Range>> advertising data field 209 // 2. "Peripheral Preferred Connection Parameters" GATT characteristic 210 // (under "GAP" service) 211 // 3. HCI LE Remote Connection Parameter Request Event 212 // 4. L2CAP Connection Parameter Update request 213 // 214 // TODO(fxbug.dev/42147867): Support #1 above. 215 // TODO(fxbug.dev/42147868): Support #3 above. 216 // 217 // This method caches |params| for later connection attempts and sends the 218 // parameters to the controller if the initializing procedures are complete 219 // (since we use more agressing initial parameters for pairing and service 220 // discovery, as recommended by the specification in v5.0, Vol 3, Part C, 221 // Section 9.3.12.1). 222 // 223 // |peer_id| uniquely identifies the peer. |handle| represents 224 // the logical link that |params| should be applied to. 225 void OnNewLEConnectionParams( 226 const hci_spec::LEPreferredConnectionParameters& params); 227 228 // As an LE peripheral, request that the connection parameters |params| be 229 // used on the given connection |conn| with peer |peer_id|. This may send an 230 // HCI LE Connection Update command or an L2CAP Connection Parameter Update 231 // Request depending on what the local and remote controllers support. 232 // 233 // Interrogation must have completed before this may be called. 234 void RequestConnectionParameterUpdate( 235 const hci_spec::LEPreferredConnectionParameters& params); 236 237 // Handler for connection parameter update command sent when an update is 238 // requested by RequestConnectionParameterUpdate. 239 // 240 // If the HCI LE Connection Update command fails with status 241 // kUnsupportedRemoteFeature, the update will be retried with an L2CAP 242 // Connection Parameter Update Request. 243 void HandleRequestConnectionParameterUpdateCommandStatus( 244 hci_spec::LEPreferredConnectionParameters params, hci::Result<> status); 245 246 // As an LE peripheral, send an L2CAP Connection Parameter Update Request 247 // requesting |params| on the LE signaling channel of the given logical link 248 // |handle|. 249 // 250 // NOTE: This should only be used if the LE peripheral and/or LE central do 251 // not support the Connection Parameters Request Link Layer Control Procedure 252 // (Core Spec v5.2 Vol 3, Part A, Sec 4.20). If they do, 253 // UpdateConnectionParams(...) should be used instead. 254 void L2capRequestConnectionParameterUpdate( 255 const hci_spec::LEPreferredConnectionParameters& params); 256 257 // Requests that the controller use the given connection |params| by sending 258 // an HCI LE Connection Update command. This may be issued on both the LE 259 // peripheral and the LE central. 260 // 261 // The link layer may modify the preferred parameters |params| before 262 // initiating the Connection Parameters Request Link Layer Control Procedure 263 // (Core Spec v5.2, Vol 6, Part B, Sec 5.1.7). 264 // 265 // If non-null, |status_cb| will be called when the HCI Command Status event 266 // is received. 267 // 268 // The HCI LE Connection Update Complete event will be generated after the 269 // parameters have been applied or if the update fails, and will indicate the 270 // (possibly modified) parameter values. 271 // 272 // NOTE: If the local host is an LE peripheral, then the local controller and 273 // the remote LE central must have indicated support for this procedure in the 274 // LE feature mask. Otherwise, L2capRequestConnectionParameterUpdate(...) 275 // should be used instead. 276 using StatusCallback = hci::ResultCallback<>; 277 void UpdateConnectionParams( 278 const hci_spec::LEPreferredConnectionParameters& params, 279 StatusCallback status_cb = nullptr); 280 281 // This event may be generated without host interaction by the Link Layer, or 282 // as the result of a Connection Update Command sent by either device, which 283 // is why it is not simply handled by the command handler. (See Core Spec 284 // v5.2, Vol 6, Part B, Sec 5.1.7.1). 285 void OnLEConnectionUpdateComplete(const hci::EventPacket& event); 286 287 // Updates or requests an update of the connection parameters, for central and 288 // peripheral roles respectively, if interrogation has completed. 289 // TODO(fxbug.dev/42159733): Wait to update connection parameters until all 290 // initialization procedures have completed. 291 void MaybeUpdateConnectionParameters(); 292 293 // Registers the peer with GATT and initiates service discovery. If 294 // |service_uuid| is specified, only discover the indicated service and the 295 // GAP service. Returns true on success, false on failure. 296 bool InitializeGatt(l2cap::Channel::WeakPtr att, 297 std::optional<UUID> service_uuid); 298 299 // Called when service discovery completes. |services| will only include 300 // services with the GAP UUID (there should only be one, but this is not 301 // guaranteed). 302 void OnGattServicesResult(att::Result<> status, gatt::ServiceList services); 303 304 // Notifies all connection refs of disconnection. 305 void CloseRefs(); 306 307 // sm::Delegate overrides: 308 void OnPairingComplete(sm::Result<> status) override; 309 void OnAuthenticationFailure(hci::Result<> status) override; 310 void OnNewSecurityProperties(const sm::SecurityProperties& sec) override; 311 std::optional<sm::IdentityInfo> OnIdentityInformationRequest() override; 312 void ConfirmPairing(ConfirmCallback confirm) override; 313 void DisplayPasskey(uint32_t passkey, 314 sm::Delegate::DisplayMethod method, 315 ConfirmCallback confirm) override; 316 void RequestPasskey(PasskeyResponseCallback respond) override; 317 318 pw::async::Dispatcher& dispatcher_; 319 320 // Notifies Peer of connection destruction. This should be ordered first so 321 // that it is destroyed last. 322 std::optional<Peer::ConnectionToken> peer_conn_token_; 323 324 Peer::WeakPtr peer_; 325 std::unique_ptr<hci::LowEnergyConnection> link_; 326 LowEnergyConnectionOptions connection_options_; 327 WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr_; 328 329 // Manages all Isochronous streams for this connection. If this connection is 330 // operating as a Central, |iso_mgr_| is used to establish an outgoing 331 // connection to a peer. When operating as a Peripheral, |iso_mgr_| is used to 332 // allow incoming requests for specified CIG/CIS combinations. 333 std::unique_ptr<iso::IsoStreamManager> iso_mgr_; 334 335 struct InspectProperties { 336 inspect::StringProperty peer_id; 337 inspect::StringProperty peer_address; 338 }; 339 InspectProperties inspect_properties_; 340 inspect::Node inspect_node_; 341 342 // Used to update the L2CAP layer to reflect the correct link security level. 343 l2cap::ChannelManager* l2cap_; 344 345 // Reference to the GATT profile layer is used to initiate service discovery 346 // and register the link. 347 gatt::GATT::WeakPtr gatt_; 348 349 // The ATT Bearer is owned by LowEnergyConnection but weak pointers are passed 350 // to the GATT layer. As such, this connection must be unregistered from the 351 // GATT layer before the Bearer is destroyed. Created during initialization, 352 // but if initialization fails this may be nullptr. 353 std::unique_ptr<att::Bearer> att_bearer_; 354 355 // SMP pairing manager. 356 std::unique_ptr<sm::SecurityManager> sm_; 357 358 hci::CommandChannel::WeakPtr cmd_; 359 360 hci::Transport::WeakPtr hci_; 361 362 // Called when the peer disconnects. 363 PeerDisconnectCallback peer_disconnect_callback_; 364 365 // Called when a fatal connection error occurs and the connection should be 366 // closed (e.g. when L2CAP reports an error). 367 ErrorCallback error_callback_; 368 369 // Event handler ID for the HCI LE Connection Update Complete event. 370 hci::CommandChannel::EventHandlerId conn_update_cmpl_handler_id_; 371 372 // Called with the status of the next HCI LE Connection Update Complete event. 373 // The HCI LE Connection Update command does not have its own complete event 374 // handler because the HCI LE Connection Complete event can be generated for 375 // other reasons. 376 fit::callback<void(pw::bluetooth::emboss::StatusCode)> 377 le_conn_update_complete_command_callback_; 378 379 // Called after kLEConnectionPausePeripheral. 380 std::optional<SmartTask> conn_pause_peripheral_timeout_; 381 382 // Called after kLEConnectionPauseCentral. 383 std::optional<SmartTask> conn_pause_central_timeout_; 384 385 // Set to true when a request to update the connection parameters has been 386 // sent. 387 bool connection_parameters_update_requested_ = false; 388 389 bool interrogation_completed_ = false; 390 391 // LowEnergyConnectionManager is responsible for making sure that these 392 // pointers are always valid. 393 using ConnectionHandleSet = std::unordered_set<LowEnergyConnectionHandle*>; 394 IntInspectable<ConnectionHandleSet> refs_; 395 396 // Null until service discovery completes. 397 std::optional<GenericAccessClient> gap_service_client_; 398 399 WeakSelf<LowEnergyConnection> weak_self_; 400 WeakSelf<sm::Delegate> weak_delegate_; 401 402 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnection); 403 }; 404 405 } // namespace internal 406 } // namespace bt::gap 407