1 // Copyright 2024 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 "emulated_peer.h"
16
17 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
18
19 namespace fbt = fuchsia_bluetooth;
20
21 namespace bt_hci_virtual {
22 namespace {
23
LeAddressTypeFromFidl(fbt::AddressType type)24 bt::DeviceAddress::Type LeAddressTypeFromFidl(fbt::AddressType type) {
25 return (type == fbt::AddressType::kRandom)
26 ? bt::DeviceAddress::Type::kLERandom
27 : bt::DeviceAddress::Type::kLEPublic;
28 }
29
LeAddressFromFidl(const fbt::Address & address)30 bt::DeviceAddress LeAddressFromFidl(const fbt::Address& address) {
31 return bt::DeviceAddress(LeAddressTypeFromFidl(address.type()),
32 address.bytes());
33 }
34
ConnectionRoleFromFidl(fbt::ConnectionRole role)35 pw::bluetooth::emboss::ConnectionRole ConnectionRoleFromFidl(
36 fbt::ConnectionRole role) {
37 switch (role) {
38 case fbt::ConnectionRole::kLeader:
39 return pw::bluetooth::emboss::ConnectionRole::CENTRAL;
40 case fbt::ConnectionRole::kFollower:
41 [[fallthrough]];
42 default:
43 break;
44 }
45 return pw::bluetooth::emboss::ConnectionRole::PERIPHERAL;
46 }
47
48 } // namespace
49
50 // static
NewLowEnergy(fuchsia_hardware_bluetooth::PeerParameters parameters,bt::testing::FakeController * fake_controller,async_dispatcher_t * dispatcher)51 EmulatedPeer::Result EmulatedPeer::NewLowEnergy(
52 fuchsia_hardware_bluetooth::PeerParameters parameters,
53 bt::testing::FakeController* fake_controller,
54 async_dispatcher_t* dispatcher) {
55 ZX_DEBUG_ASSERT(parameters.channel().has_value());
56 ZX_DEBUG_ASSERT(fake_controller);
57
58 if (!parameters.address().has_value()) {
59 bt_log(ERROR, "virtual", "A fake peer address is mandatory!\n");
60 return fpromise::error(
61 fuchsia_hardware_bluetooth::EmulatorPeerError::kParametersInvalid);
62 }
63
64 auto address = LeAddressFromFidl(parameters.address().value());
65 bool connectable =
66 parameters.connectable().has_value() && parameters.connectable().value();
67
68 // TODO(armansito): We should consider splitting bt::testing::FakePeer into
69 // separate types for BR/EDR and LE transport emulation logic.
70 auto peer = std::make_unique<bt::testing::FakePeer>(
71 address, fake_controller->pw_dispatcher(), connectable);
72
73 if (!fake_controller->AddPeer(std::move(peer))) {
74 bt_log(ERROR,
75 "virtual",
76 "A fake LE peer with given address already exists: %s\n",
77 address.ToString().c_str());
78 return fpromise::error(
79 fuchsia_hardware_bluetooth::EmulatorPeerError::kAddressRepeated);
80 }
81
82 return fpromise::ok(std::unique_ptr<EmulatedPeer>(
83 new EmulatedPeer(address,
84 std::move(parameters.channel().value()),
85 fake_controller,
86 dispatcher)));
87 }
88
89 // static
NewBredr(fuchsia_hardware_bluetooth::PeerParameters parameters,bt::testing::FakeController * fake_controller,async_dispatcher_t * dispatcher)90 EmulatedPeer::Result EmulatedPeer::NewBredr(
91 fuchsia_hardware_bluetooth::PeerParameters parameters,
92 bt::testing::FakeController* fake_controller,
93 async_dispatcher_t* dispatcher) {
94 ZX_DEBUG_ASSERT(parameters.channel().has_value());
95 ZX_DEBUG_ASSERT(fake_controller);
96
97 if (!parameters.address().has_value()) {
98 bt_log(ERROR, "virtual", "A fake peer address is mandatory!\n");
99 return fpromise::error(
100 fuchsia_hardware_bluetooth::EmulatorPeerError::kParametersInvalid);
101 }
102
103 auto address = bt::DeviceAddress(bt::DeviceAddress::Type::kBREDR,
104 parameters.address()->bytes());
105 bool connectable =
106 parameters.connectable().has_value() && parameters.connectable().value();
107
108 // TODO(armansito): We should consider splitting bt::testing::FakePeer into
109 // separate types for BR/EDR and LE transport emulation logic.
110 auto peer = std::make_unique<bt::testing::FakePeer>(
111 address, fake_controller->pw_dispatcher(), connectable, false);
112
113 if (!fake_controller->AddPeer(std::move(peer))) {
114 bt_log(ERROR,
115 "virtual",
116 "A fake BR/EDR peer with given address already exists: %s\n",
117 address.ToString().c_str());
118 return fpromise::error(
119 fuchsia_hardware_bluetooth::EmulatorPeerError::kAddressRepeated);
120 }
121
122 return fpromise::ok(std::unique_ptr<EmulatedPeer>(
123 new EmulatedPeer(address,
124 std::move(parameters.channel().value()),
125 fake_controller,
126 dispatcher)));
127 }
128
EmulatedPeer(bt::DeviceAddress address,fidl::ServerEnd<fuchsia_hardware_bluetooth::Peer> request,bt::testing::FakeController * fake_controller,async_dispatcher_t * dispatcher)129 EmulatedPeer::EmulatedPeer(
130 bt::DeviceAddress address,
131 fidl::ServerEnd<fuchsia_hardware_bluetooth::Peer> request,
132 bt::testing::FakeController* fake_controller,
133 async_dispatcher_t* dispatcher)
134 : address_(address),
135 fake_controller_(fake_controller),
136 binding_(dispatcher,
137 std::move(request),
138 this,
139 std::mem_fn(&EmulatedPeer::OnChannelClosed)) {
140 ZX_DEBUG_ASSERT(fake_controller_);
141 }
142
~EmulatedPeer()143 EmulatedPeer::~EmulatedPeer() { CleanUp(); }
144
AssignConnectionStatus(AssignConnectionStatusRequest & request,AssignConnectionStatusCompleter::Sync & completer)145 void EmulatedPeer::AssignConnectionStatus(
146 AssignConnectionStatusRequest& request,
147 AssignConnectionStatusCompleter::Sync& completer) {
148 bt_log(TRACE, "virtual", "EmulatedPeer.AssignConnectionStatus\n");
149
150 auto peer = fake_controller_->FindPeer(address_);
151 if (peer) {
152 peer->set_connect_response(
153 bthost::fidl_helpers::FidlHciErrorToStatusCode(request.status()));
154 }
155
156 completer.Reply();
157 }
158
EmulateLeConnectionComplete(EmulateLeConnectionCompleteRequest & request,EmulateLeConnectionCompleteCompleter::Sync & completer)159 void EmulatedPeer::EmulateLeConnectionComplete(
160 EmulateLeConnectionCompleteRequest& request,
161 EmulateLeConnectionCompleteCompleter::Sync& completer) {
162 bt_log(TRACE, "virtual", "EmulatedPeer.EmulateLeConnectionComplete\n");
163 fake_controller_->ConnectLowEnergy(address_,
164 ConnectionRoleFromFidl(request.role()));
165 }
166
EmulateDisconnectionComplete(EmulateDisconnectionCompleteCompleter::Sync & completer)167 void EmulatedPeer::EmulateDisconnectionComplete(
168 EmulateDisconnectionCompleteCompleter::Sync& completer) {
169 bt_log(TRACE, "virtual", "EmulatedPeer.EmulateDisconnectionComplete\n");
170 fake_controller_->Disconnect(address_);
171 }
172
WatchConnectionStates(WatchConnectionStatesCompleter::Sync & completer)173 void EmulatedPeer::WatchConnectionStates(
174 WatchConnectionStatesCompleter::Sync& completer) {
175 bt_log(TRACE, "virtual", "EmulatedPeer.WatchConnectionState\n");
176
177 {
178 std::lock_guard<std::mutex> lock(connection_states_lock_);
179 connection_states_completers_.emplace(completer.ToAsync());
180 }
181 MaybeUpdateConnectionStates();
182 }
183
SetDeviceClass(SetDeviceClassRequest & request,SetDeviceClassCompleter::Sync & completer)184 void EmulatedPeer::SetDeviceClass(SetDeviceClassRequest& request,
185 SetDeviceClassCompleter::Sync& completer) {
186 auto peer = fake_controller_->FindPeer(address_);
187 if (!peer) {
188 bt_log(WARN,
189 "virtual",
190 "Peer with address %s not found",
191 address_.ToString().c_str());
192 binding_.Close(ZX_ERR_NOT_SUPPORTED);
193 return;
194 }
195 if (!peer->supports_bredr()) {
196 bt_log(WARN, "virtual", "Expected fake BR/EDR peer");
197 binding_.Close(ZX_ERR_NOT_SUPPORTED);
198 return;
199 }
200
201 peer->set_class_of_device(bt::DeviceClass(request.value()));
202
203 completer.Reply();
204 }
205
SetServiceDefinitions(SetServiceDefinitionsRequest & request,SetServiceDefinitionsCompleter::Sync & completer)206 void EmulatedPeer::SetServiceDefinitions(
207 SetServiceDefinitionsRequest& request,
208 SetServiceDefinitionsCompleter::Sync& completer) {
209 auto peer = fake_controller_->FindPeer(address_);
210 if (!peer) {
211 bt_log(WARN,
212 "virtual",
213 "Peer with address %s not found",
214 address_.ToString().c_str());
215 binding_.Close(ZX_ERR_NOT_SUPPORTED);
216 return;
217 }
218 if (!peer->supports_bredr()) {
219 bt_log(WARN, "virtual", "Expected fake BR/EDR peer");
220 binding_.Close(ZX_ERR_NOT_SUPPORTED);
221 return;
222 }
223
224 std::vector<bt::sdp::ServiceRecord> recs;
225 for (const auto& defn : request.service_definitions()) {
226 auto rec = bthost::fidl_helpers::ServiceDefinitionToServiceRecord(defn);
227 if (rec.is_ok()) {
228 recs.emplace_back(std::move(rec.value()));
229 }
230 }
231 bt::l2cap::ChannelParameters params;
232 auto NopConnectCallback = [](auto /*channel*/, const bt::sdp::DataElement&) {
233 };
234 peer->sdp_server()->server()->RegisterService(
235 std::move(recs), params, NopConnectCallback);
236
237 completer.Reply();
238 }
239
SetLeAdvertisement(SetLeAdvertisementRequest & request,SetLeAdvertisementCompleter::Sync & completer)240 void EmulatedPeer::SetLeAdvertisement(
241 SetLeAdvertisementRequest& request,
242 SetLeAdvertisementCompleter::Sync& completer) {
243 auto peer = fake_controller_->FindPeer(address_);
244 if (!peer) {
245 bt_log(WARN,
246 "virtual",
247 "Peer with address %s not found",
248 address_.ToString().c_str());
249 completer.Reply(fit::error(
250 fuchsia_hardware_bluetooth::EmulatorPeerError::kParametersInvalid));
251 return;
252 }
253 if (!peer->supports_le()) {
254 bt_log(WARN, "virtual", "Expected fake LE peer");
255 completer.Reply(fit::error(
256 fuchsia_hardware_bluetooth::EmulatorPeerError::kParametersInvalid));
257 return;
258 }
259
260 if (request.le_address().has_value()) {
261 bt::DeviceAddress le_address =
262 LeAddressFromFidl(request.le_address().value());
263 auto le_peer = fake_controller_->FindPeer(le_address);
264 if (le_peer != peer) {
265 bt_log(ERROR,
266 "virtual",
267 "A fake LE peer with given address already exists: %s\n",
268 le_address.ToString().c_str());
269 completer.Reply(fit::error(
270 fuchsia_hardware_bluetooth::EmulatorPeerError::kAddressRepeated));
271 return;
272 }
273 peer->set_le_advertising_address(le_address);
274 }
275
276 if (request.advertisement().has_value() &&
277 request.advertisement().value().data().has_value()) {
278 peer->set_advertising_data(
279 bt::BufferView(request.advertisement().value().data().value()));
280 }
281
282 if (request.scan_response().has_value() &&
283 request.scan_response().value().data().has_value()) {
284 bt::BufferView scan_rsp =
285 bt::BufferView(request.scan_response().value().data().value());
286 peer->set_scannable(true);
287 peer->set_scan_response(scan_rsp);
288 }
289 completer.Reply(fit::success());
290 }
291
handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Peer> metadata,fidl::UnknownMethodCompleter::Sync & completer)292 void EmulatedPeer::handle_unknown_method(
293 fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Peer> metadata,
294 fidl::UnknownMethodCompleter::Sync& completer) {
295 bt_log(WARN,
296 "virtual",
297 "Unknown method in Peer request, closing with ZX_ERR_NOT_SUPPORTED");
298 completer.Close(ZX_ERR_NOT_SUPPORTED);
299 }
300
UpdateConnectionState(bool connected)301 void EmulatedPeer::UpdateConnectionState(bool connected) {
302 fuchsia_hardware_bluetooth::ConnectionState state =
303 connected ? fuchsia_hardware_bluetooth::ConnectionState::kConnected
304 : fuchsia_hardware_bluetooth::ConnectionState::kDisconnected;
305
306 connection_states_.emplace_back(state);
307 MaybeUpdateConnectionStates();
308 }
309
MaybeUpdateConnectionStates()310 void EmulatedPeer::MaybeUpdateConnectionStates() {
311 std::lock_guard<std::mutex> lock(connection_states_lock_);
312 if (connection_states_.empty() || connection_states_completers_.empty()) {
313 return;
314 }
315 while (!connection_states_completers_.empty()) {
316 connection_states_completers_.front().Reply(connection_states_);
317 connection_states_completers_.pop();
318 }
319 connection_states_.clear();
320 }
321
OnChannelClosed(fidl::UnbindInfo info)322 void EmulatedPeer::OnChannelClosed(fidl::UnbindInfo info) {
323 bt_log(TRACE, "virtual", "EmulatedPeer channel closed\n");
324 NotifyChannelClosed();
325 }
326
CleanUp()327 void EmulatedPeer::CleanUp() { fake_controller_->RemovePeer(address_); }
328
NotifyChannelClosed()329 void EmulatedPeer::NotifyChannelClosed() {
330 if (closed_callback_) {
331 closed_callback_();
332 }
333 }
334
335 } // namespace bt_hci_virtual
336