• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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