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 <cassert>
20 #include <chrono>
21 #include <cstdint>
22 #include <iostream>
23 #include <memory>
24 #include <unordered_map>
25 #include <utility>
26 
27 #include "hci/hci_packet_transport.h"
28 #include "model/hci/hci_sniffer.h"
29 #include "model/setup/async_manager.h"
30 #include "model/setup/test_command_handler.h"
31 #include "model/setup/test_model.h"
32 #include "netsim-cxx/src/lib.rs.h"
33 #include "util/filesystem.h"
34 #include "util/log.h"
35 
36 using netsim::model::State;
37 
38 namespace netsim::hci::facade {
39 
40 int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power);
41 void IncrTx(uint32_t send_id, rootcanal::Phy::Type phy_type);
42 void IncrRx(uint32_t receive_id, rootcanal::Phy::Type phy_type);
43 
44 using namespace std::literals;
45 using namespace rootcanal;
46 
47 using rootcanal::PhyDevice;
48 using rootcanal::PhyLayer;
49 
50 class SimPhyLayer : public PhyLayer {
51   // for constructor inheritance
52   using PhyLayer::PhyLayer;
53 
54   // Overrides ComputeRssi in PhyLayerFactory to provide
55   // simulated RSSI information using actual spatial
56   // device positions.
ComputeRssi(PhyDevice::Identifier sender_id,PhyDevice::Identifier receiver_id,int8_t tx_power)57   int8_t ComputeRssi(PhyDevice::Identifier sender_id,
58                      PhyDevice::Identifier receiver_id,
59                      int8_t tx_power) override {
60     return SimComputeRssi(sender_id, receiver_id, tx_power);
61   }
62 
63   // Overrides Send in PhyLayerFactory to add Rx/Tx statistics.
Send(std::vector<uint8_t> const & packet,int8_t tx_power,PhyDevice::Identifier sender_id)64   void Send(std::vector<uint8_t> const &packet, int8_t tx_power,
65             PhyDevice::Identifier sender_id) override {
66     IncrTx(sender_id, type);
67     for (const auto &device : phy_devices_) {
68       if (sender_id != device->id) {
69         IncrRx(device->id, type);
70         device->Receive(packet, type,
71                         ComputeRssi(sender_id, device->id, tx_power));
72       }
73     }
74   }
75 };
76 
77 class SimTestModel : public rootcanal::TestModel {
78   // for constructor inheritance
79   using rootcanal::TestModel::TestModel;
80 
CreatePhyLayer(PhyLayer::Identifier id,rootcanal::Phy::Type type)81   std::unique_ptr<rootcanal::PhyLayer> CreatePhyLayer(
82       PhyLayer::Identifier id, rootcanal::Phy::Type type) override {
83     return std::make_unique<SimPhyLayer>(id, type);
84   }
85 };
86 
87 size_t phy_low_energy_index_;
88 size_t phy_classic_index_;
89 
90 bool mStarted = false;
91 std::shared_ptr<rootcanal::AsyncManager> mAsyncManager;
92 std::unique_ptr<SimTestModel> gTestModel;
93 rootcanal::ControllerProperties controller_properties_;
94 
ChangedState(model::State a,model::State b)95 bool ChangedState(model::State a, model::State b) {
96   return (b != model::State::UNKNOWN && a != b);
97 }
98 
99 // Initialize the rootcanal library.
Start()100 void Start() {
101   if (mStarted) return;
102 
103   mAsyncManager = std::make_shared<rootcanal::AsyncManager>();
104 
105   gTestModel = std::make_unique<SimTestModel>(
106       std::bind(&rootcanal::AsyncManager::GetNextUserId, mAsyncManager),
107       std::bind(&rootcanal::AsyncManager::ExecAsync, mAsyncManager,
108                 std::placeholders::_1, std::placeholders::_2,
109                 std::placeholders::_3),
110       std::bind(&rootcanal::AsyncManager::ExecAsyncPeriodically, mAsyncManager,
111                 std::placeholders::_1, std::placeholders::_2,
112                 std::placeholders::_3, std::placeholders::_4),
113       std::bind(&rootcanal::AsyncManager::CancelAsyncTasksFromUser,
114                 mAsyncManager, std::placeholders::_1),
115       std::bind(&rootcanal::AsyncManager::CancelAsyncTask, mAsyncManager,
116                 std::placeholders::_1),
117       [](const std::string & /* server */, int /* port */,
118          rootcanal::Phy::Type /* phy_type */) { return nullptr; });
119 
120   // NOTE: 0:BR_EDR, 1:LOW_ENERGY. The order is used by bluetooth CTS.
121   phy_classic_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::BR_EDR);
122   phy_low_energy_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::LOW_ENERGY);
123 
124   // TODO: remove testCommands
125   auto testCommands = rootcanal::TestCommandHandler(*gTestModel);
126   testCommands.RegisterSendResponse([](const std::string &) {});
127   testCommands.SetTimerPeriod({"5"});
128   testCommands.StartTimer({});
129   mStarted = true;
130 };
131 
132 // Resets the root canal library.
Stop()133 void Stop() {
134   // TODO: Fix TestModel::Reset() in test_model.cc.
135   // gTestModel->Reset();
136   mStarted = false;
137 }
138 
PatchPhy(int device_id,bool isAddToPhy,bool isLowEnergy)139 void PatchPhy(int device_id, bool isAddToPhy, bool isLowEnergy) {
140   auto phy_index = (isLowEnergy) ? phy_low_energy_index_ : phy_classic_index_;
141   if (isAddToPhy) {
142     gTestModel->AddDeviceToPhy(device_id, phy_index);
143   } else {
144     gTestModel->RemoveDeviceFromPhy(device_id, phy_index);
145   }
146 }
147 
148 class ChipInfo {
149  public:
150   uint32_t simulation_device;
151   std::shared_ptr<rootcanal::HciSniffer> sniffer;
152   std::shared_ptr<model::Chip::Bluetooth> model;
153   std::shared_ptr<HciPacketTransport> transport;
154   int le_tx_count = 0;
155   int classic_tx_count = 0;
156   int le_rx_count = 0;
157   int classic_rx_count = 0;
158 
ChipInfo(uint32_t simulation_device,std::shared_ptr<rootcanal::HciSniffer> sniffer,std::shared_ptr<model::Chip::Bluetooth> model,std::shared_ptr<HciPacketTransport> transport)159   ChipInfo(uint32_t simulation_device,
160            std::shared_ptr<rootcanal::HciSniffer> sniffer,
161            std::shared_ptr<model::Chip::Bluetooth> model,
162            std::shared_ptr<HciPacketTransport> transport)
163       : simulation_device(simulation_device),
164         sniffer(sniffer),
165         model(model),
166         transport(transport) {}
167 };
168 
169 std::unordered_map<uint32_t, std::shared_ptr<ChipInfo>> id_to_chip_info_;
170 
Get(uint32_t id)171 model::Chip::Bluetooth Get(uint32_t id) {
172   model::Chip::Bluetooth model;
173   if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) {
174     model.CopyFrom(*id_to_chip_info_[id]->model.get());
175     auto chip_info = id_to_chip_info_[id];
176     model.mutable_classic()->set_tx_count(chip_info->classic_tx_count);
177     model.mutable_classic()->set_rx_count(chip_info->classic_rx_count);
178     model.mutable_low_energy()->set_tx_count(chip_info->le_tx_count);
179     model.mutable_low_energy()->set_rx_count(chip_info->le_rx_count);
180   }
181   return model;
182 }
183 
Reset(uint32_t id)184 void Reset(uint32_t id) {
185   if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) {
186     auto chip_info = id_to_chip_info_[id];
187     chip_info->le_tx_count = 0;
188     chip_info->le_rx_count = 0;
189     chip_info->classic_tx_count = 0;
190     chip_info->classic_rx_count = 0;
191   }
192   model::Chip::Bluetooth model;
193   model.mutable_classic()->set_state(model::State::ON);
194   model.mutable_low_energy()->set_state(model::State::ON);
195   Patch(id, model);
196 }
197 
Patch(uint32_t id,const model::Chip::Bluetooth & request)198 void Patch(uint32_t id, const model::Chip::Bluetooth &request) {
199   if (id_to_chip_info_.find(id) == id_to_chip_info_.end()) {
200     BtsLog("Patch an unknown id %d", id);
201     return;
202   }
203   auto model = id_to_chip_info_[id]->model;
204   auto device_index = id_to_chip_info_[id]->simulation_device;
205   // Low_energy radio state
206   auto request_state = request.low_energy().state();
207   auto *le = model->mutable_low_energy();
208   if (ChangedState(le->state(), request_state)) {
209     le->set_state(request_state);
210     PatchPhy(device_index, request_state == model::State::ON, true);
211   }
212   // Classic radio state
213   request_state = request.classic().state();
214   auto *classic = model->mutable_classic();
215   if (ChangedState(classic->state(), request_state)) {
216     classic->set_state(request_state);
217     PatchPhy(device_index, request_state == model::State::ON, false);
218   }
219 }
220 
Remove(uint32_t id)221 void Remove(uint32_t id) {
222   BtsLog("Removing HCI chip for %s");
223   id_to_chip_info_.erase(id);
224   gTestModel->RemoveDevice(id);
225   // rootcanal will call HciPacketTransport::Close().
226 }
227 
228 // Rename AddChip(model::Chip, device, transport)
229 
Add(uint32_t simulation_device)230 uint32_t Add(uint32_t simulation_device) {
231   auto transport = std::make_shared<HciPacketTransport>(mAsyncManager);
232   // rewrap the transport to include a sniffer
233   auto sniffer = std::static_pointer_cast<HciSniffer>(
234       rootcanal::HciSniffer::Create(transport));
235   auto hci_device =
236       std::make_shared<rootcanal::HciDevice>(sniffer, controller_properties_);
237   auto facade_id = gTestModel->AddHciConnection(hci_device);
238 
239   HciPacketTransport::Add(facade_id, transport);
240   BtsLog("Creating HCI facade %d for device %d", facade_id, simulation_device);
241 
242   auto model = std::make_shared<model::Chip::Bluetooth>();
243   model->mutable_classic()->set_state(model::State::ON);
244   model->mutable_low_energy()->set_state(model::State::ON);
245 
246   id_to_chip_info_.emplace(
247       facade_id,
248       std::make_shared<ChipInfo>(simulation_device, sniffer, model, transport));
249   return facade_id;
250 }
251 
IncrTx(uint32_t id,rootcanal::Phy::Type phy_type)252 void IncrTx(uint32_t id, rootcanal::Phy::Type phy_type) {
253   if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) {
254     auto chip_info = id_to_chip_info_[id];
255     if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) {
256       chip_info->le_tx_count++;
257     } else {
258       chip_info->classic_tx_count++;
259     }
260   }
261 }
262 
IncrRx(uint32_t id,rootcanal::Phy::Type phy_type)263 void IncrRx(uint32_t id, rootcanal::Phy::Type phy_type) {
264   if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) {
265     auto chip_info = id_to_chip_info_[id];
266     if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) {
267       chip_info->le_rx_count++;
268     } else {
269       chip_info->classic_rx_count++;
270     }
271   }
272 }
273 
SetPacketCapture(uint32_t id,bool isOn,std::string device_name)274 void SetPacketCapture(uint32_t id, bool isOn, std::string device_name) {
275   if (id_to_chip_info_.find(id) == id_to_chip_info_.end()) {
276     BtsLog("Missing chip_info");
277     return;
278   }
279   auto sniffer = id_to_chip_info_[id]->sniffer;
280   if (!sniffer) {
281     return;
282   }
283   if (!isOn) {
284     sniffer->SetOutputStream(nullptr);
285     return;
286   }
287   // TODO: make multi-os
288   // Filename: emulator-5554-hci.pcap
289   auto filename = "/tmp/" + device_name + "-hci.pcap";
290   for (auto i = 0; netsim::filesystem::exists(filename); ++i) {
291     filename = "/tmp/" + device_name + "-hci-" + std::to_string(i) + ".pcap";
292   }
293   auto file = std::make_shared<std::ofstream>(filename, std::ios::binary);
294   sniffer->SetOutputStream(file);
295 }
296 
SimComputeRssi(int send_id,int recv_id,int8_t tx_power)297 int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power) {
298   if (id_to_chip_info_.find(send_id) == id_to_chip_info_.end() ||
299       id_to_chip_info_.find(recv_id) == id_to_chip_info_.end()) {
300     BtsLog("Missing chip_info");
301     return tx_power;
302   }
303   auto a = id_to_chip_info_[send_id]->simulation_device;
304   auto b = id_to_chip_info_[recv_id]->simulation_device;
305   auto distance = scene_controller::GetDistance(a, b);
306   return netsim::DistanceToRssi(tx_power, distance);
307 }
308 
309 }  // namespace netsim::hci::facade
310