• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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