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