blob: d7b6a1468b243dd322c703207bcdf801573219ad [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.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_
#include <lib/async/dispatcher.h>
#include <lib/fit/thread_checker.h>
#include <lib/sys/inspect/cpp/component.h>
#include <lib/trace/event.h>
#include <zircon/compiler.h>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <fbl/macros.h>
#include "lib/async/cpp/executor.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_packet.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/le_signaling_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
namespace bt {
namespace hci {
class Transport;
} // namespace hci
namespace l2cap {
namespace internal {
class LogicalLink;
} // namespace internal
// ChannelManager implements the "Channel Manager" control block of L2CAP. In
// particular:
//
// * It acts as a routing table for incoming ACL data by directing packets to
// the appropriate internal logical link handler;
//
// * Manages priority based scheduling.
//
// * Provides an API surface for L2CAP channel creation and logical link
// management bound to a single creation thread.
//
// There can be a single instance of ChannelManager for a HCI transport.
//
// THREAD-SAFETY:
//
// This object is not thread safe. Construction/destruction must happen on the
// thread where this is used.
class ChannelManager final {
public:
using LinkErrorCallback = fit::closure;
// Creates L2CAP state for logical links and channels.
//
// |max_acl_payload_size| and |max_le_payload_size| are the "maximum size[s] of HCI ACL
// (excluding header) Data Packets... sent from the Host to the Controller" (Core v5.0 Vol 2,
// Part E, Section 4.1) used for fragmenting outbound data. Data that is fragmented will be
// passed contiguously as invocations of |acl_data_channel->SendPackets()|.
ChannelManager(size_t max_acl_payload_size, size_t max_le_payload_size,
hci::AclDataChannel* acl_data_channel, bool random_channel_ids);
~ChannelManager();
// Returns a handler for data packets received from the Bluetooth controller bound to this object.
// It must be called from the creation thread, but it is safe to call past ChannelManager's
// lifetime.
hci::ACLPacketHandler MakeInboundDataHandler();
// Registers the ACL connection with the L2CAP layer. L2CAP channels can be
// opened on the logical link represented by |handle| after a call to this
// method.
//
// |link_error_callback| will be used to notify when a channel signals a link
// error.
//
// |security_callback| will be used to request an upgrade to the link security
// level. This can be triggered by dynamic L2CAP channel creation or by a
// service-level client via Channel::UpgradeSecurity().
//
// It is an error to register the same |handle| value more than once as either
// kind of channel without first unregistering it (asserted in debug builds).
void RegisterACL(hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback link_error_callback,
SecurityUpgradeCallback security_callback);
// Registers a LE connection with the L2CAP layer. L2CAP channels can be
// opened on the logical link represented by |handle| after a call to this
// method.
//
// |conn_param_callback| will be used to notify the caller if new connection
// parameters were accepted from the remote end of the link.
//
// |link_error_callback| will be used to notify when a channel signals a link
// error.
//
// |security_callback| will be used to request an upgrade to the link security
// level. This can be triggered by dynamic L2CAP channel creation or by a
// service-level client via Channel::UpgradeSecurity().
//
// It is an error to register the same |handle| value more than once as either
// kind of channel without first unregistering it (asserted in debug builds).
void RegisterLE(hci::ConnectionHandle handle, hci::Connection::Role role,
LEConnectionParameterUpdateCallback conn_param_callback,
LinkErrorCallback link_error_callback, SecurityUpgradeCallback security_callback);
// Removes a connection. All incoming data packets on this link will be dropped. If the
// connection was previously registered, all corresponding Channels will be closed.
//
// NOTE: It is recommended that a link entry be removed AFTER the controller
// sends a HCI Disconnection Complete Event for the corresponding logical
// link. This is to prevent incorrectly buffering data if the controller has
// more packets to send after removing the link entry.
void Unregister(hci::ConnectionHandle handle);
// Assigns the security level of a logical link. Has no effect if |handle| has
// not been previously registered or the link is closed.
void AssignLinkSecurityProperties(hci::ConnectionHandle handle, sm::SecurityProperties security);
// Opens the L2CAP fixed channel with |channel_id| over the logical link
// identified by |connection_handle| and starts routing packets. Returns
// nullptr if the channel is already open.
fbl::RefPtr<Channel> OpenFixedChannel(hci::ConnectionHandle connection_handle,
ChannelId channel_id);
// Opens an out-bound connection-oriented L2CAP channel on the link specified by |handle| to the
// requested |psm| with the preferred parameters |params|.
// Returns a channel asynchronously via |callback|.
void OpenChannel(hci::ConnectionHandle handle, PSM psm, ChannelParameters params,
ChannelCallback cb);
// Register/Unregister a callback for incoming service connections.
// Incoming channels will be configured using using the preferred parameters |params|.
bool RegisterService(PSM psm, ChannelParameters params, ChannelCallback cb);
void UnregisterService(PSM psm);
// Information stored and returned for registered services that is needed to configure and forward
// new channels for this service.
using ServiceInfo = ServiceInfo<ChannelCallback>;
// Send a Connection Parameter Update Request on the LE signaling channel. When the Connection
// Parameter Update Response is received, |request_cb| will be called with the result, |accepted|.
//
// NOTE: The local Host must be an LE slave, and this request should only be sent if the slave or
// host does not support the Connection Parameters Request Link Layer Control Procedure (Core Spec
// v5.2, Vol 3, Part A, Sec 4.20).
void RequestConnectionParameterUpdate(hci::ConnectionHandle handle,
hci::LEPreferredConnectionParameters params,
ConnectionParameterUpdateRequestCallback request_cb);
// Attach ChannelManager's inspect nodes as children of |parent|.
void AttachInspect(inspect::Node& parent);
// Returns a pointer to the internal LogicalLink with the corresponding link |handle|, or nullptr
// if none exists.
// NOTE: This is intended ONLY for unit tests. Clients should use the other public methods to
// interact with the link.
fxl::WeakPtr<internal::LogicalLink> LogicalLinkForTesting(hci::ConnectionHandle handle);
private:
// 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::ConnectionHandle handle,
hci::Connection::LinkType ll_type,
hci::Connection::Role 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::ConnectionHandle handle, PSM psm);
// Maximum sizes for data packet payloads from host to controller.
const size_t max_acl_payload_size_;
const size_t max_le_payload_size_;
hci::AclDataChannel* acl_data_channel_;
using LinkMap = std::unordered_map<hci::ConnectionHandle, fbl::RefPtr<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::ConnectionHandle, LinkedList<hci::ACLDataPacket>>;
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_;
// Stored info on whether random channel ids are requested.
bool random_channel_ids_;
// TODO(fxbug.rev/63851): Find a better home for this. For now, we know that this only holds
// promises scheduled by LogicalLinks to destroy themselves, so this living here provides a
// minimal guarantee that the executor outlives the LogicalLinks.
async::Executor executor_;
fit::thread_checker thread_checker_;
fxl::WeakPtrFactory<ChannelManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelManager);
};
} // namespace l2cap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_CHANNEL_MANAGER_H_