1 /*
2  * Copyright 2020 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 "storage/storage_module.h"
18 
19 #include <bluetooth/log.h>
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 
23 #include <chrono>
24 #include <cstdio>
25 #include <filesystem>
26 #include <iomanip>
27 #include <optional>
28 #include <thread>
29 
30 #include "common/bind.h"
31 #include "module.h"
32 #include "os/fake_timer/fake_timerfd.h"
33 #include "os/files.h"
34 #include "storage/config_cache.h"
35 #include "storage/config_keys.h"
36 #include "storage/device.h"
37 #include "storage/legacy_config_file.h"
38 
39 namespace testing {
40 
41 using bluetooth::TestModuleRegistry;
42 using bluetooth::hci::Address;
43 using bluetooth::os::fake_timer::fake_timerfd_advance;
44 using bluetooth::storage::ConfigCache;
45 using bluetooth::storage::Device;
46 using bluetooth::storage::LegacyConfigFile;
47 using bluetooth::storage::StorageModule;
48 
49 static const std::chrono::milliseconds kTestConfigSaveDelay = std::chrono::milliseconds(100);
50 static const size_t kTestTempDevicesCapacity = 10;
51 
52 class TestStorageModule : public StorageModule {
53  public:
TestStorageModule(std::string config_file_path,std::chrono::milliseconds config_save_delay,bool is_restricted_mode,bool is_single_user_mode)54   TestStorageModule(
55       std::string config_file_path,
56       std::chrono::milliseconds config_save_delay,
57       bool is_restricted_mode,
58       bool is_single_user_mode)
59       : StorageModule(
60             std::move(config_file_path),
61             config_save_delay,
62             kTestTempDevicesCapacity,
63             is_restricted_mode,
64             is_single_user_mode) {}
65 
GetMemoryOnlyConfigCachePublic()66   ConfigCache* GetMemoryOnlyConfigCachePublic() {
67     return StorageModule::GetMemoryOnlyConfigCache();
68   }
69 
HasSectionPublic(const std::string & section) const70   bool HasSectionPublic(const std::string& section) const {
71     return StorageModule::HasSection(section);
72   }
HasPropertyPublic(const std::string & section,const std::string & property) const73   bool HasPropertyPublic(const std::string& section, const std::string& property) const {
74     return HasProperty(section, property);
75   }
76 
GetPropertyPublic(const std::string & section,const std::string & property) const77   std::optional<std::string> GetPropertyPublic(
78       const std::string& section, const std::string& property) const {
79     return GetProperty(section, property);
80   }
SetPropertyPublic(std::string section,std::string property,std::string value)81   void SetPropertyPublic(std::string section, std::string property, std::string value) {
82     return SetProperty(section, property, value);
83   }
84 
GetPersistentSectionsPublic() const85   std::vector<std::string> GetPersistentSectionsPublic() const {
86     return GetPersistentSections();
87   }
88 
RemovePropertyPublic(const std::string & section,const std::string & property)89   bool RemovePropertyPublic(const std::string& section, const std::string& property) {
90     return RemoveProperty(section, property);
91   }
92 
ConvertEncryptOrDecryptKeyIfNeededPublic()93   void ConvertEncryptOrDecryptKeyIfNeededPublic() {
94     return ConvertEncryptOrDecryptKeyIfNeeded();
95   }
96 
RemoveSectionWithPropertyPublic(const std::string & property)97   void RemoveSectionWithPropertyPublic(const std::string& property) {
98     return RemoveSectionWithProperty(property);
99   }
100 
RemoveSectionPublic(const std::string & section)101   void RemoveSectionPublic(const std::string& section) {
102     return RemoveSection(section);
103   }
104 };
105 
106 class StorageModuleTest : public Test {
107  protected:
SetUp()108   void SetUp() override {
109     temp_dir_ = std::filesystem::temp_directory_path();
110     temp_config_ = temp_dir_ / "temp_config.txt";
111     DeleteConfigFiles();
112     ASSERT_FALSE(std::filesystem::exists(temp_config_));
113   }
114 
TearDown()115   void TearDown() override {
116     test_registry_.StopAll();
117     DeleteConfigFiles();
118   }
119 
DeleteConfigFiles()120   void DeleteConfigFiles() {
121     if (std::filesystem::exists(temp_config_)) {
122       ASSERT_TRUE(std::filesystem::remove(temp_config_));
123     }
124   }
125 
FakeTimerAdvance(std::chrono::milliseconds time)126   void FakeTimerAdvance(std::chrono::milliseconds time) {
127     auto handler = test_registry_.GetTestModuleHandler(&StorageModule::Factory);
128     handler->Post(bluetooth::common::BindOnce(fake_timerfd_advance, time.count()));
129   }
130 
WaitForReactorIdle(std::chrono::milliseconds time)131   bool WaitForReactorIdle(std::chrono::milliseconds time) {
132     bool stopped =
133         test_registry_.GetTestThread().GetReactor()->WaitForIdle(std::chrono::seconds(2));
134     if (!stopped) {
135       return false;
136     }
137     FakeTimerAdvance(time);
138     return test_registry_.GetTestThread().GetReactor()->WaitForIdle(std::chrono::seconds(2));
139   }
140 
141   bluetooth::os::Handler* handler_;
142   TestModuleRegistry test_registry_;
143   std::filesystem::path temp_dir_;
144   std::filesystem::path temp_config_;
145 };
146 
TEST_F(StorageModuleTest,empty_config_no_op_test)147 TEST_F(StorageModuleTest, empty_config_no_op_test) {
148   // Verify state before test
149   ASSERT_FALSE(std::filesystem::exists(temp_config_));
150 
151   // Actual test
152   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
153   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
154   test_registry_.StopAll();
155 
156   // Verify states after test
157   ASSERT_TRUE(std::filesystem::exists(temp_config_));
158 
159   // Verify config after test
160   auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
161   ASSERT_TRUE(config);
162   ASSERT_TRUE(config->HasSection(StorageModule::kInfoSection));
163 }
164 
165 static const std::string kReadTestConfig =
166     "[Info]\n"
167     "TimeCreated = 2020-05-20 01:20:56\n"
168     "\n"
169     "[Metrics]\n"
170     "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
171     "\n"
172     "[Adapter]\n"
173     "Address = 01:02:03:ab:cd:ef\n"
174     "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
175     "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
176     "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
177     "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
178     "ScanMode = 2\n"
179     "DiscoveryTimeout = 120\n"
180     "\n"
181     "[01:02:03:ab:cd:ea]\n"
182     "Name = hello world\n"
183     "LinkKey = fedcba0987654321fedcba0987654328\n"
184     "\n";
185 
TEST_F(StorageModuleTest,read_existing_config_test)186 TEST_F(StorageModuleTest, read_existing_config_test) {
187   ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
188   // Actual test
189 
190   // Set up
191   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
192   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
193 
194   // Test
195   ASSERT_TRUE(storage->HasSectionPublic("Metrics"));
196   ASSERT_THAT(storage->GetPersistentSectionsPublic(), ElementsAre("01:02:03:ab:cd:ea"));
197   ASSERT_THAT(
198       storage->GetPropertyPublic(StorageModule::kAdapterSection, BTIF_STORAGE_KEY_ADDRESS),
199       Optional(StrEq("01:02:03:ab:cd:ef")));
200 
201   // Tear down
202   test_registry_.StopAll();
203 
204   // Verify states after test
205   ASSERT_TRUE(std::filesystem::exists(temp_config_));
206 
207   // Verify config after test
208   auto config = bluetooth::os::ReadSmallFile(temp_config_.string());
209   ASSERT_TRUE(config);
210   ASSERT_EQ(*config, kReadTestConfig);
211 }
212 
TEST_F(StorageModuleTest,save_config_test)213 TEST_F(StorageModuleTest, save_config_test) {
214   // Prepare config file
215   ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
216 
217   // Set up
218   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
219   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
220 
221   // Test
222   // Change a property
223   ASSERT_THAT(
224       storage->GetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
225       Optional(StrEq("hello world")));
226   storage->SetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
227   ASSERT_THAT(
228       storage->GetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
229       Optional(StrEq("foo")));
230   ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
231 
232   auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
233   ASSERT_TRUE(config);
234   ASSERT_THAT(
235       config->GetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME), Optional(StrEq("foo")));
236 
237   // Remove a property
238   storage->RemovePropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME);
239   ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
240   bluetooth::log::info("After waiting 2");
241   config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
242   ASSERT_TRUE(config);
243   ASSERT_FALSE(config->HasProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME));
244 
245   // Remove a section
246   storage->RemoveSectionPublic("01:02:03:ab:cd:ea");
247   ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
248   bluetooth::log::info("After waiting 3");
249   config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
250   ASSERT_TRUE(config);
251   ASSERT_FALSE(config->HasSection("01:02:03:ab:cd:ea"));
252 
253   // Tear down
254   test_registry_.StopAll();
255 
256   // Verify states after test
257   ASSERT_TRUE(std::filesystem::exists(temp_config_));
258 }
259 
TEST_F(StorageModuleTest,get_bonded_devices_test)260 TEST_F(StorageModuleTest, get_bonded_devices_test) {
261   // Prepare config file
262   ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
263 
264   // Set up
265   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
266   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
267 
268   ASSERT_EQ(storage->GetBondedDevices().size(), 1u);
269   auto address = Address::FromString("01:02:03:ab:cd:ea");
270   ASSERT_EQ(address, storage->GetBondedDevices()[0].GetAddress());
271 
272   // Tear down
273   test_registry_.StopAll();
274 }
275 
TEST_F(StorageModuleTest,unchanged_config_causes_no_write)276 TEST_F(StorageModuleTest, unchanged_config_causes_no_write) {
277   // Prepare config file
278   ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
279 
280   // Set up
281   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
282   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
283 
284   ASSERT_EQ(storage->GetBondedDevices().size(), 1u);
285   auto address = Address::FromString("01:02:03:ab:cd:ea");
286   ASSERT_EQ(address, storage->GetBondedDevices()[0].GetAddress());
287 
288   // Remove the file after it was read, so we can check if it was written with exists()
289   DeleteConfigFiles();
290 
291   // Tear down
292   test_registry_.StopAll();
293 
294   ASSERT_FALSE(std::filesystem::exists(temp_config_));
295 }
296 
TEST_F(StorageModuleTest,changed_config_causes_a_write)297 TEST_F(StorageModuleTest, changed_config_causes_a_write) {
298   // Prepare config file
299   ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
300 
301   // Set up
302   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
303   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
304 
305   // Remove the file after it was read, so we can check if it was written with exists()
306   DeleteConfigFiles();
307 
308   // Change a property
309   storage->SetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
310 
311   ASSERT_TRUE(WaitForReactorIdle(std::chrono::milliseconds(1)));
312 
313   // Tear down
314   test_registry_.StopAll();
315 
316   ASSERT_TRUE(std::filesystem::exists(temp_config_));
317 }
318 
TEST_F(StorageModuleTest,no_config_causes_a_write)319 TEST_F(StorageModuleTest, no_config_causes_a_write) {
320   // Set up
321   auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
322   test_registry_.InjectTestModule(&StorageModule::Factory, storage);
323 
324   // Tear down
325   test_registry_.StopAll();
326 
327   ASSERT_TRUE(std::filesystem::exists(temp_config_));
328 }
329 
330 }  // namespace testing
331