• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &param)  = delete;
84 
uwbParam(uwbParam && param)85     uwbParam(uwbParam &&param) :
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 &param = 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 &region_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 &region_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 &region_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