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