blob: fe72b08a163bd7d19e9ae73d31d3c4b739014e0f [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/weak_self.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/a2dp_offload_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/logical_link.h"
namespace bt::l2cap {
namespace {
constexpr const char* kInspectServicesNodeName = "services";
constexpr const char* kInspectServiceNodePrefix = "service_";
constexpr const char* kInspectLogicalLinksNodeName = "logical_links";
constexpr const char* kInspectLogicalLinkNodePrefix = "logical_link_";
constexpr const char* kInspectPsmPropertyName = "psm";
} // namespace
class ChannelManagerImpl final : public ChannelManager {
public:
using LinkErrorCallback = fit::closure;
ChannelManagerImpl(hci::AclDataChannel* acl_data_channel,
hci::CommandChannel* cmd_channel,
bool random_channel_ids,
pw::async::Dispatcher& dispatcher);
~ChannelManagerImpl() override;
BrEdrFixedChannels AddACLConnection(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb,
SecurityUpgradeCallback security_cb) override;
[[nodiscard]] LEFixedChannels AddLEConnection(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb,
LEConnectionParameterUpdateCallback conn_param_cb,
SecurityUpgradeCallback security_cb) override;
void RemoveConnection(hci_spec::ConnectionHandle handle) override;
void AssignLinkSecurityProperties(hci_spec::ConnectionHandle handle,
sm::SecurityProperties security) override;
Channel::WeakPtr OpenFixedChannel(
hci_spec::ConnectionHandle connection_handle,
ChannelId channel_id) override;
void OpenL2capChannel(hci_spec::ConnectionHandle handle,
Psm psm,
ChannelParameters params,
ChannelCallback cb) override;
bool RegisterService(Psm psm,
ChannelParameters params,
ChannelCallback cb) override;
void UnregisterService(Psm psm) override;
void RequestConnectionParameterUpdate(
hci_spec::ConnectionHandle handle,
hci_spec::LEPreferredConnectionParameters params,
ConnectionParameterUpdateRequestCallback request_cb) override;
void AttachInspect(inspect::Node& parent, std::string name) override;
internal::LogicalLink::WeakPtr LogicalLinkForTesting(
hci_spec::ConnectionHandle handle) override;
private:
// Returns a handler for data packets received from the Bluetooth controller
// bound to this object.
hci::ACLPacketHandler MakeInboundDataHandler();
// Called when an ACL data packet is received from the controller. This method
// is responsible for routing the packet to the corresponding LogicalLink.
void OnACLDataReceived(hci::ACLDataPacketPtr data_packet);
// Called by the various Register functions. Returns a pointer to the newly
// added link.
internal::LogicalLink* RegisterInternal(
hci_spec::ConnectionHandle handle,
bt::LinkType ll_type,
pw::bluetooth::emboss::ConnectionRole role,
size_t max_payload_size);
// If a service (identified by |psm|) requested has been registered, return a
// ServiceInfo object containing preferred channel parameters and a callback
// that passes an inbound channel to the registrant. The callback may be
// called repeatedly to pass multiple channels for |psm|, but should not be
// stored because the service may be unregistered at a later time. Calls for
// unregistered services return null.
std::optional<ServiceInfo> QueryService(hci_spec::ConnectionHandle handle,
Psm psm);
pw::async::Dispatcher& pw_dispatcher_;
// Maximum sizes for data packet payloads from host to controller.
size_t max_acl_payload_size_;
size_t max_le_payload_size_;
hci::AclDataChannel* acl_data_channel_;
hci::CommandChannel* cmd_channel_;
std::unique_ptr<A2dpOffloadManager> a2dp_offload_manager_;
using LinkMap = std::unordered_map<hci_spec::ConnectionHandle,
std::unique_ptr<internal::LogicalLink>>;
LinkMap ll_map_;
inspect::Node ll_node_;
// Stores packets received on a connection handle before a link for it has
// been created.
using PendingPacketMap =
std::unordered_map<hci_spec::ConnectionHandle,
std::queue<hci::ACLDataPacketPtr>>;
PendingPacketMap pending_packets_;
// Store information required to create and forward channels for locally-
// hosted services.
struct ServiceData {
void AttachInspect(inspect::Node& parent);
ServiceInfo info;
Psm psm;
inspect::Node node;
inspect::StringProperty psm_property;
};
using ServiceMap = std::unordered_map<Psm, ServiceData>;
ServiceMap services_;
inspect::Node services_node_;
inspect::Node node_;
// Stored info on whether random channel ids are requested.
bool random_channel_ids_;
WeakSelf<ChannelManagerImpl> weak_self_;
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelManagerImpl);
};
ChannelManagerImpl::ChannelManagerImpl(hci::AclDataChannel* acl_data_channel,
hci::CommandChannel* cmd_channel,
bool random_channel_ids,
pw::async::Dispatcher& dispatcher)
: pw_dispatcher_(dispatcher),
acl_data_channel_(acl_data_channel),
cmd_channel_(cmd_channel),
a2dp_offload_manager_(
std::make_unique<A2dpOffloadManager>(cmd_channel_->AsWeakPtr())),
random_channel_ids_(random_channel_ids),
weak_self_(this) {
BT_ASSERT(acl_data_channel_);
max_acl_payload_size_ = acl_data_channel_->GetBufferInfo().max_data_length();
max_le_payload_size_ = acl_data_channel_->GetLeBufferInfo().max_data_length();
acl_data_channel_->SetDataRxHandler(MakeInboundDataHandler());
bt_log(DEBUG, "l2cap", "initialized");
}
ChannelManagerImpl::~ChannelManagerImpl() {
bt_log(DEBUG, "l2cap", "shutting down");
// Explicitly shut down all links to force associated L2CAP channels to
// release their strong references.
for (auto& [handle, link] : ll_map_) {
link->Close();
}
}
hci::ACLPacketHandler ChannelManagerImpl::MakeInboundDataHandler() {
return [self = weak_self_.GetWeakPtr()](auto packet) {
if (self.is_alive()) {
self->OnACLDataReceived(std::move(packet));
}
};
}
ChannelManagerImpl::BrEdrFixedChannels ChannelManagerImpl::AddACLConnection(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb,
SecurityUpgradeCallback security_cb) {
bt_log(DEBUG, "l2cap", "register ACL link (handle: %#.4x)", handle);
auto* ll =
RegisterInternal(handle, bt::LinkType::kACL, role, max_acl_payload_size_);
ll->set_error_callback(std::move(link_error_cb));
ll->set_security_upgrade_callback(std::move(security_cb));
Channel::WeakPtr smp = OpenFixedChannel(handle, kSMPChannelId);
BT_ASSERT(smp.is_alive());
return BrEdrFixedChannels{.smp = std::move(smp)};
}
ChannelManagerImpl::LEFixedChannels ChannelManagerImpl::AddLEConnection(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb,
LEConnectionParameterUpdateCallback conn_param_cb,
SecurityUpgradeCallback security_cb) {
bt_log(DEBUG, "l2cap", "register LE link (handle: %#.4x)", handle);
auto* ll =
RegisterInternal(handle, bt::LinkType::kLE, role, max_le_payload_size_);
ll->set_error_callback(std::move(link_error_cb));
ll->set_security_upgrade_callback(std::move(security_cb));
ll->set_connection_parameter_update_callback(std::move(conn_param_cb));
Channel::WeakPtr att = OpenFixedChannel(handle, kATTChannelId);
Channel::WeakPtr smp = OpenFixedChannel(handle, kLESMPChannelId);
BT_ASSERT(att.is_alive());
BT_ASSERT(smp.is_alive());
return LEFixedChannels{.att = std::move(att), .smp = std::move(smp)};
}
void ChannelManagerImpl::RemoveConnection(hci_spec::ConnectionHandle handle) {
bt_log(DEBUG, "l2cap", "unregister link (handle: %#.4x)", handle);
pending_packets_.erase(handle);
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
bt_log(DEBUG,
"l2cap",
"attempt to unregister unknown link (handle: %#.4x)",
handle);
return;
}
// Explicitly shut down the link to force associated L2CAP channels to release
// their strong references.
iter->second->Close();
ll_map_.erase(iter);
}
void ChannelManagerImpl::AssignLinkSecurityProperties(
hci_spec::ConnectionHandle handle, sm::SecurityProperties security) {
bt_log(DEBUG,
"l2cap",
"received new security properties (handle: %#.4x)",
handle);
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
bt_log(DEBUG, "l2cap", "ignoring new security properties on unknown link");
return;
}
iter->second->AssignSecurityProperties(security);
}
Channel::WeakPtr ChannelManagerImpl::OpenFixedChannel(
hci_spec::ConnectionHandle handle, ChannelId channel_id) {
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
bt_log(ERROR,
"l2cap",
"cannot open fixed channel on unknown connection handle: %#.4x",
handle);
return Channel::WeakPtr();
}
return iter->second->OpenFixedChannel(channel_id);
}
void ChannelManagerImpl::OpenL2capChannel(hci_spec::ConnectionHandle handle,
Psm psm,
ChannelParameters params,
ChannelCallback cb) {
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
bt_log(ERROR,
"l2cap",
"Cannot open channel on unknown connection handle: %#.4x",
handle);
cb(Channel::WeakPtr());
return;
}
iter->second->OpenChannel(psm, params, std::move(cb));
}
bool ChannelManagerImpl::RegisterService(Psm psm,
ChannelParameters params,
ChannelCallback cb) {
// v5.0 Vol 3, Part A, Sec 4.2: PSMs shall be odd and the least significant
// bit of the most significant byte shall be zero
if (((psm & 0x0001) != 0x0001) || ((psm & 0x0100) != 0x0000)) {
return false;
}
auto iter = services_.find(psm);
if (iter != services_.end()) {
return false;
}
ServiceData service{.info = ServiceInfo(params, std::move(cb)),
.psm = psm,
.node = {},
.psm_property = {}};
service.AttachInspect(services_node_);
services_.emplace(psm, std::move(service));
return true;
}
void ChannelManagerImpl::UnregisterService(Psm psm) { services_.erase(psm); }
void ChannelManagerImpl::RequestConnectionParameterUpdate(
hci_spec::ConnectionHandle handle,
hci_spec::LEPreferredConnectionParameters params,
ConnectionParameterUpdateRequestCallback request_cb) {
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
bt_log(DEBUG,
"l2cap",
"ignoring Connection Parameter Update request on unknown link");
return;
}
iter->second->SendConnectionParameterUpdateRequest(params,
std::move(request_cb));
}
void ChannelManagerImpl::AttachInspect(inspect::Node& parent,
std::string name) {
if (!parent) {
return;
}
node_ = parent.CreateChild(name);
services_node_ = node_.CreateChild(kInspectServicesNodeName);
for (auto& [psm, service] : services_) {
service.AttachInspect(services_node_);
}
ll_node_ = node_.CreateChild(kInspectLogicalLinksNodeName);
for (auto& [_, ll] : ll_map_) {
ll->AttachInspect(ll_node_,
ll_node_.UniqueName(kInspectLogicalLinkNodePrefix));
}
}
internal::LogicalLink::WeakPtr ChannelManagerImpl::LogicalLinkForTesting(
hci_spec::ConnectionHandle handle) {
auto iter = ll_map_.find(handle);
if (iter == ll_map_.end()) {
return internal::LogicalLink::WeakPtr();
}
return iter->second->GetWeakPtr();
}
// Called when an ACL data packet is received from the controller. This method
// is responsible for routing the packet to the corresponding LogicalLink.
void ChannelManagerImpl::OnACLDataReceived(hci::ACLDataPacketPtr packet) {
auto handle = packet->connection_handle();
TRACE_DURATION(
"bluetooth", "ChannelManagerImpl::OnDataReceived", "handle", handle);
auto iter = ll_map_.find(handle);
PendingPacketMap::iterator pp_iter;
// If a LogicalLink does not exist, we set up a queue for its packets to be
// delivered when the LogicalLink gets created.
if (iter == ll_map_.end()) {
pp_iter =
pending_packets_.emplace(handle, std::queue<hci::ACLDataPacketPtr>())
.first;
} else {
// A logical link exists. |pp_iter| will be valid only if the drain task has
// not run yet (see ChannelManagerImpl::RegisterInternal()).
pp_iter = pending_packets_.find(handle);
}
if (pp_iter != pending_packets_.end()) {
packet->set_trace_id(TRACE_NONCE());
TRACE_FLOW_BEGIN("bluetooth",
"ChannelMaager::OnDataReceived queued",
packet->trace_id());
pp_iter->second.push(std::move(packet));
bt_log(TRACE, "l2cap", "queued rx packet on handle: %#.4x", handle);
return;
}
iter->second->HandleRxPacket(std::move(packet));
}
internal::LogicalLink* ChannelManagerImpl::RegisterInternal(
hci_spec::ConnectionHandle handle,
bt::LinkType ll_type,
pw::bluetooth::emboss::ConnectionRole role,
size_t max_payload_size) {
TRACE_DURATION(
"bluetooth", "ChannelManagerImpl::RegisterInternal", "handle", handle);
// TODO(armansito): Return nullptr instead of asserting. Callers shouldn't
// assume this will succeed.
auto iter = ll_map_.find(handle);
BT_DEBUG_ASSERT_MSG(iter == ll_map_.end(),
"connection handle re-used! (handle=%#.4x)",
handle);
auto ll = std::make_unique<internal::LogicalLink>(
handle,
ll_type,
role,
max_payload_size,
fit::bind_member<&ChannelManagerImpl::QueryService>(this),
acl_data_channel_,
cmd_channel_,
random_channel_ids_,
*a2dp_offload_manager_,
pw_dispatcher_);
if (ll_node_) {
ll->AttachInspect(ll_node_,
ll_node_.UniqueName(kInspectLogicalLinkNodePrefix));
}
// Route all pending packets to the link.
auto pp_iter = pending_packets_.find(handle);
if (pp_iter != pending_packets_.end()) {
auto& packets = pp_iter->second;
while (!packets.empty()) {
auto packet = std::move(packets.front());
packets.pop();
TRACE_FLOW_END("bluetooth",
"ChannelManagerImpl::OnDataReceived queued",
packet->trace_id());
ll->HandleRxPacket(std::move(packet));
}
pending_packets_.erase(pp_iter);
}
auto* ll_raw = ll.get();
ll_map_[handle] = std::move(ll);
return ll_raw;
}
std::optional<ChannelManager::ServiceInfo> ChannelManagerImpl::QueryService(
hci_spec::ConnectionHandle handle, Psm psm) {
auto iter = services_.find(psm);
if (iter == services_.end()) {
return std::nullopt;
}
// |channel_cb| will be called in LogicalLink. Each callback in |services_|
// already trampolines to the appropriate dispatcher (passed to
// RegisterService).
return ServiceInfo(iter->second.info.channel_params,
iter->second.info.channel_cb.share());
}
void ChannelManagerImpl::ServiceData::AttachInspect(inspect::Node& parent) {
if (!parent) {
return;
}
node = parent.CreateChild(parent.UniqueName(kInspectServiceNodePrefix));
psm_property = node.CreateString(kInspectPsmPropertyName, PsmToString(psm));
}
std::unique_ptr<ChannelManager> ChannelManager::Create(
hci::AclDataChannel* acl_data_channel,
hci::CommandChannel* cmd_channel,
bool random_channel_ids,
pw::async::Dispatcher& dispatcher) {
return std::make_unique<ChannelManagerImpl>(
acl_data_channel, cmd_channel, random_channel_ids, dispatcher);
}
} // namespace bt::l2cap