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 "btif_config_cache.h"
18
19 #include <limits>
20 #include <vector>
21
22 #include "stack/include/bt_octets.h"
23 #include "types/raw_address.h"
24
25 #include <base/logging.h>
26
27 namespace {
28
29 const std::unordered_set<std::string> kLinkKeyTypes = {
30 "LinkKey", "LE_KEY_PENC", "LE_KEY_PID",
31 "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
32
33 const std::unordered_set<std::string> kLocalSectionNames = {"Info", "Metrics",
34 "Adapter"};
35
is_link_key(const std::string & key)36 bool is_link_key(const std::string& key) {
37 return kLinkKeyTypes.find(key) != kLinkKeyTypes.end();
38 }
39
has_link_key_in_section(const section_t & section)40 bool has_link_key_in_section(const section_t& section) {
41 for (const auto& entry : section.entries) {
42 if (is_link_key(entry.key)) {
43 return true;
44 }
45 }
46 return false;
47 }
48
is_local_section_info(const std::string & section)49 bool is_local_section_info(const std::string& section) {
50 return kLocalSectionNames.find(section) != kLocalSectionNames.end();
51 }
52
53 // trim new line in place, return true if newline was found
trim_new_line(std::string & value)54 bool trim_new_line(std::string& value) {
55 size_t newline_position = value.find_first_of('\n');
56 if (newline_position != std::string::npos) {
57 value.erase(newline_position);
58 return true;
59 }
60 return false;
61 }
62
63 } // namespace
64
BtifConfigCache(size_t capacity)65 BtifConfigCache::BtifConfigCache(size_t capacity)
66 : unpaired_devices_cache_(capacity, "bt_config_cache") {
67 LOG(INFO) << __func__ << ", capacity: " << capacity;
68 }
69
~BtifConfigCache()70 BtifConfigCache::~BtifConfigCache() { Clear(); }
71
Clear()72 void BtifConfigCache::Clear() {
73 unpaired_devices_cache_.Clear();
74 paired_devices_list_.sections.clear();
75 }
76
Init(std::unique_ptr<config_t> source)77 void BtifConfigCache::Init(std::unique_ptr<config_t> source) {
78 // get the config persistent data from btif_config file
79 paired_devices_list_ = std::move(*source);
80 source.reset();
81 }
82
HasPersistentSection(const std::string & section_name)83 bool BtifConfigCache::HasPersistentSection(const std::string& section_name) {
84 return paired_devices_list_.Find(section_name) !=
85 paired_devices_list_.sections.end();
86 }
87
HasUnpairedSection(const std::string & section_name)88 bool BtifConfigCache::HasUnpairedSection(const std::string& section_name) {
89 return unpaired_devices_cache_.HasKey(section_name);
90 }
91
HasSection(const std::string & section_name)92 bool BtifConfigCache::HasSection(const std::string& section_name) {
93 return HasUnpairedSection(section_name) || HasPersistentSection(section_name);
94 }
95
HasKey(const std::string & section_name,const std::string & key)96 bool BtifConfigCache::HasKey(const std::string& section_name,
97 const std::string& key) {
98 auto section_iter = paired_devices_list_.Find(section_name);
99 if (section_iter != paired_devices_list_.sections.end()) {
100 return section_iter->Has(key);
101 }
102 section_t* section = unpaired_devices_cache_.Find(section_name);
103 if (section == nullptr) {
104 return false;
105 }
106 return section->Has(key);
107 }
108
109 // remove sections with the restricted key
RemovePersistentSectionsWithKey(const std::string & key)110 void BtifConfigCache::RemovePersistentSectionsWithKey(const std::string& key) {
111 for (auto it = paired_devices_list_.sections.begin();
112 it != paired_devices_list_.sections.end();) {
113 if (it->Has(key)) {
114 it = paired_devices_list_.sections.erase(it);
115 continue;
116 }
117 it++;
118 }
119 }
120
121 /* remove a key from section, section itself is removed when empty */
RemoveKey(const std::string & section_name,const std::string & key)122 bool BtifConfigCache::RemoveKey(const std::string& section_name,
123 const std::string& key) {
124 section_t* section = unpaired_devices_cache_.Find(section_name);
125 if (section != nullptr) {
126 auto entry_iter = section->Find(key);
127 if (entry_iter == section->entries.end()) {
128 return false;
129 }
130 section->entries.erase(entry_iter);
131 if (section->entries.empty()) {
132 unpaired_devices_cache_.Remove(section_name);
133 }
134 return true;
135 } else {
136 auto section_iter = paired_devices_list_.Find(section_name);
137 if (section_iter == paired_devices_list_.sections.end()) {
138 return false;
139 }
140 auto entry_iter = section_iter->Find(key);
141 if (entry_iter == section_iter->entries.end()) {
142 return false;
143 }
144 section_iter->entries.erase(entry_iter);
145 if (section_iter->entries.empty()) {
146 paired_devices_list_.sections.erase(section_iter);
147 } else if (!has_link_key_in_section(*section_iter)) {
148 // if no link key in section after removal, move it to unpaired section
149 auto moved_section = std::move(*section_iter);
150 paired_devices_list_.sections.erase(section_iter);
151 unpaired_devices_cache_.Put(section_name, std::move(moved_section));
152 }
153 return true;
154 }
155 }
156
GetPersistentSectionNames()157 std::vector<std::string> BtifConfigCache::GetPersistentSectionNames() {
158 std::vector<std::string> result;
159 result.reserve(paired_devices_list_.sections.size());
160 for (const auto& section : paired_devices_list_.sections) {
161 result.emplace_back(section.name);
162 }
163 return result;
164 }
165
166 /* clone persistent sections (Local Adapter sections, remote paired devices
167 * section,..) */
PersistentSectionCopy()168 config_t BtifConfigCache::PersistentSectionCopy() {
169 return paired_devices_list_;
170 }
171
SetString(std::string section_name,std::string key,std::string value)172 void BtifConfigCache::SetString(std::string section_name, std::string key,
173 std::string value) {
174 trim_new_line(section_name);
175 trim_new_line(key);
176 trim_new_line(value);
177 if (section_name.empty()) {
178 LOG(FATAL) << "Empty section not allowed";
179 return;
180 }
181 if (key.empty()) {
182 LOG(FATAL) << "Empty key not allowed";
183 return;
184 }
185 if (!paired_devices_list_.Has(section_name)) {
186 // section is not in paired_device_list, handle it in unpaired devices cache
187 section_t section = {};
188 bool in_unpaired_cache = true;
189 if (!unpaired_devices_cache_.Get(section_name, §ion)) {
190 // it's a new unpaired section, add it to unpaired devices cache
191 section.name = section_name;
192 in_unpaired_cache = false;
193 }
194 // set key to value and replace existing key if already exist
195 section.Set(key, value);
196
197 if (is_local_section_info(section_name) ||
198 (is_link_key(key) && RawAddress::IsValidAddress(section_name))) {
199 // remove this section that has the LinkKey from unpaired devices cache.
200 if (in_unpaired_cache) {
201 unpaired_devices_cache_.Remove(section_name);
202 }
203 // when a unpaired section got the LinkKey, move this section to the
204 // paired devices list
205 paired_devices_list_.sections.emplace_back(std::move(section));
206 } else {
207 // update to the unpaired devices cache
208 unpaired_devices_cache_.Put(section_name, section);
209 }
210 } else {
211 // already have section in paired device list, add key-value entry.
212 auto section_found = paired_devices_list_.Find(section_name);
213 if (section_found == paired_devices_list_.sections.end()) {
214 LOG(WARNING) << __func__ << " , section_found not found!";
215 return;
216 }
217 section_found->Set(key, value);
218 }
219 }
220
GetString(const std::string & section_name,const std::string & key)221 std::optional<std::string> BtifConfigCache::GetString(
222 const std::string& section_name, const std::string& key) {
223 // Check paired sections first
224 auto section_iter = paired_devices_list_.Find(section_name);
225 if (section_iter != paired_devices_list_.sections.end()) {
226 auto entry_iter = section_iter->Find(key);
227 if (entry_iter == section_iter->entries.end()) {
228 return std::nullopt;
229 }
230 return entry_iter->value;
231 }
232 // Check unpaired sections later
233 section_t section = {};
234 if (!unpaired_devices_cache_.Get(section_name, §ion)) {
235 return std::nullopt;
236 }
237 auto entry_iter = section.Find(key);
238 if (entry_iter == section.entries.end()) {
239 return std::nullopt;
240 }
241 return entry_iter->value;
242 }
243
SetInt(std::string section_name,std::string key,int value)244 void BtifConfigCache::SetInt(std::string section_name, std::string key,
245 int value) {
246 SetString(std::move(section_name), std::move(key), std::to_string(value));
247 }
248
GetInt(const std::string & section_name,const std::string & key)249 std::optional<int> BtifConfigCache::GetInt(const std::string& section_name,
250 const std::string& key) {
251 auto value = GetString(section_name, key);
252 if (!value) {
253 return std::nullopt;
254 }
255 char* endptr;
256 long ret_long = strtol(value->c_str(), &endptr, 0);
257 if (*endptr != '\0') {
258 LOG(WARNING) << "Failed to parse value to long for section " << section_name
259 << ", key " << key;
260 return std::nullopt;
261 }
262 if (ret_long >= std::numeric_limits<int>::max()) {
263 LOG(WARNING) << "Integer overflow when parsing value to int for section "
264 << section_name << ", key " << key;
265 return std::nullopt;
266 }
267 return static_cast<int>(ret_long);
268 }
269
SetUint64(std::string section_name,std::string key,uint64_t value)270 void BtifConfigCache::SetUint64(std::string section_name, std::string key,
271 uint64_t value) {
272 SetString(std::move(section_name), std::move(key), std::to_string(value));
273 }
274
GetUint64(const std::string & section_name,const std::string & key)275 std::optional<uint64_t> BtifConfigCache::GetUint64(
276 const std::string& section_name, const std::string& key) {
277 auto value = GetString(section_name, key);
278 if (!value) {
279 return std::nullopt;
280 }
281 char* endptr;
282 uint64_t ret = strtoull(value->c_str(), &endptr, 0);
283 if (*endptr != '\0') {
284 LOG(WARNING) << "Failed to parse value to uint64 for section "
285 << section_name << ", key " << key;
286 return std::nullopt;
287 }
288 return ret;
289 }
290
SetBool(std::string section_name,std::string key,bool value)291 void BtifConfigCache::SetBool(std::string section_name, std::string key,
292 bool value) {
293 SetString(std::move(section_name), std::move(key), value ? "true" : "false");
294 }
295
GetBool(const std::string & section_name,const std::string & key)296 std::optional<bool> BtifConfigCache::GetBool(const std::string& section_name,
297 const std::string& key) {
298 auto value = GetString(section_name, key);
299 if (!value) {
300 return std::nullopt;
301 }
302 if (*value == "true") {
303 return true;
304 }
305 if (*value == "false") {
306 return false;
307 }
308 LOG(WARNING) << "Failed to parse value to boolean for section "
309 << section_name << ", key " << key;
310 return std::nullopt;
311 }
312