blob: b80fde6cd321a2a85b1bb81a4d2dea39b53ad1d2 [file] [log] [blame]
/*
* Copyright 2018 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 "test_model.h"
#include <stdlib.h> // for size_t
#include <iomanip> // for operator<<, setfill
#include <iostream> // for basic_ostream
#include <memory> // for shared_ptr, make...
#include <optional>
#include <type_traits> // for remove_extent_t
#include <utility> // for move
#include "include/phy.h" // for Phy, Phy::Type
#include "log.h"
#include "phy_layer.h"
namespace rootcanal {
TestModel::TestModel(
std::function<AsyncUserId()> get_user_id,
std::function<AsyncTaskId(AsyncUserId, std::chrono::milliseconds,
const TaskCallback&)>
event_scheduler,
std::function<AsyncTaskId(AsyncUserId, std::chrono::milliseconds,
std::chrono::milliseconds, const TaskCallback&)>
periodic_event_scheduler,
std::function<void(AsyncUserId)> cancel_tasks_from_user,
std::function<void(AsyncTaskId)> cancel,
std::function<std::shared_ptr<Device>(const std::string&, int, Phy::Type)>
connect_to_remote,
std::array<uint8_t, 5> bluetooth_address_prefix)
: bluetooth_address_prefix_(std::move(bluetooth_address_prefix)),
get_user_id_(std::move(get_user_id)),
schedule_task_(std::move(event_scheduler)),
schedule_periodic_task_(std::move(periodic_event_scheduler)),
cancel_task_(std::move(cancel)),
cancel_tasks_from_user_(std::move(cancel_tasks_from_user)),
connect_to_remote_(std::move(connect_to_remote)) {
model_user_id_ = get_user_id_();
}
TestModel::~TestModel() {
StopTimer();
}
void TestModel::SetTimerPeriod(std::chrono::milliseconds new_period) {
timer_period_ = new_period;
if (timer_tick_task_ == kInvalidTaskId) {
return;
}
// Restart the timer with the new period
StopTimer();
StartTimer();
}
void TestModel::StartTimer() {
INFO("StartTimer()");
timer_tick_task_ =
schedule_periodic_task_(model_user_id_, std::chrono::milliseconds(0),
timer_period_, [this]() { TestModel::Tick(); });
}
void TestModel::StopTimer() {
INFO("StopTimer()");
cancel_task_(timer_tick_task_);
timer_tick_task_ = kInvalidTaskId;
}
std::unique_ptr<PhyLayer> TestModel::CreatePhyLayer(PhyLayer::Identifier id,
Phy::Type type) {
return std::make_unique<PhyLayer>(id, type);
}
std::shared_ptr<PhyDevice> TestModel::CreatePhyDevice(
PhyDevice::Identifier id, std::string type,
std::shared_ptr<Device> device) {
return std::make_shared<PhyDevice>(id, std::move(type), std::move(device));
}
// Add a device to the test model.
PhyDevice::Identifier TestModel::AddDevice(std::shared_ptr<Device> device) {
std::optional<PhyDevice::Identifier> device_id{};
if (reuse_device_ids_) {
// Find the first unused identifier.
// The identifier is used to generate the bluetooth address,
// and reusing the first unused identifier lets a re-connecting
// get the same identifier and address.
for (PhyDevice::Identifier id = 0; id < next_device_id_; id++) {
if (phy_devices_.count(id) == 0) {
device_id = id;
break;
}
}
}
if (!device_id.has_value()) {
device_id = next_device_id_++;
}
std::string device_type = device->GetTypeString();
std::shared_ptr<PhyDevice> phy_device =
CreatePhyDevice(device_id.value(), device_type, std::move(device));
phy_devices_[phy_device->id] = phy_device;
return phy_device->id;
}
// Remove a device from the test model.
void TestModel::RemoveDevice(PhyDevice::Identifier device_id) {
for (auto& [_, phy_layer] : phy_layers_) {
phy_layer->Unregister(device_id);
}
phy_devices_.erase(device_id);
}
// Add a phy to the test model.
PhyLayer::Identifier TestModel::AddPhy(Phy::Type type) {
static PhyLayer::Identifier next_id = 0;
std::shared_ptr<PhyLayer> phy_layer = CreatePhyLayer(next_id++, type);
phy_layers_[phy_layer->id] = phy_layer;
return phy_layer->id;
}
// Remove a phy from the test model.
void TestModel::RemovePhy(PhyLayer::Identifier phy_id) {
if (phy_layers_.find(phy_id) != phy_layers_.end()) {
phy_layers_[phy_id]->UnregisterAll();
phy_layers_.erase(phy_id);
}
}
// Add the selected device to the selected phy.
void TestModel::AddDeviceToPhy(PhyDevice::Identifier device_id,
PhyLayer::Identifier phy_id) {
if (phy_layers_.find(phy_id) != phy_layers_.end() &&
phy_devices_.find(device_id) != phy_devices_.end()) {
phy_layers_[phy_id]->Register(phy_devices_[device_id]);
}
}
// Remove the selected device from the selected phy.
void TestModel::RemoveDeviceFromPhy(PhyDevice::Identifier device_id,
PhyLayer::Identifier phy_id) {
if (phy_layers_.find(phy_id) != phy_layers_.end()) {
phy_layers_[phy_id]->Unregister(device_id);
}
}
void TestModel::AddLinkLayerConnection(std::shared_ptr<Device> device,
Phy::Type type) {
INFO("Adding a new link layer connection of type: {}",
type == Phy::Type::BR_EDR ? "BR_EDR" : "LOW_ENERGY");
PhyDevice::Identifier device_id = AddDevice(device);
for (auto& [_, phy_layer] : phy_layers_) {
if (phy_layer->type == type) {
phy_layer->Register(phy_devices_[device_id]);
}
}
AsyncUserId user_id = get_user_id_();
device->RegisterCloseCallback([this, device_id, user_id] {
schedule_task_(user_id, std::chrono::milliseconds(0),
[this, device_id, user_id]() {
OnConnectionClosed(device_id, user_id);
});
});
}
void TestModel::AddRemote(const std::string& server, int port, Phy::Type type) {
auto device = connect_to_remote_(server, port, type);
if (device == nullptr) {
return;
}
AddLinkLayerConnection(device, type);
}
PhyDevice::Identifier TestModel::AddHciConnection(
std::shared_ptr<HciDevice> device) {
PhyDevice::Identifier device_id =
AddDevice(std::static_pointer_cast<Device>(device));
auto bluetooth_address = Address{{
uint8_t(device_id),
bluetooth_address_prefix_[4],
bluetooth_address_prefix_[3],
bluetooth_address_prefix_[2],
bluetooth_address_prefix_[1],
bluetooth_address_prefix_[0],
}};
device->SetAddress(bluetooth_address);
INFO("Initialized device with address {}", bluetooth_address.ToString());
for (auto& [_, phy_layer] : phy_layers_) {
phy_layer->Register(phy_devices_[device_id]);
}
AsyncUserId user_id = get_user_id_();
device->RegisterCloseCallback([this, device_id, user_id] {
schedule_task_(user_id, std::chrono::milliseconds(0),
[this, device_id, user_id]() {
OnConnectionClosed(device_id, user_id);
});
});
return device_id;
}
void TestModel::OnConnectionClosed(PhyDevice::Identifier device_id,
AsyncUserId user_id) {
if (phy_devices_.find(device_id) != phy_devices_.end()) {
cancel_tasks_from_user_(user_id);
RemoveDevice(device_id);
}
}
void TestModel::SetDeviceAddress(PhyDevice::Identifier device_id,
Address address) {
if (phy_devices_.find(device_id) != phy_devices_.end()) {
phy_devices_[device_id]->SetAddress(std::move(address));
}
}
const std::string& TestModel::List() {
list_string_.clear();
list_string_ += " Devices: \r\n";
for (auto const& [device_id, device] : phy_devices_) {
list_string_ += " " + std::to_string(device_id) + ":";
list_string_ += device->ToString() + " \r\n";
}
list_string_ += " Phys: \r\n";
for (auto const& [phy_id, phy] : phy_layers_) {
list_string_ += " " + std::to_string(phy_id) + ":";
list_string_ += phy->ToString() + " \r\n";
}
return list_string_;
}
void TestModel::Tick() {
for (auto& [_, device] : phy_devices_) {
device->Tick();
}
}
void TestModel::Reset() {
StopTimer();
schedule_task_(model_user_id_, std::chrono::milliseconds(0), [this]() {
INFO("Running Reset task");
for (auto& [_, phy_layer] : phy_layers_) {
phy_layer->UnregisterAll();
}
phy_devices_.clear();
next_device_id_ = 0;
});
}
} // namespace rootcanal