xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gap/fake_adapter.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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