1 /******************************************************************************
2 *
3 * Copyright (C) 2011-2012 Broadcom Corporation
4 * Copyright 2018-2019, 2023-2024 NXP
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************************/
19 //#define LOG_NDEBUG 0
20 #define LOG_TAG "NxpUwbConf"
21
22 #include <filesystem>
23 #include <limits.h>
24 #include <sys/stat.h>
25
26 #include <charconv>
27 #include <cinttypes>
28 #include <cstddef>
29 #include <cstdint>
30 #include <iomanip>
31 #include <list>
32 #include <memory>
33 #include <optional>
34 #include <span>
35 #include <sstream>
36 #include <sstream>
37 #include <string>
38 #include <string_view>
39 #include <unordered_map>
40 #include <unordered_set>
41 #include <vector>
42
43 #include <android-base/logging.h>
44 #include <cutils/properties.h>
45 #include <log/log.h>
46
47 #include "phNxpConfig.h"
48 #include "phNxpUciHal.h"
49 #include "phNxpUciHal_ext.h"
50 #include "phNxpUciHal_utils.h"
51 #include "phNxpLog.h"
52
53 namespace {
54
55 constexpr std::string_view default_nxp_config_path = "/vendor/etc/libuwb-nxp.conf";
56 constexpr std::string_view country_code_config_name = "libuwb-countrycode.conf";
57 constexpr std::string_view nxp_uci_config_file = "libuwb-uci.conf";
58 constexpr std::string_view default_uci_config_path = "/vendor/etc/";
59 constexpr std::string_view factory_file_prefix = "cal-factory";
60
61 constexpr std::string_view country_code_specifier = "<country>";
62 constexpr std::string_view sku_specifier = "<sku>";
63 constexpr std::string_view extid_specifier = "<extid>";
64 constexpr std::string_view revision_specifier = "<revision>";
65
66 constexpr std::string_view extid_config_name = "cal.extid";
67 constexpr std::string_view extid_default_value = "defaultextid";
68
69 constexpr char prop_name_calsku[] = "persist.vendor.uwb.cal.sku";
70 constexpr char prop_default_calsku[] = "defaultsku";
71
72 constexpr char prop_name_revision[] = "persist.vendor.uwb.cal.revision";
73 constexpr char prop_default_revision[] = "defaultrevision";
74
75 class uwbParam
76 {
77 public:
78 enum class type { STRING, NUMBER, BYTEARRAY, STRINGARRAY };
79
uwbParam()80 uwbParam() : m_numValue(0), m_type(type::NUMBER) {}
81
82 // only movable.
83 uwbParam(const uwbParam ¶m) = delete;
84
uwbParam(uwbParam && param)85 uwbParam(uwbParam &¶m) :
86 m_numValue(param.m_numValue),
87 m_str_value(std::move(param.m_str_value)),
88 m_arrValue(std::move(param.m_arrValue)),
89 m_arrStrValue(std::move(param.m_arrStrValue)),
90 m_type(param.m_type) {}
91
uwbParam(const std::string & value)92 uwbParam(const std::string& value) :
93 m_numValue(0),
94 m_str_value(value),
95 m_type(type::STRING) {}
96
uwbParam(uint64_t value)97 uwbParam(uint64_t value) :
98 m_numValue(value),
99 m_type(type::NUMBER) {}
100
uwbParam(std::vector<uint8_t> && value)101 uwbParam(std::vector<uint8_t> &&value) :
102 m_arrValue(std::move(value)),
103 m_type(type::BYTEARRAY) {}
104
uwbParam(std::vector<std::string> && value)105 uwbParam(std::vector<std::string> &&value) :
106 m_arrStrValue(std::move(value)),
107 m_type(type::STRINGARRAY) {}
108
getType() const109 type getType() const { return m_type; }
110
numValue() const111 uint64_t numValue() const { return m_numValue; }
112
str_value() const113 std::string_view str_value() const { return m_str_value; }
114
arr_value() const115 std::span<const uint8_t> arr_value() const { return m_arrValue; }
116
str_arr_value() const117 std::vector<std::string> str_arr_value() const { return m_arrStrValue; }
118
dump(const std::string & tag) const119 void dump(const std::string &tag) const {
120 if (m_type == type::NUMBER) {
121 ALOGV(" - %s = 0x%" PRIx64, tag.c_str(), m_numValue);
122 } else if (m_type == type::STRING) {
123 ALOGV(" - %s = %s", tag.c_str(), m_str_value.c_str());
124 } else if (m_type == type::BYTEARRAY) {
125 std::stringstream ss_hex;
126 ss_hex.fill('0');
127 for (auto b : m_arrValue) {
128 ss_hex << std::setw(2) << std::hex << (int)b << " ";
129 }
130 ALOGV(" - %s = { %s}", tag.c_str(), ss_hex.str().c_str());
131 } else if (m_type == type::STRINGARRAY) {
132 std::stringstream ss;
133 for (auto s : m_arrStrValue) {
134 ss << "\"" << s << "\", ";
135 }
136 ALOGV(" - %s = { %s}", tag.c_str(), ss.str().c_str());
137 }
138 }
139 private:
140 uint64_t m_numValue;
141 std::string m_str_value;
142 std::vector<uint8_t> m_arrValue;
143 std::vector<std::string> m_arrStrValue;
144 type m_type;
145 };
146
147 class CUwbNxpConfig
148 {
149 public:
150 using HashType = std::unordered_map<std::string, uwbParam>;
151
152 CUwbNxpConfig();
153 CUwbNxpConfig(std::string_view filepath);
154
155 // only movable
156 CUwbNxpConfig(CUwbNxpConfig&& config);
157 CUwbNxpConfig& operator=(CUwbNxpConfig&& config);
158
159 CUwbNxpConfig(CUwbNxpConfig& config) = delete;
160 CUwbNxpConfig& operator=(CUwbNxpConfig& config) = delete;
161
162 virtual ~CUwbNxpConfig();
163
isValid() const164 bool isValid() const { return mValidFile; }
isFactory() const165 bool isFactory() const { return mFactoryFile; }
reset()166 void reset() {
167 m_map.clear();
168 mValidFile = false;
169 }
170
171 const uwbParam* find(std::string_view key) const;
172 void setCountry(const std::string& strCountry);
getFilePath() const173 const char* getFilePath() const {
174 return mFilePath.c_str();
175 }
176
177 void dump() const;
178
get_data() const179 const HashType& get_data() const {
180 return m_map;
181 }
182
183 private:
184 bool readConfig();
185
186 std::filesystem::path mFilePath;
187 bool mFactoryFile = false;
188 bool mValidFile = false;
189
190 HashType m_map;
191 };
192
193 /*******************************************************************************
194 **
195 ** Function: isPrintable()
196 **
197 ** Description: determine if 'c' is printable
198 **
199 ** Returns: 1, if printable, otherwise 0
200 **
201 *******************************************************************************/
isPrintable(char c)202 static inline bool isPrintable(char c)
203 {
204 return (c >= 'A' && c <= 'Z') ||
205 (c >= 'a' && c <= 'z') ||
206 (c >= '0' && c <= '9') ||
207 c == '/' || c == '_' || c == '-' || c == '.' || c == ',';
208 }
209
210 /*******************************************************************************
211 **
212 ** Function: isDigit()
213 **
214 ** Description: determine if 'c' is numeral digit
215 **
216 ** Returns: true, if numerical digit
217 **
218 *******************************************************************************/
isDigit(char c,int base)219 static inline bool isDigit(char c, int base)
220 {
221 if (base == 10) {
222 return isdigit(c);
223 } else if (base == 16) {
224 return isxdigit(c);
225 } else {
226 return false;
227 }
228 }
229
isArrayDelimeter(char c)230 static inline bool isArrayDelimeter(char c)
231 {
232 return (isspace(c) || c== ',' || c == ':' || c == '-' || c == '}');
233 }
234
235 /*******************************************************************************
236 **
237 ** Function: getDigitValue()
238 **
239 ** Description: return numerical value of a decimal or hex char
240 **
241 ** Returns: numerical value if decimal or hex char, otherwise 0
242 **
243 *******************************************************************************/
getDigitValue(char c,int base)244 inline int getDigitValue(char c, int base)
245 {
246 if ('0' <= c && c <= '9')
247 return c - '0';
248 if (base == 16)
249 {
250 if ('A' <= c && c <= 'F')
251 return c - 'A' + 10;
252 else if ('a' <= c && c <= 'f')
253 return c - 'a' + 10;
254 }
255 return 0;
256 }
257
258 /*******************************************************************************
259 **
260 ** Function: CUwbNxpConfig::readConfig()
261 **
262 ** Description: read Config settings and parse them into a linked list
263 ** move the element from linked list to a array at the end
264 **
265 ** Returns: 1, if there are any config data, 0 otherwise
266 **
267 *******************************************************************************/
readConfig()268 bool CUwbNxpConfig::readConfig()
269 {
270 enum {
271 BEGIN_LINE = 1,
272 TOKEN,
273 STR_VALUE,
274 NUM_VALUE,
275 ARR_SPACE,
276 ARR_STR,
277 ARR_STR_SPACE,
278 ARR_NUM,
279 BEGIN_HEX,
280 BEGIN_QUOTE,
281 END_LINE
282 };
283
284 FILE* fd;
285 std::string token;
286 std::string strValue;
287 unsigned long numValue = 0;
288 std::vector<uint8_t> arrValue;
289 std::vector<std::string> arrStr;
290 int base = 0;
291 int c;
292 const char *name = mFilePath.c_str();
293 unsigned long state = BEGIN_LINE;
294
295 mValidFile = false;
296 m_map.clear();
297
298 /* open config file, read it into a buffer */
299 if ((fd = fopen(name, "r")) == NULL)
300 {
301 ALOGV("Extra calibration file %s failed to open.", name);
302 return false;
303 }
304 ALOGV("%s Opened config %s\n", __func__, name);
305
306 for (;;) {
307 c = fgetc(fd);
308
309 switch (state) {
310 case BEGIN_LINE:
311 if (isPrintable(c)) {
312 token.clear();
313 numValue = 0;
314 strValue.clear();
315 arrValue.clear();
316 arrStr.clear();
317 state = TOKEN;
318 token.push_back(c);
319 } else {
320 state = END_LINE;
321 }
322 break;
323 case TOKEN:
324 if (c == '=') {
325 state = BEGIN_QUOTE;
326 } else if (isPrintable(c)) {
327 token.push_back(c);
328 } else {
329 state = END_LINE;
330 }
331 break;
332 case BEGIN_QUOTE:
333 if (c == '"') {
334 state = STR_VALUE;
335 base = 0;
336 } else if (c == '0') {
337 state = BEGIN_HEX;
338 } else if (isDigit(c, 10)) {
339 state = NUM_VALUE;
340 base = 10;
341 numValue = getDigitValue(c, base);
342 } else if (c == '{') {
343 state = ARR_SPACE;
344 base = 16;
345 } else {
346 state = END_LINE;
347 }
348 break;
349 case BEGIN_HEX:
350 if (c == 'x' || c == 'X') {
351 state = NUM_VALUE;
352 base = 16;
353 numValue = 0;
354 } else if (isDigit(c, 10)) {
355 state = NUM_VALUE;
356 base = 10;
357 numValue = getDigitValue(c, base);
358 } else {
359 m_map.try_emplace(token, uwbParam(numValue));
360 state = END_LINE;
361 }
362 break;
363 case NUM_VALUE:
364 if (isDigit(c, base)) {
365 numValue *= base;
366 numValue += getDigitValue(c, base);
367 } else {m_map.try_emplace(token, uwbParam(numValue));
368 state = END_LINE;
369 }
370 break;
371 case ARR_SPACE:
372 if (isDigit(c, 16)) {
373 numValue = getDigitValue(c, base);
374 state = ARR_NUM;
375 } else if (c == '}') {
376 m_map.try_emplace(token, uwbParam(std::move(arrValue)));
377 arrValue = {};
378 state = END_LINE;
379 } else if (c == '"') {
380 state = ARR_STR;
381 } else if (c == EOF) {
382 state = END_LINE;
383 }
384 break;
385 case ARR_STR:
386 if (c == '"') {
387 arrStr.emplace_back(strValue);
388 strValue.clear();
389 state = ARR_STR_SPACE;
390 } else {
391 strValue.push_back(c);
392 }
393 break;
394 case ARR_STR_SPACE:
395 if (c == '}') {
396 m_map.try_emplace(token, uwbParam(std::move(arrStr)));
397 arrStr = {};
398 state = END_LINE;
399 } else if (c == '"') {
400 state = ARR_STR;
401 }
402 break;
403 case ARR_NUM:
404 if (isDigit(c, 16)) {
405 numValue *= 16;
406 numValue += getDigitValue(c, base);
407 } else if (isArrayDelimeter(c)) {
408 arrValue.push_back(numValue & 0xff);
409 state = ARR_SPACE;
410 } else {
411 state = END_LINE;
412 }
413 if (c == '}') {
414 m_map.try_emplace(token, uwbParam(std::move(arrValue)));
415 arrValue = {};
416 state = END_LINE;
417 }
418 break;
419 case STR_VALUE:
420 if (c == '"') {
421 state = END_LINE;
422 m_map.try_emplace(token, uwbParam(strValue));
423 } else {
424 strValue.push_back(c);
425 }
426 break;
427 case END_LINE:
428 // do nothing
429 default:
430 break;
431 }
432 if (c == EOF)
433 break;
434 else if (state == END_LINE && (c == '\n' || c == '\r'))
435 state = BEGIN_LINE;
436 else if (c == '#')
437 state = END_LINE;
438 }
439
440 if (fclose(fd) != 0) {
441 ALOGE("[%s] fclose failed", __func__);
442 }
443
444 if (m_map.size() > 0) {
445 mValidFile = true;
446 ALOGI("Extra calibration file %s opened.", name);
447 }
448
449 // Checks if this is a factory calibrated file by filename matching
450 std::string filename = mFilePath.stem();
451 if (filename.starts_with(factory_file_prefix)) {
452 mFactoryFile = true;
453 }
454
455 return mValidFile;
456 }
457
458 /*******************************************************************************
459 **
460 ** Function: CUwbNxpConfig::CUwbNxpConfig()
461 **
462 ** Description: class constructor
463 **
464 ** Returns: none
465 **
466 *******************************************************************************/
CUwbNxpConfig()467 CUwbNxpConfig::CUwbNxpConfig() : mFactoryFile(false), mValidFile(false) {}
468
469 /*******************************************************************************
470 **
471 ** Function: CUwbNxpConfig::~CUwbNxpConfig()
472 **
473 ** Description: class destructor
474 **
475 ** Returns: none
476 **
477 *******************************************************************************/
~CUwbNxpConfig()478 CUwbNxpConfig::~CUwbNxpConfig()
479 {
480 }
481
CUwbNxpConfig(std::string_view filepath)482 CUwbNxpConfig::CUwbNxpConfig(std::string_view filepath) : mFilePath(filepath)
483 {
484 readConfig();
485 }
486
CUwbNxpConfig(CUwbNxpConfig && config)487 CUwbNxpConfig::CUwbNxpConfig(CUwbNxpConfig&& config)
488 {
489 m_map = std::move(config.m_map);
490 mValidFile = config.mValidFile;
491 mFilePath = std::move(config.mFilePath);
492 mFactoryFile = config.mFactoryFile;
493
494 config.mValidFile = false;
495 }
496
operator =(CUwbNxpConfig && config)497 CUwbNxpConfig& CUwbNxpConfig::operator=(CUwbNxpConfig&& config)
498 {
499 m_map = std::move(config.m_map);
500 mValidFile = config.mValidFile;
501 mFilePath = std::move(config.mFilePath);
502 mFactoryFile = config.mFactoryFile;
503
504 config.mValidFile = false;
505 return *this;
506 }
507
find(std::string_view key) const508 const uwbParam* CUwbNxpConfig::find(std::string_view key) const
509 {
510 // TODO: how can we use the same hash function for string and string_view?
511 const auto it = m_map.find(std::string(key));
512 if (it == m_map.cend()) {
513 return nullptr;
514 }
515 return &it->second;
516 }
517
518 /*******************************************************************************
519 **
520 ** Function: CUwbNxpConfig::dump()
521 **
522 ** Description: prints all elements in the list
523 **
524 ** Returns: none
525 **
526 *******************************************************************************/
dump() const527 void CUwbNxpConfig::dump() const
528 {
529 ALOGV("Dump configuration file %s : %s, %zu entries", mFilePath.c_str(),
530 mValidFile ? "valid" : "invalid", m_map.size());
531
532 for (auto &it : m_map) {
533 auto &key = it.first;
534 auto ¶m = it.second;
535 param.dump(key);
536 }
537 }
538
539 /*******************************************************************************/
540 class RegionCodeMap {
541 public:
loadMapping(std::string_view filepath)542 void loadMapping(std::string_view filepath) {
543 CUwbNxpConfig config(filepath);
544 if (!config.isValid()) {
545 ALOGW("Region mapping was not provided.");
546 return;
547 }
548
549 ALOGI("Region mapping was provided by %s", std::string(filepath).c_str());
550 auto &all_params = config.get_data();
551 for (auto &it : all_params) {
552 const auto ®ion_str = it.first;
553 const uwbParam *param = &it.second;
554
555 // split space-separated strings into set
556 std::stringstream ss(std::string(param->str_value()));
557 std::string cc;
558 std::unordered_set<std::string> cc_set;
559 while (ss >> cc) {
560 if (cc.length() == 2 && isupper(cc[0]) && isupper(cc[1])) {
561 cc_set.emplace(std::move(cc));
562 }
563 }
564 auto result = m_map.try_emplace(region_str, std::move(cc_set));
565 if (!result.second) {
566 // region conlifct : merge
567 result.first->second.merge(std::move(cc_set));
568 }
569 }
570 m_config = std::move(config);
571 }
xlateCountryCode(const char country_code[2])572 std::string xlateCountryCode(const char country_code[2]) {
573 std::string code{country_code[0], country_code[1]};
574 if (m_config.isValid()) {
575 for (auto &it : m_map) {
576 const auto ®ion_str = it.first;
577 const auto &cc_set = it.second;
578 if (cc_set.find(code) != cc_set.end()) {
579 ALOGV("map country code %c%c --> %s",
580 country_code[0], country_code[1], region_str.c_str());
581 return region_str;
582 }
583 }
584 }
585 return code;
586 }
reset()587 void reset() {
588 m_config.reset();
589 m_map.clear();
590 }
dump()591 void dump() {
592 ALOGV("Region mapping dump:");
593 for (auto &entry : m_map) {
594 const auto ®ion_str = entry.first;
595 const auto &cc_set = entry.second;
596 std::stringstream ss;
597 for (const auto s : cc_set) {
598 ss << "\"" << s << "\", ";
599 }
600 ALOGV("- %s = { %s}", region_str.c_str(), ss.str().c_str());
601 }
602 }
603 private:
604 CUwbNxpConfig m_config;
605 std::unordered_map<std::string, std::unordered_set<std::string>> m_map;
606 };
607
608 /*******************************************************************************/
609 class CascadeConfig {
610 public:
611 CascadeConfig();
612
613 void init(std::string_view main_config);
614 void deinit();
615 bool setCountryCode(const char country_code[2]);
616
617 const uwbParam* find(std::string_view key, bool include_factory) const;
618 private:
619 // default_nxp_config_path
620 CUwbNxpConfig mMainConfig;
621
622 // uci config
623 CUwbNxpConfig mUciConfig;
624
625 // EXTRA_CONF_PATH[N]
626 std::vector<std::pair<std::string, CUwbNxpConfig>> mExtraConfig;
627
628 // [COUNTRY_CODE_CAP_FILE_LOCATION]/country_code_config_name
629 CUwbNxpConfig mCapsConfig;
630
631 // Region Code mapping
632 RegionCodeMap mRegionMap;
633
634 // current set of specifiers for EXTRA_CONF_PATH[]
635 struct ExtraConfPathSpecifiers {
636 std::string mCurSku;
637 std::string mCurExtid;
638 std::string mCurRegionCode;
639 std::string mCurRevision;
reset__anon3242e0020111::CascadeConfig::ExtraConfPathSpecifiers640 void reset() {
641 mCurSku.clear();
642 mCurExtid.clear();
643 mCurRegionCode.clear();
644 mCurRevision.clear();
645 }
646 };
647 ExtraConfPathSpecifiers mExtraConfSpecifiers;
648
649 // Re-evaluate filepaths of mExtraConfig with mExtraConfSpecifiers, and re-load them.
650 // returns true if any of entries were updated.
651 bool evaluateExtraConfPaths();
652
dump()653 void dump() {
654 mMainConfig.dump();
655 mUciConfig.dump();
656
657 for (const auto &[filename, config] : mExtraConfig)
658 config.dump();
659
660 mCapsConfig.dump();
661 mRegionMap.dump();
662 }
663 };
664
CascadeConfig()665 CascadeConfig::CascadeConfig()
666 {
667 }
668
evaluateExtraConfPaths()669 bool CascadeConfig::evaluateExtraConfPaths()
670 {
671 int nr_updated = 0;
672
673 for (auto& [filename, config] : mExtraConfig) {
674 std::string new_filename(filename);
675
676 auto posSku = new_filename.find(sku_specifier);
677 if (posSku != std::string::npos && !mExtraConfSpecifiers.mCurSku.empty()) {
678 new_filename.replace(posSku, sku_specifier.length(), mExtraConfSpecifiers.mCurSku);
679 }
680
681 auto posExtid = new_filename.find(extid_specifier);
682 if (posExtid != std::string::npos && !mExtraConfSpecifiers.mCurExtid.empty()) {
683 new_filename.replace(posExtid, extid_specifier.length(), mExtraConfSpecifiers.mCurExtid);
684 }
685
686 auto posCountry = new_filename.find(country_code_specifier);
687 if (posCountry != std::string::npos && !mExtraConfSpecifiers.mCurRegionCode.empty()) {
688 new_filename.replace(posCountry, country_code_specifier.length(), mExtraConfSpecifiers.mCurRegionCode);
689 }
690
691 auto posRevision = new_filename.find(revision_specifier);
692 if (posRevision != std::string::npos && !mExtraConfSpecifiers.mCurRevision.empty()) {
693 new_filename.replace(posRevision, revision_specifier.length(), mExtraConfSpecifiers.mCurRevision);
694 }
695 // re-open the file if filepath got re-evaluated.
696 if (new_filename != config.getFilePath()) {
697 config = CUwbNxpConfig(new_filename.c_str());
698 ++nr_updated;
699 }
700 }
701 ALOGI("%d new configuration files found.", nr_updated);
702 return (nr_updated > 0);
703 }
704
init(std::string_view main_config)705 void CascadeConfig::init(std::string_view main_config)
706 {
707 ALOGV("CascadeConfig initialize with %s", std::string(main_config).c_str());
708
709 // Main config file
710 CUwbNxpConfig config(main_config);
711 if (!config.isValid()) {
712 ALOGW("Failed to load main config file");
713 return;
714 }
715 mMainConfig = std::move(config);
716
717 {
718 // UCI config file
719 std::string uciConfigFilePath(default_uci_config_path);
720 uciConfigFilePath += nxp_uci_config_file;
721
722 CUwbNxpConfig config(uciConfigFilePath);
723 if (!config.isValid()) {
724 ALOGW("Failed to load uci config file:%s",
725 uciConfigFilePath.c_str());
726 } else {
727 mUciConfig = std::move(config);
728 }
729 }
730
731 char sku_value[PROPERTY_VALUE_MAX];
732 char revision_value[PROPERTY_VALUE_MAX];
733 property_get(prop_name_calsku, sku_value, prop_default_calsku);
734 property_get(prop_name_revision, revision_value, prop_default_revision);
735
736 // Read EXTRA_CONF_PATH[N]
737 for (int i = 1; i <= 10; i++) {
738 char key[32];
739 snprintf(key, sizeof(key), "EXTRA_CONF_PATH_%d", i);
740 const uwbParam *param = mMainConfig.find(key);
741 if (!param)
742 continue;
743
744 std::string filename(param->str_value());
745
746 auto entry = std::make_pair(param->str_value(), CUwbNxpConfig(filename.c_str()));
747 mExtraConfig.emplace_back(std::move(entry));
748 }
749
750 // evaluate <sku> and <revision>
751 mExtraConfSpecifiers.mCurSku = sku_value;
752 mExtraConfSpecifiers.mCurRevision = revision_value;
753 evaluateExtraConfPaths();
754
755 // re-evaluate with "<extid>"
756 mExtraConfSpecifiers.mCurExtid =
757 NxpConfig_GetStr(extid_config_name).value_or(extid_default_value);
758 evaluateExtraConfPaths();
759
760 ALOGI("Provided specifiers: sku=[%s] revision=[%s] extid=[%s]", sku_value, revision_value,
761 mExtraConfSpecifiers.mCurExtid.c_str());
762
763 // Pick one libuwb-countrycode.conf with the highest VERSION number
764 // from multiple directories specified by COUNTRY_CODE_CAP_FILE_LOCATION
765 // XXX: Can't we just drop this feature of COUNTRY_CODE_CAP_FILE_LOCATION?
766 std::vector<std::string> locations = NxpConfig_GetStrArray(NAME_COUNTRY_CODE_CAP_FILE_LOCATION);
767 if ( locations.size() > 0) {
768 int max_version = -1;
769 std::string strPickedPath;
770 bool foundCapFile = false;
771 CUwbNxpConfig pickedConfig;
772
773 for (const std::string& loc : locations) {
774 if (loc.empty()) { continue; }
775
776 std::string strPath(loc);
777 strPath += country_code_config_name;
778 ALOGV("Try to load %s", strPath.c_str());
779
780 CUwbNxpConfig config(strPath.c_str());
781 // This cannot be provided from factory cal file.
782 if (config.isFactory()) { continue; }
783
784 const uwbParam *param = config.find(NAME_NXP_COUNTRY_CODE_VERSION);
785 int version = -2;
786 if (param) {
787 std::string_view v = param->str_value();
788 int n;
789 auto [ptr, ec] = std::from_chars(v.data(), v.data() + v.size(), n);
790 if (ec == std::errc()) { version = n; }
791 }
792 if (version > max_version) {
793 foundCapFile = true;
794 pickedConfig = std::move(config);
795 strPickedPath = std::move(strPath);
796 max_version = version;
797 }
798 }
799 if (foundCapFile) {
800 mCapsConfig = std::move(pickedConfig);
801 ALOGI("CountryCodeCaps file %s loaded with VERSION=%d", strPickedPath.c_str(), max_version);
802 } else {
803 ALOGI("No CountryCodeCaps specified");
804 }
805 } else {
806 ALOGI(NAME_COUNTRY_CODE_CAP_FILE_LOCATION " was not specified, skip loading CountryCodeCaps");
807 }
808
809 // Load region mapping
810 const uwbParam *param = find(NAME_REGION_MAP_PATH, /*include_factory=*/false);
811 if (param) {
812 mRegionMap.loadMapping(param->str_value());
813 }
814
815 ALOGD("CascadeConfig initialized");
816
817 dump();
818 }
819
deinit()820 void CascadeConfig::deinit()
821 {
822 mMainConfig.reset();
823 mExtraConfig.clear();
824 mCapsConfig.reset();
825 mRegionMap.reset();
826 mUciConfig.reset();
827 mExtraConfSpecifiers.reset();
828 }
829
setCountryCode(const char country_code[2])830 bool CascadeConfig::setCountryCode(const char country_code[2])
831 {
832 std::string strRegion = mRegionMap.xlateCountryCode(country_code);
833
834 if (strRegion == mExtraConfSpecifiers.mCurRegionCode) {
835 ALOGI("Same region code(%c%c --> %s), per-country configuration not updated.",
836 country_code[0], country_code[1], strRegion.c_str());
837 return false;
838 }
839
840 ALOGI("Apply country code %c%c --> %s\n", country_code[0], country_code[1], strRegion.c_str());
841 mExtraConfSpecifiers.mCurRegionCode = strRegion;
842
843 return evaluateExtraConfPaths();
844 }
845
find(std::string_view key,bool include_factory) const846 const uwbParam* CascadeConfig::find(std::string_view key, bool include_factory) const
847 {
848 const uwbParam* param = NULL;
849
850 param = mCapsConfig.find(key);
851 if (param)
852 return param;
853
854 for (auto it = mExtraConfig.rbegin(); it != mExtraConfig.rend(); it++) {
855 auto &config = it->second;
856 if (!include_factory && config.isFactory()) { continue; }
857 param = config.find(key);
858 if (param)
859 break;
860 }
861 if (!param) {
862 param = mMainConfig.find(key);
863 }
864 if (!param) {
865 param = mUciConfig.find(key);
866 }
867 return param;
868 }
869
870 } // namespace
871
872 static CascadeConfig gConfig;
873
NxpConfig_Init(void)874 void NxpConfig_Init(void)
875 {
876 gConfig.init(default_nxp_config_path);
877 }
878
NxpConfig_Deinit(void)879 void NxpConfig_Deinit(void)
880 {
881 gConfig.deinit();
882 }
883
884 // return true if new per-country configuration file was load.
885 // false if it can stay at the current configuration.
NxpConfig_SetCountryCode(const char country_code[2])886 bool NxpConfig_SetCountryCode(const char country_code[2])
887 {
888 return gConfig.setCountryCode(country_code);
889 }
890
NxpConfig_GetStr(std::string_view key,bool include_factory)891 std::optional<std::string_view> NxpConfig_GetStr(std::string_view key, bool include_factory)
892 {
893 const uwbParam *param = gConfig.find(key, include_factory);
894 if (param == nullptr || param->getType() != uwbParam::type::STRING) {
895 return std::nullopt;
896 }
897 return param->str_value();
898 }
899
NxpConfig_GetByteArray(std::string_view key,bool include_factory)900 std::optional<std::span<const uint8_t>> NxpConfig_GetByteArray(std::string_view key, bool include_factory)
901 {
902 const uwbParam *param = gConfig.find(key, include_factory);
903 if (param == nullptr || param->getType() != uwbParam::type::BYTEARRAY) {
904 return std::nullopt;
905 }
906 return param->arr_value();
907 }
908
NxpConfig_GetUint64(std::string_view key,bool include_factory)909 std::optional<uint64_t> NxpConfig_GetUint64(std::string_view key, bool include_factory)
910 {
911 const uwbParam* pParam = gConfig.find(key, include_factory);
912
913 if ((pParam == nullptr) || (pParam->getType() != uwbParam::type::NUMBER)) {
914 return std::nullopt;
915 }
916 return pParam->numValue();
917 }
918
NxpConfig_GetBool(std::string_view key,bool include_factory)919 std::optional<bool> NxpConfig_GetBool(std::string_view key, bool include_factory)
920 {
921 const uwbParam* pParam = gConfig.find(key, include_factory);
922 if (pParam == nullptr || pParam->getType() != uwbParam::type::NUMBER) {
923 return std::nullopt;
924 }
925 return pParam->numValue();
926 }
927
NxpConfig_GetStrArray(std::string_view key,bool include_factory)928 std::vector<std::string> NxpConfig_GetStrArray(std::string_view key, bool include_factory)
929 {
930 const uwbParam* param = gConfig.find(key, include_factory);
931 if (param == nullptr || param->getType() != uwbParam::type::STRINGARRAY) {
932 return std::vector<std::string>{};
933 }
934 return param->str_arr_value();
935 }
936