• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "hci/bluetooth_facade.h"
16 
17 #include <sys/types.h>
18 
19 #include <array>
20 #include <cassert>
21 #include <chrono>
22 #include <cstdint>
23 #include <cstring>
24 #include <future>
25 #include <iostream>
26 #include <memory>
27 #include <unordered_map>
28 #include <utility>
29 
30 #include "hci/address.h"
31 #include "hci/hci_packet_transport.h"
32 #include "model/setup/async_manager.h"
33 #include "model/setup/test_command_handler.h"
34 #include "model/setup/test_model.h"
35 #include "netsim-daemon/src/ffi.rs.h"
36 #include "netsim/config.pb.h"
37 #include "rust/cxx.h"
38 #include "util/filesystem.h"
39 #include "util/log.h"
40 
41 #ifndef NETSIM_ANDROID_EMULATOR
42 #include "net/posix/posix_async_socket_server.h"
43 #endif
44 
45 namespace rootcanal::log {
46 void SetLogColorEnable(bool);
47 }
48 
49 namespace netsim::hci::facade {
50 
51 int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power);
52 void IncrTx(uint32_t send_id, rootcanal::Phy::Type phy_type);
53 void IncrRx(uint32_t receive_id, rootcanal::Phy::Type phy_type);
54 
55 using namespace std::literals;
56 using namespace rootcanal;
57 
58 using rootcanal::PhyDevice;
59 using rootcanal::PhyLayer;
60 
61 class SimPhyLayer : public PhyLayer {
62   // for constructor inheritance
63   using PhyLayer::PhyLayer;
64 
65   // Overrides ComputeRssi in PhyLayerFactory to provide
66   // simulated RSSI information using actual spatial
67   // device positions.
ComputeRssi(PhyDevice::Identifier sender_id,PhyDevice::Identifier receiver_id,int8_t tx_power)68   int8_t ComputeRssi(PhyDevice::Identifier sender_id,
69                      PhyDevice::Identifier receiver_id,
70                      int8_t tx_power) override {
71     return SimComputeRssi(sender_id, receiver_id, tx_power);
72   }
73 
74   // Check if the device is present in the phy_devices
Contains(PhyDevice::Identifier device_id,const std::list<std::shared_ptr<rootcanal::PhyDevice>> & phy_devices)75   static bool Contains(
76       PhyDevice::Identifier device_id,
77       const std::list<std::shared_ptr<rootcanal::PhyDevice>> &phy_devices) {
78     return std::any_of(
79         phy_devices.begin(), phy_devices.end(),
80         [device_id](const auto &device) { return device->id == device_id; });
81   }
82 
83   // Overrides Send in PhyLayerFactory to add Rx/Tx statistics.
Send(std::vector<uint8_t> const & packet,int8_t tx_power,PhyDevice::Identifier sender_id)84   void Send(std::vector<uint8_t> const &packet, int8_t tx_power,
85             PhyDevice::Identifier sender_id) override {
86     // Skip if the sender's phy is in the "down" state. Prevents all outgoing
87     // messages including advertisements occurring when the radio is down.
88     if (!Contains(sender_id, phy_devices_)) {
89       return;
90     }
91     IncrTx(sender_id, type);
92     for (const auto &device : phy_devices_) {
93       if (sender_id != device->id) {
94         IncrRx(device->id, type);
95         device->Receive(packet, type,
96                         ComputeRssi(sender_id, device->id, tx_power));
97       }
98     }
99   }
100 };
101 
102 class SimTestModel : public rootcanal::TestModel {
103   // for constructor inheritance
104   using rootcanal::TestModel::TestModel;
105 
CreatePhyLayer(PhyLayer::Identifier id,rootcanal::Phy::Type type)106   std::unique_ptr<rootcanal::PhyLayer> CreatePhyLayer(
107       PhyLayer::Identifier id, rootcanal::Phy::Type type) override {
108     return std::make_unique<SimPhyLayer>(id, type);
109   }
110 };
111 
112 size_t phy_low_energy_index_;
113 size_t phy_classic_index_;
114 
115 bool gStarted = false;
116 std::shared_ptr<rootcanal::AsyncManager> gAsyncManager;
117 rootcanal::AsyncUserId gSocketUserId{};
118 std::shared_ptr<SimTestModel> gTestModel;
119 std::shared_ptr<rootcanal::configuration::Controller> controller_proto_;
120 
121 #ifndef NETSIM_ANDROID_EMULATOR
122 // test port
123 std::unique_ptr<rootcanal::TestCommandHandler> gTestChannel;
124 std::unique_ptr<rootcanal::TestChannelTransport> gTestChannelTransport;
125 std::shared_ptr<AsyncDataChannelServer> gTestSocketServer;
126 bool gTestChannelOpen{false};
127 constexpr int kDefaultTestPort = 7500;
128 #endif
129 
130 namespace {
131 #ifndef NETSIM_ANDROID_EMULATOR
132 
133 using ::android::net::PosixAsyncSocketServer;
134 
SetUpTestChannel(uint16_t instance_num)135 void SetUpTestChannel(uint16_t instance_num) {
136   gTestSocketServer = std::make_shared<PosixAsyncSocketServer>(
137       kDefaultTestPort + instance_num - 1, gAsyncManager.get());
138 
139   gTestChannel = std::make_unique<rootcanal::TestCommandHandler>(*gTestModel);
140 
141   gTestChannelTransport = std::make_unique<rootcanal::TestChannelTransport>();
142   gTestChannelTransport->RegisterCommandHandler(
143       [](const std::string &name, const std::vector<std::string> &args) {
144         gAsyncManager->ExecAsync(gSocketUserId, std::chrono::milliseconds(0),
145                                  [name, args]() {
146                                    std::string args_str = "";
147                                    for (auto arg : args) args_str += " " + arg;
148                                    if (name == "END_SIMULATION") {
149                                    } else {
150                                      gTestChannel->HandleCommand(name, args);
151                                    }
152                                  });
153       });
154 
155   bool transport_configured = gTestChannelTransport->SetUp(
156       gTestSocketServer, [](std::shared_ptr<AsyncDataChannel> conn_fd,
157                             AsyncDataChannelServer *server) {
158         BtsLogInfo("Test channel connection accepted.");
159         server->StartListening();
160         if (gTestChannelOpen) {
161           BtsLogWarn("Only one connection at a time is supported");
162           rootcanal::TestChannelTransport::SendResponse(
163               conn_fd, "The connection is broken");
164           return false;
165         }
166         gTestChannelOpen = true;
167         gTestChannel->RegisterSendResponse(
168             [conn_fd](const std::string &response) {
169               rootcanal::TestChannelTransport::SendResponse(conn_fd, response);
170             });
171 
172         conn_fd->WatchForNonBlockingRead([](AsyncDataChannel *conn_fd) {
173           gTestChannelTransport->OnCommandReady(
174               conn_fd, []() { gTestChannelOpen = false; });
175         });
176         return false;
177       });
178 
179   gTestChannel->SetTimerPeriod({"5"});
180   gTestChannel->StartTimer({});
181 
182   if (!transport_configured) {
183     BtsLogError("Failed to set up test channel.");
184     return;
185   }
186 
187   BtsLogInfo("Set up test channel.");
188 }
189 #endif
190 
191 }  // namespace
192 
193 // Initialize the rootcanal library.
Start(const rust::Slice<::std::uint8_t const> proto_bytes,uint16_t instance_num)194 void Start(const rust::Slice<::std::uint8_t const> proto_bytes,
195            uint16_t instance_num) {
196   if (gStarted) return;
197 
198   // output is to a file, so no color wanted
199   rootcanal::log::SetLogColorEnable(false);
200 
201   config::Bluetooth config;
202   config.ParseFromArray(proto_bytes.data(), proto_bytes.size());
203   controller_proto_ = std::make_shared<rootcanal::configuration::Controller>(
204       config.properties());
205 
206   // When emulators restore from a snapshot the PacketStreamer connection to
207   // netsim is recreated with a new (uninitialized) Rootcanal device. However
208   // the Android Bluetooth Stack does not re-initialize the controller. Our
209   // solution is for Rootcanal to recognize that it is receiving HCI commands
210   // before a HCI Reset. The flag below causes a hardware error event that
211   // triggers the Reset from the Bluetooth Stack.
212 
213   controller_proto_->mutable_quirks()->set_hardware_error_before_reset(true);
214 
215   gAsyncManager = std::make_shared<rootcanal::AsyncManager>();
216   // Get a user ID for tasks scheduled within the test environment.
217   gSocketUserId = gAsyncManager->GetNextUserId();
218 
219   gTestModel = std::make_unique<SimTestModel>(
220       std::bind(&rootcanal::AsyncManager::GetNextUserId, gAsyncManager),
221       std::bind(&rootcanal::AsyncManager::ExecAsync, gAsyncManager,
222                 std::placeholders::_1, std::placeholders::_2,
223                 std::placeholders::_3),
224       std::bind(&rootcanal::AsyncManager::ExecAsyncPeriodically, gAsyncManager,
225                 std::placeholders::_1, std::placeholders::_2,
226                 std::placeholders::_3, std::placeholders::_4),
227       std::bind(&rootcanal::AsyncManager::CancelAsyncTasksFromUser,
228                 gAsyncManager, std::placeholders::_1),
229       std::bind(&rootcanal::AsyncManager::CancelAsyncTask, gAsyncManager,
230                 std::placeholders::_1),
231       [](const std::string & /* server */, int /* port */,
232          rootcanal::Phy::Type /* phy_type */) { return nullptr; });
233 
234   // Disable Address Reuse if '--disable_address_reuse' flag is true
235   // Enable Address Reuse if 'address_reuse' is true
236   if (config.has_disable_address_reuse()) {
237     gTestModel->SetReuseDeviceAddresses(!config.disable_address_reuse());
238   }
239 
240   // NOTE: 0:BR_EDR, 1:LOW_ENERGY. The order is used by bluetooth CTS.
241   phy_classic_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::BR_EDR);
242   phy_low_energy_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::LOW_ENERGY);
243 
244   // TODO: Remove test channel.
245 #ifdef NETSIM_ANDROID_EMULATOR
246   auto testCommands = rootcanal::TestCommandHandler(*gTestModel);
247   testCommands.RegisterSendResponse([](const std::string &) {});
248   testCommands.SetTimerPeriod({"5"});
249   testCommands.StartTimer({});
250 #else
251   SetUpTestChannel(instance_num);
252 #endif
253   gStarted = true;
254 };
255 
256 // Resets the root canal library.
Stop()257 void Stop() {
258   // TODO: Fix TestModel::Reset() in test_model.cc.
259   // gTestModel->Reset();
260   gStarted = false;
261 }
262 
AddDeviceToPhy(uint32_t rootcanal_id,bool isLowEnergy)263 void AddDeviceToPhy(uint32_t rootcanal_id, bool isLowEnergy) {
264   auto phy_index = (isLowEnergy) ? phy_low_energy_index_ : phy_classic_index_;
265   gTestModel->AddDeviceToPhy(rootcanal_id, phy_index);
266 }
267 
RemoveDeviceFromPhy(uint32_t rootcanal_id,bool isLowEnergy)268 void RemoveDeviceFromPhy(uint32_t rootcanal_id, bool isLowEnergy) {
269   auto phy_index = (isLowEnergy) ? phy_low_energy_index_ : phy_classic_index_;
270   gTestModel->RemoveDeviceFromPhy(rootcanal_id, phy_index);
271 }
272 
273 class ChipInfo {
274  public:
275   uint32_t chip_id;
276   std::shared_ptr<model::Chip::Bluetooth> model;
277   int le_tx_count = 0;
278   int classic_tx_count = 0;
279   int le_rx_count = 0;
280   int classic_rx_count = 0;
281   std::shared_ptr<rootcanal::configuration::Controller> controller_proto;
282   std::unique_ptr<rootcanal::ControllerProperties> controller_properties;
283 
ChipInfo(uint32_t chip_id,std::shared_ptr<model::Chip::Bluetooth> model)284   ChipInfo(uint32_t chip_id, std::shared_ptr<model::Chip::Bluetooth> model)
285       : chip_id(chip_id), model(model) {}
ChipInfo(uint32_t chip_id,std::shared_ptr<model::Chip::Bluetooth> model,std::shared_ptr<rootcanal::configuration::Controller> controller_proto,std::unique_ptr<rootcanal::ControllerProperties> controller_properties)286   ChipInfo(
287       uint32_t chip_id, std::shared_ptr<model::Chip::Bluetooth> model,
288       std::shared_ptr<rootcanal::configuration::Controller> controller_proto,
289       std::unique_ptr<rootcanal::ControllerProperties> controller_properties)
290       : chip_id(chip_id),
291         model(model),
292         controller_proto(std::move(controller_proto)),
293         controller_properties(std::move(controller_properties)) {}
294 };
295 
296 std::unordered_map<uint32_t, std::shared_ptr<ChipInfo>> id_to_chip_info_;
297 
Get(uint32_t id)298 model::Chip::Bluetooth Get(uint32_t id) {
299   model::Chip::Bluetooth model;
300   if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) {
301     model.CopyFrom(*id_to_chip_info_[id]->model.get());
302     auto chip_info = id_to_chip_info_[id];
303     model.mutable_classic()->set_tx_count(chip_info->classic_tx_count);
304     model.mutable_classic()->set_rx_count(chip_info->classic_rx_count);
305     model.mutable_low_energy()->set_tx_count(chip_info->le_tx_count);
306     model.mutable_low_energy()->set_rx_count(chip_info->le_rx_count);
307     if (chip_info->controller_proto) {
308       model.mutable_bt_properties()->CopyFrom(*chip_info->controller_proto);
309     }
310   }
311   return model;
312 }
313 
Reset(uint32_t id)314 void Reset(uint32_t id) {
315   if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
316     auto chip_info = it->second;
317     chip_info->le_tx_count = 0;
318     chip_info->le_rx_count = 0;
319     chip_info->classic_tx_count = 0;
320     chip_info->classic_rx_count = 0;
321   }
322   // First remove LOW_ENERGY and BR_EDR Phy
323   RemoveDeviceFromPhy(id, true);
324   RemoveDeviceFromPhy(id, false);
325   // Add to LOW_ENERGY and BR_EDR Phy
326   AddDeviceToPhy(id, true);
327   AddDeviceToPhy(id, false);
328 }
329 
Remove(uint32_t id)330 void Remove(uint32_t id) {
331   BtsLogInfo("Removing HCI chip rootcanal_id: %d.", id);
332   id_to_chip_info_.erase(id);
333   // Call the transport close callback. This invokes HciDevice::Close and
334   // TestModel close callback.
335   gAsyncManager->ExecAsync(gSocketUserId, std::chrono::milliseconds(0), [id]() {
336     // rootcanal will call HciPacketTransport::Close().
337     HciPacketTransport::Remove(id);
338   });
339 }
340 
341 // Rename AddChip(model::Chip, device, transport)
342 
Add(uint32_t chip_id,const std::string & address_string,const rust::Slice<::std::uint8_t const> controller_proto_bytes)343 uint32_t Add(uint32_t chip_id, const std::string &address_string,
344              const rust::Slice<::std::uint8_t const> controller_proto_bytes) {
345   auto transport = std::make_shared<HciPacketTransport>(chip_id, gAsyncManager);
346 
347   std::shared_ptr<rootcanal::configuration::Controller> controller_proto =
348       controller_proto_;
349   // If the Bluetooth Controller protobuf is provided, we use the provided
350   if (controller_proto_bytes.size() != 0) {
351     rootcanal::configuration::Controller custom_proto;
352     custom_proto.ParseFromArray(controller_proto_bytes.data(),
353                                 controller_proto_bytes.size());
354     BtsLogInfo("chip_id: %d has rootcanal Controller configuration: %s",
355                chip_id, custom_proto.ShortDebugString().c_str());
356 
357     // When emulators restore from a snapshot the PacketStreamer connection to
358     // netsim is recreated with a new (uninitialized) Rootcanal device. However
359     // the Android Bluetooth Stack does not re-initialize the controller. Our
360     // solution is for Rootcanal to recognize that it is receiving HCI commands
361     // before a HCI Reset. The flag below causes a hardware error event that
362     // triggers the Reset from the Bluetooth Stack.
363     custom_proto.mutable_quirks()->set_hardware_error_before_reset(true);
364 
365     controller_proto =
366         std::make_shared<rootcanal::configuration::Controller>(custom_proto);
367   }
368   std::unique_ptr<rootcanal::ControllerProperties> controller_properties =
369       std::make_unique<rootcanal::ControllerProperties>(*controller_proto);
370 
371   auto hci_device =
372       std::make_shared<rootcanal::HciDevice>(transport, *controller_properties);
373 
374   // Pass netsim::hci::facade::ReportInvalidPacket signature into hci_device
375   hci_device->RegisterInvalidPacketHandler(
376       [](uint32_t rootcanal_id, rootcanal::InvalidPacketReason reason,
377          std::string description, const std::vector<uint8_t> &packet) {
378         netsim::hci::facade::ReportInvalidPacket(
379             rootcanal_id, static_cast<int32_t>(reason), description, packet);
380       });
381 
382   // Use the `AsyncManager` to ensure that the `AddHciConnection` method is
383   // invoked atomically, preventing data races.
384   std::promise<uint32_t> rootcanal_id_promise;
385   auto rootcanal_id_future = rootcanal_id_promise.get_future();
386 
387   std::optional<Address> address_option;
388   if (address_string != "") {
389     address_option = rootcanal::Address::FromString(address_string);
390   }
391   gAsyncManager->ExecAsync(
392       gSocketUserId, std::chrono::milliseconds(0),
393       [hci_device, &rootcanal_id_promise, address_option]() {
394         rootcanal_id_promise.set_value(
395             gTestModel->AddHciConnection(hci_device, address_option));
396       });
397   auto rootcanal_id = rootcanal_id_future.get();
398 
399   HciPacketTransport::Add(rootcanal_id, transport);
400   BtsLogInfo("Creating HCI rootcanal_id: %d for chip_id: %d", rootcanal_id,
401              chip_id);
402 
403   auto model = std::make_shared<model::Chip::Bluetooth>();
404   model->mutable_classic()->set_state(true);
405   model->mutable_low_energy()->set_state(true);
406 
407   id_to_chip_info_.emplace(rootcanal_id, std::make_shared<ChipInfo>(
408                                              chip_id, model, controller_proto,
409                                              std::move(controller_properties)));
410   return rootcanal_id;
411 }
412 
RemoveRustDevice(uint32_t rootcanal_id)413 void RemoveRustDevice(uint32_t rootcanal_id) {
414   gTestModel->RemoveDevice(rootcanal_id);
415 }
416 
AddRustDevice(uint32_t chip_id,rust::Box<DynRustBluetoothChipCallbacks> callbacks,const std::string & type,const std::string & address)417 rust::Box<AddRustDeviceResult> AddRustDevice(
418     uint32_t chip_id, rust::Box<DynRustBluetoothChipCallbacks> callbacks,
419     const std::string &type, const std::string &address) {
420   auto rust_device =
421       std::make_shared<RustDevice>(std::move(callbacks), type, address);
422 
423   // TODO: Use the `AsyncManager` to ensure that the `AddDevice` and
424   // `AddDeviceToPhy` methods are invoked atomically, preventing data races.
425   // For unknown reason, use `AsyncManager` hangs.
426   auto rootcanal_id = gTestModel->AddDevice(rust_device);
427   gTestModel->AddDeviceToPhy(rootcanal_id, phy_low_energy_index_);
428 
429   auto model = std::make_shared<model::Chip::Bluetooth>();
430   // Only enable ble for beacon.
431   model->mutable_low_energy()->set_state(true);
432   id_to_chip_info_.emplace(rootcanal_id,
433                            std::make_shared<ChipInfo>(chip_id, model));
434   return CreateAddRustDeviceResult(
435       rootcanal_id, std::make_unique<RustBluetoothChip>(rust_device));
436 }
437 
SetRustDeviceAddress(uint32_t rootcanal_id,std::array<uint8_t,rootcanal::Address::kLength> address)438 void SetRustDeviceAddress(
439     uint32_t rootcanal_id,
440     std::array<uint8_t, rootcanal::Address::kLength> address) {
441   uint8_t addr[rootcanal::Address::kLength];
442   std::memcpy(addr, address.data(), rootcanal::Address::kLength);
443   gTestModel->SetDeviceAddress(rootcanal_id, rootcanal::Address(addr));
444 }
445 
IncrTx(uint32_t id,rootcanal::Phy::Type phy_type)446 void IncrTx(uint32_t id, rootcanal::Phy::Type phy_type) {
447   if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
448     auto chip_info = it->second;
449     if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) {
450       chip_info->le_tx_count++;
451     } else {
452       chip_info->classic_tx_count++;
453     }
454   }
455 }
456 
IncrRx(uint32_t id,rootcanal::Phy::Type phy_type)457 void IncrRx(uint32_t id, rootcanal::Phy::Type phy_type) {
458   if (auto it = id_to_chip_info_.find(id); it != id_to_chip_info_.end()) {
459     auto chip_info = it->second;
460     if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) {
461       chip_info->le_rx_count++;
462     } else {
463       chip_info->classic_rx_count++;
464     }
465   }
466 }
467 
468 // TODO: Make SimComputeRssi invoke netsim::device::GetDistanceRust with dev
469 // flag
SimComputeRssi(int send_id,int recv_id,int8_t tx_power)470 int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power) {
471   if (id_to_chip_info_.find(send_id) == id_to_chip_info_.end() ||
472       id_to_chip_info_.find(recv_id) == id_to_chip_info_.end()) {
473 #ifdef NETSIM_ANDROID_EMULATOR
474     // NOTE: Ignore log messages in Cuttlefish for beacon devices created by
475     // test channel.
476     BtsLogWarn("Missing chip_info");
477 #endif
478     return tx_power;
479   }
480   auto a = id_to_chip_info_[send_id]->chip_id;
481   auto b = id_to_chip_info_[recv_id]->chip_id;
482   auto distance = netsim::device::GetDistanceCxx(a, b);
483   return netsim::DistanceToRssi(tx_power, distance);
484 }
485 
GetCxx(uint32_t id)486 rust::Vec<::std::uint8_t> GetCxx(uint32_t id) {
487   auto bluetooth = Get(id);
488   std::vector<uint8_t> proto_bytes(bluetooth.ByteSizeLong());
489   bluetooth.SerializeToArray(proto_bytes.data(), proto_bytes.size());
490   rust::Vec<uint8_t> proto_rust_bytes;
491   std::copy(proto_bytes.begin(), proto_bytes.end(),
492             std::back_inserter(proto_rust_bytes));
493   return proto_rust_bytes;
494 }
495 
496 }  // namespace netsim::hci::facade
497