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