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/config_cache.h"
18 
19 #include <ios>
20 #include <sstream>
21 #include <utility>
22 
23 #include "hci/enum_helper.h"
24 #include "storage/mutation.h"
25 
26 namespace {
27 
TrimAfterNewLine(std::string & value)28 bool TrimAfterNewLine(std::string& value) {
29   std::string value_no_newline;
30   size_t newline_position = value.find_first_of('\n');
31   if (newline_position != std::string::npos) {
32     value.erase(newline_position);
33     return true;
34   }
35   return false;
36 }
37 
38 }  // namespace
39 
40 namespace bluetooth {
41 namespace storage {
42 
43 const std::unordered_set<std::string_view> kLePropertyNames = {
44     "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
45 
46 const std::unordered_set<std::string_view> kClassicPropertyNames = {
47     "LinkKey", "SdpDiMaufacturer", "SdpDiModel", "SdpDiHardwareVersion", "SdpDiVendorSource"};
48 
49 const std::string ConfigCache::kDefaultSectionName = "Global";
50 
ConfigCache(size_t temp_device_capacity,std::unordered_set<std::string_view> persistent_property_names)51 ConfigCache::ConfigCache(size_t temp_device_capacity, std::unordered_set<std::string_view> persistent_property_names)
52     : persistent_property_names_(std::move(persistent_property_names)),
53       information_sections_(),
54       persistent_devices_(),
55       temporary_devices_(temp_device_capacity) {}
56 
SetPersistentConfigChangedCallback(std::function<void ()> persistent_config_changed_callback)57 void ConfigCache::SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback) {
58   std::lock_guard<std::recursive_mutex> lock(mutex_);
59   persistent_config_changed_callback_ = std::move(persistent_config_changed_callback);
60 }
61 
ConfigCache(ConfigCache && other)62 ConfigCache::ConfigCache(ConfigCache&& other) noexcept
63     : persistent_config_changed_callback_(std::move(other.persistent_config_changed_callback_)),
64       persistent_property_names_(std::move(other.persistent_property_names_)),
65       information_sections_(std::move(other.information_sections_)),
66       persistent_devices_(std::move(other.persistent_devices_)),
67       temporary_devices_(std::move(other.temporary_devices_)) {
68   // std::function will be in a valid but unspecified state after std::move(), hence resetting it
69   other.persistent_config_changed_callback_ = {};
70 }
71 
operator =(ConfigCache && other)72 ConfigCache& ConfigCache::operator=(ConfigCache&& other) noexcept {
73   if (&other == this) {
74     return *this;
75   }
76   std::lock_guard<std::recursive_mutex> my_lock(mutex_);
77   std::lock_guard<std::recursive_mutex> others_lock(other.mutex_);
78   persistent_config_changed_callback_.swap(other.persistent_config_changed_callback_);
79   other.persistent_config_changed_callback_ = {};
80   persistent_property_names_ = std::move(other.persistent_property_names_);
81   information_sections_ = std::move(other.information_sections_);
82   persistent_devices_ = std::move(other.persistent_devices_);
83   temporary_devices_ = std::move(other.temporary_devices_);
84   return *this;
85 }
86 
operator ==(const ConfigCache & rhs) const87 bool ConfigCache::operator==(const ConfigCache& rhs) const {
88   std::lock_guard<std::recursive_mutex> my_lock(mutex_);
89   std::lock_guard<std::recursive_mutex> others_lock(rhs.mutex_);
90   return persistent_property_names_ == rhs.persistent_property_names_ &&
91          information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ &&
92          temporary_devices_ == rhs.temporary_devices_;
93 }
94 
operator !=(const ConfigCache & rhs) const95 bool ConfigCache::operator!=(const ConfigCache& rhs) const {
96   return !(*this == rhs);
97 }
98 
Clear()99 void ConfigCache::Clear() {
100   std::lock_guard<std::recursive_mutex> lock(mutex_);
101   if (information_sections_.size() > 0) {
102     information_sections_.clear();
103     PersistentConfigChangedCallback();
104   }
105   if (persistent_devices_.size() > 0) {
106     persistent_devices_.clear();
107     PersistentConfigChangedCallback();
108   }
109   if (temporary_devices_.size() > 0) {
110     temporary_devices_.clear();
111   }
112 }
113 
HasSection(const std::string & section) const114 bool ConfigCache::HasSection(const std::string& section) const {
115   std::lock_guard<std::recursive_mutex> lock(mutex_);
116   return information_sections_.contains(section) || persistent_devices_.contains(section) ||
117          temporary_devices_.contains(section);
118 }
119 
HasProperty(const std::string & section,const std::string & property) const120 bool ConfigCache::HasProperty(const std::string& section, const std::string& property) const {
121   std::lock_guard<std::recursive_mutex> lock(mutex_);
122   auto section_iter = information_sections_.find(section);
123   if (section_iter != information_sections_.end()) {
124     return section_iter->second.find(property) != section_iter->second.end();
125   }
126   section_iter = persistent_devices_.find(section);
127   if (section_iter != persistent_devices_.end()) {
128     return section_iter->second.find(property) != section_iter->second.end();
129   }
130   section_iter = temporary_devices_.find(section);
131   if (section_iter != temporary_devices_.end()) {
132     return section_iter->second.find(property) != section_iter->second.end();
133   }
134   return false;
135 }
136 
GetProperty(const std::string & section,const std::string & property) const137 std::optional<std::string> ConfigCache::GetProperty(const std::string& section, const std::string& property) const {
138   std::lock_guard<std::recursive_mutex> lock(mutex_);
139   auto section_iter = information_sections_.find(section);
140   if (section_iter != information_sections_.end()) {
141     auto property_iter = section_iter->second.find(property);
142     if (property_iter != section_iter->second.end()) {
143       return property_iter->second;
144     }
145   }
146   section_iter = persistent_devices_.find(section);
147   if (section_iter != persistent_devices_.end()) {
148     auto property_iter = section_iter->second.find(property);
149     if (property_iter != section_iter->second.end()) {
150       return property_iter->second;
151     }
152   }
153   section_iter = temporary_devices_.find(section);
154   if (section_iter != temporary_devices_.end()) {
155     auto property_iter = section_iter->second.find(property);
156     if (property_iter != section_iter->second.end()) {
157       return property_iter->second;
158     }
159   }
160   return std::nullopt;
161 }
162 
SetProperty(std::string section,std::string property,std::string value)163 void ConfigCache::SetProperty(std::string section, std::string property, std::string value) {
164   std::lock_guard<std::recursive_mutex> lock(mutex_);
165   if (TrimAfterNewLine(section) || TrimAfterNewLine(property) || TrimAfterNewLine(value)) {
166     android_errorWriteLog(0x534e4554, "70808273");
167   }
168   ASSERT_LOG(!section.empty(), "Empty section name not allowed");
169   ASSERT_LOG(!property.empty(), "Empty property name not allowed");
170   if (!IsDeviceSection(section)) {
171     auto section_iter = information_sections_.find(section);
172     if (section_iter == information_sections_.end()) {
173       section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
174     }
175     section_iter->second.insert_or_assign(property, std::move(value));
176     PersistentConfigChangedCallback();
177     return;
178   }
179   auto section_iter = persistent_devices_.find(section);
180   if (section_iter == persistent_devices_.end() && IsPersistentProperty(property)) {
181     // move paired devices or create new paired device when a link key is set
182     auto section_properties = temporary_devices_.extract(section);
183     if (section_properties) {
184       section_iter = persistent_devices_.try_emplace_back(section, std::move(section_properties->second)).first;
185     } else {
186       section_iter = persistent_devices_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
187     }
188   }
189   if (section_iter != persistent_devices_.end()) {
190     section_iter->second.insert_or_assign(property, std::move(value));
191     PersistentConfigChangedCallback();
192     return;
193   }
194   section_iter = temporary_devices_.find(section);
195   if (section_iter == temporary_devices_.end()) {
196     auto triple = temporary_devices_.try_emplace(section, common::ListMap<std::string, std::string>{});
197     section_iter = std::get<0>(triple);
198   }
199   section_iter->second.insert_or_assign(property, std::move(value));
200 }
201 
RemoveSection(const std::string & section)202 bool ConfigCache::RemoveSection(const std::string& section) {
203   std::lock_guard<std::recursive_mutex> lock(mutex_);
204   // sections are unique among all three maps, hence removing from one of them is enough
205   if (information_sections_.extract(section) || persistent_devices_.extract(section)) {
206     PersistentConfigChangedCallback();
207     return true;
208   } else {
209     return temporary_devices_.extract(section).has_value();
210   }
211 }
212 
RemoveProperty(const std::string & section,const std::string & property)213 bool ConfigCache::RemoveProperty(const std::string& section, const std::string& property) {
214   std::lock_guard<std::recursive_mutex> lock(mutex_);
215   auto section_iter = information_sections_.find(section);
216   if (section_iter != information_sections_.end()) {
217     auto value = section_iter->second.extract(property);
218     // if section is empty after removal, remove the whole section as empty section is not allowed
219     if (section_iter->second.size() == 0) {
220       information_sections_.erase(section_iter);
221     }
222     if (value.has_value()) {
223       PersistentConfigChangedCallback();
224       return true;
225     } else {
226       return false;
227     }
228   }
229   section_iter = persistent_devices_.find(section);
230   if (section_iter != persistent_devices_.end()) {
231     auto value = section_iter->second.extract(property);
232     // if section is empty after removal, remove the whole section as empty section is not allowed
233     if (section_iter->second.size() == 0) {
234       persistent_devices_.erase(section_iter);
235     } else if (value && IsPersistentProperty(property)) {
236       // move unpaired device
237       auto section_properties = persistent_devices_.extract(section);
238       temporary_devices_.insert_or_assign(section, std::move(section_properties->second));
239     }
240     if (value.has_value()) {
241       PersistentConfigChangedCallback();
242       return true;
243     } else {
244       return false;
245     }
246   }
247   section_iter = temporary_devices_.find(section);
248   if (section_iter != temporary_devices_.end()) {
249     auto value = section_iter->second.extract(property);
250     if (section_iter->second.size() == 0) {
251       temporary_devices_.erase(section_iter);
252     }
253     return value.has_value();
254   }
255   return false;
256 }
257 
IsDeviceSection(const std::string & section)258 bool ConfigCache::IsDeviceSection(const std::string& section) {
259   return hci::Address::IsValidAddress(section);
260 }
261 
IsPersistentProperty(const std::string & property) const262 bool ConfigCache::IsPersistentProperty(const std::string& property) const {
263   return persistent_property_names_.find(property) != persistent_property_names_.end();
264 }
265 
RemoveSectionWithProperty(const std::string & property)266 void ConfigCache::RemoveSectionWithProperty(const std::string& property) {
267   std::lock_guard<std::recursive_mutex> lock(mutex_);
268   size_t num_persistent_removed = 0;
269   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
270     for (auto it = config_section->begin(); it != config_section->end();) {
271       if (it->second.contains(property)) {
272         LOG_DEBUG("Removing persistent section %s with property %s", it->first.c_str(), property.c_str());
273         it = config_section->erase(it);
274         num_persistent_removed++;
275         continue;
276       }
277       it++;
278     }
279   }
280   for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) {
281     if (it->second.contains(property)) {
282       LOG_DEBUG("Removing temporary section %s with property %s", it->first.c_str(), property.c_str());
283       it = temporary_devices_.erase(it);
284       continue;
285     }
286     it++;
287   }
288   if (num_persistent_removed > 0) {
289     PersistentConfigChangedCallback();
290   }
291 }
292 
GetPersistentSections() const293 std::vector<std::string> ConfigCache::GetPersistentSections() const {
294   std::lock_guard<std::recursive_mutex> lock(mutex_);
295   std::vector<std::string> paired_devices;
296   paired_devices.reserve(persistent_devices_.size());
297   for (const auto& elem : persistent_devices_) {
298     paired_devices.emplace_back(elem.first);
299   }
300   return paired_devices;
301 }
302 
Commit(std::queue<MutationEntry> & mutation_entries)303 void ConfigCache::Commit(std::queue<MutationEntry>& mutation_entries) {
304   std::lock_guard<std::recursive_mutex> lock(mutex_);
305   while (!mutation_entries.empty()) {
306     auto entry = std::move(mutation_entries.front());
307     mutation_entries.pop();
308     switch (entry.entry_type) {
309       case MutationEntry::EntryType::SET:
310         SetProperty(std::move(entry.section), std::move(entry.property), std::move(entry.value));
311         break;
312       case MutationEntry::EntryType::REMOVE_PROPERTY:
313         RemoveProperty(entry.section, entry.property);
314         break;
315       case MutationEntry::EntryType::REMOVE_SECTION:
316         RemoveSection(entry.section);
317         break;
318         // do not write a default case so that when a new enum is defined, compilation would fail automatically
319     }
320   }
321 }
322 
SerializeToLegacyFormat() const323 std::string ConfigCache::SerializeToLegacyFormat() const {
324   std::lock_guard<std::recursive_mutex> lock(mutex_);
325   std::stringstream serialized;
326   for (const auto* config_section : {&information_sections_, &persistent_devices_}) {
327     for (const auto& section : *config_section) {
328       serialized << "[" << section.first << "]" << std::endl;
329       for (const auto& property : section.second) {
330         serialized << property.first << " = " << property.second << std::endl;
331       }
332       serialized << std::endl;
333     }
334   }
335   return serialized.str();
336 }
337 
GetSectionNamesWithProperty(const std::string & property) const338 std::vector<ConfigCache::SectionAndPropertyValue> ConfigCache::GetSectionNamesWithProperty(
339     const std::string& property) const {
340   std::lock_guard<std::recursive_mutex> lock(mutex_);
341   std::vector<SectionAndPropertyValue> result;
342   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
343     for (const auto& elem : *config_section) {
344       auto it = elem.second.find(property);
345       if (it != elem.second.end()) {
346         result.emplace_back(SectionAndPropertyValue{.section = elem.first, .property = it->second});
347         continue;
348       }
349     }
350   }
351   for (const auto& elem : temporary_devices_) {
352     auto it = elem.second.find(property);
353     if (it != elem.second.end()) {
354       result.emplace_back(SectionAndPropertyValue{.section = elem.first, .property = it->second});
355       continue;
356     }
357   }
358   return result;
359 }
360 
361 namespace {
362 
FixDeviceTypeInconsistencyInSection(const std::string & section_name,common::ListMap<std::string,std::string> & device_section_entries)363 bool FixDeviceTypeInconsistencyInSection(
364     const std::string& section_name, common::ListMap<std::string, std::string>& device_section_entries) {
365   if (!hci::Address::IsValidAddress(section_name)) {
366     return false;
367   }
368   bool is_le = false;
369   bool is_classic = false;
370   // default
371   hci::DeviceType device_type = hci::DeviceType::BR_EDR;
372   for (const auto& entry : device_section_entries) {
373     if (kLePropertyNames.find(entry.first) != kLePropertyNames.end()) {
374       is_le = true;
375     }
376     if (kClassicPropertyNames.find(entry.first) != kClassicPropertyNames.end()) {
377       is_classic = true;
378     }
379   }
380   if (is_classic && is_le) {
381     device_type = hci::DeviceType::DUAL;
382   } else if (is_classic) {
383     device_type = hci::DeviceType::BR_EDR;
384   } else if (is_le) {
385     device_type = hci::DeviceType::LE;
386   }
387   bool inconsistent = true;
388   std::string device_type_str = std::to_string(device_type);
389   auto it = device_section_entries.find("DevType");
390   if (it != device_section_entries.end()) {
391     inconsistent = device_type_str != it->second;
392     if (inconsistent) {
393       it->second = std::move(device_type_str);
394     }
395   } else {
396     device_section_entries.insert_or_assign("DevType", std::move(device_type_str));
397   }
398   return inconsistent;
399 }
400 
401 }  // namespace
402 
FixDeviceTypeInconsistencies()403 bool ConfigCache::FixDeviceTypeInconsistencies() {
404   std::lock_guard<std::recursive_mutex> lock(mutex_);
405   bool persistent_device_changed = false;
406   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
407     for (auto& elem : *config_section) {
408       if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
409         persistent_device_changed = true;
410       }
411     }
412   }
413   bool temp_device_changed = false;
414   for (auto& elem : temporary_devices_) {
415     if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
416       temp_device_changed = true;
417     }
418   }
419   if (persistent_device_changed) {
420     PersistentConfigChangedCallback();
421   }
422   return persistent_device_changed || temp_device_changed;
423 }
424 
HasAtLeastOneMatchingPropertiesInSection(const std::string & section,const std::unordered_set<std::string_view> & property_names) const425 bool ConfigCache::HasAtLeastOneMatchingPropertiesInSection(
426     const std::string& section, const std::unordered_set<std::string_view>& property_names) const {
427   std::lock_guard<std::recursive_mutex> lock(mutex_);
428   const common::ListMap<std::string, std::string>* section_ptr;
429   if (!IsDeviceSection(section)) {
430     auto section_iter = information_sections_.find(section);
431     if (section_iter == information_sections_.end()) {
432       return false;
433     }
434     section_ptr = &section_iter->second;
435   } else {
436     auto section_iter = persistent_devices_.find(section);
437     if (section_iter == persistent_devices_.end()) {
438       section_iter = temporary_devices_.find(section);
439       if (section_iter == temporary_devices_.end()) {
440         return false;
441       }
442     }
443     section_ptr = &section_iter->second;
444   }
445   for (const auto& property : *section_ptr) {
446     if (property_names.count(property.first) > 0) {
447       return true;
448     }
449   }
450   return false;
451 }
452 
IsPersistentSection(const std::string & section) const453 bool ConfigCache::IsPersistentSection(const std::string& section) const {
454   std::lock_guard<std::recursive_mutex> lock(mutex_);
455   return persistent_devices_.contains(section);
456 }
457 
458 }  // namespace storage
459 }  // namespace bluetooth