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 = §ion_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 = §ion_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