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