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