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