• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
151 /* clone persistent sections (Local Adapter sections, remote paired devices
152  * section,..) */
PersistentSectionCopy()153 config_t BtifConfigCache::PersistentSectionCopy() {
154   return paired_devices_list_;
155 }
156 
GetPersistentSections()157 const std::list<section_t>& BtifConfigCache::GetPersistentSections() {
158   return paired_devices_list_.sections;
159 }
160 
SetString(std::string section_name,std::string key,std::string value)161 void BtifConfigCache::SetString(std::string section_name, std::string key,
162                                 std::string value) {
163   if (trim_new_line(section_name) || trim_new_line(key) ||
164       trim_new_line(value)) {
165     android_errorWriteLog(0x534e4554, "70808273");
166   }
167   if (section_name.empty()) {
168     LOG(FATAL) << "Empty section not allowed";
169     return;
170   }
171   if (key.empty()) {
172     LOG(FATAL) << "Empty key not allowed";
173     return;
174   }
175   if (!paired_devices_list_.Has(section_name)) {
176     // section is not in paired_device_list, handle it in unpaired devices cache
177     section_t section = {};
178     bool in_unpaired_cache = true;
179     if (!unpaired_devices_cache_.Get(section_name, &section)) {
180       // it's a new unpaired section, add it to unpaired devices cache
181       section.name = section_name;
182       in_unpaired_cache = false;
183     }
184     // set key to value and replace existing key if already exist
185     section.Set(key, value);
186 
187     if (is_local_section_info(section_name) ||
188         (is_link_key(key) && RawAddress::IsValidAddress(section_name))) {
189       // remove this section that has the LinkKey from unpaired devices cache.
190       if (in_unpaired_cache) {
191         unpaired_devices_cache_.Remove(section_name);
192       }
193       // when a unpaired section got the LinkKey, move this section to the
194       // paired devices list
195       paired_devices_list_.sections.emplace_back(std::move(section));
196     } else {
197       // update to the unpaired devices cache
198       unpaired_devices_cache_.Put(section_name, section);
199     }
200   } else {
201     // already have section in paired device list, add key-value entry.
202     auto section_found = paired_devices_list_.Find(section_name);
203     if (section_found == paired_devices_list_.sections.end()) {
204       LOG(WARNING) << __func__ << " , section_found not found!";
205       return;
206     }
207     section_found->Set(key, value);
208   }
209 }
210 
GetString(const std::string & section_name,const std::string & key)211 std::optional<std::string> BtifConfigCache::GetString(
212     const std::string& section_name, const std::string& key) {
213   // Check paired sections first
214   auto section_iter = paired_devices_list_.Find(section_name);
215   if (section_iter != paired_devices_list_.sections.end()) {
216     auto entry_iter = section_iter->Find(key);
217     if (entry_iter == section_iter->entries.end()) {
218       return std::nullopt;
219     }
220     return entry_iter->value;
221   }
222   // Check unpaired sections later
223   section_t section = {};
224   if (!unpaired_devices_cache_.Get(section_name, &section)) {
225     return std::nullopt;
226   }
227   auto entry_iter = section.Find(key);
228   if (entry_iter == section.entries.end()) {
229     return std::nullopt;
230   }
231   return entry_iter->value;
232 }
233 
SetInt(std::string section_name,std::string key,int value)234 void BtifConfigCache::SetInt(std::string section_name, std::string key,
235                              int value) {
236   SetString(std::move(section_name), std::move(key), std::to_string(value));
237 }
238 
GetInt(const std::string & section_name,const std::string & key)239 std::optional<int> BtifConfigCache::GetInt(const std::string& section_name,
240                                            const std::string& key) {
241   auto value = GetString(section_name, key);
242   if (!value) {
243     return std::nullopt;
244   }
245   char* endptr;
246   long ret_long = strtol(value->c_str(), &endptr, 0);
247   if (*endptr != '\0') {
248     LOG(WARNING) << "Failed to parse value to long for section " << section_name
249                  << ", key " << key;
250     return std::nullopt;
251   }
252   if (ret_long >= std::numeric_limits<int>::max()) {
253     LOG(WARNING) << "Integer overflow when parsing value to int for section "
254                  << section_name << ", key " << key;
255     return std::nullopt;
256   }
257   return static_cast<int>(ret_long);
258 }
259 
SetUint64(std::string section_name,std::string key,uint64_t value)260 void BtifConfigCache::SetUint64(std::string section_name, std::string key,
261                                 uint64_t value) {
262   SetString(std::move(section_name), std::move(key), std::to_string(value));
263 }
264 
GetUint64(const std::string & section_name,const std::string & key)265 std::optional<uint64_t> BtifConfigCache::GetUint64(
266     const std::string& section_name, const std::string& key) {
267   auto value = GetString(section_name, key);
268   if (!value) {
269     return std::nullopt;
270   }
271   char* endptr;
272   uint64_t ret = strtoull(value->c_str(), &endptr, 0);
273   if (*endptr != '\0') {
274     LOG(WARNING) << "Failed to parse value to uint64 for section "
275                  << section_name << ", key " << key;
276     return std::nullopt;
277   }
278   return ret;
279 }
280 
SetBool(std::string section_name,std::string key,bool value)281 void BtifConfigCache::SetBool(std::string section_name, std::string key,
282                               bool value) {
283   SetString(std::move(section_name), std::move(key), value ? "true" : "false");
284 }
285 
GetBool(const std::string & section_name,const std::string & key)286 std::optional<bool> BtifConfigCache::GetBool(const std::string& section_name,
287                                              const std::string& key) {
288   auto value = GetString(section_name, key);
289   if (!value) {
290     return std::nullopt;
291   }
292   if (*value == "true") {
293     return true;
294   }
295   if (*value == "false") {
296     return false;
297   }
298   LOG(WARNING) << "Failed to parse value to boolean for section "
299                << section_name << ", key " << key;
300   return std::nullopt;
301 }
302