| // 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_TESTING_FAKE_L2CAP_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_FAKE_L2CAP_H_ |
| |
| #include <lib/fit/function.h> |
| |
| #include <unordered_map> |
| |
| #include "fake_dynamic_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h" |
| |
| namespace bt::testing { |
| |
| // This class unpacks data units received from ACL-U and LE-U logical links |
| // into L2CAP SDUs and then routes them to indvidually-registered handler |
| // functions. Each FakePeer should have its own FakeL2cap instance and its |
| // own set of ACL-U and LE-U logical links. |
| // This class also manages the dynamic L2CAP channels, stored in |
| // FakeDynamicChannel objects. |
| // Generally, dynamic channels are created by means of ConnectionRequest packets |
| // sent over the fixed signaling channel, but FakeL2cap actually holds the |
| // channels created and modified by those requests. |
| // Services can register with FakeL2cap and then assign callbacks to individual |
| // FakeDynamicChannels (based on their PSMs), but those services do not own and |
| // cannot directly delete or create new channels without using the FakeL2cap |
| // instance managing those dynamic channels. |
| class FakeL2cap final { |
| public: |
| // Entities that instantiate FakeL2cap must provide a |
| // SendFrameCallback function to handle adding necessary protocol data unit |
| // header information to the packet and actually sending the packet using |
| // the associated device. |
| using SendFrameCallback = |
| fit::function<void(hci::ConnectionHandle conn, l2cap::ChannelId cid, const ByteBuffer& pdu)>; |
| |
| // After registering a channel with RegisterHandler, ChannelReceiveCallback |
| // is called with a received L2CAP Service Data Unit |sdu| when FakeL2cap |
| // handles a packet for the registered channel. Includes the |handle| that |
| // the L2CAP packet was received on. |
| using ChannelReceiveCallback = |
| fit::function<void(hci::ConnectionHandle handle, const ByteBuffer& sdu)>; |
| |
| // When FakeL2cap receives a packet with a ChannelID that does not have a |
| // registered handler, it calls UnexpectedPduCallback (set via the |
| // constructor or defaulted to a no-op). To aid with debugging, this callback |
| // takes the entire Protocol Data Unit |pdu| (including the intact L2CAP |
| // header). Also includes the |handle| that the L2CAP packet was received on. |
| using UnexpectedPduCallback = |
| fit::function<void(hci::ConnectionHandle handle, const ByteBuffer& pdu)>; |
| |
| // Each service that registers itself on a particular PSM provides a callback |
| // that takes a pointer to the FakeDynamicChannel with its |
| using FakeDynamicChannelCallback = fit::function<void(fxl::WeakPtr<FakeDynamicChannel>)>; |
| |
| // Calls |send_frame_callback| to send packets through the device |
| // instantiating the FakeL2cap instance. |
| // Calls |unexpected_pdu_callback| for packets received that don't have a |
| // handler registered. Defaults to a no-op if no callback provided. |
| // Assumes the largest possible dynamic channel ID is |
| // l2cap::kLastACLDynamicChannelId. |
| explicit FakeL2cap( |
| SendFrameCallback send_frame_callback, |
| UnexpectedPduCallback unexpected_pdu_callback = [](auto handle, auto& pdu) {}, |
| l2cap::ChannelId largest_channel_id = l2cap::kLastACLDynamicChannelId); |
| |
| // Public methods for services/clients that operate over L2CAP channels: |
| // TODO (fxbug.dev/57535) Add Unit tests for public methods |
| |
| // Register |callback| to be called when a Service Data Unit (SDU) is |
| // received on an L2CAP channel identified by |cid|. |callback| will be |
| // retained until overwritten by another RegisterHandler call or this |
| // class is destroyed. To remove a specific |callback|, overwrite it by |
| // registering a no-op (or other handler) on the corresponding |cid|. |
| // This should only be used for registering fixed channel handlers - use |
| // RegisterDynamicChannel for dynamic channel management. |
| void RegisterHandler(l2cap::ChannelId cid, ChannelReceiveCallback callback); |
| |
| // Register a |callback| function that configures a FakeDynamicChannel which |
| // operates over a Protocol Service Multiplexer |psm|. When |
| // RegisterDynamicChannelWithPsm() will call this FakeDynamicChannelCallback |
| // if the channel is open. |
| void RegisterService(l2cap::PSM psm, FakeDynamicChannelCallback callback); |
| |
| // The following methods are generally for use by the FakeSignalingServer when |
| // creating and managing individual FakeDynamicChannel instances. |
| |
| // Register a new FakeDynamicChannel with an associated connection handle |
| // |conn|, Protocol Service Multiplexer PSM, remote Channel ID |
| // |remote_cid|, and local Channel ID |local_cid|. |
| // This channel will be closed, as this function only allocates |
| // a local ID for the channel. This function also does not register the |
| // channel with the service associated with the channel's PSM, as this |
| // happens after the channel is open. |
| // Return status of if the registration was successful. |
| // This should only be used for registering dynamic channels - use |
| // RegisterHandler for fixed channel management. |
| bool RegisterDynamicChannel(hci::ConnectionHandle conn, l2cap::PSM psm, |
| l2cap::ChannelId local_cid, l2cap::ChannelId remote_cid); |
| |
| // Call the DynamicChannelCallback associated with the service operating |
| // on the channel's PSM once this channel has been opened. |
| // Applications should only call this function after confirming that the |
| // channel associated with the connection handle |conn| and local channel ID |
| // |local_cid| is already open, as the DynamicChannelCallback associated with |
| // the PSM will assume that it can operate over the channel. |
| // Return status of if the registration was successful. |
| bool RegisterDynamicChannelWithPsm(hci::ConnectionHandle conn, l2cap::ChannelId local_cid); |
| |
| // Return true if there is a FakeDynamicChannelCallback registered for |
| // Protocol Service Multiplexer |psm|, return false otherwise. |
| bool ServiceRegisteredForPsm(l2cap::PSM psm); |
| |
| // Methods to observe and manipulate L2CAP channel states: |
| |
| // Return the first local dynamic Channel Id that does not already have a |
| // FakeDynamicChannel object associated with it. This incrementally checks |
| // each individual ID starting from l2cap::kFirstDynamicChannelId up to this |
| // FakeL2cap instance's largest_channel_id_, so runtime increases linearly |
| // with the number of consecutive registered channels but can be limited by |
| // limiting the value of largest_channel_id_ when initializing FakeL2cap. If |
| // there are no available Channel IDs, returns l2cap::kInvalidChannelId. |
| // Requires identification of the specific ConnectionHandle |conn| associated |
| // with this connection. |
| l2cap::ChannelId FindAvailableDynamicChannelId(hci::ConnectionHandle conn); |
| |
| // Find the FakeDynamicChannel object with the local channel ID |local_cid| |
| // and connection handle |conn| in the dynamic_channels_ map. If there is no |
| // channel registered with the |local_cid|, return a nullptr. |
| fxl::WeakPtr<FakeDynamicChannel> FindDynamicChannelByLocalId(hci::ConnectionHandle conn, |
| l2cap::ChannelId local_cid); |
| |
| // Find the FakeDynamicChannel object with the connection handle |conn| and |
| // local channel ID |remote_cid| in the dynamic_channels_ map. If there is |
| // no channel registered with the |remote_cid|, return a nullptr. |
| fxl::WeakPtr<FakeDynamicChannel> FindDynamicChannelByRemoteId(hci::ConnectionHandle conn, |
| l2cap::ChannelId remote_cid); |
| |
| // Remove a dynamic channel associated with the connection handle |conn| and locassigned |
| // |local_cid|. Will call the channel's ChannelClosedCallback if it has one, set the channel to |
| // closed, and then delete the value from the map FakeL2cap uses to store |
| // the channels (which should also destroy the channel itself), |
| // This is the only way that dynamic channels should be deleted - if they are |
| // torn down individually, FakeL2cap will incorrectly hold that local_cid. |
| void DeleteDynamicChannelByLocalId(hci::ConnectionHandle conn, l2cap::ChannelId local_cid); |
| |
| // Routes the |pdu| to the appropriate calllback function by extracting the |
| // ChannelID of the received packet |pdu| and calling the corresponding |
| // registered handler function (and providing it with the |handle| the packet |
| // was received on and the payload Service Data Unit |sdu|. |
| void HandlePdu(hci::ConnectionHandle conn, const ByteBuffer& pdu); |
| |
| // Return the SendFrameCallback associated with this FakeL2cap instance. |
| SendFrameCallback& send_frame_callback() { return send_frame_callback_; } |
| |
| private: |
| // Map of channel IDs and corresponding functions. Use an unordered map for |
| // constant-time (in the best case) for search/insertion/deletion when |
| // accessing calllbacks associated with specific channel IDs. |
| std::unordered_map<l2cap::ChannelId, ChannelReceiveCallback> callbacks_; |
| |
| // Map of dynamically allocated Channel IDs and corresponding channels. Use |
| // an unordered map for constant-time search/insertion/deletion when |
| // accessing channels associated with specific channel IDs. |
| std::unordered_map<hci::ConnectionHandle, |
| std::unordered_map<l2cap::ChannelId, std::unique_ptr<FakeDynamicChannel>>> |
| dynamic_channels_; |
| |
| // Map of individual channel configuration callbacks associated with |
| // individual services |
| std::unordered_map<l2cap::PSM, FakeDynamicChannelCallback> registered_services_; |
| |
| // Function provided by the device that instantiates the FakeL2cap instance |
| // that adds PDU header information and actually sends the packet. |
| SendFrameCallback send_frame_callback_; |
| |
| // Handler function associated with unexpected PDUs. Defaults to a no-op. |
| UnexpectedPduCallback unexpected_pdu_callback_; |
| |
| // Largest dynamic channel ID this FakeL2cap instance can allocate IDs for. |
| // Defaults to l2cap::kLastACLDynamicChannelId. |
| l2cap::ChannelId largest_channel_id_; |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FakeL2cap); |
| }; |
| |
| } // namespace bt::testing |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_FAKE_L2CAP_H_ |