xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/testing/fake_signaling_server.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_signaling_server.h"
16 
17 #include <pw_bytes/endian.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/fake_l2cap.h"
21 
22 namespace bt::testing {
23 
RegisterWithL2cap(FakeL2cap * l2cap_)24 void FakeSignalingServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
25   auto cb = [&](auto conn, auto& sdu) { return HandleSdu(conn, sdu); };
26   l2cap_->RegisterHandler(l2cap::kSignalingChannelId, cb);
27   fake_l2cap_ = l2cap_;
28 }
29 
HandleSdu(hci_spec::ConnectionHandle conn,const ByteBuffer & sdu)30 void FakeSignalingServer::HandleSdu(hci_spec::ConnectionHandle conn,
31                                     const ByteBuffer& sdu) {
32   PW_CHECK(sdu.size() >= sizeof(l2cap::CommandHeader),
33            "SDU has only %zu bytes",
34            sdu.size());
35   PW_CHECK(sdu.To<l2cap::CommandHeader>().length ==
36            (sdu.size() - sizeof(l2cap::CommandHeader)));
37   // Extract CommandCode and strip signaling packet header from sdu.
38   l2cap::CommandHeader packet_header = sdu.To<l2cap::CommandHeader>();
39   l2cap::CommandCode packet_code = packet_header.code;
40   l2cap::CommandId packet_id = packet_header.id;
41   auto payload = sdu.view(sizeof(l2cap::CommandHeader));
42   switch (packet_code) {
43     case l2cap::kInformationRequest:
44       ProcessInformationRequest(conn, packet_id, payload);
45       break;
46     case l2cap::kConnectionRequest:
47       ProcessConnectionRequest(conn, packet_id, payload);
48       break;
49     case l2cap::kConfigurationRequest:
50       ProcessConfigurationRequest(conn, packet_id, payload);
51       break;
52     case l2cap::kConfigurationResponse:
53       ProcessConfigurationResponse(conn, packet_id, payload);
54       break;
55     case l2cap::kDisconnectionRequest:
56       ProcessDisconnectionRequest(conn, packet_id, payload);
57       break;
58     default:
59       bt_log(ERROR,
60              "hci-emulator",
61              "Does not support request code: %#.4hhx",
62              packet_code);
63       break;
64   }
65 }
66 
ProcessInformationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & info_req)67 void FakeSignalingServer::ProcessInformationRequest(
68     hci_spec::ConnectionHandle conn,
69     l2cap::CommandId id,
70     const ByteBuffer& info_req) {
71   auto info_type = info_req.To<l2cap::InformationType>();
72   if (info_type == l2cap::InformationType::kExtendedFeaturesSupported) {
73     SendInformationResponseExtendedFeatures(conn, id);
74   } else if (info_type == l2cap::InformationType::kFixedChannelsSupported) {
75     SendInformationResponseFixedChannels(conn, id);
76   } else {
77     SendCommandReject(conn, id, l2cap::RejectReason::kNotUnderstood);
78   }
79 }
80 
ProcessConnectionRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & connection_req)81 void FakeSignalingServer::ProcessConnectionRequest(
82     hci_spec::ConnectionHandle conn,
83     l2cap::CommandId id,
84     const ByteBuffer& connection_req) {
85   const auto& conn_req = connection_req.To<l2cap::ConnectionRequestPayload>();
86   const l2cap::Psm psm =
87       pw::bytes::ConvertOrderFrom(cpp20::endian::little, conn_req.psm);
88   const l2cap::ChannelId remote_cid =
89       pw::bytes::ConvertOrderFrom(cpp20::endian::little, conn_req.src_cid);
90 
91   // Validate the remote channel ID prior to assigning it a local ID.
92   if (remote_cid == l2cap::kInvalidChannelId) {
93     bt_log(ERROR,
94            "hci-emulator",
95            "Invalid source CID; rejecting connection for PSM %#.4x from "
96            "channel %#.4x",
97            psm,
98            remote_cid);
99     SendConnectionResponse(conn,
100                            id,
101                            l2cap::kInvalidChannelId,
102                            remote_cid,
103                            l2cap::ConnectionResult::kInvalidSourceCID,
104                            l2cap::ConnectionStatus::kNoInfoAvailable);
105     return;
106   }
107   if (fake_l2cap_->FindDynamicChannelByRemoteId(conn, remote_cid).is_alive()) {
108     bt_log(ERROR,
109            "l2cap-bredr",
110            "Remote CID already in use; rejecting connection for PSM %#.4x from "
111            "channel %#.4x",
112            psm,
113            remote_cid);
114     SendConnectionResponse(conn,
115                            id,
116                            l2cap::kInvalidChannelId,
117                            remote_cid,
118                            l2cap::ConnectionResult::kSourceCIDAlreadyAllocated,
119                            l2cap::ConnectionStatus::kNoInfoAvailable);
120     return;
121   }
122   if (fake_l2cap_->ServiceRegisteredForPsm(psm) == false) {
123     bt_log(ERROR, "l2cap-bredr", "No service registered for PSM %#.4x", psm);
124     SendConnectionResponse(conn,
125                            id,
126                            l2cap::kInvalidChannelId,
127                            remote_cid,
128                            l2cap::ConnectionResult::kPsmNotSupported,
129                            l2cap::ConnectionStatus::kNoInfoAvailable);
130     return;
131   }
132 
133   // Assign the new channel a local channel ID.
134   l2cap::ChannelId local_cid = fake_l2cap_->FindAvailableDynamicChannelId(conn);
135   if (local_cid == l2cap::kInvalidChannelId) {
136     bt_log(DEBUG,
137            "l2cap-bredr",
138            "Out of IDs; rejecting connection for PSM %#.4x from channel %#.4x",
139            psm,
140            remote_cid);
141     SendConnectionResponse(conn,
142                            id,
143                            local_cid,
144                            remote_cid,
145                            l2cap::ConnectionResult::kNoResources,
146                            l2cap::ConnectionStatus::kNoInfoAvailable);
147     return;
148   }
149 
150   // Send a ConnectionResponse and ConfigurationRequest, and then register the
151   // channel.
152   SendConnectionResponse(conn,
153                          id,
154                          local_cid,
155                          remote_cid,
156                          l2cap::ConnectionResult::kSuccess,
157                          l2cap::ConnectionStatus::kNoInfoAvailable);
158   SendConfigurationRequest(conn, id, remote_cid);
159   fake_l2cap_->RegisterDynamicChannel(conn, psm, local_cid, remote_cid);
160 }
161 
ProcessConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_req)162 void FakeSignalingServer::ProcessConfigurationRequest(
163     hci_spec::ConnectionHandle conn,
164     l2cap::CommandId id,
165     const ByteBuffer& configuration_req) {
166   // Ignore the data/flags associated with the request for the purpose of the
167   // emulator
168   const l2cap::ChannelId local_cid =
169       configuration_req
170           .ReadMember<&l2cap::ConfigurationRequestPayload::dst_cid>();
171   auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
172   if (channel.is_alive()) {
173     channel->set_configuration_request_received();
174     SendConfigurationResponse(
175         conn, id, local_cid, l2cap::ConfigurationResult::kSuccess);
176     if (channel->configuration_response_received() &&
177         channel->configuration_request_received()) {
178       channel->set_opened();
179       fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
180     }
181   } else {
182     bt_log(
183         ERROR, "fake-hci", "No local channel at channel ID %#.4x", local_cid);
184     SendConfigurationResponse(
185         conn, id, local_cid, l2cap::ConfigurationResult::kRejected);
186   }
187 }
188 
ProcessConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_res)189 void FakeSignalingServer::ProcessConfigurationResponse(
190     hci_spec::ConnectionHandle conn,
191     l2cap::CommandId id,
192     const ByteBuffer& configuration_res) {
193   const l2cap::ChannelId local_cid =
194       configuration_res
195           .ReadMember<&l2cap::ConfigurationResponsePayload::src_cid>();
196   const l2cap::ConfigurationResult result =
197       configuration_res
198           .ReadMember<&l2cap::ConfigurationResponsePayload::result>();
199   if (result != l2cap::ConfigurationResult::kSuccess) {
200     bt_log(ERROR,
201            "fake-hci",
202            "Failed to create local channel at channel ID %#.4x",
203            local_cid);
204   }
205   auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
206   if (channel.is_alive()) {
207     channel->set_configuration_response_received();
208     if (channel->configuration_response_received() &&
209         channel->configuration_request_received()) {
210       channel->set_opened();
211       fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
212     }
213   }
214 }
215 
ProcessDisconnectionRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & disconnection_req)216 void FakeSignalingServer::ProcessDisconnectionRequest(
217     hci_spec::ConnectionHandle conn,
218     l2cap::CommandId id,
219     const ByteBuffer& disconnection_req) {
220   const auto& disconn_req =
221       disconnection_req.To<l2cap::DisconnectionRequestPayload>();
222   const l2cap::ChannelId local_cid =
223       pw::bytes::ConvertOrderFrom(cpp20::endian::little, disconn_req.dst_cid);
224   const l2cap::ChannelId remote_cid =
225       pw::bytes::ConvertOrderFrom(cpp20::endian::little, disconn_req.src_cid);
226   auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
227   if (channel.is_alive()) {
228     fake_l2cap_->DeleteDynamicChannelByLocalId(conn, local_cid);
229     return SendDisconnectionResponse(conn, id, local_cid, remote_cid);
230   } else {
231     bt_log(ERROR,
232            "fake-hci",
233            "Received disconnection request for non-existent local channel at "
234            "%#.4x",
235            local_cid);
236   }
237 }
238 
SendCFrame(hci_spec::ConnectionHandle conn,l2cap::CommandCode code,l2cap::CommandId id,DynamicByteBuffer & payload_buffer)239 void FakeSignalingServer::SendCFrame(hci_spec::ConnectionHandle conn,
240                                      l2cap::CommandCode code,
241                                      l2cap::CommandId id,
242                                      DynamicByteBuffer& payload_buffer) {
243   size_t kResponseSize = sizeof(l2cap::CommandHeader) + payload_buffer.size();
244   DynamicByteBuffer response_buffer(kResponseSize);
245   MutablePacketView<l2cap::CommandHeader> command_packet(&response_buffer,
246                                                          payload_buffer.size());
247   command_packet.mutable_header()->code = code;
248   command_packet.mutable_header()->id = id;
249   command_packet.mutable_header()->length =
250       static_cast<uint16_t>(payload_buffer.size());
251   command_packet.mutable_payload_data().Write(payload_buffer);
252   auto& callback = fake_l2cap_->send_frame_callback();
253   return callback(conn, l2cap::kSignalingChannelId, response_buffer);
254 }
255 
SendCommandReject(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::RejectReason reason)256 void FakeSignalingServer::SendCommandReject(hci_spec::ConnectionHandle conn,
257                                             l2cap::CommandId id,
258                                             l2cap::RejectReason reason) {
259   DynamicByteBuffer payload_buffer(sizeof(l2cap::CommandRejectPayload));
260   MutablePacketView<l2cap::CommandRejectPayload> payload_view(&payload_buffer);
261   payload_view.mutable_header()->reason =
262       static_cast<uint16_t>(l2cap::RejectReason::kNotUnderstood);
263   SendCFrame(conn, l2cap::kCommandRejectCode, id, payload_buffer);
264 }
265 
SendInformationResponseExtendedFeatures(hci_spec::ConnectionHandle conn,l2cap::CommandId id)266 void FakeSignalingServer::SendInformationResponseExtendedFeatures(
267     hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
268   l2cap::ExtendedFeatures extended_features =
269       l2cap::kExtendedFeaturesBitFixedChannels |
270       l2cap::kExtendedFeaturesBitEnhancedRetransmission;
271   constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
272                                   sizeof(l2cap::ExtendedFeatures);
273   DynamicByteBuffer payload_buffer(kPayloadSize);
274   MutablePacketView<l2cap::InformationResponsePayload> payload_view(
275       &payload_buffer, sizeof(l2cap::ExtendedFeatures));
276   payload_view.mutable_header()->type =
277       static_cast<l2cap::InformationType>(pw::bytes::ConvertOrderTo(
278           cpp20::endian::little,
279           static_cast<uint16_t>(
280               l2cap::InformationType::kExtendedFeaturesSupported)));
281   payload_view.mutable_header()->result =
282       static_cast<l2cap::InformationResult>(pw::bytes::ConvertOrderTo(
283           cpp20::endian::little,
284           static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
285   payload_view.mutable_payload_data().WriteObj(
286       pw::bytes::ConvertOrderTo(cpp20::endian::little, extended_features));
287   SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
288 }
289 
SendInformationResponseFixedChannels(hci_spec::ConnectionHandle conn,l2cap::CommandId id)290 void FakeSignalingServer::SendInformationResponseFixedChannels(
291     hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
292   l2cap::FixedChannelsSupported fixed_channels =
293       l2cap::kFixedChannelsSupportedBitSignaling;
294   constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
295                                   sizeof(l2cap::FixedChannelsSupported);
296   DynamicByteBuffer payload_buffer(kPayloadSize);
297   MutablePacketView<l2cap::InformationResponsePayload> payload_view(
298       &payload_buffer, sizeof(l2cap::FixedChannelsSupported));
299   payload_view.mutable_header()->type =
300       static_cast<l2cap::InformationType>(pw::bytes::ConvertOrderTo(
301           cpp20::endian::little,
302           static_cast<uint16_t>(
303               l2cap::InformationType::kFixedChannelsSupported)));
304   payload_view.mutable_header()->result =
305       static_cast<l2cap::InformationResult>(pw::bytes::ConvertOrderTo(
306           cpp20::endian::little,
307           static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
308   payload_view.mutable_payload_data().WriteObj(
309       pw::bytes::ConvertOrderTo(cpp20::endian::little, fixed_channels));
310   SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
311 }
312 
SendConnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid,l2cap::ConnectionResult result,l2cap::ConnectionStatus status)313 void FakeSignalingServer::SendConnectionResponse(
314     hci_spec::ConnectionHandle conn,
315     l2cap::CommandId id,
316     l2cap::ChannelId local_cid,
317     l2cap::ChannelId remote_cid,
318     l2cap::ConnectionResult result,
319     l2cap::ConnectionStatus status) {
320   DynamicByteBuffer payload_buffer(sizeof(l2cap::ConnectionResponsePayload));
321   MutablePacketView<l2cap::ConnectionResponsePayload> payload_view(
322       &payload_buffer);
323   // Destination cid is the endpoint on the device sending the response packet.
324   payload_view.mutable_header()->dst_cid = local_cid;
325   // Source cid is the endpoint on the device receiving the response packet.
326   payload_view.mutable_header()->src_cid = remote_cid;
327   payload_view.mutable_header()->result = result;
328   payload_view.mutable_header()->status = status;
329   SendCFrame(conn, l2cap::kConnectionResponse, id, payload_buffer);
330 }
331 
SendConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId remote_cid)332 void FakeSignalingServer::SendConfigurationRequest(
333     hci_spec::ConnectionHandle conn,
334     l2cap::CommandId id,
335     l2cap::ChannelId remote_cid) {
336   DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationRequestPayload));
337   MutablePacketView<l2cap::ConfigurationRequestPayload> payload_view(
338       &payload_buffer);
339   // Config request dest_cid contains channel endpoint on the device receiving
340   // the request.
341   payload_view.mutable_header()->dst_cid = remote_cid;
342   // No continuation flag or additional data associated with this request.
343   payload_view.mutable_header()->flags = 0x0000;
344   SendCFrame(conn, l2cap::kConfigurationRequest, id, payload_buffer);
345 }
346 
SendConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ConfigurationResult result)347 void FakeSignalingServer::SendConfigurationResponse(
348     hci_spec::ConnectionHandle conn,
349     l2cap::CommandId id,
350     l2cap::ChannelId local_cid,
351     l2cap::ConfigurationResult result) {
352   DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationResponsePayload));
353   MutablePacketView<l2cap::ConfigurationResponsePayload> payload_view(
354       &payload_buffer);
355   // Config response src_cid contains the channel endpoint on the device sending
356   // the request.
357   payload_view.mutable_header()->src_cid = local_cid;
358   payload_view.mutable_header()->result = result;
359   // No continuation flag or additional data associated with this request.
360   payload_view.mutable_header()->flags = 0x0000;
361   SendCFrame(conn, l2cap::kConfigurationResponse, id, payload_buffer);
362 }
363 
SendDisconnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid)364 void FakeSignalingServer::SendDisconnectionResponse(
365     hci_spec::ConnectionHandle conn,
366     l2cap::CommandId id,
367     l2cap::ChannelId local_cid,
368     l2cap::ChannelId remote_cid) {
369   DynamicByteBuffer payload_buffer(sizeof(l2cap::DisconnectionResponsePayload));
370   MutablePacketView<l2cap::DisconnectionResponsePayload> payload_view(
371       &payload_buffer);
372   // Endpoint on the device receiving the response.
373   payload_view.mutable_header()->src_cid = remote_cid;
374   // Endpoint on device sending the response.
375   payload_view.mutable_header()->dst_cid = local_cid;
376   SendCFrame(conn, l2cap::kDisconnectionResponse, id, payload_buffer);
377 }
378 
379 }  // namespace bt::testing
380