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