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