blob: 15e21c62d33b1684766997e4d3c78621c6f3aa0f [file] [log] [blame]
// Copyright 2020 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_CONNECTION_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTION_H_
#include <lib/async/dispatcher.h>
#include <lib/sys/inspect/cpp/component.h>
#include "gap.h"
#include "low_energy_connection_handle.h"
#include "low_energy_connection_request.h"
#include "src/connectivity/bluetooth/core/bt-host/common/identifier.h"
#include "src/connectivity/bluetooth/core/bt-host/common/inspectable.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/generic_access_client.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/gatt.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_connection.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/security_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/types.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt::gap {
class LowEnergyConnectionManager;
namespace internal {
// LowEnergyConnector constructs LowEnergyConnection instances immediately upon successful
// completion of the link layer connection procedure (to hook up HCI event callbacks). However,
// LowEnergyConnections aren't exposed to the rest of the stack (including the
// LowEnergyConnectionManager) until fully interrogated, as completion of the link-layer connection
// process is insufficient to guarantee a working connection. Thus this class represents the state
// of an active *AND* (outside of LowEnergyConnector) known-functional connection.
//
// Instances are kept alive as long as there is at least one LowEnergyConnectionHandle that
// references them. Instances are expected to be destroyed immediately after a peer disconnect
// event is received (as indicated by peer_disconnect_cb).
class LowEnergyConnection final : public sm::Delegate {
public:
// |peer| is the peer that this connection is connected to.
// |link| is the underlying LE HCI connection that this connection corresponds to.
// |peer_disconnect_cb| will be called when the peer disconnects.
// |error_cb| will be called when a fatal connection error occurs and the connection should be
// closed (e.g. when L2CAP reports an error).
// |conn_mgr| is the LowEnergyConnectionManager that owns this connection.
// |l2cap|, |gatt|, and |transport| are pointers to the interfaces of the corresponding layers.
using PeerDisconnectCallback = fit::callback<void(hci_spec::StatusCode)>;
using ErrorCallback = fit::callback<void()>;
LowEnergyConnection(fxl::WeakPtr<Peer> peer, std::unique_ptr<hci::LowEnergyConnection> link,
LowEnergyConnectionOptions connection_options,
PeerDisconnectCallback peer_disconnect_cb, ErrorCallback error_cb,
fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr, l2cap::L2cap* l2cap,
fxl::WeakPtr<gatt::GATT> gatt, fxl::WeakPtr<hci::Transport> transport);
// Notifies request callbacks and connection refs of the disconnection.
~LowEnergyConnection() override;
// Create a reference to this connection. When the last reference is dropped, this connection will
// be disconnected.
std::unique_ptr<LowEnergyConnectionHandle> AddRef();
// Decrements the ref count. Must be called when a LowEnergyConnectionHandle is
// released/destroyed.
void DropRef(LowEnergyConnectionHandle* ref);
// Used to respond to protocol/service requests for increased security.
void OnSecurityRequest(sm::SecurityLevel level, sm::ResultFunction<> cb);
// Handles a pairing request (i.e. security upgrade) received from "higher levels", likely
// initiated from GAP. This will only be used by pairing requests that are initiated
// in the context of testing. May only be called on an already-established connection.
void UpgradeSecurity(sm::SecurityLevel level, sm::BondableMode bondable_mode,
sm::ResultFunction<> cb);
// Cancels any on-going pairing procedures and sets up SMP to use the provided
// new I/O capabilities for future pairing procedures.
void ResetSecurityManager(sm::IOCapability ioc);
// Must be called when interrogation has completed. May update connection parameters if all
// initialization procedures have completed.
void OnInterrogationComplete();
// Attach connection as child node of |parent| with specified |name|.
void AttachInspect(inspect::Node& parent, std::string name);
void set_security_mode(LESecurityMode mode) {
ZX_ASSERT(sm_);
sm_->set_security_mode(mode);
}
// Sets a callback that will be called when the peer disconnects.
void set_peer_disconnect_callback(PeerDisconnectCallback cb) {
ZX_ASSERT(cb);
peer_disconnect_callback_ = std::move(cb);
}
// |peer_conn_token| is a token generated by the connected Peer, and is used to
// synchronize connection state.
void set_peer_conn_token(Peer::ConnectionToken peer_conn_token) {
ZX_ASSERT(interrogation_completed_);
ZX_ASSERT(!peer_conn_token_);
peer_conn_token_ = std::move(peer_conn_token);
}
// Sets a callback that will be called when a fatal connection error occurs.
void set_error_callback(ErrorCallback cb) {
ZX_ASSERT(cb);
error_callback_ = std::move(cb);
}
size_t ref_count() const { return refs_->size(); }
PeerId peer_id() const { return peer_->identifier(); }
hci_spec::ConnectionHandle handle() const { return link_->handle(); }
hci::LowEnergyConnection* link() const { return link_.get(); }
sm::BondableMode bondable_mode() const {
ZX_ASSERT(sm_);
return sm_->bondable_mode();
}
sm::SecurityProperties security() const {
ZX_ASSERT(sm_);
return sm_->security();
}
fxl::WeakPtr<LowEnergyConnection> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
private:
// Registers this connection with L2CAP and initializes the fixed channel
// protocols.
void InitializeFixedChannels();
// Register handlers for HCI events that correspond to this connection.
void RegisterEventHandlers();
// Start kLEConnectionPauseCentral/Peripheral timeout that will update connection parameters.
// Should be called as soon as this GAP connection is established.
void StartConnectionPauseTimeout();
// Start kLEConnectionPausePeripheral timeout that will send a connection parameter update
// request. Should be called as soon as connection is established.
void StartConnectionPausePeripheralTimeout();
// Start kLEConnectionPauseCentral timeout that will update connection parameters.
// Should be called as soon as connection is established.
void StartConnectionPauseCentralTimeout();
// Called by the L2CAP layer once the link has been registered and the fixed
// channels have been opened.
void OnL2capFixedChannelsOpened(fbl::RefPtr<l2cap::Channel> att, fbl::RefPtr<l2cap::Channel> smp,
LowEnergyConnectionOptions connection_options);
// Called when the preferred connection parameters have been received for a LE
// peripheral. This can happen in the form of:
//
// 1. <<Peripheral Connection Interval Range>> advertising data field
// 2. "Peripheral Preferred Connection Parameters" GATT characteristic
// (under "GAP" service)
// 3. HCI LE Remote Connection Parameter Request Event
// 4. L2CAP Connection Parameter Update request
//
// TODO(fxbug.dev/68803): Support #1 above.
// TODO(fxbug.dev/68804): Support #3 above.
//
// This method caches |params| for later connection attempts and sends the
// parameters to the controller if the initializing procedures are complete
// (since we use more agressing initial parameters for pairing and service
// discovery, as recommended by the specification in v5.0, Vol 3, Part C,
// Section 9.3.12.1).
//
// |peer_id| uniquely identifies the peer. |handle| represents
// the logical link that |params| should be applied to.
void OnNewLEConnectionParams(const hci_spec::LEPreferredConnectionParameters& params);
// As an LE peripheral, request that the connection parameters |params| be used on the given
// connection |conn| with peer |peer_id|. This may send an HCI LE Connection Update command or an
// L2CAP Connection Parameter Update Request depending on what the local and remote controllers
// support.
//
// Interrogation must have completed before this may be called.
void RequestConnectionParameterUpdate(const hci_spec::LEPreferredConnectionParameters& params);
// Handler for connection parameter update command sent when an update is requested by
// RequestConnectionParameterUpdate.
//
// If the HCI LE Connection Update command fails with status kUnsupportedRemoteFeature, the update
// will be retried with an L2CAP Connection Parameter Update Request.
void HandleRequestConnectionParameterUpdateCommandStatus(
hci_spec::LEPreferredConnectionParameters params, hci::Result<> status);
// As an LE peripheral, send an L2CAP Connection Parameter Update Request requesting |params| on
// the LE signaling channel of the given logical link |handle|.
//
// NOTE: This should only be used if the LE peripheral and/or LE central do not support the
// Connection Parameters Request Link Layer Control Procedure (Core Spec v5.2 Vol 3, Part A,
// Sec 4.20). If they do, UpdateConnectionParams(...) should be used instead.
void L2capRequestConnectionParameterUpdate(
const hci_spec::LEPreferredConnectionParameters& params);
// Requests that the controller use the given connection |params| by sending an HCI LE Connection
// Update command. This may be issued on both the LE peripheral and the LE central.
//
// The link layer may modify the preferred parameters |params| before initiating the Connection
// Parameters Request Link Layer Control Procedure (Core Spec v5.2, Vol 6, Part B, Sec 5.1.7).
//
// If non-null, |status_cb| will be called when the HCI Command Status event is received.
//
// The HCI LE Connection Update Complete event will be generated after the parameters have been
// applied or if the update fails, and will indicate the (possibly modified) parameter values.
//
// NOTE: If the local host is an LE peripheral, then the local controller and the remote
// LE central must have indicated support for this procedure in the LE feature mask. Otherwise,
// L2capRequestConnectionParameterUpdate(...) should be used instead.
using StatusCallback = hci::ResultCallback<>;
void UpdateConnectionParams(const hci_spec::LEPreferredConnectionParameters& params,
StatusCallback status_cb = nullptr);
// This event may be generated without host interaction by the Link Layer, or as the result of a
// Connection Update Command sent by either device, which is why it is not simply handled by the
// command handler. (See Core Spec v5.2, Vol 6, Part B, Sec 5.1.7.1).
void OnLEConnectionUpdateComplete(const hci::EventPacket& event);
// Updates or requests an update of the connection parameters, for central and peripheral roles
// respectively, if interrogation has completed.
// TODO(fxbug.dev/79491): Wait to update connection parameters until all initialization
// procedures have completed.
void MaybeUpdateConnectionParameters();
// Registers the peer with GATT and initiates service discovery. If |service_uuid| is specified,
// only discover the indicated service and the GAP service.
void InitializeGatt(fbl::RefPtr<l2cap::Channel> att, std::optional<UUID> service_uuid);
// Called when service discovery completes. |services| will only include services with the GAP
// UUID (there should only be one, but this is not guaranteed).
void OnGattServicesResult(att::Result<> status, gatt::ServiceList services);
// Notifies all connection refs of disconnection.
void CloseRefs();
// sm::Delegate overrides:
void OnNewPairingData(const sm::PairingData& pairing_data) override;
void OnPairingComplete(sm::Result<> status) override;
void OnAuthenticationFailure(hci::Result<> status) override;
void OnNewSecurityProperties(const sm::SecurityProperties& sec) override;
std::optional<sm::IdentityInfo> OnIdentityInformationRequest() override;
void ConfirmPairing(ConfirmCallback confirm) override;
void DisplayPasskey(uint32_t passkey, sm::Delegate::DisplayMethod method,
ConfirmCallback confirm) override;
void RequestPasskey(PasskeyResponseCallback respond) override;
// Notifies Peer of connection destruction. This should be ordered first so that it is
// destroyed last.
std::optional<Peer::ConnectionToken> peer_conn_token_;
fxl::WeakPtr<Peer> peer_;
std::unique_ptr<hci::LowEnergyConnection> link_;
LowEnergyConnectionOptions connection_options_;
fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr_;
struct InspectProperties {
inspect::StringProperty peer_id;
inspect::StringProperty peer_address;
};
InspectProperties inspect_properties_;
inspect::Node inspect_node_;
// Used to update the L2CAP layer to reflect the correct link security level.
l2cap::L2cap* l2cap_;
// Reference to the GATT profile layer is used to initiate service discovery
// and register the link.
fxl::WeakPtr<gatt::GATT> gatt_;
// SMP pairing manager.
std::unique_ptr<sm::SecurityManager> sm_;
fxl::WeakPtr<hci::Transport> transport_;
// Called when the peer disconnects.
PeerDisconnectCallback peer_disconnect_callback_;
// Called when a fatal connection error occurs and the connection should be
// closed (e.g. when L2CAP reports an error).
ErrorCallback error_callback_;
// Event handler ID for the HCI LE Connection Update Complete event.
hci::CommandChannel::EventHandlerId conn_update_cmpl_handler_id_;
// Called with the status of the next HCI LE Connection Update Complete event.
// The HCI LE Connection Update command does not have its own complete event handler because the
// HCI LE Connection Complete event can be generated for other reasons.
fit::callback<void(hci_spec::StatusCode)> le_conn_update_complete_command_callback_;
// Called after kLEConnectionPausePeripheral.
std::optional<async::TaskClosure> conn_pause_peripheral_timeout_;
// Called after kLEConnectionPauseCentral.
std::optional<async::TaskClosure> conn_pause_central_timeout_;
// Set to true when a request to update the connection parameters has been sent.
bool connection_parameters_update_requested_ = false;
bool interrogation_completed_ = false;
// LowEnergyConnectionManager is responsible for making sure that these
// pointers are always valid.
using ConnectionHandleSet = std::unordered_set<LowEnergyConnectionHandle*>;
IntInspectable<ConnectionHandleSet> refs_;
// Null until service discovery completes.
std::optional<GenericAccessClient> gap_service_client_;
fxl::WeakPtrFactory<LowEnergyConnection> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnection);
};
} // namespace internal
} // namespace bt::gap
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTION_H_