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/legacy_config_file.h"
18 
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 
22 #include <filesystem>
23 
24 #include "os/files.h"
25 #include "storage/device.h"
26 
27 namespace testing {
28 
29 using bluetooth::os::ReadSmallFile;
30 using bluetooth::os::WriteToFile;
31 using bluetooth::storage::ConfigCache;
32 using bluetooth::storage::Device;
33 using bluetooth::storage::LegacyConfigFile;
34 
TEST(LegacyConfigFileTest,write_and_read_loop_back_test)35 TEST(LegacyConfigFileTest, write_and_read_loop_back_test) {
36   auto temp_dir = std::filesystem::temp_directory_path();
37   auto temp_config = temp_dir / "temp_config.txt";
38 
39   ConfigCache config(100, Device::kLinkKeyProperties);
40   config.SetProperty("A", "B", "C");
41   config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
42   config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
43   config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
44   EXPECT_TRUE(config.HasProperty("CC:DD:EE:FF:00:11", "LinkKey"));
45   EXPECT_THAT(config.GetPersistentSections(), ElementsAre("CC:DD:EE:FF:00:11"));
46 
47   EXPECT_TRUE(LegacyConfigFile::FromPath(temp_config.string()).Write(config));
48   auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
49   EXPECT_TRUE(config_read);
50   // Unpaired devices do not exist in persistent config file
51   config.RemoveSection("AA:BB:CC:DD:EE:FF");
52   EXPECT_EQ(config, *config_read);
53   EXPECT_THAT(config_read->GetPersistentSections(), ElementsAre("CC:DD:EE:FF:00:11"));
54   EXPECT_THAT(config_read->GetProperty("A", "B"), Optional(StrEq("C")));
55   EXPECT_THAT(config_read->GetProperty("CC:DD:EE:FF:00:11", "LinkKey"), Optional(StrEq("AABBAABBCCDDEE")));
56 
57   EXPECT_TRUE(std::filesystem::remove(temp_config));
58 }
59 
60 static const std::string kReadTestConfig =
61     "[Info]\n"
62     "FileSource = Empty\n"
63     "TimeCreated = 2020-05-20 01:20:56\n"
64     "\n"
65     "[Metrics]\n"
66     "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
67     "\n"
68     "[Adapter]\n"
69     "Address = 01:02:03:ab:cd:ef\n"
70     "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
71     "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
72     "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
73     "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
74     "ScanMode = 2\n"
75     "DiscoveryTimeout = 120\n"
76     "\n"
77     "[01:02:03:ab:cd:ea]\n"
78     "name = hello world\n"
79     "LinkKey = fedcba0987654321fedcba0987654328\n";
80 
TEST(LegacyConfigFileTest,read_test)81 TEST(LegacyConfigFileTest, read_test) {
82   auto temp_dir = std::filesystem::temp_directory_path();
83   auto temp_config = temp_dir / "temp_config.txt";
84   EXPECT_TRUE(WriteToFile(temp_config.string(), kReadTestConfig));
85 
86   auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
87   EXPECT_TRUE(config_read);
88   EXPECT_THAT(config_read->GetPersistentSections(), ElementsAre("01:02:03:ab:cd:ea"));
89   EXPECT_THAT(config_read->GetProperty("Info", "FileSource"), Optional(StrEq("Empty")));
90   EXPECT_THAT(config_read->GetProperty("Info", "FileSource"), Optional(StrEq("Empty")));
91   EXPECT_THAT(
92       config_read->GetProperty("01:02:03:ab:cd:ea", "LinkKey"), Optional(StrEq("fedcba0987654321fedcba0987654328")));
93 
94   EXPECT_TRUE(std::filesystem::remove(temp_config));
95 }
96 
97 static const std::string kWriteTestConfig =
98     "[Info]\n"
99     "FileSource = Empty\n"
100     "TimeCreated = \n"
101     "\n"
102     "[Adapter]\n"
103     "Address = 01:02:03:ab:cd:ef\n"
104     "\n"
105     "[01:02:03:ab:cd:ea]\n"
106     "name = hello world\n"
107     "LinkKey = fedcba0987654321fedcba0987654328\n"
108     "\n";
109 
TEST(LegacyConfigFileTest,write_test)110 TEST(LegacyConfigFileTest, write_test) {
111   auto temp_dir = std::filesystem::temp_directory_path();
112   auto temp_config = temp_dir / "temp_config.txt";
113 
114   ConfigCache config(100, Device::kLinkKeyProperties);
115   config.SetProperty("Info", "FileSource", "Empty");
116   config.SetProperty("Info", "TimeCreated", "");
117   config.SetProperty("Adapter", "Address", "01:02:03:ab:cd:ef");
118   config.SetProperty("01:02:03:ab:cd:ea", "name", "hello world");
119   config.SetProperty("01:02:03:ab:cd:ea", "LinkKey", "fedcba0987654321fedcba0987654328");
120   EXPECT_TRUE(LegacyConfigFile::FromPath(temp_config.string()).Write(config));
121 
122   EXPECT_THAT(ReadSmallFile(temp_config.string()), Optional(StrEq(kWriteTestConfig)));
123 
124   EXPECT_TRUE(std::filesystem::remove(temp_config));
125 }
126 
127 static const std::string kConfigWithDuplicateSectionAndKey =
128     "                                                                                \n\
129 first_key=value                                                                      \n\
130                                                                                      \n\
131 # Device ID (DID) configuration                                                      \n\
132 [DID]                                                                                \n\
133                                                                                      \n\
134 # Record Number: 1, 2 or 3 - maximum of 3 records                                    \n\
135 recordNumber = 1                                                                     \n\
136                                                                                      \n\
137 # Primary Record - true or false (default)                                           \n\
138 # There can be only one primary record                                               \n\
139 primaryRecord = true                                                                 \n\
140                                                                                      \n\
141 # Vendor ID '0xFFFF' indicates no Device ID Service Record is present in the device  \n\
142 # 0x000F = Broadcom Corporation (default)                                            \n\
143 #vendorId = 0x000F                                                                   \n\
144                                                                                      \n\
145 # Vendor ID Source                                                                   \n\
146 # 0x0001 = Bluetooth SIG assigned Device ID Vendor ID value (default)                \n\
147 # 0x0002 = USB Implementer's Forum assigned Device ID Vendor ID value                \n\
148 #vendorIdSource = 0x0001                                                             \n\
149                                                                                      \n\
150 # Product ID & Product Version                                                       \n\
151 # Per spec DID v1.3 0xJJMN for version is interpreted as JJ.M.N                      \n\
152 # JJ: major version number, M: minor version number, N: sub-minor version number     \n\
153 # For example: 1200, v14.3.6                                                         \n\
154 productId = 0x1200                                                                   \n\
155 version = 0x1111                                                                     \n\
156                                                                                      \n\
157 # Optional attributes                                                                \n\
158 #clientExecutableURL =                                                               \n\
159 #serviceDescription =                                                                \n\
160 #documentationURL =                                                                  \n\
161                                                                                      \n\
162 # Additional optional DID records. Bluedroid supports up to 3 records.               \n\
163 [DID]                                                                                \n\
164 [DID]                                                                                \n\
165 version = 0x1436                                                                     \n\
166                                                                                      \n\
167 HiSyncId = 18446744073709551615                                                      \n\
168 HiSyncId2 = 15001900                                                                 \n\
169 ";
170 
TEST(LegacyConfigFileTest,duplicate_section_and_key_test)171 TEST(LegacyConfigFileTest, duplicate_section_and_key_test) {
172   auto temp_dir = std::filesystem::temp_directory_path();
173   auto temp_config = temp_dir / "temp_config.txt";
174   ASSERT_TRUE(WriteToFile(temp_config.string(), kConfigWithDuplicateSectionAndKey));
175 
176   auto config_read = LegacyConfigFile::FromPath(temp_config.string()).Read(100);
177   ASSERT_TRUE(config_read);
178   EXPECT_THAT(config_read->GetProperty(ConfigCache::kDefaultSectionName, "first_key"), Optional(StrEq("value")));
179   // All sections with the same name merge into the same key-value pair
180   EXPECT_THAT(config_read->GetProperty("DID", "primaryRecord"), Optional(StrEq("true")));
181   // When keys are repeated, the later one wins
182   EXPECT_THAT(config_read->GetProperty("DID", "version"), Optional(StrEq("0x1436")));
183 
184   EXPECT_TRUE(std::filesystem::remove(temp_config));
185 }
186 
187 }  // namespace testing