• 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/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