blob: 2603abfe2070a4d106d2d3fcb9758cace8336f9c [file] [log] [blame]
// 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 <sys/types.h>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <utility>
#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<uint8_t> 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<rootcanal::PhyLayer> CreatePhyLayer(
PhyLayer::Identifier id, rootcanal::Phy::Type type) override {
return std::make_unique<SimPhyLayer>(id, type);
}
};
size_t phy_low_energy_index_;
size_t phy_classic_index_;
bool mStarted = false;
std::shared_ptr<rootcanal::AsyncManager> mAsyncManager;
std::unique_ptr<SimTestModel> gTestModel;
std::string 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<rootcanal::AsyncManager>();
gTestModel = std::make_unique<SimTestModel>(
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<rootcanal::HciSniffer> sniffer;
std::shared_ptr<model::Chip::Bluetooth> model;
std::shared_ptr<HciPacketTransport> 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<rootcanal::HciSniffer> sniffer,
std::shared_ptr<model::Chip::Bluetooth> model,
std::shared_ptr<HciPacketTransport> transport)
: simulation_device(simulation_device),
sniffer(sniffer),
model(model),
transport(transport) {}
};
std::unordered_map<uint32_t, std::shared_ptr<ChipInfo>> 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<HciPacketTransport>(mAsyncManager);
// rewrap the transport to include a sniffer
auto sniffer = std::static_pointer_cast<HciSniffer>(
rootcanal::HciSniffer::Create(transport));
auto hci_device =
std::make_shared<rootcanal::HciDevice>(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::Chip::Bluetooth>();
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<ChipInfo>(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++;
}
}
}
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