| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTOR_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTOR_H_ |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/device_address.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/low_energy_connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/low_energy_connection_request.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/low_energy_interrogator.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_connector.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/error.h" |
| |
| namespace bt::gap::internal { |
| |
| class LowEnergyConnector final { |
| public: |
| using ResultCallback = hci::ResultCallback<std::unique_ptr<LowEnergyConnection>>; |
| |
| // Initiate an outbound connection to |peer_id|. |cb| will be called with the result of the |
| // procedure. |
| static std::unique_ptr<LowEnergyConnector> CreateOutboundConnector( |
| PeerId peer_id, LowEnergyConnectionOptions options, hci::LowEnergyConnector* connector, |
| zx::duration request_timeout, fxl::WeakPtr<hci::Transport> transport, PeerCache* peer_cache, |
| fxl::WeakPtr<LowEnergyDiscoveryManager> discovery_manager, |
| fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr, l2cap::L2cap* l2cap, |
| fxl::WeakPtr<gatt::GATT> gatt, ResultCallback cb); |
| |
| // Start interrogating peer using an already established |connection|. |cb| will be called with |
| // the result of the procedure. |
| static std::unique_ptr<LowEnergyConnector> CreateInboundConnector( |
| PeerId peer_id, std::unique_ptr<hci::LowEnergyConnection> connection, |
| LowEnergyConnectionOptions options, fxl::WeakPtr<hci::Transport> transport, |
| PeerCache* peer_cache, fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr, l2cap::L2cap* l2cap, |
| fxl::WeakPtr<gatt::GATT> gatt, ResultCallback cb); |
| |
| // Instances should only be destroyed after the result callback is called (except for stack tear |
| // down). Due to the asynchronous nature of cancelling the connection process, it is NOT safe to |
| // destroy a connector before the result callback has been called. The connector will be unable to |
| // wait for the HCI connection cancellation to complete, which can lead to failure to connect in |
| // later connectors (as the HCI connector is still pending). |
| ~LowEnergyConnector(); |
| |
| // Cancelling an already completed connector is a no-op. |
| void Cancel(); |
| |
| // Attach connector inspect node as a child node of |parent| with the name |name|. |
| void AttachInspect(inspect::Node& parent, std::string name); |
| |
| private: |
| enum class State { |
| kIdle, |
| kStartingScanning, |
| kScanning, |
| kConnecting, |
| kInterrogating, |
| kAwaitingConnectionFailedToBeEstablishedDisconnect, |
| kPauseBeforeConnectionRetry, |
| kComplete, |
| kFailed, |
| }; |
| |
| LowEnergyConnector(bool outbound, PeerId peer_id, |
| std::unique_ptr<hci::LowEnergyConnection> connection, |
| LowEnergyConnectionOptions options, hci::LowEnergyConnector* connector, |
| zx::duration request_timeout, fxl::WeakPtr<hci::Transport> transport, |
| PeerCache* peer_cache, fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr, |
| fxl::WeakPtr<LowEnergyDiscoveryManager> discovery_manager, l2cap::L2cap* l2cap, |
| fxl::WeakPtr<gatt::GATT> gatt, ResultCallback cb); |
| |
| static const char* StateToString(State); |
| |
| // Initiate scanning for peer before connecting to ensure it is advertising. |
| void StartScanningForPeer(); |
| void OnScanStart(LowEnergyDiscoverySessionPtr session); |
| |
| // Initiate HCI connection procedure. |
| void RequestCreateConnection(); |
| void OnConnectResult(hci::Result<> status, std::unique_ptr<hci::LowEnergyConnection> link); |
| |
| // Creates LowEnergyConnection and initializes fixed channels & timers. |
| // Returns true on success, false on failure. |
| bool InitializeConnection(std::unique_ptr<hci::LowEnergyConnection> link); |
| |
| void StartInterrogation(); |
| void OnInterrogationComplete(hci::Result<> status); |
| |
| // Handle a disconnect during kInterrogating or |
| // kAwaitingConnectionFailedToBeEstablishedDisconnect. |
| void OnPeerDisconnect(hci_spec::StatusCode status); |
| |
| // Returns true if the connection is retried. |
| // |
| // The link layer only considers a connection established after a packet is received from the peer |
| // before (6 * connInterval), even though it notifies the host immediately after sending a |
| // CONNECT_IND pdu. See Core Spec v5.2, Vol 6, Part B, Sec 4.5 for details. |
| // |
| // In the field, we have noticed a substantial amount of 0x3e (Connection Failed to be |
| // Established) HCI link errors occurring on links AFTER being notified of successful HCI-level |
| // connection. To work around this issue, we perform link-layer interrogation on the peer before |
| // returning gap::LowEnergyConnections to higher layer clients. If we receive the 0x3e error |
| // during interrogation, we will retry the connection process a number of times. |
| bool MaybeRetryConnection(); |
| |
| void NotifySuccess(); |
| void NotifyFailure(hci::Result<> status = ToResult(HostError::kFailed)); |
| |
| StringInspectable<State> state_; |
| |
| PeerId peer_id_; |
| DeviceAddress peer_address_; |
| PeerCache* peer_cache_; |
| |
| // Layer pointers to be passed to LowEnergyConnection. |
| l2cap::L2cap* l2cap_; |
| fxl::WeakPtr<gatt::GATT> gatt_; |
| |
| // True if this connector is connecting an outbound connection, false if it is connecting an |
| // inbound connection. |
| const bool is_outbound_; |
| |
| // Time after which an outbound HCI connection request is considered to have timed out. This |
| // is configurable to allow unit tests to set a shorter value. |
| zx::duration hci_request_timeout_; |
| |
| LowEnergyConnectionOptions options_; |
| |
| // Callback used to return the result of the connection procedure to the owning class. |
| ResultCallback result_cb_; |
| |
| // Used to connect outbound connections during the kConnecting state. |
| hci::LowEnergyConnector* hci_connector_; |
| |
| // The LowEnergyConnection to be passed to LowEnergyConnectionManager. Created during the |
| // kConnecting state for outbound connections, or during construction for inbound connections. |
| std::unique_ptr<internal::LowEnergyConnection> connection_; |
| |
| // For outbound connections, this is a 0-indexed counter of which connection attempt the connector |
| // is on. |
| IntInspectable<int> connection_attempt_; |
| |
| async::TaskClosureMethod<LowEnergyConnector, &LowEnergyConnector::RequestCreateConnection> |
| request_create_connection_task_{this}; |
| |
| // Task called after the scan attempt times out. |
| std::optional<async::TaskClosure> scan_timeout_task_; |
| |
| std::unique_ptr<LowEnergyDiscoverySession> discovery_session_; |
| |
| // Sends HCI commands that request version and feature support information from peer |
| // controllers. |
| LowEnergyInterrogator interrogator_; |
| |
| fxl::WeakPtr<LowEnergyDiscoveryManager> discovery_manager_; |
| |
| fxl::WeakPtr<hci::Transport> transport_; |
| |
| // Only used to construct a LowEnergyConnection. |
| fxl::WeakPtr<LowEnergyConnectionManager> le_connection_manager_; |
| |
| struct InspectProperties { |
| inspect::StringProperty peer_id; |
| inspect::BoolProperty is_outbound; |
| }; |
| InspectProperties inspect_properties_; |
| inspect::Node inspect_node_; |
| |
| fxl::WeakPtrFactory<LowEnergyConnector> weak_ptr_factory_; |
| }; |
| |
| } // namespace bt::gap::internal |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTOR_H_ |