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, auto&) {},
79       l2cap::ChannelId largest_channel_id = l2cap::kLastACLDynamicChannelId);
80 
81   // Public methods for services/clients that operate over L2CAP channels:
82   // TODO(fxbug.dev/42135392) Add Unit tests for public methods
83 
84   // Register |callback| to be called when a Service Data Unit (SDU) is
85   // received on an L2CAP channel identified by |cid|. |callback| will be
86   // retained until overwritten by another RegisterHandler call or this
87   // class is destroyed. To remove a specific |callback|, overwrite it by
88   // registering a no-op (or other handler) on the corresponding |cid|.
89   // This should only be used for registering fixed channel handlers - use
90   // RegisterDynamicChannel for dynamic channel management.
91   void RegisterHandler(l2cap::ChannelId cid, ChannelReceiveCallback callback);
92 
93   // Register a |callback| function that configures a FakeDynamicChannel which
94   // operates over a Protocol Service Multiplexer |psm|. When
95   // RegisterDynamicChannelWithPsm() will call this FakeDynamicChannelCallback
96   // if the channel is open.
97   void RegisterService(l2cap::Psm psm, FakeDynamicChannelCallback callback);
98 
99   // The following methods are generally for use by the FakeSignalingServer when
100   // creating and managing individual FakeDynamicChannel instances.
101 
102   // Register a new FakeDynamicChannel with an associated connection handle
103   // |conn|, Protocol Service Multiplexer PSM, remote Channel ID
104   // |remote_cid|, and local Channel ID |local_cid|.
105   // This channel will be closed, as this function only allocates
106   // a local ID for the channel. This function also does not register the
107   // channel with the service associated with the channel's PSM, as this
108   // happens after the channel is open.
109   // Return status of if the registration was successful.
110   // This should only be used for registering dynamic channels - use
111   // RegisterHandler for fixed channel management.
112   bool RegisterDynamicChannel(hci_spec::ConnectionHandle conn,
113                               l2cap::Psm psm,
114                               l2cap::ChannelId local_cid,
115                               l2cap::ChannelId remote_cid);
116 
117   // Call the DynamicChannelCallback associated with the service operating
118   // on the channel's PSM once this channel has been opened.
119   // Applications should only call this function after confirming that the
120   // channel associated with the connection handle |conn| and local channel ID
121   // |local_cid| is already open, as the DynamicChannelCallback associated with
122   // the PSM will assume that it can operate over the channel.
123   // Return status of if the registration was successful.
124   bool RegisterDynamicChannelWithPsm(hci_spec::ConnectionHandle conn,
125                                      l2cap::ChannelId local_cid);
126 
127   // Return true if there is a FakeDynamicChannelCallback registered for
128   // Protocol Service Multiplexer |psm|, return false otherwise.
129   bool ServiceRegisteredForPsm(l2cap::Psm psm);
130 
131   // Methods to observe and manipulate L2CAP channel states:
132 
133   // Return the first local dynamic Channel Id that does not already have a
134   // FakeDynamicChannel object associated with it. This incrementally checks
135   // each individual ID starting from l2cap::kFirstDynamicChannelId up to this
136   // FakeL2cap instance's largest_channel_id_, so runtime increases linearly
137   // with the number of consecutive registered channels but can be limited by
138   // limiting the value of largest_channel_id_ when initializing FakeL2cap. If
139   // there are no available Channel IDs, returns l2cap::kInvalidChannelId.
140   // Requires identification of the specific ConnectionHandle |conn| associated
141   // with this connection.
142   l2cap::ChannelId FindAvailableDynamicChannelId(
143       hci_spec::ConnectionHandle conn);
144 
145   // Find the FakeDynamicChannel object with the local channel ID |local_cid|
146   // and connection handle |conn| in the dynamic_channels_ map. If there is no
147   // channel registered with the |local_cid|, return a nullptr.
148   FakeDynamicChannel::WeakPtr FindDynamicChannelByLocalId(
149       hci_spec::ConnectionHandle conn, l2cap::ChannelId local_cid);
150 
151   // Find the FakeDynamicChannel object with the connection handle |conn| and
152   // local channel ID |remote_cid| in the dynamic_channels_ map. If there is
153   // no channel registered with the |remote_cid|, return a nullptr.
154   FakeDynamicChannel::WeakPtr FindDynamicChannelByRemoteId(
155       hci_spec::ConnectionHandle conn, l2cap::ChannelId remote_cid);
156 
157   // Remove a dynamic channel associated with the connection handle |conn| and
158   // locassigned |local_cid|. Will call the channel's ChannelClosedCallback if
159   // it has one, set the channel to closed, and then delete the value from the
160   // map FakeL2cap uses to store the channels (which should also destroy the
161   // channel itself), This is the only way that dynamic channels should be
162   // deleted - if they are torn down individually, FakeL2cap will incorrectly
163   // hold that local_cid.
164   void DeleteDynamicChannelByLocalId(hci_spec::ConnectionHandle conn,
165                                      l2cap::ChannelId local_cid);
166 
167   // Routes the |pdu| to the appropriate calllback function by extracting the
168   // ChannelID of the received packet |pdu| and calling the corresponding
169   // registered handler function (and providing it with the |handle| the packet
170   // was received on and the payload Service Data Unit |sdu|.
171   void HandlePdu(hci_spec::ConnectionHandle conn, const ByteBuffer& pdu);
172 
173   // Return the SendFrameCallback associated with this FakeL2cap instance.
send_frame_callback()174   SendFrameCallback& send_frame_callback() { return send_frame_callback_; }
175 
176  private:
177   // Map of channel IDs and corresponding functions. Use an unordered map for
178   // constant-time (in the best case) for search/insertion/deletion when
179   // accessing calllbacks associated with specific channel IDs.
180   std::unordered_map<l2cap::ChannelId, ChannelReceiveCallback> callbacks_;
181 
182   // Map of dynamically allocated Channel IDs and corresponding channels. Use
183   // an unordered map for constant-time search/insertion/deletion when
184   // accessing channels associated with specific channel IDs.
185   std::unordered_map<
186       hci_spec::ConnectionHandle,
187       std::unordered_map<l2cap::ChannelId, std::unique_ptr<FakeDynamicChannel>>>
188       dynamic_channels_;
189 
190   // Map of individual channel configuration callbacks associated with
191   // individual services
192   std::unordered_map<l2cap::Psm, FakeDynamicChannelCallback>
193       registered_services_;
194 
195   // Function provided by the device that instantiates the FakeL2cap instance
196   // that adds PDU header information and actually sends the packet.
197   SendFrameCallback send_frame_callback_;
198 
199   // Handler function associated with unexpected PDUs. Defaults to a no-op.
200   UnexpectedPduCallback unexpected_pdu_callback_;
201 
202   // Largest dynamic channel ID this FakeL2cap instance can allocate IDs for.
203   // Defaults to l2cap::kLastACLDynamicChannelId.
204   l2cap::ChannelId largest_channel_id_;
205   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FakeL2cap);
206 };
207 
208 }  // namespace bt::testing
209