• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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