// Copyright 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "hci/bluetooth_facade.h" #include #include #include #include #include #include #include #include #include "hci/hci_packet_transport.h" #include "model/hci/hci_sniffer.h" #include "model/setup/async_manager.h" #include "model/setup/test_command_handler.h" #include "model/setup/test_model.h" #include "netsim-cxx/src/lib.rs.h" #include "util/filesystem.h" #include "util/log.h" using netsim::model::State; namespace netsim::hci::facade { int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power); void IncrTx(uint32_t send_id, rootcanal::Phy::Type phy_type); void IncrRx(uint32_t receive_id, rootcanal::Phy::Type phy_type); using namespace std::literals; using namespace rootcanal; using rootcanal::PhyDevice; using rootcanal::PhyLayer; class SimPhyLayer : public PhyLayer { // for constructor inheritance using PhyLayer::PhyLayer; // Overrides ComputeRssi in PhyLayerFactory to provide // simulated RSSI information using actual spatial // device positions. int8_t ComputeRssi(PhyDevice::Identifier sender_id, PhyDevice::Identifier receiver_id, int8_t tx_power) override { return SimComputeRssi(sender_id, receiver_id, tx_power); } // Overrides Send in PhyLayerFactory to add Rx/Tx statistics. void Send(std::vector const &packet, int8_t tx_power, PhyDevice::Identifier sender_id) override { IncrTx(sender_id, type); for (const auto &device : phy_devices_) { if (sender_id != device->id) { IncrRx(device->id, type); device->Receive(packet, type, ComputeRssi(sender_id, device->id, tx_power)); } } } }; class SimTestModel : public rootcanal::TestModel { // for constructor inheritance using rootcanal::TestModel::TestModel; std::unique_ptr CreatePhyLayer( PhyLayer::Identifier id, rootcanal::Phy::Type type) override { return std::make_unique(id, type); } }; size_t phy_low_energy_index_; size_t phy_classic_index_; bool mStarted = false; std::shared_ptr mAsyncManager; std::unique_ptr gTestModel; rootcanal::ControllerProperties controller_properties_; bool ChangedState(model::State a, model::State b) { return (b != model::State::UNKNOWN && a != b); } // Initialize the rootcanal library. void Start() { if (mStarted) return; mAsyncManager = std::make_shared(); gTestModel = std::make_unique( std::bind(&rootcanal::AsyncManager::GetNextUserId, mAsyncManager), std::bind(&rootcanal::AsyncManager::ExecAsync, mAsyncManager, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), std::bind(&rootcanal::AsyncManager::ExecAsyncPeriodically, mAsyncManager, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), std::bind(&rootcanal::AsyncManager::CancelAsyncTasksFromUser, mAsyncManager, std::placeholders::_1), std::bind(&rootcanal::AsyncManager::CancelAsyncTask, mAsyncManager, std::placeholders::_1), [](const std::string & /* server */, int /* port */, rootcanal::Phy::Type /* phy_type */) { return nullptr; }); // NOTE: 0:BR_EDR, 1:LOW_ENERGY. The order is used by bluetooth CTS. phy_classic_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::BR_EDR); phy_low_energy_index_ = gTestModel->AddPhy(rootcanal::Phy::Type::LOW_ENERGY); // TODO: remove testCommands auto testCommands = rootcanal::TestCommandHandler(*gTestModel); testCommands.RegisterSendResponse([](const std::string &) {}); testCommands.SetTimerPeriod({"5"}); testCommands.StartTimer({}); mStarted = true; }; // Resets the root canal library. void Stop() { // TODO: Fix TestModel::Reset() in test_model.cc. // gTestModel->Reset(); mStarted = false; } void PatchPhy(int device_id, bool isAddToPhy, bool isLowEnergy) { auto phy_index = (isLowEnergy) ? phy_low_energy_index_ : phy_classic_index_; if (isAddToPhy) { gTestModel->AddDeviceToPhy(device_id, phy_index); } else { gTestModel->RemoveDeviceFromPhy(device_id, phy_index); } } class ChipInfo { public: uint32_t simulation_device; std::shared_ptr sniffer; std::shared_ptr model; std::shared_ptr transport; int le_tx_count = 0; int classic_tx_count = 0; int le_rx_count = 0; int classic_rx_count = 0; ChipInfo(uint32_t simulation_device, std::shared_ptr sniffer, std::shared_ptr model, std::shared_ptr transport) : simulation_device(simulation_device), sniffer(sniffer), model(model), transport(transport) {} }; std::unordered_map> id_to_chip_info_; model::Chip::Bluetooth Get(uint32_t id) { model::Chip::Bluetooth model; if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) { model.CopyFrom(*id_to_chip_info_[id]->model.get()); auto chip_info = id_to_chip_info_[id]; model.mutable_classic()->set_tx_count(chip_info->classic_tx_count); model.mutable_classic()->set_rx_count(chip_info->classic_rx_count); model.mutable_low_energy()->set_tx_count(chip_info->le_tx_count); model.mutable_low_energy()->set_rx_count(chip_info->le_rx_count); } return model; } void Reset(uint32_t id) { if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) { auto chip_info = id_to_chip_info_[id]; chip_info->le_tx_count = 0; chip_info->le_rx_count = 0; chip_info->classic_tx_count = 0; chip_info->classic_rx_count = 0; } model::Chip::Bluetooth model; model.mutable_classic()->set_state(model::State::ON); model.mutable_low_energy()->set_state(model::State::ON); Patch(id, model); } void Patch(uint32_t id, const model::Chip::Bluetooth &request) { if (id_to_chip_info_.find(id) == id_to_chip_info_.end()) { BtsLog("Patch an unknown id %d", id); return; } auto model = id_to_chip_info_[id]->model; auto device_index = id_to_chip_info_[id]->simulation_device; // Low_energy radio state auto request_state = request.low_energy().state(); auto *le = model->mutable_low_energy(); if (ChangedState(le->state(), request_state)) { le->set_state(request_state); PatchPhy(device_index, request_state == model::State::ON, true); } // Classic radio state request_state = request.classic().state(); auto *classic = model->mutable_classic(); if (ChangedState(classic->state(), request_state)) { classic->set_state(request_state); PatchPhy(device_index, request_state == model::State::ON, false); } } void Remove(uint32_t id) { BtsLog("Removing HCI chip for %s"); id_to_chip_info_.erase(id); gTestModel->RemoveDevice(id); // rootcanal will call HciPacketTransport::Close(). } // Rename AddChip(model::Chip, device, transport) uint32_t Add(uint32_t simulation_device) { auto transport = std::make_shared(mAsyncManager); // rewrap the transport to include a sniffer auto sniffer = std::static_pointer_cast( rootcanal::HciSniffer::Create(transport)); auto hci_device = std::make_shared(sniffer, controller_properties_); auto facade_id = gTestModel->AddHciConnection(hci_device); HciPacketTransport::Add(facade_id, transport); BtsLog("Creating HCI facade %d for device %d", facade_id, simulation_device); auto model = std::make_shared(); model->mutable_classic()->set_state(model::State::ON); model->mutable_low_energy()->set_state(model::State::ON); id_to_chip_info_.emplace( facade_id, std::make_shared(simulation_device, sniffer, model, transport)); return facade_id; } void IncrTx(uint32_t id, rootcanal::Phy::Type phy_type) { if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) { auto chip_info = id_to_chip_info_[id]; if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) { chip_info->le_tx_count++; } else { chip_info->classic_tx_count++; } } } void IncrRx(uint32_t id, rootcanal::Phy::Type phy_type) { if (id_to_chip_info_.find(id) != id_to_chip_info_.end()) { auto chip_info = id_to_chip_info_[id]; if (phy_type == rootcanal::Phy::Type::LOW_ENERGY) { chip_info->le_rx_count++; } else { chip_info->classic_rx_count++; } } } void SetPacketCapture(uint32_t id, bool isOn, std::string device_name) { if (id_to_chip_info_.find(id) == id_to_chip_info_.end()) { BtsLog("Missing chip_info"); return; } auto sniffer = id_to_chip_info_[id]->sniffer; if (!sniffer) { return; } if (!isOn) { sniffer->SetOutputStream(nullptr); return; } // TODO: make multi-os // Filename: emulator-5554-hci.pcap auto filename = "/tmp/" + device_name + "-hci.pcap"; for (auto i = 0; netsim::filesystem::exists(filename); ++i) { filename = "/tmp/" + device_name + "-hci-" + std::to_string(i) + ".pcap"; } auto file = std::make_shared(filename, std::ios::binary); sniffer->SetOutputStream(file); } int8_t SimComputeRssi(int send_id, int recv_id, int8_t tx_power) { if (id_to_chip_info_.find(send_id) == id_to_chip_info_.end() || id_to_chip_info_.find(recv_id) == id_to_chip_info_.end()) { BtsLog("Missing chip_info"); return tx_power; } auto a = id_to_chip_info_[send_id]->simulation_device; auto b = id_to_chip_info_[recv_id]->simulation_device; auto distance = scene_controller::GetDistance(a, b); return netsim::DistanceToRssi(tx_power, distance); } } // namespace netsim::hci::facade