blob: 4b8d7d782ad1657726d3727a54fb00d58e73cade [file] [log] [blame]
// 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_