xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/testing/fake_l2cap.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include "pw_bluetooth_sapphire/internal/host/testing/fake_l2cap.h"
16 
17 #include <pw_bytes/endian.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/fake_dynamic_channel.h"
22 
23 namespace bt::testing {
24 
FakeL2cap(SendFrameCallback send_frame_callback,UnexpectedPduCallback unexpected_pdu_callback,l2cap::ChannelId largest_channel_id)25 FakeL2cap::FakeL2cap(SendFrameCallback send_frame_callback,
26                      UnexpectedPduCallback unexpected_pdu_callback,
27                      l2cap::ChannelId largest_channel_id)
28     : send_frame_callback_(std::move(send_frame_callback)),
29       unexpected_pdu_callback_(std::move(unexpected_pdu_callback)),
30       largest_channel_id_(largest_channel_id) {}
31 
RegisterHandler(l2cap::ChannelId cid,ChannelReceiveCallback callback)32 void FakeL2cap::RegisterHandler(l2cap::ChannelId cid,
33                                 ChannelReceiveCallback callback) {
34   PW_CHECK(cid < l2cap::kFirstDynamicChannelId);
35   if (callbacks_.find(cid) != callbacks_.end()) {
36     bt_log(WARN,
37            "fake-hci",
38            "Overwriting previous handler for Channel ID %hu",
39            cid);
40   }
41   callbacks_.insert_or_assign(cid, std::move(callback));
42 }
43 
RegisterService(l2cap::Psm psm,FakeDynamicChannelCallback callback)44 void FakeL2cap::RegisterService(l2cap::Psm psm,
45                                 FakeDynamicChannelCallback callback) {
46   if (callbacks_.find(psm) != callbacks_.end()) {
47     bt_log(WARN, "fake-hci", "Overwriting previous handler for PSM %.4hu", psm);
48   }
49   registered_services_.insert_or_assign(psm, std::move(callback));
50 }
51 
RegisterDynamicChannel(hci_spec::ConnectionHandle conn,l2cap::Psm psm,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid)52 bool FakeL2cap::RegisterDynamicChannel(hci_spec::ConnectionHandle conn,
53                                        l2cap::Psm psm,
54                                        l2cap::ChannelId local_cid,
55                                        l2cap::ChannelId remote_cid) {
56   PW_CHECK(local_cid >= l2cap::kFirstDynamicChannelId);
57   auto channel_map = dynamic_channels_.find(conn);
58   if (channel_map != dynamic_channels_.end()) {
59     if (channel_map->second.find(local_cid) == channel_map->second.end()) {
60       bt_log(ERROR,
61              "fake-hci",
62              "Dynamic channel already exists at handle %hu and Channel ID %hu",
63              conn,
64              local_cid);
65       return false;
66     }
67   }
68   if (!ServiceRegisteredForPsm(psm)) {
69     bt_log(ERROR, "fake-hci", "No service registered for psm %hu", psm);
70     return false;
71   }
72   if (dynamic_channels_.find(conn) == dynamic_channels_.end()) {
73     std::unordered_map<l2cap::ChannelId, std::unique_ptr<FakeDynamicChannel>>
74         new_conn_map;
75     dynamic_channels_.emplace(conn, std::move(new_conn_map));
76   }
77   dynamic_channels_.find(conn)->second.insert_or_assign(
78       local_cid,
79       std::make_unique<FakeDynamicChannel>(conn, psm, local_cid, remote_cid));
80   return true;
81 }
82 
RegisterDynamicChannelWithPsm(hci_spec::ConnectionHandle conn,l2cap::ChannelId local_cid)83 bool FakeL2cap::RegisterDynamicChannelWithPsm(hci_spec::ConnectionHandle conn,
84                                               l2cap::ChannelId local_cid) {
85   auto channel = FindDynamicChannelByLocalId(conn, local_cid);
86   if (channel.is_alive()) {
87     PW_CHECK(channel->opened());
88     auto psm_iter = registered_services_.find(channel->psm());
89     if (psm_iter == registered_services_.end()) {
90       bt_log(ERROR,
91              "fake-hci",
92              "No service registered for psm %hu",
93              channel->psm());
94       return false;
95     }
96     psm_iter->second(channel);
97     // If the callback does not assign a send_packet_callback, default to
98     // FakeL2cap's version.
99     if (!channel->send_packet_callback()) {
100       auto send_cb = [this, channel](auto& packet) -> void {
101         auto& l2cap_callback = this->send_frame_callback();
102         l2cap_callback(channel->handle(), channel->local_cid(), packet);
103       };
104       channel->set_send_packet_callback(send_cb);
105     }
106     return true;
107   }
108   return false;
109 }
110 
ServiceRegisteredForPsm(l2cap::Psm psm)111 bool FakeL2cap::ServiceRegisteredForPsm(l2cap::Psm psm) {
112   auto iter = registered_services_.find(psm);
113   if (iter == registered_services_.end()) {
114     return false;
115   }
116   return true;
117 }
118 
FindAvailableDynamicChannelId(hci_spec::ConnectionHandle conn)119 l2cap::ChannelId FakeL2cap::FindAvailableDynamicChannelId(
120     hci_spec::ConnectionHandle conn) {
121   auto channel_map = dynamic_channels_.find(conn);
122   // If there are no dynamic channel connections on the ConnectHandle, assign
123   // it the first ID
124   if (channel_map == dynamic_channels_.end()) {
125     return l2cap::kFirstDynamicChannelId;
126   }
127   for (l2cap::ChannelId id = l2cap::kFirstDynamicChannelId;
128        id < largest_channel_id_ + 1;
129        id++) {
130     if (channel_map->second.count(id) == 0) {
131       return id;
132     }
133   }
134   return l2cap::kInvalidChannelId;
135 }
136 
FindDynamicChannelByLocalId(hci_spec::ConnectionHandle conn,l2cap::ChannelId local_cid)137 FakeDynamicChannel::WeakPtr FakeL2cap::FindDynamicChannelByLocalId(
138     hci_spec::ConnectionHandle conn, l2cap::ChannelId local_cid) {
139   auto channel_map = dynamic_channels_.find(conn);
140   if (channel_map == dynamic_channels_.end()) {
141     return FakeDynamicChannel::WeakPtr();
142   }
143   auto channel = channel_map->second.find(local_cid);
144   if (channel == channel_map->second.end()) {
145     return FakeDynamicChannel::WeakPtr();
146   }
147   return channel->second->AsWeakPtr();
148 }
149 
FindDynamicChannelByRemoteId(hci_spec::ConnectionHandle conn,l2cap::ChannelId remote_cid)150 FakeDynamicChannel::WeakPtr FakeL2cap::FindDynamicChannelByRemoteId(
151     hci_spec::ConnectionHandle conn, l2cap::ChannelId remote_cid) {
152   auto channel_map = dynamic_channels_.find(conn);
153   if (channel_map == dynamic_channels_.end()) {
154     return FakeDynamicChannel::WeakPtr();
155   }
156   for (auto& [id, channel_ptr] : channel_map->second) {
157     if (channel_ptr->remote_cid() == remote_cid) {
158       return channel_ptr->AsWeakPtr();
159     }
160   }
161   return FakeDynamicChannel::WeakPtr();
162 }
163 
DeleteDynamicChannelByLocalId(hci_spec::ConnectionHandle conn,l2cap::ChannelId local_cid)164 void FakeL2cap::DeleteDynamicChannelByLocalId(hci_spec::ConnectionHandle conn,
165                                               l2cap::ChannelId local_cid) {
166   auto channel_map = dynamic_channels_.find(conn);
167   if (channel_map == dynamic_channels_.end()) {
168     bt_log(ERROR, "hci-fake", "Attempt to delete unregistered channel.");
169     return;
170   }
171   auto channel = channel_map->second.find(local_cid);
172   if (channel == channel_map->second.end()) {
173     bt_log(ERROR, "hci-fake", "Attempt to delete unregistered channel.");
174     return;
175   }
176   channel->second = nullptr;
177   dynamic_channels_[conn].erase(local_cid);
178 }
179 
HandlePdu(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)180 void FakeL2cap::HandlePdu(hci_spec::ConnectionHandle conn,
181                           const ByteBuffer& pdu) {
182   if (pdu.size() < sizeof(l2cap::BasicHeader)) {
183     bt_log(WARN, "fake-hci", "malformed L2CAP packet!");
184     return;
185   }
186 
187   // Extract channel ID and strip the L2CAP header from the pdu.
188   const auto& header = pdu.To<l2cap::BasicHeader>();
189   l2cap::ChannelId cid =
190       pw::bytes::ConvertOrderFrom(cpp20::endian::little, header.channel_id);
191   auto header_len = sizeof(header);
192   uint16_t payload_len =
193       pw::bytes::ConvertOrderFrom(cpp20::endian::little, header.length);
194   auto sdu = DynamicByteBuffer(payload_len);
195   pdu.Copy(&sdu, header_len, payload_len);
196 
197   // Execute corresponding handler function based on the channel type.
198   if (cid < l2cap::kFirstDynamicChannelId) {
199     auto handler = callbacks_.find(cid);
200     if (handler == callbacks_.end()) {
201       return unexpected_pdu_callback_(conn, pdu);
202     } else {
203       return handler->second(conn, sdu);
204     }
205   } else {
206     auto channel = FindDynamicChannelByLocalId(conn, cid);
207     if (channel.is_alive()) {
208       auto& callback = channel->packet_handler_callback();
209       return callback(sdu);
210     }
211   }
212   return unexpected_pdu_callback_(conn, pdu);
213 }
214 
215 }  // namespace bt::testing
216