xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/testing/fake_peer.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_peer.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
21 
22 namespace bt::testing {
23 using pw::bluetooth::emboss::LEExtendedAdvertisingReportDataWriter;
24 
FakePeer(const DeviceAddress & address,pw::async::Dispatcher & pw_dispatcher,bool connectable,bool scannable,bool send_advertising_report)25 FakePeer::FakePeer(const DeviceAddress& address,
26                    pw::async::Dispatcher& pw_dispatcher,
27                    bool connectable,
28                    bool scannable,
29                    bool send_advertising_report)
30     : controller_(nullptr),
31       address_(address),
32       name_("FakePeer"),
33       connected_(false),
34       connectable_(connectable),
35       scannable_(scannable),
36       advertising_enabled_(true),
37       directed_(false),
38       address_resolved_(false),
39       send_advertising_report_(send_advertising_report),
40       connect_status_(pw::bluetooth::emboss::StatusCode::SUCCESS),
41       connect_response_(pw::bluetooth::emboss::StatusCode::SUCCESS),
42       force_pending_connect_(false),
43       supports_ll_conn_update_procedure_(true),
44       le_features_(hci_spec::LESupportedFeatures{0}),
45       l2cap_(fit::bind_member<&FakePeer::SendPacket>(this)),
46       gatt_server_(this),
47       sdp_server_(pw_dispatcher) {
48   signaling_server_.RegisterWithL2cap(&l2cap_);
49   gatt_server_.RegisterWithL2cap(&l2cap_);
50   sdp_server_.RegisterWithL2cap(&l2cap_);
51 }
52 
set_scan_response(const ByteBuffer & data)53 void FakePeer::set_scan_response(const ByteBuffer& data) {
54   PW_DCHECK(scannable_);
55   scan_response_ = DynamicByteBuffer(data);
56 }
57 
CreateInquiryResponseEvent(pw::bluetooth::emboss::InquiryMode mode) const58 DynamicByteBuffer FakePeer::CreateInquiryResponseEvent(
59     pw::bluetooth::emboss::InquiryMode mode) const {
60   PW_DCHECK(address_.type() == DeviceAddress::Type::kBREDR);
61 
62   if (mode == pw::bluetooth::emboss::InquiryMode::STANDARD) {
63     size_t packet_size =
64         pw::bluetooth::emboss::InquiryResultEvent::MinSizeInBytes() +
65         pw::bluetooth::emboss::InquiryResult::IntrinsicSizeInBytes();
66     auto packet =
67         hci::EventPacket::New<pw::bluetooth::emboss::InquiryResultEventWriter>(
68             hci_spec::kInquiryResultEventCode, packet_size);
69     auto view = packet.view_t();
70     view.num_responses().Write(1);
71     view.responses()[0].bd_addr().CopyFrom(address_.value().view());
72     view.responses()[0].page_scan_repetition_mode().Write(
73         pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
74     view.responses()[0].class_of_device().BackingStorage().WriteUInt(
75         class_of_device_.to_int());
76     return DynamicByteBuffer{packet.data()};
77   }
78 
79   constexpr size_t packet_size =
80       pw::bluetooth::emboss::InquiryResultWithRssiEvent::MinSizeInBytes() +
81       pw::bluetooth::emboss::InquiryResultWithRssi::IntrinsicSizeInBytes();
82   auto packet = hci::EventPacket::New<
83       pw::bluetooth::emboss::InquiryResultWithRssiEventWriter>(
84       hci_spec::kInquiryResultEventCode, packet_size);
85   auto view = packet.view_t();
86 
87   // TODO(jamuraa): simulate clock offset and RSSI
88   view.num_responses().Write(1);
89   auto response = view.responses()[0];
90   response.bd_addr().CopyFrom(address_.value().view());
91   response.page_scan_repetition_mode().Write(
92       pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
93   response.class_of_device().BackingStorage().WriteUInt(
94       class_of_device_.to_int());
95   response.clock_offset().BackingStorage().WriteUInt(0);
96   response.rssi().Write(-30);
97   return DynamicByteBuffer{packet.data()};
98 }
99 
AddLink(hci_spec::ConnectionHandle handle)100 void FakePeer::AddLink(hci_spec::ConnectionHandle handle) {
101   PW_DCHECK(!HasLink(handle));
102   logical_links_.insert(handle);
103 
104   if (logical_links_.size() == 1u) {
105     set_connected(true);
106   }
107 }
108 
RemoveLink(hci_spec::ConnectionHandle handle)109 void FakePeer::RemoveLink(hci_spec::ConnectionHandle handle) {
110   PW_DCHECK(HasLink(handle));
111   logical_links_.erase(handle);
112   if (logical_links_.empty())
113     set_connected(false);
114 }
115 
HasLink(hci_spec::ConnectionHandle handle) const116 bool FakePeer::HasLink(hci_spec::ConnectionHandle handle) const {
117   return logical_links_.count(handle) != 0u;
118 }
119 
Disconnect()120 FakePeer::HandleSet FakePeer::Disconnect() {
121   set_connected(false);
122   return std::move(logical_links_);
123 }
124 
OnRxL2CAP(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)125 void FakePeer::OnRxL2CAP(hci_spec::ConnectionHandle conn,
126                          const ByteBuffer& pdu) {
127   if (pdu.size() < sizeof(l2cap::BasicHeader)) {
128     bt_log(WARN, "fake-hci", "malformed L2CAP packet!");
129     return;
130   }
131   l2cap_.HandlePdu(conn, pdu);
132 }
133 
SendPacket(hci_spec::ConnectionHandle conn,l2cap::ChannelId cid,const ByteBuffer & packet) const134 void FakePeer::SendPacket(hci_spec::ConnectionHandle conn,
135                           l2cap::ChannelId cid,
136                           const ByteBuffer& packet) const {
137   controller()->SendL2CAPBFrame(conn, cid, packet);
138 }
139 
WriteScanResponseReport(pw::bluetooth::emboss::LEAdvertisingReportDataWriter report) const140 void FakePeer::WriteScanResponseReport(
141     pw::bluetooth::emboss::LEAdvertisingReportDataWriter report) const {
142   PW_DCHECK(scannable_);
143   PW_DCHECK(scan_response_.size() < 0xFF);
144   report.data_length().Write(static_cast<uint8_t>(scan_response_.size()));
145   report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
146       report.BackingStorage().data(),
147       report.SizeInBytes() + scan_response_.size());
148 
149   report.event_type().Write(
150       pw::bluetooth::emboss::LEAdvertisingEventType::SCAN_RESPONSE);
151 
152   report.address().CopyFrom(address_.value().view());
153   report.address_type().Write(
154       address_.type() == DeviceAddress::Type::kLERandom
155           ? pw::bluetooth::emboss::LEAddressType::RANDOM
156           : pw::bluetooth::emboss::LEAddressType::PUBLIC);
157 
158   std::memcpy(report.data().BackingStorage().data(),
159               scan_response_.data(),
160               scan_response_.size());
161 
162   report.rssi().Write(rssi());
163 }
164 
BuildLegacyAdvertisingReportEvent(bool include_scan_rsp) const165 DynamicByteBuffer FakePeer::BuildLegacyAdvertisingReportEvent(
166     bool include_scan_rsp) const {
167   PW_DCHECK(advertising_data_.size() <= hci_spec::kMaxLEAdvertisingDataLength);
168   std::vector<size_t> reports_sizes;
169   reports_sizes.push_back(
170       pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
171       advertising_data_.size());
172   size_t reports_size = reports_sizes[0];
173 
174   if (include_scan_rsp) {
175     PW_DCHECK(scannable_);
176     reports_sizes.push_back(
177         pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
178         scan_response_.size());
179     reports_size += reports_sizes[1];
180   }
181 
182   size_t packet_size =
183       reports_size +
184       pw::bluetooth::emboss::LEAdvertisingReportSubevent::MinSizeInBytes();
185   auto event = hci::EventPacket::New<
186       pw::bluetooth::emboss::LEAdvertisingReportSubeventWriter>(
187       hci_spec::kLEMetaEventCode, packet_size);
188 
189   auto view = event.view_t();
190   view.le_meta_event().subevent_code().Write(
191       hci_spec::kLEAdvertisingReportSubeventCode);
192 
193   PW_DCHECK(reports_sizes.size() < 0xFF);
194   view.num_reports().Write(static_cast<uint8_t>(reports_sizes.size()));
195 
196   // Initially construct an incomplete view to write the |data_length| field.
197   auto report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
198       view.reports().BackingStorage().data(), reports_sizes[0]);
199   report.data_length().Write(static_cast<int32_t>(advertising_data_.size()));
200   // Remake view with the proper size, taking into account |data_length|.
201   report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
202       view.reports().BackingStorage().data(), reports_sizes[0]);
203   if (directed_) {
204     report.event_type().Write(
205         pw::bluetooth::emboss::LEAdvertisingEventType::CONNECTABLE_DIRECTED);
206   } else if (connectable_) {
207     report.event_type().Write(pw::bluetooth::emboss::LEAdvertisingEventType::
208                                   CONNECTABLE_AND_SCANNABLE_UNDIRECTED);
209   } else if (scannable_) {
210     report.event_type().Write(
211         pw::bluetooth::emboss::LEAdvertisingEventType::SCANNABLE_UNDIRECTED);
212   } else {
213     report.event_type().Write(pw::bluetooth::emboss::LEAdvertisingEventType::
214                                   NON_CONNECTABLE_UNDIRECTED);
215   }
216   if (address_.type() == DeviceAddress::Type::kLERandom) {
217     report.address_type().Write(
218         address_resolved_
219             ? pw::bluetooth::emboss::LEAddressType::RANDOM_IDENTITY
220             : pw::bluetooth::emboss::LEAddressType::RANDOM);
221   } else {
222     report.address_type().Write(
223         address_resolved_
224             ? pw::bluetooth::emboss::LEAddressType::PUBLIC_IDENTITY
225             : pw::bluetooth::emboss::LEAddressType::PUBLIC);
226   }
227   report.address().CopyFrom(address_.value().view());
228   std::memcpy(report.data().BackingStorage().data(),
229               advertising_data_.data(),
230               advertising_data_.size());
231   report.rssi().Write(rssi());
232 
233   if (include_scan_rsp) {
234     WriteScanResponseReport(
235         pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
236             view.reports().BackingStorage().data() + reports_sizes[0],
237             reports_sizes[1]));
238   }
239 
240   return DynamicByteBuffer(event.data());
241 }
242 
BuildLegacyScanResponseReportEvent() const243 DynamicByteBuffer FakePeer::BuildLegacyScanResponseReportEvent() const {
244   PW_DCHECK(scannable_);
245   PW_DCHECK(scan_response_.size() <= hci_spec::kMaxLEAdvertisingDataLength);
246   size_t report_size =
247       pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
248       scan_response_.size();
249   size_t packet_size =
250       report_size +
251       pw::bluetooth::emboss::LEAdvertisingReportSubevent::MinSizeInBytes();
252   auto event = hci::EventPacket::New<
253       pw::bluetooth::emboss::LEAdvertisingReportSubeventWriter>(
254       hci_spec::kLEMetaEventCode, packet_size);
255   auto view = event.view_t();
256   view.le_meta_event().subevent_code().Write(
257       hci_spec::kLEAdvertisingReportSubeventCode);
258   view.num_reports().Write(1);
259 
260   WriteScanResponseReport(
261       pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
262           view.reports().BackingStorage().data(), report_size));
263 
264   return DynamicByteBuffer(event.data());
265 }
266 
FillExtendedAdvertisingReport(LEExtendedAdvertisingReportDataWriter report,const ByteBuffer & data,bool is_fragmented,bool is_scan_response) const267 void FakePeer::FillExtendedAdvertisingReport(
268     LEExtendedAdvertisingReportDataWriter report,
269     const ByteBuffer& data,
270     bool is_fragmented,
271     bool is_scan_response) const {
272   if (use_extended_advertising_pdus_) {
273     report.event_type().directed().Write(directed_);
274     report.event_type().connectable().Write(connectable_);
275     report.event_type().scannable().Write(scannable_);
276     report.event_type().scan_response().Write(is_scan_response);
277 
278     if (is_fragmented) {
279       report.event_type().data_status().Write(
280           pw::bluetooth::emboss::LEAdvertisingDataStatus::INCOMPLETE);
281     } else {
282       report.event_type().data_status().Write(
283           pw::bluetooth::emboss::LEAdvertisingDataStatus::COMPLETE);
284     }
285   } else {
286     report.event_type().legacy().Write(true);
287     if (is_scan_response) {
288       report.event_type().scan_response().Write(true);
289     }
290 
291     if (directed_) {  // ADV_DIRECT_IND
292       report.event_type().directed().Write(true);
293       report.event_type().connectable().Write(true);
294     } else if (connectable_) {  // ADV_IND
295       report.event_type().connectable().Write(true);
296       report.event_type().scannable().Write(true);
297     } else if (scannable_) {  // ADV_SCAN_IND
298       report.event_type().scannable().Write(true);
299     }
300     // else ADV_NONCONN_IND
301   }
302 
303   if (address_.type() == DeviceAddress::Type::kLERandom) {
304     if (address_resolved_) {
305       report.address_type().Write(
306           pw::bluetooth::emboss::LEExtendedAddressType::RANDOM_IDENTITY);
307     } else {
308       report.address_type().Write(
309           pw::bluetooth::emboss::LEExtendedAddressType::RANDOM);
310     }
311   } else {
312     if (address_resolved_) {
313       report.address_type().Write(
314           pw::bluetooth::emboss::LEExtendedAddressType::PUBLIC_IDENTITY);
315     } else {
316       report.address_type().Write(
317           pw::bluetooth::emboss::LEExtendedAddressType::PUBLIC);
318     }
319   }
320 
321   report.address().bd_addr().CopyFrom(address_.value().view().bd_addr());
322   report.primary_phy().Write(
323       pw::bluetooth::emboss::LEPrimaryAdvertisingPHY::LE_1M);
324   report.secondary_phy().Write(
325       pw::bluetooth::emboss::LESecondaryAdvertisingPHY::NONE);
326   report.advertising_sid().Write(0);
327   report.tx_power().Write(tx_power());
328   report.rssi().Write(rssi());
329   report.periodic_advertising_interval().Write(0);
330 
331   // skip direct_address_type and direct_address for now since we don't use it
332 
333   PW_DCHECK(data.size() < 0xFF);
334   report.data_length().Write(static_cast<uint8_t>(data.size()));
335   std::memcpy(report.data().BackingStorage().begin(), data.data(), data.size());
336 }
337 
BuildExtendedAdvertisingReports(const ByteBuffer & data,bool is_scan_response) const338 DynamicByteBuffer FakePeer::BuildExtendedAdvertisingReports(
339     const ByteBuffer& data, bool is_scan_response) const {
340   namespace LEExtendedAdvertisingReportData =
341       pw::bluetooth::emboss::LEExtendedAdvertisingReportData;
342   using pw::bluetooth::emboss::LEExtendedAdvertisingReportDataWriter;
343   using pw::bluetooth::emboss::LEExtendedAdvertisingReportSubeventWriter;
344 
345   size_t num_full_reports =
346       data.size() / LEExtendedAdvertisingReportData::data_length_max();
347   size_t full_report_size =
348       pw::bluetooth::emboss::LEExtendedAdvertisingReportData::MinSizeInBytes() +
349       LEExtendedAdvertisingReportData::data_length_max();
350   size_t last_report_size =
351       pw::bluetooth::emboss::LEExtendedAdvertisingReportData::MinSizeInBytes() +
352       (data.size() % LEExtendedAdvertisingReportData::data_length_max());
353 
354   size_t reports_size = num_full_reports * full_report_size + last_report_size;
355   size_t packet_size =
356       pw::bluetooth::emboss::LEExtendedAdvertisingReportSubevent::
357           MinSizeInBytes() +
358       reports_size;
359 
360   auto event = hci::EventPacket::New<LEExtendedAdvertisingReportSubeventWriter>(
361       hci_spec::kLEMetaEventCode, packet_size);
362   auto packet = event.view<LEExtendedAdvertisingReportSubeventWriter>(
363       static_cast<int32_t>(reports_size));
364   packet.le_meta_event().subevent_code().Write(
365       hci_spec::kLEExtendedAdvertisingReportSubeventCode);
366 
367   uint8_t num_reports = static_cast<uint8_t>(num_full_reports + 1);
368   packet.num_reports().Write(num_reports);
369 
370   for (size_t i = 0; i < num_full_reports; i++) {
371     bool is_fragmented = false;
372     if (num_reports > 1) {
373       is_fragmented = true;
374     }
375 
376     LEExtendedAdvertisingReportDataWriter report(
377         packet.reports().BackingStorage().begin() + full_report_size * i,
378         full_report_size);
379     FillExtendedAdvertisingReport(
380         report, data, is_fragmented, is_scan_response);
381   }
382 
383   LEExtendedAdvertisingReportDataWriter report(
384       packet.reports().BackingStorage().begin() +
385           full_report_size * num_full_reports,
386       last_report_size);
387   FillExtendedAdvertisingReport(
388       report, data, /*is_fragmented=*/false, is_scan_response);
389 
390   return event.release();
391 }
392 
BuildExtendedAdvertisingReportEvent() const393 DynamicByteBuffer FakePeer::BuildExtendedAdvertisingReportEvent() const {
394   PW_DCHECK(advertising_data_.size() <=
395             hci_spec::kMaxLEExtendedAdvertisingDataLength);
396   return BuildExtendedAdvertisingReports(advertising_data_,
397                                          /*is_scan_response=*/false);
398 }
399 
BuildExtendedScanResponseEvent() const400 DynamicByteBuffer FakePeer::BuildExtendedScanResponseEvent() const {
401   PW_DCHECK(scannable_);
402   PW_DCHECK(scan_response_.size() <=
403             hci_spec::kMaxLEExtendedAdvertisingDataLength);
404   return BuildExtendedAdvertisingReports(scan_response_,
405                                          /*is_scan_response=*/true);
406 }
407 
408 }  // namespace bt::testing
409