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