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
NotifyScanResult(const Peer & peer)91 void FakeAdapter::FakeLowEnergy::NotifyScanResult(const Peer& peer) {
92 for (auto iter = discovery_sessions_.begin();
93 iter != discovery_sessions_.end();) {
94 // Get next iterator before result handler possible invalidates this one by
95 // stopping session.
96 auto next = std::next(iter);
97 (*iter)->NotifyDiscoveryResult(peer);
98 iter = next;
99 }
100 }
101
Connect(PeerId peer_id,ConnectionResultCallback callback,LowEnergyConnectionOptions connection_options)102 void FakeAdapter::FakeLowEnergy::Connect(
103 PeerId peer_id,
104 ConnectionResultCallback callback,
105 LowEnergyConnectionOptions connection_options) {
106 auto accept_cis_cb = [](iso::CigCisIdentifier, iso::CisEstablishedCallback) {
107 return iso::AcceptCisStatus::kSuccess;
108 };
109 auto bondable_cb = [connection_options]() {
110 return connection_options.bondable_mode;
111 };
112 auto security_cb = []() { return sm::SecurityProperties(); };
113 auto role_cb = []() {
114 return pw::bluetooth::emboss::ConnectionRole::CENTRAL;
115 };
116 auto release_cb = [this](LowEnergyConnectionHandle* handle) {
117 // NOTE: This assumes there is only 1 connection handle in tests.
118 PW_CHECK(connections_.erase(handle->peer_identifier()));
119 };
120 auto handle =
121 std::make_unique<LowEnergyConnectionHandle>(peer_id,
122 /*handle=*/1,
123 std::move(release_cb),
124 std::move(accept_cis_cb),
125 std::move(bondable_cb),
126 std::move(security_cb),
127 std::move(role_cb));
128 connections_[peer_id] = Connection{peer_id, connection_options, handle.get()};
129 callback(fit::ok(std::move(handle)));
130 }
131
Disconnect(PeerId peer_id)132 bool FakeAdapter::FakeLowEnergy::Disconnect(PeerId peer_id) {
133 auto conn_iter = connections_.find(peer_id);
134 if (conn_iter == connections_.end()) {
135 return false;
136 }
137 conn_iter->second.handle->MarkClosed();
138 connections_.erase(conn_iter);
139 return true;
140 }
141
OpenL2capChannel(PeerId,l2cap::Psm psm,l2cap::ChannelParameters params,sm::SecurityLevel,l2cap::ChannelCallback cb)142 void FakeAdapter::FakeLowEnergy::OpenL2capChannel(
143 PeerId,
144 l2cap::Psm psm,
145 l2cap::ChannelParameters params,
146 sm::SecurityLevel,
147 l2cap::ChannelCallback cb) {
148 l2cap::ChannelInfo info(
149 params.mode.value_or(
150 l2cap::CreditBasedFlowControlMode::kLeCreditBasedFlowControl),
151 params.max_rx_sdu_size.value_or(l2cap::kDefaultMTU),
152 /*max_tx_sdu_size=*/l2cap::kDefaultMTU,
153 /*n_frames_in_tx_window=*/0,
154 /*max_transmissions=*/0,
155 /*max_tx_pdu_payload_size=*/0,
156 psm,
157 params.flush_timeout);
158
159 l2cap::ChannelId local_id = next_channel_id_++;
160 auto channel = std::make_unique<l2cap::testing::FakeChannel>(
161 /*id=*/local_id,
162 /*remote_id=*/l2cap::kFirstDynamicChannelId,
163 /*handle=*/1,
164 bt::LinkType::kLE,
165 info);
166
167 l2cap::Channel::WeakPtr weak_channel = channel->GetWeakPtr();
168 channels_.emplace(local_id, std::move(channel));
169 cb(weak_channel);
170 }
171
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)172 void FakeAdapter::FakeLowEnergy::StartAdvertising(
173 AdvertisingData data,
174 AdvertisingData scan_rsp,
175 AdvertisingInterval /*interval*/,
176 bool extended_pdu,
177 bool anonymous,
178 bool include_tx_power_level,
179 std::optional<ConnectableAdvertisingParameters> connectable,
180 std::optional<DeviceAddress::Type> address_type,
181 AdvertisingStatusCallback status_callback) {
182 if (advertising_result_override_.has_value()) {
183 status_callback(AdvertisementInstance(),
184 advertising_result_override_.value());
185 return;
186 }
187
188 fake_address_delegate_.EnsureLocalAddress(
189 address_type,
190 [this,
191 data = std::move(data),
192 scan_rsp = std::move(scan_rsp),
193 include_tx_power_level,
194 extended_pdu,
195 connectable = std::move(connectable),
196 anonymous,
197 status_callback = std::move(status_callback)](
198 fit::result<HostError, DeviceAddress> result) mutable {
199 if (result.is_error()) {
200 status_callback(AdvertisementInstance(),
201 fit::error(result.error_value()));
202 return;
203 }
204
205 RegisteredAdvertisement adv{
206 .data = std::move(data),
207 .scan_response = std::move(scan_rsp),
208 .include_tx_power_level = include_tx_power_level,
209 .addr_type = result.value().type(),
210 .extended_pdu = extended_pdu,
211 .anonymous = anonymous,
212 .connectable = std::move(connectable),
213 };
214
215 AdvertisementId adv_id = next_advertisement_id_;
216 next_advertisement_id_ =
217 AdvertisementId(next_advertisement_id_.value() + 1);
218 advertisements_.emplace(adv_id, std::move(adv));
219
220 auto stop_advertising = [this](AdvertisementId id) {
221 advertisements_.erase(id);
222 };
223 status_callback(
224 AdvertisementInstance(adv_id, std::move(stop_advertising)),
225 fit::ok());
226 });
227 }
228
StartDiscovery(bool active,std::vector<hci::DiscoveryFilter> discovery_filters,SessionCallback callback)229 void FakeAdapter::FakeLowEnergy::StartDiscovery(
230 bool active,
231 std::vector<hci::DiscoveryFilter> discovery_filters,
232 SessionCallback callback) {
233 auto session = std::make_unique<LowEnergyDiscoverySession>(
234 next_scan_id_++,
235 active,
236 std::move(discovery_filters),
237 *adapter_->peer_cache(),
238 adapter_->pw_dispatcher_,
239 /*on_stop_cb=*/
240 [this](LowEnergyDiscoverySession* s) { discovery_sessions_.erase(s); },
241 /*cached_scan_results_fn=*/
242 [this]() -> const std::unordered_set<PeerId>& {
243 return cached_scan_results_;
244 });
245 discovery_sessions_.insert(session.get());
246 callback(std::move(session));
247 }
248
EnablePrivacy(bool enabled)249 void FakeAdapter::FakeLowEnergy::EnablePrivacy(bool enabled) {
250 fake_address_delegate_.EnablePrivacy(enabled);
251 }
252
253 FakeAdapter::FakeBrEdr::RegistrationHandle
RegisterService(std::vector<sdp::ServiceRecord> records,l2cap::ChannelParameters chan_params,ServiceConnectCallback conn_cb)254 FakeAdapter::FakeBrEdr::RegisterService(std::vector<sdp::ServiceRecord> records,
255 l2cap::ChannelParameters chan_params,
256 ServiceConnectCallback conn_cb) {
257 auto handle = next_service_handle_++;
258 registered_services_.emplace(
259 handle,
260 RegisteredService{std::move(records), chan_params, std::move(conn_cb)});
261 return handle;
262 }
263
UnregisterService(RegistrationHandle handle)264 bool FakeAdapter::FakeBrEdr::UnregisterService(RegistrationHandle handle) {
265 return registered_services_.erase(handle);
266 }
267
SetLocalName(std::string name,hci::ResultFunction<> callback)268 void FakeAdapter::SetLocalName(std::string name,
269 hci::ResultFunction<> callback) {
270 local_name_ = name;
271 callback(fit::ok());
272 }
273
SetDeviceClass(DeviceClass dev_class,hci::ResultFunction<> callback)274 void FakeAdapter::SetDeviceClass(DeviceClass dev_class,
275 hci::ResultFunction<> callback) {
276 device_class_ = dev_class;
277 callback(fit::ok());
278 }
279
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)280 void FakeAdapter::GetSupportedDelayRange(
281 const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>&,
282 pw::bluetooth::emboss::LogicalTransportType,
283 pw::bluetooth::emboss::DataPathDirection,
284 const std::optional<std::vector<uint8_t>>&,
285 GetSupportedDelayRangeCallback cb) {
286 cb(PW_STATUS_OK,
287 0,
288 pw::bluetooth::emboss::
289 ReadLocalSupportedControllerDelayCommandCompleteEvent::
290 max_delay_usecs());
291 }
292
AddServiceSearch(const UUID & uuid,std::unordered_set<sdp::AttributeId> attributes,SearchCallback callback)293 BrEdrConnectionManager::SearchId FakeAdapter::FakeBrEdr::AddServiceSearch(
294 const UUID& uuid,
295 std::unordered_set<sdp::AttributeId> attributes,
296 SearchCallback callback) {
297 auto handle = next_search_handle_++;
298 registered_searches_.emplace(
299 handle,
300 RegisteredSearch{uuid, std::move(attributes), std::move(callback)});
301 return SearchId(handle);
302 }
303
TriggerServiceFound(PeerId peer_id,UUID uuid,std::map<sdp::AttributeId,sdp::DataElement> attributes)304 void FakeAdapter::FakeBrEdr::TriggerServiceFound(
305 PeerId peer_id,
306 UUID uuid,
307 std::map<sdp::AttributeId, sdp::DataElement> attributes) {
308 for (auto it = registered_searches_.begin(); it != registered_searches_.end();
309 it++) {
310 if (it->second.uuid == uuid) {
311 it->second.callback(peer_id, attributes);
312 }
313 }
314 }
315
316 } // namespace bt::gap::testing
317