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 <endian.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/packet_view.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
23 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
24
25 namespace bt::testing {
26 using pw::bluetooth::emboss::LEExtendedAdvertisingReportDataWriter;
27
FakePeer(const DeviceAddress & address,pw::async::Dispatcher & pw_dispatcher,bool connectable,bool scannable)28 FakePeer::FakePeer(const DeviceAddress& address,
29 pw::async::Dispatcher& pw_dispatcher,
30 bool connectable,
31 bool scannable)
32 : controller_(nullptr),
33 address_(address),
34 name_("FakePeer"),
35 connected_(false),
36 connectable_(connectable),
37 scannable_(scannable),
38 advertising_enabled_(true),
39 directed_(false),
40 address_resolved_(false),
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 should_batch_reports_(false),
47 l2cap_(fit::bind_member<&FakePeer::SendPacket>(this)),
48 gatt_server_(this),
49 sdp_server_(pw_dispatcher) {
50 signaling_server_.RegisterWithL2cap(&l2cap_);
51 gatt_server_.RegisterWithL2cap(&l2cap_);
52 sdp_server_.RegisterWithL2cap(&l2cap_);
53 }
54
set_scan_response(bool should_batch_reports,const ByteBuffer & data)55 void FakePeer::set_scan_response(bool should_batch_reports,
56 const ByteBuffer& data) {
57 BT_DEBUG_ASSERT(scannable_);
58 scan_rsp_ = DynamicByteBuffer(data);
59 should_batch_reports_ = should_batch_reports;
60 }
61
CreateInquiryResponseEvent(pw::bluetooth::emboss::InquiryMode mode) const62 DynamicByteBuffer FakePeer::CreateInquiryResponseEvent(
63 pw::bluetooth::emboss::InquiryMode mode) const {
64 BT_DEBUG_ASSERT(address_.type() == DeviceAddress::Type::kBREDR);
65
66 if (mode == pw::bluetooth::emboss::InquiryMode::STANDARD) {
67 size_t packet_size =
68 pw::bluetooth::emboss::InquiryResultEvent::MinSizeInBytes() +
69 pw::bluetooth::emboss::InquiryResult::IntrinsicSizeInBytes();
70 auto packet = hci::EmbossEventPacket::New<
71 pw::bluetooth::emboss::InquiryResultEventWriter>(
72 hci_spec::kInquiryResultEventCode, packet_size);
73 auto view = packet.view_t();
74 view.num_responses().Write(1);
75 view.responses()[0].bd_addr().CopyFrom(address_.value().view());
76 view.responses()[0].page_scan_repetition_mode().Write(
77 pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
78 view.responses()[0].class_of_device().BackingStorage().WriteUInt(
79 class_of_device_.to_int());
80 return DynamicByteBuffer{packet.data()};
81 }
82
83 constexpr size_t packet_size =
84 pw::bluetooth::emboss::InquiryResultWithRssiEvent::MinSizeInBytes() +
85 pw::bluetooth::emboss::InquiryResultWithRssi::IntrinsicSizeInBytes();
86 auto packet = hci::EmbossEventPacket::New<
87 pw::bluetooth::emboss::InquiryResultWithRssiEventWriter>(
88 hci_spec::kInquiryResultEventCode, packet_size);
89 auto view = packet.view_t();
90
91 // TODO(jamuraa): simulate clock offset and RSSI
92 view.num_responses().Write(1);
93 auto response = view.responses()[0];
94 response.bd_addr().CopyFrom(address_.value().view());
95 response.page_scan_repetition_mode().Write(
96 pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
97 response.class_of_device().BackingStorage().WriteUInt(
98 class_of_device_.to_int());
99 response.clock_offset().BackingStorage().WriteUInt(0);
100 response.rssi().Write(-30);
101 return DynamicByteBuffer{packet.data()};
102 }
103
AddLink(hci_spec::ConnectionHandle handle)104 void FakePeer::AddLink(hci_spec::ConnectionHandle handle) {
105 BT_DEBUG_ASSERT(!HasLink(handle));
106 logical_links_.insert(handle);
107
108 if (logical_links_.size() == 1u) {
109 set_connected(true);
110 }
111 }
112
RemoveLink(hci_spec::ConnectionHandle handle)113 void FakePeer::RemoveLink(hci_spec::ConnectionHandle handle) {
114 BT_DEBUG_ASSERT(HasLink(handle));
115 logical_links_.erase(handle);
116 if (logical_links_.empty())
117 set_connected(false);
118 }
119
HasLink(hci_spec::ConnectionHandle handle) const120 bool FakePeer::HasLink(hci_spec::ConnectionHandle handle) const {
121 return logical_links_.count(handle) != 0u;
122 }
123
Disconnect()124 FakePeer::HandleSet FakePeer::Disconnect() {
125 set_connected(false);
126 return std::move(logical_links_);
127 }
128
OnRxL2CAP(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)129 void FakePeer::OnRxL2CAP(hci_spec::ConnectionHandle conn,
130 const ByteBuffer& pdu) {
131 if (pdu.size() < sizeof(l2cap::BasicHeader)) {
132 bt_log(WARN, "fake-hci", "malformed L2CAP packet!");
133 return;
134 }
135 l2cap_.HandlePdu(conn, pdu);
136 }
137
SendPacket(hci_spec::ConnectionHandle conn,l2cap::ChannelId cid,const ByteBuffer & packet) const138 void FakePeer::SendPacket(hci_spec::ConnectionHandle conn,
139 l2cap::ChannelId cid,
140 const ByteBuffer& packet) const {
141 controller()->SendL2CAPBFrame(conn, cid, packet);
142 }
143
144 } // namespace bt::testing
145