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/gap/fake_adapter.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/types.h"
19 #include "pw_bluetooth_sapphire/internal/host/transport/link_type.h"
20
21 namespace bt::gap::testing {
22
FakeAdapter(pw::async::Dispatcher & pw_dispatcher)23 FakeAdapter::FakeAdapter(pw::async::Dispatcher& pw_dispatcher)
24 : init_state_(InitState::kNotInitialized),
25 fake_le_(std::make_unique<FakeLowEnergy>(this)),
26 fake_bredr_(std::make_unique<FakeBrEdr>()),
27 pw_dispatcher_(pw_dispatcher),
28 heap_dispatcher_(pw_dispatcher),
29 peer_cache_(pw_dispatcher),
30 weak_self_(this) {}
31
Initialize(InitializeCallback callback,fit::closure)32 bool FakeAdapter::Initialize(InitializeCallback callback, fit::closure) {
33 init_state_ = InitState::kInitializing;
34 (void)heap_dispatcher_.Post(
35 [this, cb = std::move(callback)](pw::async::Context /*ctx*/,
36 pw::Status status) mutable {
37 if (status.ok()) {
38 init_state_ = InitState::kInitialized;
39 cb(/*success=*/true);
40 }
41 });
42 return true;
43 }
44
ShutDown()45 void FakeAdapter::ShutDown() { init_state_ = InitState::kNotInitialized; }
46
~FakeBrEdr()47 FakeAdapter::FakeBrEdr::~FakeBrEdr() {
48 for (auto& chan : channels_) {
49 chan.second->Close();
50 }
51 }
52
OpenL2capChannel(PeerId,l2cap::Psm psm,BrEdrSecurityRequirements,l2cap::ChannelParameters params,l2cap::ChannelCallback cb)53 void FakeAdapter::FakeBrEdr::OpenL2capChannel(PeerId,
54 l2cap::Psm psm,
55 BrEdrSecurityRequirements,
56 l2cap::ChannelParameters params,
57 l2cap::ChannelCallback cb) {
58 l2cap::ChannelInfo info(
59 params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic),
60 params.max_rx_sdu_size.value_or(l2cap::kDefaultMTU),
61 /*max_tx_sdu_size=*/l2cap::kDefaultMTU,
62 /*n_frames_in_tx_window=*/0,
63 /*max_transmissions=*/0,
64 /*max_tx_pdu_payload_size=*/0,
65 psm,
66 params.flush_timeout);
67 l2cap::ChannelId local_id = next_channel_id_++;
68 auto channel = std::make_unique<l2cap::testing::FakeChannel>(
69 /*id=*/local_id,
70 /*remote_id=*/l2cap::kFirstDynamicChannelId,
71 /*handle=*/1,
72 bt::LinkType::kACL,
73 info);
74 l2cap::testing::FakeChannel::WeakPtr weak_fake_channel = channel->AsWeakPtr();
75 l2cap::Channel::WeakPtr weak_channel = channel->GetWeakPtr();
76 channels_.emplace(local_id, std::move(channel));
77 if (channel_cb_) {
78 channel_cb_(weak_fake_channel);
79 }
80 cb(weak_channel);
81 }
82
UpdateRandomAddress(DeviceAddress & address)83 void FakeAdapter::FakeLowEnergy::UpdateRandomAddress(DeviceAddress& address) {
84 fake_address_delegate_.UpdateRandomAddress(address);
85 }
86
set_advertising_result(hci::Result<> result)87 void FakeAdapter::FakeLowEnergy::set_advertising_result(hci::Result<> result) {
88 advertising_result_override_ = result;
89 }
90
Connect(PeerId peer_id,ConnectionResultCallback callback,LowEnergyConnectionOptions connection_options)91 void FakeAdapter::FakeLowEnergy::Connect(
92 PeerId peer_id,
93 ConnectionResultCallback callback,
94 LowEnergyConnectionOptions connection_options) {
95 connections_[peer_id] = Connection{peer_id, connection_options};
96
97 auto accept_cis_cb = [](iso::CigCisIdentifier, iso::CisEstablishedCallback) {
98 return iso::AcceptCisStatus::kSuccess;
99 };
100 auto bondable_cb = [connection_options]() {
101 return connection_options.bondable_mode;
102 };
103 auto security_cb = []() { return sm::SecurityProperties(); };
104 auto role_cb = []() {
105 return pw::bluetooth::emboss::ConnectionRole::CENTRAL;
106 };
107 auto handle = std::make_unique<LowEnergyConnectionHandle>(
108 peer_id,
109 /*handle=*/1,
110 /*release_cb=*/[](auto) {},
111 std::move(accept_cis_cb),
112 std::move(bondable_cb),
113 std::move(security_cb),
114 std::move(role_cb));
115 callback(fit::ok(std::move(handle)));
116 }
117
Disconnect(PeerId peer_id)118 bool FakeAdapter::FakeLowEnergy::Disconnect(PeerId peer_id) {
119 return connections_.erase(peer_id);
120 }
121
OpenL2capChannel(PeerId,l2cap::Psm psm,l2cap::ChannelParameters params,sm::SecurityLevel,l2cap::ChannelCallback cb)122 void FakeAdapter::FakeLowEnergy::OpenL2capChannel(
123 PeerId,
124 l2cap::Psm psm,
125 l2cap::ChannelParameters params,
126 sm::SecurityLevel,
127 l2cap::ChannelCallback cb) {
128 l2cap::ChannelInfo info(
129 params.mode.value_or(
130 l2cap::CreditBasedFlowControlMode::kLeCreditBasedFlowControl),
131 params.max_rx_sdu_size.value_or(l2cap::kDefaultMTU),
132 /*max_tx_sdu_size=*/l2cap::kDefaultMTU,
133 /*n_frames_in_tx_window=*/0,
134 /*max_transmissions=*/0,
135 /*max_tx_pdu_payload_size=*/0,
136 psm,
137 params.flush_timeout);
138
139 l2cap::ChannelId local_id = next_channel_id_++;
140 auto channel = std::make_unique<l2cap::testing::FakeChannel>(
141 /*id=*/local_id,
142 /*remote_id=*/l2cap::kFirstDynamicChannelId,
143 /*handle=*/1,
144 bt::LinkType::kLE,
145 info);
146
147 l2cap::Channel::WeakPtr weak_channel = channel->GetWeakPtr();
148 channels_.emplace(local_id, std::move(channel));
149 cb(weak_channel);
150 }
151
StartAdvertising(AdvertisingData data,AdvertisingData scan_rsp,AdvertisingInterval,bool extended_pdu,bool anonymous,bool include_tx_power_level,std::optional<ConnectableAdvertisingParameters> connectable,std::optional<DeviceAddress::Type> address_type,AdvertisingStatusCallback status_callback)152 void FakeAdapter::FakeLowEnergy::StartAdvertising(
153 AdvertisingData data,
154 AdvertisingData scan_rsp,
155 AdvertisingInterval /*interval*/,
156 bool extended_pdu,
157 bool anonymous,
158 bool include_tx_power_level,
159 std::optional<ConnectableAdvertisingParameters> connectable,
160 std::optional<DeviceAddress::Type> address_type,
161 AdvertisingStatusCallback status_callback) {
162 if (advertising_result_override_.has_value()) {
163 status_callback(AdvertisementInstance(),
164 advertising_result_override_.value());
165 return;
166 }
167
168 fake_address_delegate_.EnsureLocalAddress(
169 address_type,
170 [this,
171 data = std::move(data),
172 scan_rsp = std::move(scan_rsp),
173 include_tx_power_level,
174 extended_pdu,
175 connectable = std::move(connectable),
176 anonymous,
177 status_callback = std::move(status_callback)](
178 fit::result<HostError, DeviceAddress> result) mutable {
179 if (result.is_error()) {
180 status_callback(AdvertisementInstance(),
181 fit::error(result.error_value()));
182 return;
183 }
184
185 RegisteredAdvertisement adv{
186 .data = std::move(data),
187 .scan_response = std::move(scan_rsp),
188 .include_tx_power_level = include_tx_power_level,
189 .addr_type = result.value().type(),
190 .extended_pdu = extended_pdu,
191 .anonymous = anonymous,
192 .connectable = std::move(connectable),
193 };
194
195 AdvertisementId adv_id = next_advertisement_id_;
196 next_advertisement_id_ =
197 AdvertisementId(next_advertisement_id_.value() + 1);
198 advertisements_.emplace(adv_id, std::move(adv));
199
200 auto stop_advertising = [this](AdvertisementId id) {
201 advertisements_.erase(id);
202 };
203 status_callback(
204 AdvertisementInstance(adv_id, std::move(stop_advertising)),
205 fit::ok());
206 });
207 }
208
EnablePrivacy(bool enabled)209 void FakeAdapter::FakeLowEnergy::EnablePrivacy(bool enabled) {
210 fake_address_delegate_.EnablePrivacy(enabled);
211 }
212
213 FakeAdapter::FakeBrEdr::RegistrationHandle
RegisterService(std::vector<sdp::ServiceRecord> records,l2cap::ChannelParameters chan_params,ServiceConnectCallback conn_cb)214 FakeAdapter::FakeBrEdr::RegisterService(std::vector<sdp::ServiceRecord> records,
215 l2cap::ChannelParameters chan_params,
216 ServiceConnectCallback conn_cb) {
217 auto handle = next_service_handle_++;
218 registered_services_.emplace(
219 handle,
220 RegisteredService{std::move(records), chan_params, std::move(conn_cb)});
221 return handle;
222 }
223
UnregisterService(RegistrationHandle handle)224 bool FakeAdapter::FakeBrEdr::UnregisterService(RegistrationHandle handle) {
225 return registered_services_.erase(handle);
226 }
227
SetLocalName(std::string name,hci::ResultFunction<> callback)228 void FakeAdapter::SetLocalName(std::string name,
229 hci::ResultFunction<> callback) {
230 local_name_ = name;
231 callback(fit::ok());
232 }
233
SetDeviceClass(DeviceClass dev_class,hci::ResultFunction<> callback)234 void FakeAdapter::SetDeviceClass(DeviceClass dev_class,
235 hci::ResultFunction<> callback) {
236 device_class_ = dev_class;
237 callback(fit::ok());
238 }
239
GetSupportedDelayRange(const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> &,pw::bluetooth::emboss::LogicalTransportType,pw::bluetooth::emboss::DataPathDirection,const std::optional<std::vector<uint8_t>> &,GetSupportedDelayRangeCallback cb)240 void FakeAdapter::GetSupportedDelayRange(
241 const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>&,
242 pw::bluetooth::emboss::LogicalTransportType,
243 pw::bluetooth::emboss::DataPathDirection,
244 const std::optional<std::vector<uint8_t>>&,
245 GetSupportedDelayRangeCallback cb) {
246 cb(PW_STATUS_OK,
247 0,
248 pw::bluetooth::emboss::
249 ReadLocalSupportedControllerDelayCommandCompleteEvent::
250 max_delay_usecs());
251 }
252
AddServiceSearch(const UUID & uuid,std::unordered_set<sdp::AttributeId> attributes,SearchCallback callback)253 BrEdrConnectionManager::SearchId FakeAdapter::FakeBrEdr::AddServiceSearch(
254 const UUID& uuid,
255 std::unordered_set<sdp::AttributeId> attributes,
256 SearchCallback callback) {
257 auto handle = next_search_handle_++;
258 registered_searches_.emplace(
259 handle,
260 RegisteredSearch{uuid, std::move(attributes), std::move(callback)});
261 return SearchId(handle);
262 }
263
TriggerServiceFound(PeerId peer_id,UUID uuid,std::map<sdp::AttributeId,sdp::DataElement> attributes)264 void FakeAdapter::FakeBrEdr::TriggerServiceFound(
265 PeerId peer_id,
266 UUID uuid,
267 std::map<sdp::AttributeId, sdp::DataElement> attributes) {
268 for (auto it = registered_searches_.begin(); it != registered_searches_.end();
269 it++) {
270 if (it->second.uuid == uuid) {
271 it->second.callback(peer_id, attributes);
272 }
273 }
274 }
275
276 } // namespace bt::gap::testing
277