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