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, §ion)) {
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, §ion)) {
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