blob: 25ac37d6aeb1989608900fd0168555f0aeb5fb5e [file] [log] [blame]
// Copyright 2017 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.
#include "adapter.h"
#include <endian.h>
#include <lib/fit/thread_checker.h>
#include "bredr_connection_manager.h"
#include "bredr_discovery_manager.h"
#include "event_masks.h"
#include "low_energy_address_manager.h"
#include "low_energy_advertising_manager.h"
#include "low_energy_connection_manager.h"
#include "low_energy_discovery_manager.h"
#include "peer.h"
#include "src/connectivity/bluetooth/core/bt-host//hci-spec/vendor_protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/metrics.h"
#include "src/connectivity/bluetooth/core/bt-host/common/random.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/util.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/android_extended_low_energy_advertiser.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/extended_low_energy_advertiser.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/legacy_low_energy_advertiser.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/legacy_low_energy_scanner.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_connector.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/sequential_command_runner.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/transport.h"
namespace bt::gap {
namespace hci_android = hci_spec::vendor::android;
static constexpr const char* kInspectLowEnergyDiscoveryManagerNodeName =
"low_energy_discovery_manager";
static constexpr const char* kInspectLowEnergyConnectionManagerNodeName =
"low_energy_connection_manager";
static constexpr const char* kInspectBrEdrConnectionManagerNodeName = "bredr_connection_manager";
static constexpr const char* kInspectBrEdrDiscoveryManagerNodeName = "bredr_discovery_manager";
// All asynchronous callbacks are posted on the Loop on which this Adapter
// instance is created.
class AdapterImpl final : public Adapter {
public:
// There must be an async_t dispatcher registered as a default when an AdapterImpl
// instance is created. The Adapter instance will use it for all of its
// asynchronous tasks.
explicit AdapterImpl(fxl::WeakPtr<hci::Transport> hci, fxl::WeakPtr<gatt::GATT> gatt,
std::unique_ptr<l2cap::L2cap> l2cap);
~AdapterImpl() override;
AdapterId identifier() const override { return identifier_; }
bool Initialize(InitializeCallback callback, fit::closure transport_closed_cb) override;
void ShutDown() override;
bool IsInitializing() const override { return init_state_ == State::kInitializing; }
bool IsInitialized() const override { return init_state_ == State::kInitialized; }
const AdapterState& state() const override { return state_; }
class LowEnergyImpl final : public LowEnergy {
public:
explicit LowEnergyImpl(AdapterImpl* adapter) : adapter_(adapter) {}
void Connect(PeerId peer_id, ConnectionResultCallback callback,
LowEnergyConnectionOptions connection_options) override {
adapter_->le_connection_manager_->Connect(peer_id, std::move(callback), connection_options);
adapter_->metrics_.le.outgoing_connection_requests.Add();
}
bool Disconnect(PeerId peer_id) override {
return adapter_->le_connection_manager_->Disconnect(peer_id);
}
void Pair(PeerId peer_id, sm::SecurityLevel pairing_level, sm::BondableMode bondable_mode,
sm::ResultFunction<> cb) override {
adapter_->le_connection_manager_->Pair(peer_id, pairing_level, bondable_mode, std::move(cb));
adapter_->metrics_.le.pair_requests.Add();
}
void SetSecurityMode(LESecurityMode mode) override {
adapter_->le_connection_manager_->SetSecurityMode(mode);
}
LESecurityMode security_mode() const override {
return adapter_->le_connection_manager_->security_mode();
}
void StartAdvertising(AdvertisingData data, AdvertisingData scan_rsp,
AdvertisingInterval interval, bool anonymous, bool include_tx_power_level,
std::optional<ConnectableAdvertisingParameters> connectable,
AdvertisingStatusCallback status_callback) override {
LowEnergyAdvertisingManager::ConnectionCallback advertisement_connect_cb = nullptr;
if (connectable) {
ZX_ASSERT(connectable->connection_cb);
// All advertisement connections are first registered with LowEnergyConnectionManager before
// being reported to higher layers.
advertisement_connect_cb = [this, connectable = std::move(connectable)](
AdvertisementId advertisement_id,
std::unique_ptr<hci::LowEnergyConnection> link) mutable {
auto register_link_cb = [advertisement_id,
connection_callback = std::move(connectable->connection_cb)](
ConnectionResult result) {
connection_callback(advertisement_id, std::move(result));
};
adapter_->le_connection_manager_->RegisterRemoteInitiatedLink(
std::move(link), connectable->bondable_mode, std::move(register_link_cb));
};
}
adapter_->le_advertising_manager_->StartAdvertising(
std::move(data), std::move(scan_rsp), std::move(advertisement_connect_cb), interval,
anonymous, include_tx_power_level, std::move(status_callback));
adapter_->metrics_.le.start_advertising_events.Add();
}
void StopAdvertising(AdvertisementId advertisement_id) override {
adapter_->le_advertising_manager_->StopAdvertising(advertisement_id);
adapter_->metrics_.le.stop_advertising_events.Add();
}
void StartDiscovery(bool active, SessionCallback callback) override {
adapter_->le_discovery_manager_->StartDiscovery(active, std::move(callback));
adapter_->metrics_.le.start_discovery_events.Add();
}
void EnablePrivacy(bool enabled) override {
adapter_->le_address_manager_->EnablePrivacy(enabled);
}
void set_irk(const std::optional<UInt128>& irk) override {
adapter_->le_address_manager_->set_irk(irk);
}
std::optional<UInt128> irk() const override { return adapter_->le_address_manager_->irk(); }
void set_request_timeout_for_testing(zx::duration value) override {
adapter_->le_connection_manager_->set_request_timeout_for_testing(value);
}
void set_scan_period_for_testing(zx::duration period) override {
adapter_->le_discovery_manager_->set_scan_period(period);
}
private:
AdapterImpl* adapter_;
};
LowEnergy* le() const override { return low_energy_.get(); }
class BrEdrImpl final : public BrEdr {
public:
explicit BrEdrImpl(AdapterImpl* adapter) : adapter_(adapter) {}
bool Connect(PeerId peer_id, ConnectResultCallback callback) override {
return adapter_->bredr_connection_manager_->Connect(peer_id, std::move(callback));
adapter_->metrics_.bredr.outgoing_connection_requests.Add();
}
bool Disconnect(PeerId peer_id, DisconnectReason reason) override {
return adapter_->bredr_connection_manager_->Disconnect(peer_id, reason);
}
void OpenL2capChannel(PeerId peer_id, l2cap::PSM psm,
BrEdrSecurityRequirements security_requirements,
l2cap::ChannelParameters params, l2cap::ChannelCallback cb) override {
adapter_->metrics_.bredr.open_l2cap_channel_requests.Add();
adapter_->bredr_connection_manager_->OpenL2capChannel(peer_id, psm, security_requirements,
params, std::move(cb));
}
PeerId GetPeerId(hci_spec::ConnectionHandle handle) const override {
return adapter_->bredr_connection_manager_->GetPeerId(handle);
}
SearchId AddServiceSearch(const UUID& uuid, std::unordered_set<sdp::AttributeId> attributes,
SearchCallback callback) override {
return adapter_->bredr_connection_manager_->AddServiceSearch(uuid, std::move(attributes),
std::move(callback));
}
bool RemoveServiceSearch(SearchId id) override {
return adapter_->bredr_connection_manager_->RemoveServiceSearch(id);
}
void Pair(PeerId peer_id, BrEdrSecurityRequirements security,
hci::ResultFunction<> callback) override {
adapter_->bredr_connection_manager_->Pair(peer_id, security, std::move(callback));
adapter_->metrics_.bredr.pair_requests.Add();
}
void SetConnectable(bool connectable, hci::ResultFunction<> status_cb) override {
adapter_->bredr_connection_manager_->SetConnectable(connectable, std::move(status_cb));
if (connectable) {
adapter_->metrics_.bredr.set_connectable_true_events.Add();
} else {
adapter_->metrics_.bredr.set_connectable_false_events.Add();
}
}
void RequestDiscovery(DiscoveryCallback callback) override {
adapter_->bredr_discovery_manager_->RequestDiscovery(std::move(callback));
adapter_->metrics_.bredr.request_discovery_events.Add();
}
void RequestDiscoverable(DiscoverableCallback callback) override {
adapter_->bredr_discovery_manager_->RequestDiscoverable(std::move(callback));
adapter_->metrics_.bredr.request_discoverable_events.Add();
}
RegistrationHandle RegisterService(std::vector<sdp::ServiceRecord> records,
l2cap::ChannelParameters chan_params,
ServiceConnectCallback conn_cb) override {
return adapter_->sdp_server_->RegisterService(std::move(records), chan_params,
std::move(conn_cb));
}
bool UnregisterService(RegistrationHandle handle) override {
return adapter_->sdp_server_->UnregisterService(handle);
}
std::optional<ScoRequestHandle> OpenScoConnection(
PeerId peer_id, hci_spec::SynchronousConnectionParameters parameters,
sco::ScoConnectionManager::OpenConnectionCallback callback) override {
return adapter_->bredr_connection_manager_->OpenScoConnection(peer_id, parameters,
std::move(callback));
}
std::optional<ScoRequestHandle> AcceptScoConnection(
PeerId peer_id, std::vector<hci_spec::SynchronousConnectionParameters> parameters,
sco::ScoConnectionManager::AcceptConnectionCallback callback) override {
return adapter_->bredr_connection_manager_->AcceptScoConnection(peer_id, parameters,
std::move(callback));
}
private:
AdapterImpl* adapter_;
};
BrEdr* bredr() const override { return bredr_.get(); }
PeerCache* peer_cache() override { return &peer_cache_; }
bool AddBondedPeer(BondingData bonding_data) override;
void SetPairingDelegate(fxl::WeakPtr<PairingDelegate> delegate) override;
bool IsDiscoverable() const override;
bool IsDiscovering() const override;
void SetLocalName(std::string name, hci::ResultFunction<> callback) override;
std::string local_name() const override { return bredr_discovery_manager_->local_name(); }
void SetDeviceClass(DeviceClass dev_class, hci::ResultFunction<> callback) override;
void set_auto_connect_callback(AutoConnectCallback callback) override {
auto_conn_cb_ = std::move(callback);
}
void AttachInspect(inspect::Node& parent, std::string name) override;
fxl::WeakPtr<Adapter> AsWeakPtr() override { return weak_ptr_factory_.GetWeakPtr(); }
private:
// Second step of the initialization sequence. Called by Initialize() when the
// first batch of HCI commands have been sent.
void InitializeStep2(InitializeCallback callback);
// Third step of the initialization sequence. Called by InitializeStep2() when
// the second batch of HCI commands have been sent.
void InitializeStep3(InitializeCallback callback);
// Fourth step of the initialization sequence. Called by InitializeStep3()
// when the third batch of HCI commands have been sent.
void InitializeStep4(InitializeCallback callback);
// Assigns properties to |adapter_node_| using values discovered during other initialization
// steps.
void UpdateInspectProperties();
// Called by ShutDown() and during Initialize() in case of failure. This
// synchronously cleans up the transports and resets initialization state.
void CleanUp();
// Called by Transport after it has been unexpectedly closed.
void OnTransportClosed();
// Called when a directed connectable advertisement is received from a bonded
// LE device. This amounts to a connection request from a bonded peripheral
// which is handled by routing the request to |le_connection_manager_| to
// initiate a Direct Connection Establishment procedure (Vol 3, Part C,
// 9.3.8).
void OnLeAutoConnectRequest(Peer* peer);
// Called by |le_address_manager_| to query whether it is currently allowed to
// reconfigure the LE random address.
bool IsLeRandomAddressChangeAllowed();
std::unique_ptr<hci::LowEnergyAdvertiser> CreateAdvertiser() {
constexpr hci_spec::LESupportedFeature feature =
hci_spec::LESupportedFeature::kLEExtendedAdvertising;
if (state().low_energy_state().IsFeatureSupported(feature)) {
bt_log(INFO, "gap", "controller supports extended advertising, using extended LE commands");
return std::make_unique<hci::ExtendedLowEnergyAdvertiser>(hci_);
}
if (state().IsVendorFeatureSupported(hci::VendorFeaturesBits::kAndroidVendorExtensions)) {
uint8_t max_advt = state().android_vendor_capabilities().max_simultaneous_advertisements();
bt_log(INFO, "gap",
"controller supports android vendor extensions, max simultaneous advertisements: %d",
max_advt);
return std::make_unique<hci::AndroidExtendedLowEnergyAdvertiser>(hci_, max_advt);
}
bt_log(INFO, "gap", "controller supports only legacy advertising, using legacy LE commands");
return std::make_unique<hci::LegacyLowEnergyAdvertiser>(hci_);
}
// Must be initialized first so that child nodes can be passed to other constructors.
inspect::Node adapter_node_;
struct InspectProperties {
inspect::StringProperty adapter_id;
inspect::StringProperty hci_version;
inspect::UintProperty bredr_max_num_packets;
inspect::UintProperty bredr_max_data_length;
inspect::UintProperty le_max_num_packets;
inspect::UintProperty le_max_data_length;
inspect::UintProperty sco_max_num_packets;
inspect::UintProperty sco_max_data_length;
inspect::StringProperty lmp_features;
inspect::StringProperty le_features;
};
InspectProperties inspect_properties_;
// Metrics properties
inspect::Node metrics_node_;
inspect::Node metrics_bredr_node_;
inspect::Node metrics_le_node_;
struct AdapterMetrics {
struct LeMetrics {
UintMetricCounter outgoing_connection_requests;
UintMetricCounter pair_requests;
UintMetricCounter start_advertising_events;
UintMetricCounter stop_advertising_events;
UintMetricCounter start_discovery_events;
} le;
struct BrEdrMetrics {
UintMetricCounter outgoing_connection_requests;
UintMetricCounter pair_requests;
UintMetricCounter set_connectable_true_events;
UintMetricCounter set_connectable_false_events;
UintMetricCounter request_discovery_events;
UintMetricCounter request_discoverable_events;
UintMetricCounter open_l2cap_channel_requests;
} bredr;
};
AdapterMetrics metrics_;
// Uniquely identifies this adapter on the current system.
AdapterId identifier_;
async_dispatcher_t* dispatcher_;
fxl::WeakPtr<hci::Transport> hci_;
// Callback invoked to notify clients when the underlying transport is closed.
fit::closure transport_closed_cb_;
// Parameters relevant to the initialization sequence.
// TODO(armansito): The Initialize()/ShutDown() pattern has become common
// enough in this project that it might be worth considering moving the
// init-state-keeping into an abstract base.
enum State {
kNotInitialized = 0,
kInitializing,
kInitialized,
};
std::atomic<State> init_state_;
std::unique_ptr<hci::SequentialCommandRunner> init_seq_runner_;
// Contains the global adapter state.
AdapterState state_;
// The maximum LMP feature page that we will read.
size_t max_lmp_feature_page_index_;
// Provides access to discovered, connected, and/or bonded remote Bluetooth
// devices.
PeerCache peer_cache_;
// L2CAP layer used by GAP. This must be destroyed after the following members because they raw
// pointers to this member.
std::unique_ptr<l2cap::L2cap> l2cap_;
// The GATT profile. We use this reference to add and remove data bearers and
// for service discovery.
fxl::WeakPtr<gatt::GATT> gatt_;
// Objects that abstract the controller for connection and advertising
// procedures.
std::unique_ptr<hci::LowEnergyAdvertiser> hci_le_advertiser_;
std::unique_ptr<hci::LowEnergyConnector> hci_le_connector_;
std::unique_ptr<hci::LowEnergyScanner> hci_le_scanner_;
// Objects that perform LE procedures.
std::unique_ptr<LowEnergyAddressManager> le_address_manager_;
std::unique_ptr<LowEnergyDiscoveryManager> le_discovery_manager_;
std::unique_ptr<LowEnergyConnectionManager> le_connection_manager_;
std::unique_ptr<LowEnergyAdvertisingManager> le_advertising_manager_;
std::unique_ptr<LowEnergyImpl> low_energy_;
// Objects that perform BR/EDR procedures.
std::unique_ptr<BrEdrConnectionManager> bredr_connection_manager_;
std::unique_ptr<BrEdrDiscoveryManager> bredr_discovery_manager_;
std::unique_ptr<sdp::Server> sdp_server_;
std::unique_ptr<BrEdrImpl> bredr_;
// Callback to propagate ownership of an auto-connected LE link.
AutoConnectCallback auto_conn_cb_;
fit::thread_checker thread_checker_;
// This must remain the last member to make sure that all weak pointers are
// invalidating before other members are destroyed.
fxl::WeakPtrFactory<AdapterImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdapterImpl);
};
AdapterImpl::AdapterImpl(fxl::WeakPtr<hci::Transport> hci, fxl::WeakPtr<gatt::GATT> gatt,
std::unique_ptr<l2cap::L2cap> l2cap)
: identifier_(Random<AdapterId>()),
dispatcher_(async_get_default_dispatcher()),
hci_(std::move(hci)),
init_state_(State::kNotInitialized),
max_lmp_feature_page_index_(0),
peer_cache_(),
l2cap_(std::move(l2cap)),
gatt_(gatt),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(hci_);
ZX_DEBUG_ASSERT(gatt_);
ZX_DEBUG_ASSERT_MSG(dispatcher_, "must create on a thread with a dispatcher");
init_seq_runner_ = std::make_unique<hci::SequentialCommandRunner>(dispatcher_, hci_);
auto self = weak_ptr_factory_.GetWeakPtr();
hci_->SetTransportClosedCallback([self] {
if (self) {
self->OnTransportClosed();
}
});
gatt_->SetPersistServiceChangedCCCCallback(
[this](PeerId peer_id, gatt::ServiceChangedCCCPersistedData gatt_data) {
Peer* peer = peer_cache_.FindById(peer_id);
if (!peer) {
bt_log(WARN, "gap", "Unable to find peer %s when storing persisted GATT data.",
bt_str(peer_id));
} else if (!peer->le()) {
bt_log(WARN, "gap", "Tried to store persisted GATT data for non-LE peer %s.",
bt_str(peer_id));
} else {
peer->MutLe().set_service_changed_gatt_data(gatt_data);
}
});
gatt_->SetRetrieveServiceChangedCCCCallback([this](PeerId peer_id) {
Peer* peer = peer_cache_.FindById(peer_id);
if (!peer) {
bt_log(WARN, "gap", "Unable to find peer %s when retrieving persisted GATT data.",
peer_id.ToString().c_str());
return std::optional<gatt::ServiceChangedCCCPersistedData>();
}
if (!peer->le()) {
bt_log(WARN, "gap", "Tried to retrieve persisted GATT data for non-LE peer %s.",
peer_id.ToString().c_str());
return std::optional<gatt::ServiceChangedCCCPersistedData>();
}
return std::optional(peer->le()->get_service_changed_gatt_data());
});
}
AdapterImpl::~AdapterImpl() {
if (IsInitialized()) {
ShutDown();
}
}
bool AdapterImpl::Initialize(InitializeCallback callback, fit::closure transport_closed_cb) {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
ZX_DEBUG_ASSERT(callback);
ZX_DEBUG_ASSERT(transport_closed_cb);
if (IsInitialized()) {
bt_log(WARN, "gap", "Adapter already initialized");
return false;
}
ZX_DEBUG_ASSERT(!IsInitializing());
init_state_ = State::kInitializing;
ZX_DEBUG_ASSERT(init_seq_runner_->IsReady());
ZX_DEBUG_ASSERT(!init_seq_runner_->HasQueuedCommands());
transport_closed_cb_ = std::move(transport_closed_cb);
state_.vendor_features_ = hci_->GetVendorFeatures();
// Start by resetting the controller to a clean state and then send
// informational parameter commands that are not specific to LE or BR/EDR. The
// commands sent here are mandatory for all LE controllers.
//
// NOTE: It's safe to pass capture |this| directly in the callbacks as
// |init_seq_runner_| will internally invalidate the callbacks if it ever gets
// deleted.
// HCI_Reset
init_seq_runner_->QueueCommand(hci::CommandPacket::New(hci_spec::kReset));
// HCI_Read_Local_Version_Information
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kReadLocalVersionInfo),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read local version info failed")) {
return;
}
auto params = cmd_complete.return_params<hci_spec::ReadLocalVersionInfoReturnParams>();
state_.hci_version_ = params->hci_version;
});
// HCI_Read_Local_Supported_Commands
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kReadLocalSupportedCommands),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read local supported commands failed")) {
return;
}
auto params =
cmd_complete.return_params<hci_spec::ReadLocalSupportedCommandsReturnParams>();
std::memcpy(state_.supported_commands_, params->supported_commands,
sizeof(params->supported_commands));
});
// HCI_Read_Local_Supported_Features
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kReadLocalSupportedFeatures),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read local supported features failed")) {
return;
}
auto params =
cmd_complete.return_params<hci_spec::ReadLocalSupportedFeaturesReturnParams>();
state_.features_.SetPage(0, le64toh(params->lmp_features));
});
// HCI_Read_BD_ADDR
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kReadBDADDR), [this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read BR_ADDR failed")) {
return;
}
auto params = cmd_complete.return_params<hci_spec::ReadBDADDRReturnParams>();
state_.controller_address_ = params->bd_addr;
});
if (state().IsVendorFeatureSupported(hci::VendorFeaturesBits::kAndroidVendorExtensions)) {
bt_log(INFO, "gap", "controller supports android hci extensions, querying exact feature set");
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_android::kLEGetVendorCapabilities),
[this](const hci::EventPacket& event) {
if (hci_is_error(event, WARN, "gap",
"Failed to query android hci extension capabilities")) {
return;
}
auto params = event.return_params<hci_android::LEGetVendorCapabilitiesReturnParams>();
state_.android_vendor_capabilities_.Initialize(*params);
});
}
init_seq_runner_->RunCommands(
[callback = std::move(callback), this](hci::Result<> status) mutable {
if (bt_is_error(status, ERROR, "gap", "Failed to obtain initial controller information: %s",
bt_str(status))) {
CleanUp();
callback(false);
return;
}
InitializeStep2(std::move(callback));
});
return true;
}
void AdapterImpl::ShutDown() {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
bt_log(DEBUG, "gap", "adapter shutting down");
if (IsInitializing()) {
ZX_DEBUG_ASSERT(!init_seq_runner_->IsReady());
init_seq_runner_->Cancel();
}
CleanUp();
}
bool AdapterImpl::AddBondedPeer(BondingData bonding_data) {
return peer_cache()->AddBondedPeer(bonding_data);
}
void AdapterImpl::SetPairingDelegate(fxl::WeakPtr<PairingDelegate> delegate) {
le_connection_manager_->SetPairingDelegate(delegate);
bredr_connection_manager_->SetPairingDelegate(delegate);
}
bool AdapterImpl::IsDiscoverable() const {
return (le_advertising_manager_ && le_advertising_manager_->advertising()) ||
(bredr_discovery_manager_ && bredr_discovery_manager_->discoverable());
}
bool AdapterImpl::IsDiscovering() const {
return (le_discovery_manager_ && le_discovery_manager_->discovering()) ||
(bredr_discovery_manager_ && bredr_discovery_manager_->discovering());
}
void AdapterImpl::SetLocalName(std::string name, hci::ResultFunction<> callback) {
// TODO(fxbug.dev/40836): set the public LE advertisement name from |name|
// If BrEdr is not supported, skip the name update.
if (!bredr_discovery_manager_) {
callback(ToResult(bt::HostError::kNotSupported));
return;
}
// Make a copy of |name| to move separately into the lambda.
std::string name_copy(name);
bredr_discovery_manager_->UpdateLocalName(
std::move(name),
[this, cb = std::move(callback), local_name = std::move(name_copy)](auto status) {
if (!bt_is_error(status, WARN, "gap", "set local name failed")) {
state_.local_name_ = local_name;
}
cb(status);
});
}
void AdapterImpl::SetDeviceClass(DeviceClass dev_class, hci::ResultFunction<> callback) {
auto write_dev_class = hci::CommandPacket::New(hci_spec::kWriteClassOfDevice,
sizeof(hci_spec::WriteClassOfDeviceCommandParams));
write_dev_class->mutable_payload<hci_spec::WriteClassOfDeviceCommandParams>()->class_of_device =
dev_class;
hci_->command_channel()->SendCommand(
std::move(write_dev_class), [cb = std::move(callback)](auto, const hci::EventPacket& event) {
hci_is_error(event, WARN, "gap", "set device class failed");
cb(event.ToResult());
});
}
void AdapterImpl::AttachInspect(inspect::Node& parent, std::string name) {
adapter_node_ = parent.CreateChild(name);
UpdateInspectProperties();
peer_cache_.AttachInspect(adapter_node_);
metrics_node_ = adapter_node_.CreateChild(kMetricsInspectNodeName);
metrics_le_node_ = metrics_node_.CreateChild("le");
metrics_.le.outgoing_connection_requests.AttachInspect(metrics_le_node_,
"outgoing_connection_requests");
metrics_.le.pair_requests.AttachInspect(metrics_le_node_, "pair_requests");
metrics_.le.start_advertising_events.AttachInspect(metrics_le_node_, "start_advertising_events");
metrics_.le.stop_advertising_events.AttachInspect(metrics_le_node_, "stop_advertising_events");
metrics_.le.start_discovery_events.AttachInspect(metrics_le_node_, "start_discovery_events");
metrics_bredr_node_ = metrics_node_.CreateChild("bredr");
metrics_.bredr.outgoing_connection_requests.AttachInspect(metrics_bredr_node_,
"outgoing_connection_requests");
metrics_.bredr.pair_requests.AttachInspect(metrics_bredr_node_, "pair_requests");
metrics_.bredr.set_connectable_true_events.AttachInspect(metrics_bredr_node_,
"set_connectable_true_events");
metrics_.bredr.set_connectable_false_events.AttachInspect(metrics_bredr_node_,
"set_connectable_false_events");
metrics_.bredr.request_discovery_events.AttachInspect(metrics_bredr_node_,
"request_discovery_events");
metrics_.bredr.request_discoverable_events.AttachInspect(metrics_bredr_node_,
"request_discoverable_events");
metrics_.bredr.open_l2cap_channel_requests.AttachInspect(metrics_bredr_node_,
"open_l2cap_channel_requests");
}
void AdapterImpl::InitializeStep2(InitializeCallback callback) {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
ZX_DEBUG_ASSERT(IsInitializing());
// Low Energy MUST be supported. We don't support BR/EDR-only controllers.
if (!state_.IsLowEnergySupported()) {
bt_log(ERROR, "gap", "Bluetooth LE not supported by controller");
CleanUp();
callback(false);
return;
}
// Check the HCI version. We officially only support 4.2+ only but for now we
// just log a warning message if the version is legacy.
if (state_.hci_version() < hci_spec::HCIVersion::k4_2) {
bt_log(WARN, "gap", "controller is using legacy HCI version %s",
hci_spec::HCIVersionToString(state_.hci_version()).c_str());
}
ZX_DEBUG_ASSERT(init_seq_runner_->IsReady());
// If the controller supports the Read Buffer Size command then send it.
// Otherwise we'll default to 0 when initializing the ACLDataChannel.
if (state_.IsCommandSupported(14, hci_spec::SupportedCommand::kReadBufferSize)) {
// HCI_Read_Buffer_Size
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kReadBufferSize),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read buffer size failed")) {
return;
}
auto params = cmd_complete.return_params<hci_spec::ReadBufferSizeReturnParams>();
uint16_t acl_mtu = le16toh(params->hc_acl_data_packet_length);
uint16_t acl_max_count = le16toh(params->hc_total_num_acl_data_packets);
if (acl_mtu && acl_max_count) {
state_.bredr_data_buffer_info_ = hci::DataBufferInfo(acl_mtu, acl_max_count);
}
uint16_t sco_mtu = le16toh(params->hc_synchronous_data_packet_length);
uint16_t sco_max_count = le16toh(params->hc_total_num_synchronous_data_packets);
if (sco_mtu && sco_max_count) {
state_.sco_buffer_info_ = hci::DataBufferInfo(sco_mtu, sco_max_count);
}
});
}
// HCI_LE_Read_Local_Supported_Features
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kLEReadLocalSupportedFeatures),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "LE read local supported features failed")) {
return;
}
auto params =
cmd_complete.return_params<hci_spec::LEReadLocalSupportedFeaturesReturnParams>();
state_.le_state_.supported_features_ = le64toh(params->le_features);
});
// HCI_LE_Read_Supported_States
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kLEReadSupportedStates),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "LE read local supported states failed")) {
return;
}
auto params = cmd_complete.return_params<hci_spec::LEReadSupportedStatesReturnParams>();
state_.le_state_.supported_states_ = le64toh(params->le_states);
});
// HCI_LE_Read_Buffer_Size
init_seq_runner_->QueueCommand(
hci::CommandPacket::New(hci_spec::kLEReadBufferSize),
[this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "LE read buffer size failed")) {
return;
}
auto params = cmd_complete.return_params<hci_spec::LEReadBufferSizeReturnParams>();
uint16_t mtu = le16toh(params->hc_le_acl_data_packet_length);
uint8_t max_count = params->hc_total_num_le_acl_data_packets;
if (mtu && max_count) {
state_.le_state_.data_buffer_info_ = hci::DataBufferInfo(mtu, max_count);
}
});
if (state_.features().HasBit(0u, hci_spec::LMPFeature::kSecureSimplePairing)) {
// HCI_Write_Simple_Pairing_Mode
auto write_ssp = hci::CommandPacket::New(hci_spec::kWriteSimplePairingMode,
sizeof(hci_spec::WriteSimplePairingModeCommandParams));
write_ssp->mutable_payload<hci_spec::WriteSimplePairingModeCommandParams>()
->simple_pairing_mode = hci_spec::GenericEnableParam::kEnable;
init_seq_runner_->QueueCommand(std::move(write_ssp), [](const auto& event) {
// Warn if the command failed
hci_is_error(event, WARN, "gap", "write simple pairing mode failed");
});
}
// If there are extended features then try to read the first page of the
// extended features.
if (state_.features().HasBit(0u, hci_spec::LMPFeature::kExtendedFeatures)) {
// Page index 1 must be available.
max_lmp_feature_page_index_ = 1;
// HCI_Read_Local_Extended_Features
auto cmd_packet =
hci::CommandPacket::New(hci_spec::kReadLocalExtendedFeatures,
sizeof(hci_spec::ReadLocalExtendedFeaturesCommandParams));
// Try to read page 1.
cmd_packet->mutable_payload<hci_spec::ReadLocalExtendedFeaturesCommandParams>()->page_number =
1;
init_seq_runner_->QueueCommand(
std::move(cmd_packet), [this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read local extended features failed")) {
return;
}
auto params =
cmd_complete.return_params<hci_spec::ReadLocalExtendedFeaturesReturnParams>();
state_.features_.SetPage(1, le64toh(params->extended_lmp_features));
max_lmp_feature_page_index_ = params->maximum_page_number;
});
}
init_seq_runner_->RunCommands(
[callback = std::move(callback), this](hci::Result<> status) mutable {
if (bt_is_error(status, ERROR, "gap",
"failed to obtain initial controller information (step 2)")) {
CleanUp();
callback(false);
return;
}
InitializeStep3(std::move(callback));
});
}
void AdapterImpl::InitializeStep3(InitializeCallback callback) {
ZX_ASSERT(thread_checker_.is_thread_valid());
ZX_ASSERT(IsInitializing());
ZX_ASSERT(init_seq_runner_->IsReady());
ZX_ASSERT(!init_seq_runner_->HasQueuedCommands());
if (!state_.bredr_data_buffer_info().IsAvailable() &&
!state_.low_energy_state().data_buffer_info().IsAvailable()) {
bt_log(ERROR, "gap", "Both BR/EDR and LE buffers are unavailable");
CleanUp();
callback(false);
return;
}
// Now that we have all the ACL data buffer information it's time to
// initialize the ACLDataChannel.
if (!hci_->InitializeACLDataChannel(state_.bredr_data_buffer_info(),
state_.low_energy_state().data_buffer_info())) {
bt_log(ERROR, "gap", "Failed to initialize ACLDataChannel (step 3)");
CleanUp();
callback(false);
return;
}
// The controller may not support SCO flow control (as implied by not supporting
// HCI_Write_Synchronous_Flow_Control_Enable), in which case we don't support HCI SCO on this
// controller yet.
// TODO(fxbug.dev/89689): Support controllers that don't support SCO flow control.
bool sco_flow_control_supported = state_.IsCommandSupported(
/*octet=*/10, hci_spec::SupportedCommand::kWriteSynchronousFlowControlEnable);
if (state_.sco_buffer_info().IsAvailable() && sco_flow_control_supported) {
// Enable SCO flow control.
auto cmd_packet =
hci::CommandPacket::New(hci_spec::kWriteSynchronousFlowControlEnable,
sizeof(hci_spec::WriteSynchronousFlowControlEnableParams));
auto* flow_control_params =
cmd_packet->mutable_payload<hci_spec::WriteSynchronousFlowControlEnableParams>();
flow_control_params->synchronous_flow_control_enable = hci_spec::GenericEnableParam::kEnable;
init_seq_runner_->QueueCommand(std::move(cmd_packet), [this](const auto& event) {
if (hci_is_error(event, ERROR, "gap",
"Write synchronous flow control enable failed, proceeding without HCI "
"SCO support")) {
return;
}
if (!hci_->InitializeScoDataChannel(state_.sco_buffer_info())) {
bt_log(WARN, "gap",
"Failed to initialize ScoDataChannel, proceeding without HCI SCO support");
return;
}
bt_log(DEBUG, "gap", "ScoDataChannel initialized successfully");
});
} else {
bt_log(INFO, "gap",
"HCI SCO not supported (SCO buffer available: %d, SCO flow control supported: %d)",
state_.sco_buffer_info().IsAvailable(), sco_flow_control_supported);
}
hci_->AttachInspect(adapter_node_);
// Create ChannelManager, if we haven't been provided one for testing. Doing so here lets us
// guarantee that AclDataChannel's lifetime is a superset of ChannelManager's lifetime.
if (!l2cap_) {
// Initialize ChannelManager to make it available for the next initialization step. The
// AclDataChannel must be initialized before creating ChannelManager.
l2cap_ = std::make_unique<l2cap::ChannelManager>(hci_->acl_data_channel(),
/*random_channel_ids=*/true);
l2cap_->AttachInspect(adapter_node_, l2cap::L2cap::kInspectNodeName);
}
// HCI_Set_Event_Mask
{
uint64_t event_mask = BuildEventMask();
auto cmd_packet = hci::CommandPacket::New(hci_spec::kSetEventMask,
sizeof(hci_spec::SetEventMaskCommandParams));
cmd_packet->mutable_payload<hci_spec::SetEventMaskCommandParams>()->event_mask =
htole64(event_mask);
init_seq_runner_->QueueCommand(std::move(cmd_packet), [](const auto& event) {
hci_is_error(event, WARN, "gap", "set event mask failed");
});
}
// HCI_LE_Set_Event_Mask
{
uint64_t event_mask = BuildLEEventMask();
auto cmd_packet = hci::CommandPacket::New(hci_spec::kLESetEventMask,
sizeof(hci_spec::LESetEventMaskCommandParams));
cmd_packet->mutable_payload<hci_spec::LESetEventMaskCommandParams>()->le_event_mask =
htole64(event_mask);
init_seq_runner_->QueueCommand(std::move(cmd_packet), [](const auto& event) {
hci_is_error(event, WARN, "gap", "LE set event mask failed");
});
}
// HCI_Write_LE_Host_Support if the appropriate feature bit is not set AND if
// the controller supports this command.
if (!state_.features().HasBit(1, hci_spec::LMPFeature::kLESupportedHost) &&
state_.IsCommandSupported(24, hci_spec::SupportedCommand::kWriteLEHostSupport)) {
auto cmd_packet = hci::CommandPacket::New(hci_spec::kWriteLEHostSupport,
sizeof(hci_spec::WriteLEHostSupportCommandParams));
auto params = cmd_packet->mutable_payload<hci_spec::WriteLEHostSupportCommandParams>();
params->le_supported_host = hci_spec::GenericEnableParam::kEnable;
params->simultaneous_le_host = 0x00; // note: ignored
init_seq_runner_->QueueCommand(std::move(cmd_packet), [](const auto& event) {
hci_is_error(event, WARN, "gap", "write LE host support failed");
});
}
// If we know that Page 2 of the extended features bitfield is available, then
// request it.
if (max_lmp_feature_page_index_ > 1) {
auto cmd_packet =
hci::CommandPacket::New(hci_spec::kReadLocalExtendedFeatures,
sizeof(hci_spec::ReadLocalExtendedFeaturesCommandParams));
// Try to read page 2.
cmd_packet->mutable_payload<hci_spec::ReadLocalExtendedFeaturesCommandParams>()->page_number =
2;
// HCI_Read_Local_Extended_Features
init_seq_runner_->QueueCommand(
std::move(cmd_packet), [this](const hci::EventPacket& cmd_complete) {
if (hci_is_error(cmd_complete, WARN, "gap", "read local extended features failed")) {
return;
}
auto params =
cmd_complete.return_params<hci_spec::ReadLocalExtendedFeaturesReturnParams>();
state_.features_.SetPage(2, le64toh(params->extended_lmp_features));
max_lmp_feature_page_index_ = params->maximum_page_number;
});
}
init_seq_runner_->RunCommands(
[callback = std::move(callback), this](hci::Result<> status) mutable {
if (bt_is_error(status, ERROR, "gap",
"failed to obtain initial controller information (step 3)")) {
CleanUp();
callback(false);
return;
}
InitializeStep4(std::move(callback));
});
}
void AdapterImpl::InitializeStep4(InitializeCallback callback) {
// Initialize the scan manager and low energy adapters based on current feature support
ZX_DEBUG_ASSERT(IsInitializing());
// We use the public controller address as the local LE identity address.
DeviceAddress adapter_identity(DeviceAddress::Type::kLEPublic, state_.controller_address());
// Initialize the LE local address manager.
le_address_manager_ = std::make_unique<LowEnergyAddressManager>(
adapter_identity, fit::bind_member<&AdapterImpl::IsLeRandomAddressChangeAllowed>(this), hci_);
// Initialize the HCI adapters.
hci_le_advertiser_ = CreateAdvertiser();
hci_le_connector_ = std::make_unique<hci::LowEnergyConnector>(
hci_, le_address_manager_.get(), dispatcher_,
fit::bind_member<&hci::LowEnergyAdvertiser::OnIncomingConnection>(hci_le_advertiser_.get()));
hci_le_scanner_ =
std::make_unique<hci::LegacyLowEnergyScanner>(le_address_manager_.get(), hci_, dispatcher_);
// Initialize the LE manager objects
le_discovery_manager_ =
std::make_unique<LowEnergyDiscoveryManager>(hci_, hci_le_scanner_.get(), &peer_cache_);
le_discovery_manager_->AttachInspect(adapter_node_, kInspectLowEnergyDiscoveryManagerNodeName);
le_discovery_manager_->set_peer_connectable_callback(
fit::bind_member<&AdapterImpl::OnLeAutoConnectRequest>(this));
le_connection_manager_ = std::make_unique<LowEnergyConnectionManager>(
hci_, le_address_manager_.get(), hci_le_connector_.get(), &peer_cache_, l2cap_.get(), gatt_,
le_discovery_manager_->GetWeakPtr(), sm::SecurityManager::Create);
le_connection_manager_->AttachInspect(adapter_node_, kInspectLowEnergyConnectionManagerNodeName);
le_advertising_manager_ = std::make_unique<LowEnergyAdvertisingManager>(
hci_le_advertiser_.get(), le_address_manager_.get());
low_energy_ = std::make_unique<LowEnergyImpl>(this);
// Initialize the BR/EDR manager objects if the controller supports BR/EDR.
if (state_.IsBREDRSupported()) {
DeviceAddress local_bredr_address(DeviceAddress::Type::kBREDR, state_.controller_address());
bredr_connection_manager_ = std::make_unique<BrEdrConnectionManager>(
hci_, &peer_cache_, local_bredr_address, l2cap_.get(),
state_.features().HasBit(0, hci_spec::LMPFeature::kInterlacedPageScan));
bredr_connection_manager_->AttachInspect(adapter_node_, kInspectBrEdrConnectionManagerNodeName);
hci_spec::InquiryMode mode = hci_spec::InquiryMode::kStandard;
if (state_.features().HasBit(0, hci_spec::LMPFeature::kExtendedInquiryResponse)) {
mode = hci_spec::InquiryMode::kExtended;
} else if (state_.features().HasBit(0, hci_spec::LMPFeature::kRSSIwithInquiryResults)) {
mode = hci_spec::InquiryMode::kRSSI;
}
bredr_discovery_manager_ = std::make_unique<BrEdrDiscoveryManager>(hci_, mode, &peer_cache_);
bredr_discovery_manager_->AttachInspect(adapter_node_, kInspectBrEdrDiscoveryManagerNodeName);
sdp_server_ = std::make_unique<sdp::Server>(l2cap_.get());
sdp_server_->AttachInspect(adapter_node_);
bredr_ = std::make_unique<BrEdrImpl>(this);
}
// Override the current privacy setting and always use the local stable identity address (i.e. not
// a RPA) when initiating connections. This improves interoperability with certain Bluetooth
// peripherals that fail to authenticate following a RPA rotation.
//
// The implication here is that the public address is revealed in LL connection request PDUs. LE
// central privacy is still preserved during an active scan, i.e. in LL scan request PDUs.
//
// TODO(fxbug.dev/63123): Remove this temporary fix once we determine the root cause for
// authentication failures.
hci_le_connector_->UseLocalIdentityAddress();
// Update properties before callback called so properties can be verified in unit tests.
UpdateInspectProperties();
// Assign a default name and device class before notifying completion.
auto self = weak_ptr_factory_.GetWeakPtr();
SetLocalName(kDefaultLocalName, [self, callback = std::move(callback)](auto status) mutable {
// Set the default device class - a computer with audio.
// TODO(fxbug.dev/1234): set this from a platform configuration file
DeviceClass dev_class(DeviceClass::MajorClass::kComputer);
dev_class.SetServiceClasses({DeviceClass::ServiceClass::kAudio});
self->SetDeviceClass(dev_class, [self, callback = std::move(callback)](const auto&) {
// This completes the initialization sequence.
self->init_state_ = State::kInitialized;
callback(true);
});
});
}
void AdapterImpl::UpdateInspectProperties() {
inspect_properties_.adapter_id = adapter_node_.CreateString("adapter_id", identifier_.ToString());
inspect_properties_.hci_version =
adapter_node_.CreateString("hci_version", hci_spec::HCIVersionToString(state_.hci_version()));
inspect_properties_.bredr_max_num_packets = adapter_node_.CreateUint(
"bredr_max_num_packets", state_.bredr_data_buffer_info().max_num_packets());
inspect_properties_.bredr_max_data_length = adapter_node_.CreateUint(
"bredr_max_data_length", state_.bredr_data_buffer_info().max_data_length());
inspect_properties_.le_max_num_packets = adapter_node_.CreateUint(
"le_max_num_packets", state_.low_energy_state().data_buffer_info().max_num_packets());
inspect_properties_.le_max_data_length = adapter_node_.CreateUint(
"le_max_data_length", state_.low_energy_state().data_buffer_info().max_data_length());
inspect_properties_.sco_max_num_packets =
adapter_node_.CreateUint("sco_max_num_packets", state_.sco_buffer_info().max_num_packets());
inspect_properties_.sco_max_data_length =
adapter_node_.CreateUint("sco_max_data_length", state_.sco_buffer_info().max_data_length());
inspect_properties_.lmp_features =
adapter_node_.CreateString("lmp_features", state_.features().ToString());
auto le_features =
bt_lib_cpp_string::StringPrintf("0x%016lx", state_.low_energy_state().supported_features());
inspect_properties_.le_features = adapter_node_.CreateString("le_features", le_features);
}
void AdapterImpl::CleanUp() {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
if (init_state_ == State::kNotInitialized) {
bt_log(DEBUG, "gap", "clean up: not initialized");
return;
}
init_state_ = State::kNotInitialized;
state_ = AdapterState();
transport_closed_cb_ = nullptr;
// Destroy objects in reverse order of construction.
low_energy_ = nullptr;
bredr_ = nullptr;
sdp_server_ = nullptr;
bredr_discovery_manager_ = nullptr;
le_advertising_manager_ = nullptr;
le_connection_manager_ = nullptr;
le_discovery_manager_ = nullptr;
hci_le_connector_ = nullptr;
hci_le_advertiser_ = nullptr;
hci_le_scanner_ = nullptr;
le_address_manager_ = nullptr;
l2cap_ = nullptr;
hci_ = nullptr;
}
void AdapterImpl::OnTransportClosed() {
bt_log(INFO, "gap", "HCI transport was closed");
if (transport_closed_cb_)
transport_closed_cb_();
}
void AdapterImpl::OnLeAutoConnectRequest(Peer* peer) {
ZX_DEBUG_ASSERT(le_connection_manager_);
ZX_DEBUG_ASSERT(peer);
ZX_DEBUG_ASSERT(peer->le());
PeerId peer_id = peer->identifier();
if (!peer->le()->should_auto_connect()) {
bt_log(DEBUG, "gap",
"ignoring auto-connection (peer->should_auto_connect() is false) (peer: %s)",
bt_str(peer_id));
return;
}
LowEnergyConnectionOptions options{.auto_connect = true};
auto self = weak_ptr_factory_.GetWeakPtr();
le_connection_manager_->Connect(
peer_id,
[self, peer_id](auto result) {
if (!self) {
bt_log(DEBUG, "gap", "ignoring auto-connection (adapter destroyed)");
return;
}
if (result.is_error()) {
bt_log(INFO, "gap", "failed to auto-connect (peer: %s, error: %s)", bt_str(peer_id),
HostErrorToString(result.error_value()).c_str());
return;
}
auto conn = std::move(result).value();
ZX_ASSERT(conn);
bt_log(INFO, "gap", "peer auto-connected (peer: %s)", bt_str(peer_id));
if (self->auto_conn_cb_) {
self->auto_conn_cb_(std::move(conn));
}
},
options);
}
bool AdapterImpl::IsLeRandomAddressChangeAllowed() {
return hci_le_advertiser_->AllowsRandomAddressChange() &&
hci_le_scanner_->AllowsRandomAddressChange() &&
hci_le_connector_->AllowsRandomAddressChange();
}
std::unique_ptr<Adapter> Adapter::Create(fxl::WeakPtr<hci::Transport> hci,
fxl::WeakPtr<gatt::GATT> gatt,
std::unique_ptr<l2cap::L2cap> l2cap) {
return std::make_unique<AdapterImpl>(hci, gatt, std::move(l2cap));
}
} // namespace bt::gap