1 /*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "test_model.h"
18
19 #include <stdlib.h> // for size_t
20
21 #include <iomanip> // for operator<<, setfill
22 #include <iostream> // for basic_ostream
23 #include <memory> // for shared_ptr, make...
24 #include <optional>
25 #include <type_traits> // for remove_extent_t
26 #include <utility> // for move
27
28 #include "include/phy.h" // for Phy, Phy::Type
29 #include "log.h"
30 #include "phy_layer.h"
31
32 namespace rootcanal {
33
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)34 TestModel::TestModel(
35 std::function<AsyncUserId()> get_user_id,
36 std::function<AsyncTaskId(AsyncUserId, std::chrono::milliseconds, const TaskCallback&)>
37 event_scheduler,
38
39 std::function<AsyncTaskId(AsyncUserId, std::chrono::milliseconds, std::chrono::milliseconds,
40 const TaskCallback&)>
41 periodic_event_scheduler,
42
43 std::function<void(AsyncUserId)> cancel_tasks_from_user,
44 std::function<void(AsyncTaskId)> cancel,
45 std::function<std::shared_ptr<Device>(const std::string&, int, Phy::Type)>
46 connect_to_remote,
47 std::array<uint8_t, 5> bluetooth_address_prefix)
48 : bluetooth_address_prefix_(std::move(bluetooth_address_prefix)),
49 get_user_id_(std::move(get_user_id)),
50 schedule_task_(std::move(event_scheduler)),
51 schedule_periodic_task_(std::move(periodic_event_scheduler)),
52 cancel_task_(std::move(cancel)),
53 cancel_tasks_from_user_(std::move(cancel_tasks_from_user)),
54 connect_to_remote_(std::move(connect_to_remote)) {
55 model_user_id_ = get_user_id_();
56 }
57
~TestModel()58 TestModel::~TestModel() { StopTimer(); }
59
SetTimerPeriod(std::chrono::milliseconds new_period)60 void TestModel::SetTimerPeriod(std::chrono::milliseconds new_period) {
61 timer_period_ = new_period;
62
63 if (timer_tick_task_ == kInvalidTaskId) {
64 return;
65 }
66
67 // Restart the timer with the new period
68 StopTimer();
69 StartTimer();
70 }
71
StartTimer()72 void TestModel::StartTimer() {
73 INFO("StartTimer()");
74 timer_tick_task_ = schedule_periodic_task_(model_user_id_, std::chrono::milliseconds(0),
75 timer_period_, [this]() { TestModel::Tick(); });
76 }
77
StopTimer()78 void TestModel::StopTimer() {
79 INFO("StopTimer()");
80 cancel_task_(timer_tick_task_);
81 timer_tick_task_ = kInvalidTaskId;
82 }
83
CreatePhyLayer(PhyLayer::Identifier id,Phy::Type type)84 std::unique_ptr<PhyLayer> TestModel::CreatePhyLayer(PhyLayer::Identifier id, Phy::Type type) {
85 return std::make_unique<PhyLayer>(id, type);
86 }
87
CreatePhyDevice(std::string type,std::shared_ptr<Device> device)88 std::shared_ptr<PhyDevice> TestModel::CreatePhyDevice(std::string type,
89 std::shared_ptr<Device> device) {
90 return std::make_shared<PhyDevice>(std::move(type), std::move(device));
91 }
92
GenerateBluetoothAddress(uint32_t device_id) const93 Address TestModel::GenerateBluetoothAddress(uint32_t device_id) const {
94 Address address({
95 static_cast<uint8_t>(device_id),
96 bluetooth_address_prefix_[4],
97 bluetooth_address_prefix_[3],
98 bluetooth_address_prefix_[2],
99 bluetooth_address_prefix_[1],
100 bluetooth_address_prefix_[0],
101 });
102
103 if (reuse_device_addresses_) {
104 // Find the first unused address.
105 for (uint16_t b0 = 0; b0 <= 0xff; b0++) {
106 address.address[0] = b0;
107 bool used = std::any_of(phy_devices_.begin(), phy_devices_.end(), [address](auto& device) {
108 return device.second->GetAddress() == address;
109 });
110 if (!used) {
111 break;
112 }
113 }
114 }
115
116 return address;
117 }
118
119 // Add a device to the test model.
AddDevice(std::shared_ptr<Device> device)120 PhyDevice::Identifier TestModel::AddDevice(std::shared_ptr<Device> device) {
121 std::string device_type = device->GetTypeString();
122 std::shared_ptr<PhyDevice> phy_device = CreatePhyDevice(device_type, std::move(device));
123 phy_devices_[phy_device->id] = phy_device;
124 return phy_device->id;
125 }
126
127 // Remove a device from the test model.
RemoveDevice(PhyDevice::Identifier device_id)128 void TestModel::RemoveDevice(PhyDevice::Identifier device_id) {
129 for (auto& [_, phy_layer] : phy_layers_) {
130 phy_layer->Unregister(device_id);
131 }
132 phy_devices_.erase(device_id);
133 }
134
135 // Add a phy to the test model.
AddPhy(Phy::Type type)136 PhyLayer::Identifier TestModel::AddPhy(Phy::Type type) {
137 static PhyLayer::Identifier next_id = 0;
138 std::shared_ptr<PhyLayer> phy_layer = CreatePhyLayer(next_id++, type);
139 phy_layers_[phy_layer->id] = phy_layer;
140 return phy_layer->id;
141 }
142
143 // Remove a phy from the test model.
RemovePhy(PhyLayer::Identifier phy_id)144 void TestModel::RemovePhy(PhyLayer::Identifier phy_id) {
145 if (phy_layers_.find(phy_id) != phy_layers_.end()) {
146 phy_layers_[phy_id]->UnregisterAll();
147 phy_layers_.erase(phy_id);
148 }
149 }
150
151 // Add the selected device to the selected phy.
AddDeviceToPhy(PhyDevice::Identifier device_id,PhyLayer::Identifier phy_id)152 void TestModel::AddDeviceToPhy(PhyDevice::Identifier device_id, PhyLayer::Identifier phy_id) {
153 if (phy_layers_.find(phy_id) != phy_layers_.end() &&
154 phy_devices_.find(device_id) != phy_devices_.end()) {
155 phy_layers_[phy_id]->Register(phy_devices_[device_id]);
156 }
157 }
158
159 // Remove the selected device from the selected phy.
RemoveDeviceFromPhy(PhyDevice::Identifier device_id,PhyLayer::Identifier phy_id)160 void TestModel::RemoveDeviceFromPhy(PhyDevice::Identifier device_id, PhyLayer::Identifier phy_id) {
161 if (phy_layers_.find(phy_id) != phy_layers_.end()) {
162 phy_layers_[phy_id]->Unregister(device_id);
163 }
164 }
165
AddLinkLayerConnection(std::shared_ptr<Device> device,Phy::Type type)166 void TestModel::AddLinkLayerConnection(std::shared_ptr<Device> device, Phy::Type type) {
167 INFO(device->id_, "Adding a new link layer connection of type: {}",
168 type == Phy::Type::BR_EDR ? "BR_EDR" : "LOW_ENERGY");
169
170 PhyDevice::Identifier device_id = AddDevice(device);
171
172 for (auto& [_, phy_layer] : phy_layers_) {
173 if (phy_layer->type == type) {
174 phy_layer->Register(phy_devices_[device_id]);
175 }
176 }
177
178 AsyncUserId user_id = get_user_id_();
179 device->RegisterCloseCallback([this, device_id, user_id] {
180 schedule_task_(user_id, std::chrono::milliseconds(0),
181 [this, device_id, user_id]() { OnConnectionClosed(device_id, user_id); });
182 });
183 }
184
AddRemote(const std::string & server,int port,Phy::Type type)185 void TestModel::AddRemote(const std::string& server, int port, Phy::Type type) {
186 auto device = connect_to_remote_(server, port, type);
187 if (device == nullptr) {
188 return;
189 }
190 AddLinkLayerConnection(device, type);
191 }
192
AddHciConnection(std::shared_ptr<HciDevice> device,std::optional<Address> address)193 PhyDevice::Identifier TestModel::AddHciConnection(std::shared_ptr<HciDevice> device,
194 std::optional<Address> address) {
195 // clients can specify BD_ADDR or have it set based on device_id.
196 device->SetAddress(address.value_or(GenerateBluetoothAddress(device->id_)));
197 AddDevice(std::static_pointer_cast<Device>(device));
198
199 INFO(device->id_, "Initialized device with address {}", device->GetAddress());
200
201 for (auto& [_, phy_layer] : phy_layers_) {
202 phy_layer->Register(phy_devices_[device->id_]);
203 }
204
205 PhyDevice::Identifier device_id = device->id_;
206 AsyncUserId user_id = get_user_id_();
207 device->RegisterCloseCallback([this, device_id, user_id] {
208 schedule_task_(user_id, std::chrono::milliseconds(0),
209 [this, device_id, user_id]() { OnConnectionClosed(device_id, user_id); });
210 });
211 return device->id_;
212 }
213
OnConnectionClosed(PhyDevice::Identifier device_id,AsyncUserId user_id)214 void TestModel::OnConnectionClosed(PhyDevice::Identifier device_id, AsyncUserId user_id) {
215 if (phy_devices_.find(device_id) != phy_devices_.end()) {
216 cancel_tasks_from_user_(user_id);
217 RemoveDevice(device_id);
218 }
219 }
220
SetDeviceAddress(PhyDevice::Identifier device_id,Address address)221 void TestModel::SetDeviceAddress(PhyDevice::Identifier device_id, Address address) {
222 if (phy_devices_.find(device_id) != phy_devices_.end()) {
223 phy_devices_[device_id]->SetAddress(std::move(address));
224 }
225 }
226
SetDeviceConfiguration(PhyDevice::Identifier device_id,rootcanal::configuration::Controller const & configuration)227 void TestModel::SetDeviceConfiguration(PhyDevice::Identifier device_id,
228 rootcanal::configuration::Controller const& configuration) {
229 if (phy_devices_.find(device_id) != phy_devices_.end()) {
230 if (phy_devices_[device_id]->GetDevice()->GetTypeString() == "hci_device") {
231 std::shared_ptr<DualModeController> device =
232 std::static_pointer_cast<HciDevice>(phy_devices_[device_id]->GetDevice());
233 device->SetProperties(ControllerProperties(configuration));
234 } else {
235 ERROR(device_id, "failed to update the configuration, device is not a controller device");
236 }
237 }
238 }
239
List()240 const std::string& TestModel::List() {
241 list_string_.clear();
242 list_string_ += " Devices: \r\n";
243
244 for (auto const& [device_id, device] : phy_devices_) {
245 list_string_ += " " + std::to_string(device_id) + ":";
246 list_string_ += device->ToString() + " \r\n";
247 }
248
249 list_string_ += " Phys: \r\n";
250
251 for (auto const& [phy_id, phy] : phy_layers_) {
252 list_string_ += " " + std::to_string(phy_id) + ":";
253 list_string_ += phy->ToString() + " \r\n";
254 }
255
256 return list_string_;
257 }
258
Tick()259 void TestModel::Tick() {
260 for (auto& [_, device] : phy_devices_) {
261 device->Tick();
262 }
263 }
264
Reset()265 void TestModel::Reset() {
266 StopTimer();
267 schedule_task_(model_user_id_, std::chrono::milliseconds(0), [this]() {
268 INFO("Running Reset task");
269 for (auto& [_, phy_layer] : phy_layers_) {
270 phy_layer->UnregisterAll();
271 }
272 phy_devices_.clear();
273 });
274 }
275
276 } // namespace rootcanal
277