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