1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 #include <lib/fit/function.h> 17 18 #include <unordered_map> 19 20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 21 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 23 #include "pw_bluetooth_sapphire/internal/host/testing/fake_dynamic_channel.h" 24 25 namespace bt::testing { 26 27 // This class unpacks data units received from ACL-U and LE-U logical links 28 // into L2CAP SDUs and then routes them to indvidually-registered handler 29 // functions. Each FakePeer should have its own FakeL2cap instance and its 30 // own set of ACL-U and LE-U logical links. 31 // This class also manages the dynamic L2CAP channels, stored in 32 // FakeDynamicChannel objects. 33 // Generally, dynamic channels are created by means of ConnectionRequest packets 34 // sent over the fixed signaling channel, but FakeL2cap actually holds the 35 // channels created and modified by those requests. 36 // Services can register with FakeL2cap and then assign callbacks to individual 37 // FakeDynamicChannels (based on their PSMs), but those services do not own and 38 // cannot directly delete or create new channels without using the FakeL2cap 39 // instance managing those dynamic channels. 40 class FakeL2cap final { 41 public: 42 // Entities that instantiate FakeL2cap must provide a 43 // SendFrameCallback function to handle adding necessary protocol data unit 44 // header information to the packet and actually sending the packet using 45 // the associated device. 46 using SendFrameCallback = fit::function<void(hci_spec::ConnectionHandle conn, 47 l2cap::ChannelId cid, 48 const ByteBuffer& pdu)>; 49 50 // After registering a channel with RegisterHandler, ChannelReceiveCallback 51 // is called with a received L2CAP Service Data Unit |sdu| when FakeL2cap 52 // handles a packet for the registered channel. Includes the |handle| that 53 // the L2CAP packet was received on. 54 using ChannelReceiveCallback = fit::function<void( 55 hci_spec::ConnectionHandle handle, const ByteBuffer& sdu)>; 56 57 // When FakeL2cap receives a packet with a ChannelID that does not have a 58 // registered handler, it calls UnexpectedPduCallback (set via the 59 // constructor or defaulted to a no-op). To aid with debugging, this callback 60 // takes the entire Protocol Data Unit |pdu| (including the intact L2CAP 61 // header). Also includes the |handle| that the L2CAP packet was received on. 62 using UnexpectedPduCallback = fit::function<void( 63 hci_spec::ConnectionHandle handle, const ByteBuffer& pdu)>; 64 65 // Each service that registers itself on a particular PSM provides a callback 66 // that takes a pointer to the FakeDynamicChannel with its 67 using FakeDynamicChannelCallback = 68 fit::function<void(FakeDynamicChannel::WeakPtr)>; 69 70 // Calls |send_frame_callback| to send packets through the device 71 // instantiating the FakeL2cap instance. 72 // Calls |unexpected_pdu_callback| for packets received that don't have a 73 // handler registered. Defaults to a no-op if no callback provided. 74 // Assumes the largest possible dynamic channel ID is 75 // l2cap::kLastACLDynamicChannelId. 76 explicit FakeL2cap( 77 SendFrameCallback send_frame_callback, 78 UnexpectedPduCallback unexpected_pdu_callback = [](auto handle, 79 auto& pdu) {}, 80 l2cap::ChannelId largest_channel_id = l2cap::kLastACLDynamicChannelId); 81 82 // Public methods for services/clients that operate over L2CAP channels: 83 // TODO(fxbug.dev/42135392) Add Unit tests for public methods 84 85 // Register |callback| to be called when a Service Data Unit (SDU) is 86 // received on an L2CAP channel identified by |cid|. |callback| will be 87 // retained until overwritten by another RegisterHandler call or this 88 // class is destroyed. To remove a specific |callback|, overwrite it by 89 // registering a no-op (or other handler) on the corresponding |cid|. 90 // This should only be used for registering fixed channel handlers - use 91 // RegisterDynamicChannel for dynamic channel management. 92 void RegisterHandler(l2cap::ChannelId cid, ChannelReceiveCallback callback); 93 94 // Register a |callback| function that configures a FakeDynamicChannel which 95 // operates over a Protocol Service Multiplexer |psm|. When 96 // RegisterDynamicChannelWithPsm() will call this FakeDynamicChannelCallback 97 // if the channel is open. 98 void RegisterService(l2cap::Psm psm, FakeDynamicChannelCallback callback); 99 100 // The following methods are generally for use by the FakeSignalingServer when 101 // creating and managing individual FakeDynamicChannel instances. 102 103 // Register a new FakeDynamicChannel with an associated connection handle 104 // |conn|, Protocol Service Multiplexer PSM, remote Channel ID 105 // |remote_cid|, and local Channel ID |local_cid|. 106 // This channel will be closed, as this function only allocates 107 // a local ID for the channel. This function also does not register the 108 // channel with the service associated with the channel's PSM, as this 109 // happens after the channel is open. 110 // Return status of if the registration was successful. 111 // This should only be used for registering dynamic channels - use 112 // RegisterHandler for fixed channel management. 113 bool RegisterDynamicChannel(hci_spec::ConnectionHandle conn, 114 l2cap::Psm psm, 115 l2cap::ChannelId local_cid, 116 l2cap::ChannelId remote_cid); 117 118 // Call the DynamicChannelCallback associated with the service operating 119 // on the channel's PSM once this channel has been opened. 120 // Applications should only call this function after confirming that the 121 // channel associated with the connection handle |conn| and local channel ID 122 // |local_cid| is already open, as the DynamicChannelCallback associated with 123 // the PSM will assume that it can operate over the channel. 124 // Return status of if the registration was successful. 125 bool RegisterDynamicChannelWithPsm(hci_spec::ConnectionHandle conn, 126 l2cap::ChannelId local_cid); 127 128 // Return true if there is a FakeDynamicChannelCallback registered for 129 // Protocol Service Multiplexer |psm|, return false otherwise. 130 bool ServiceRegisteredForPsm(l2cap::Psm psm); 131 132 // Methods to observe and manipulate L2CAP channel states: 133 134 // Return the first local dynamic Channel Id that does not already have a 135 // FakeDynamicChannel object associated with it. This incrementally checks 136 // each individual ID starting from l2cap::kFirstDynamicChannelId up to this 137 // FakeL2cap instance's largest_channel_id_, so runtime increases linearly 138 // with the number of consecutive registered channels but can be limited by 139 // limiting the value of largest_channel_id_ when initializing FakeL2cap. If 140 // there are no available Channel IDs, returns l2cap::kInvalidChannelId. 141 // Requires identification of the specific ConnectionHandle |conn| associated 142 // with this connection. 143 l2cap::ChannelId FindAvailableDynamicChannelId( 144 hci_spec::ConnectionHandle conn); 145 146 // Find the FakeDynamicChannel object with the local channel ID |local_cid| 147 // and connection handle |conn| in the dynamic_channels_ map. If there is no 148 // channel registered with the |local_cid|, return a nullptr. 149 FakeDynamicChannel::WeakPtr FindDynamicChannelByLocalId( 150 hci_spec::ConnectionHandle conn, l2cap::ChannelId local_cid); 151 152 // Find the FakeDynamicChannel object with the connection handle |conn| and 153 // local channel ID |remote_cid| in the dynamic_channels_ map. If there is 154 // no channel registered with the |remote_cid|, return a nullptr. 155 FakeDynamicChannel::WeakPtr FindDynamicChannelByRemoteId( 156 hci_spec::ConnectionHandle conn, l2cap::ChannelId remote_cid); 157 158 // Remove a dynamic channel associated with the connection handle |conn| and 159 // locassigned |local_cid|. Will call the channel's ChannelClosedCallback if 160 // it has one, set the channel to closed, and then delete the value from the 161 // map FakeL2cap uses to store the channels (which should also destroy the 162 // channel itself), This is the only way that dynamic channels should be 163 // deleted - if they are torn down individually, FakeL2cap will incorrectly 164 // hold that local_cid. 165 void DeleteDynamicChannelByLocalId(hci_spec::ConnectionHandle conn, 166 l2cap::ChannelId local_cid); 167 168 // Routes the |pdu| to the appropriate calllback function by extracting the 169 // ChannelID of the received packet |pdu| and calling the corresponding 170 // registered handler function (and providing it with the |handle| the packet 171 // was received on and the payload Service Data Unit |sdu|. 172 void HandlePdu(hci_spec::ConnectionHandle conn, const ByteBuffer& pdu); 173 174 // Return the SendFrameCallback associated with this FakeL2cap instance. send_frame_callback()175 SendFrameCallback& send_frame_callback() { return send_frame_callback_; } 176 177 private: 178 // Map of channel IDs and corresponding functions. Use an unordered map for 179 // constant-time (in the best case) for search/insertion/deletion when 180 // accessing calllbacks associated with specific channel IDs. 181 std::unordered_map<l2cap::ChannelId, ChannelReceiveCallback> callbacks_; 182 183 // Map of dynamically allocated Channel IDs and corresponding channels. Use 184 // an unordered map for constant-time search/insertion/deletion when 185 // accessing channels associated with specific channel IDs. 186 std::unordered_map< 187 hci_spec::ConnectionHandle, 188 std::unordered_map<l2cap::ChannelId, std::unique_ptr<FakeDynamicChannel>>> 189 dynamic_channels_; 190 191 // Map of individual channel configuration callbacks associated with 192 // individual services 193 std::unordered_map<l2cap::Psm, FakeDynamicChannelCallback> 194 registered_services_; 195 196 // Function provided by the device that instantiates the FakeL2cap instance 197 // that adds PDU header information and actually sends the packet. 198 SendFrameCallback send_frame_callback_; 199 200 // Handler function associated with unexpected PDUs. Defaults to a no-op. 201 UnexpectedPduCallback unexpected_pdu_callback_; 202 203 // Largest dynamic channel ID this FakeL2cap instance can allocate IDs for. 204 // Defaults to l2cap::kLastACLDynamicChannelId. 205 l2cap::ChannelId largest_channel_id_; 206 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FakeL2cap); 207 }; 208 209 } // namespace bt::testing 210