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 <limits.h>
23 #include <sys/stat.h>
24 
25 #include <cstddef>
26 #include <cstdint>
27 #include <iomanip>
28 #include <list>
29 #include <memory>
30 #include <sstream>
31 #include <sstream>
32 #include <string>
33 #include <unordered_map>
34 #include <unordered_set>
35 #include <vector>
36 
37 #include <android-base/logging.h>
38 #include <cutils/properties.h>
39 #include <log/log.h>
40 
41 #include "phNxpConfig.h"
42 #include "phNxpUciHal.h"
43 #include "phNxpUciHal_ext.h"
44 #include "phNxpUciHal_utils.h"
45 #include "phNxpLog.h"
46 
47 namespace {
48 
49 static const char default_nxp_config_path[] = "/vendor/etc/libuwb-nxp.conf";
50 static const char country_code_config_name[] = "libuwb-countrycode.conf";
51 static const char nxp_uci_config_file[] = "libuwb-uci.conf";
52 static const char default_uci_config_path[] = "/vendor/etc/";
53 
54 static const char country_code_specifier[] = "<country>";
55 static const char sku_specifier[] = "<sku>";
56 static const char extid_specifier[] = "<extid>";
57 static const char revision_specifier[] = "<revision>";
58 
59 static const char extid_config_name[] = "cal.extid";
60 static const char extid_default_value[] = "defaultextid";
61 
62 static const char prop_name_calsku[] = "persist.vendor.uwb.cal.sku";
63 static const char prop_default_calsku[] = "defaultsku";
64 
65 static const char prop_name_revision[] = "persist.vendor.uwb.cal.revision";
66 static const char prop_default_revision[] = "defaultrevision";
67 
68 using namespace::std;
69 
70 class uwbParam
71 {
72 public:
73     enum class type { STRING, NUMBER, BYTEARRAY, STRINGARRAY };
74     uwbParam();
75     uwbParam(const uwbParam& param);
76     uwbParam(uwbParam&& param);
77 
78     uwbParam(const string& value);
79     uwbParam(vector<uint8_t>&& value);
80     uwbParam(unsigned long value);
81     uwbParam(vector<string>&& value);
82 
83     virtual ~uwbParam();
84 
getType() const85     type getType() const { return m_type; }
numValue() const86     unsigned long numValue() const {return m_numValue;}
str_value() const87     const char*   str_value() const {return m_str_value.c_str();}
str_len() const88     size_t        str_len() const   {return m_str_value.length();}
arr_value() const89     const uint8_t* arr_value() const { return m_arrValue.data(); }
arr_len() const90     size_t arr_len() const { return m_arrValue.size(); }
91 
str_arr_len() const92     size_t str_arr_len() const { return m_arrStrValue.size(); }
str_arr_elem(const int index) const93     const char* str_arr_elem(const int index) const { return m_arrStrValue[index].c_str(); }
str_arr_elem_len(const int index) const94     size_t str_arr_elem_len(const int index) const { return m_arrStrValue[index].length(); }
95 
96     void dump(const string &tag) const;
97 private:
98     // TODO: use uint64_t or uint32_t instead of unsigned long.
99     unsigned long   m_numValue;
100     string          m_str_value;
101     vector<uint8_t>  m_arrValue;
102     vector<string>  m_arrStrValue;
103     type m_type;
104 };
105 
106 class CUwbNxpConfig
107 {
108 public:
109     CUwbNxpConfig();
110     CUwbNxpConfig(CUwbNxpConfig&& config);
111     CUwbNxpConfig(const char *filepath);
112     virtual ~CUwbNxpConfig();
113     CUwbNxpConfig& operator=(CUwbNxpConfig&& config);
114 
115     bool open(const char *filepath);
isValid() const116     bool isValid() const { return mValidFile; }
reset()117     void reset() {
118         m_map.clear();
119         mValidFile = false;
120     }
121 
122     const uwbParam*    find(const char* p_name) const;
123     void    setCountry(const string& strCountry);
getFilePath() const124     const char* getFilePath() const {
125         return mFilePath.c_str();
126     }
127 
128     void    dump() const;
129 
get_data() const130     const unordered_map<string, uwbParam>& get_data() const {
131         return m_map;
132     }
133 private:
134     bool    readConfig();
135 
136     unordered_map<string, uwbParam> m_map;
137     bool    mValidFile;
138     string  mFilePath;
139 };
140 
141 /*******************************************************************************
142 **
143 ** Function:    isPrintable()
144 **
145 ** Description: determine if 'c' is printable
146 **
147 ** Returns:     1, if printable, otherwise 0
148 **
149 *******************************************************************************/
isPrintable(char c)150 static inline bool isPrintable(char c)
151 {
152     return  (c >= 'A' && c <= 'Z') ||
153             (c >= 'a' && c <= 'z') ||
154             (c >= '0' && c <= '9') ||
155             c == '/' || c == '_' || c == '-' || c == '.' || c == ',';
156 }
157 
158 /*******************************************************************************
159 **
160 ** Function:    isDigit()
161 **
162 ** Description: determine if 'c' is numeral digit
163 **
164 ** Returns:     true, if numerical digit
165 **
166 *******************************************************************************/
isDigit(char c,int base)167 static inline bool isDigit(char c, int base)
168 {
169     if (base == 10) {
170         return isdigit(c);
171     } else if (base == 16) {
172         return isxdigit(c);
173     } else {
174         return false;
175     }
176 }
177 
isArrayDelimeter(char c)178 static inline bool isArrayDelimeter(char c)
179 {
180     return (isspace(c) || c== ',' || c == ':' || c == '-' || c == '}');
181 }
182 
183 /*******************************************************************************
184 **
185 ** Function:    getDigitValue()
186 **
187 ** Description: return numerical value of a decimal or hex char
188 **
189 ** Returns:     numerical value if decimal or hex char, otherwise 0
190 **
191 *******************************************************************************/
getDigitValue(char c,int base)192 inline int getDigitValue(char c, int base)
193 {
194     if ('0' <= c && c <= '9')
195         return c - '0';
196     if (base == 16)
197     {
198         if ('A' <= c && c <= 'F')
199             return c - 'A' + 10;
200         else if ('a' <= c && c <= 'f')
201             return c - 'a' + 10;
202     }
203     return 0;
204 }
205 
206 /*******************************************************************************
207 **
208 ** Function:    CUwbNxpConfig::readConfig()
209 **
210 ** Description: read Config settings and parse them into a linked list
211 **              move the element from linked list to a array at the end
212 **
213 ** Returns:     1, if there are any config data, 0 otherwise
214 **
215 *******************************************************************************/
readConfig()216 bool CUwbNxpConfig::readConfig()
217 {
218     enum {
219         BEGIN_LINE = 1,
220         TOKEN,
221         STR_VALUE,
222         NUM_VALUE,
223         ARR_SPACE,
224         ARR_STR,
225         ARR_STR_SPACE,
226         ARR_NUM,
227         BEGIN_HEX,
228         BEGIN_QUOTE,
229         END_LINE
230     };
231 
232     FILE*   fd;
233     string  token;
234     string  strValue;
235     unsigned long    numValue = 0;
236     vector<uint8_t> arrValue;
237     vector<string> arrStr;
238     int     base = 0;
239     int     c;
240     const char *name = mFilePath.c_str();
241     unsigned long state = BEGIN_LINE;
242 
243     mValidFile = false;
244     m_map.clear();
245 
246     /* open config file, read it into a buffer */
247     if ((fd = fopen(name, "r")) == NULL)
248     {
249         ALOGV("Extra calibration file %s failed to open.", name);
250         return false;
251     }
252     ALOGV("%s Opened config %s\n", __func__, name);
253 
254     for (;;) {
255         c = fgetc(fd);
256 
257         switch (state) {
258         case BEGIN_LINE:
259             if (isPrintable(c)) {
260                 token.clear();
261                 numValue = 0;
262                 strValue.clear();
263                 arrValue.clear();
264                 arrStr.clear();
265                 state = TOKEN;
266                 token.push_back(c);
267             } else {
268                 state = END_LINE;
269             }
270             break;
271         case TOKEN:
272             if (c == '=') {
273                 state = BEGIN_QUOTE;
274             } else if (isPrintable(c)) {
275                 token.push_back(c);
276             } else {
277                 state = END_LINE;
278             }
279             break;
280         case BEGIN_QUOTE:
281             if (c == '"') {
282                 state = STR_VALUE;
283                 base = 0;
284             } else if (c == '0') {
285                 state = BEGIN_HEX;
286             } else if (isDigit(c, 10)) {
287                 state = NUM_VALUE;
288                 base = 10;
289                 numValue = getDigitValue(c, base);
290             } else if (c == '{') {
291                 state = ARR_SPACE;
292                 base = 16;
293             } else {
294                 state = END_LINE;
295             }
296             break;
297         case BEGIN_HEX:
298             if (c == 'x' || c == 'X') {
299                 state = NUM_VALUE;
300                 base = 16;
301                 numValue = 0;
302             } else if (isDigit(c, 10)) {
303                 state = NUM_VALUE;
304                 base = 10;
305                 numValue = getDigitValue(c, base);
306             } else {
307                 m_map.try_emplace(token, move(uwbParam(numValue)));
308                 state = END_LINE;
309             }
310             break;
311         case NUM_VALUE:
312             if (isDigit(c, base)) {
313                 numValue *= base;
314                 numValue += getDigitValue(c, base);
315             } else {m_map.try_emplace(token, move(uwbParam(numValue)));
316                 state = END_LINE;
317             }
318             break;
319         case ARR_SPACE:
320             if (isDigit(c, 16)) {
321                 numValue = getDigitValue(c, base);
322                 state = ARR_NUM;
323             } else if (c == '}') {
324                 m_map.try_emplace(token, move(uwbParam(move(arrValue))));
325                 state = END_LINE;
326             } else if (c == '"') {
327                 state = ARR_STR;
328             } else if (c == EOF) {
329                 state = END_LINE;
330             }
331             break;
332         case ARR_STR:
333             if (c == '"') {
334                 arrStr.emplace_back(move(strValue));
335                 strValue.clear();
336                 state = ARR_STR_SPACE;
337             } else {
338                 strValue.push_back(c);
339             }
340             break;
341         case ARR_STR_SPACE:
342             if (c == '}') {
343                 m_map.try_emplace(token, move(uwbParam(move(arrStr))));
344                 state = END_LINE;
345             } else if (c == '"') {
346                 state = ARR_STR;
347             }
348             break;
349         case ARR_NUM:
350             if (isDigit(c, 16)) {
351                 numValue *= 16;
352                 numValue += getDigitValue(c, base);
353             } else if (isArrayDelimeter(c)) {
354                 arrValue.push_back(numValue & 0xff);
355                 state = ARR_SPACE;
356             } else {
357                 state = END_LINE;
358             }
359             if (c == '}') {
360                 m_map.try_emplace(token, move(uwbParam(move(arrValue))));
361                 state = END_LINE;
362             }
363             break;
364         case STR_VALUE:
365             if (c == '"') {
366                 state = END_LINE;
367                 m_map.try_emplace(token, move(uwbParam(strValue)));
368             } else {
369                 strValue.push_back(c);
370             }
371             break;
372         case END_LINE:
373             // do nothing
374         default:
375             break;
376         }
377         if (c == EOF)
378             break;
379         else if (state == END_LINE && (c == '\n' || c == '\r'))
380             state = BEGIN_LINE;
381         else if (c == '#')
382             state = END_LINE;
383     }
384 
385     if (fclose(fd) != 0) {
386       ALOGE("[%s] fclose failed", __func__);
387     }
388 
389     if (m_map.size() > 0) {
390         mValidFile = true;
391         ALOGI("Extra calibration file %s opened.", name);
392     }
393 
394     return mValidFile;
395 }
396 
397 /*******************************************************************************
398 **
399 ** Function:    CUwbNxpConfig::CUwbNxpConfig()
400 **
401 ** Description: class constructor
402 **
403 ** Returns:     none
404 **
405 *******************************************************************************/
CUwbNxpConfig()406 CUwbNxpConfig::CUwbNxpConfig() :
407     mValidFile(false)
408 {
409 }
410 
411 /*******************************************************************************
412 **
413 ** Function:    CUwbNxpConfig::~CUwbNxpConfig()
414 **
415 ** Description: class destructor
416 **
417 ** Returns:     none
418 **
419 *******************************************************************************/
~CUwbNxpConfig()420 CUwbNxpConfig::~CUwbNxpConfig()
421 {
422 }
423 
CUwbNxpConfig(const char * filepath)424 CUwbNxpConfig::CUwbNxpConfig(const char *filepath)
425 {
426     open(filepath);
427 }
428 
CUwbNxpConfig(CUwbNxpConfig && config)429 CUwbNxpConfig::CUwbNxpConfig(CUwbNxpConfig&& config)
430 {
431     m_map = move(config.m_map);
432     mValidFile = config.mValidFile;
433     mFilePath = move(config.mFilePath);
434 
435     config.mValidFile = false;
436 }
437 
operator =(CUwbNxpConfig && config)438 CUwbNxpConfig& CUwbNxpConfig::operator=(CUwbNxpConfig&& config)
439 {
440     m_map = move(config.m_map);
441     mValidFile = config.mValidFile;
442     mFilePath = move(config.mFilePath);
443 
444     config.mValidFile = false;
445     return *this;
446 }
447 
open(const char * filepath)448 bool CUwbNxpConfig::open(const char *filepath)
449 {
450     mValidFile = false;
451     mFilePath = filepath;
452 
453     return readConfig();
454 }
455 
456 /*******************************************************************************
457 **
458 ** Function:    CUwbNxpConfig::find()
459 **
460 ** Description: search if a setting exist in the setting array
461 **
462 ** Returns:     pointer to the setting object
463 **
464 *******************************************************************************/
find(const char * p_name) const465 const uwbParam* CUwbNxpConfig::find(const char* p_name) const
466 {
467     const auto it = m_map.find(p_name);
468 
469     if (it == m_map.cend()) {
470         return NULL;
471     }
472     return &it->second;
473 }
474 
475 /*******************************************************************************
476 **
477 ** Function:    CUwbNxpConfig::dump()
478 **
479 ** Description: prints all elements in the list
480 **
481 ** Returns:     none
482 **
483 *******************************************************************************/
dump() const484 void CUwbNxpConfig::dump() const
485 {
486     ALOGV("Dump configuration file %s : %s, %zu entries", mFilePath.c_str(),
487         mValidFile ? "valid" : "invalid", m_map.size());
488 
489     for (auto &it : m_map) {
490         auto &key = it.first;
491         auto &param = it.second;
492         param.dump(key);
493     }
494 }
495 
496 /*******************************************************************************/
uwbParam()497 uwbParam::uwbParam() :
498     m_numValue(0),
499     m_type(type::NUMBER)
500 {
501 }
502 
~uwbParam()503 uwbParam::~uwbParam()
504 {
505 }
506 
uwbParam(const uwbParam & param)507 uwbParam::uwbParam(const uwbParam &param) :
508     m_numValue(param.m_numValue),
509     m_str_value(param.m_str_value),
510     m_arrValue(param.m_arrValue),
511     m_arrStrValue(param.m_arrStrValue),
512     m_type(param.m_type)
513 {
514 }
515 
uwbParam(uwbParam && param)516 uwbParam::uwbParam(uwbParam &&param) :
517     m_numValue(param.m_numValue),
518     m_str_value(move(param.m_str_value)),
519     m_arrValue(move(param.m_arrValue)),
520     m_arrStrValue(move(param.m_arrStrValue)),
521     m_type(param.m_type)
522 {
523 }
524 
uwbParam(const string & value)525 uwbParam::uwbParam(const string& value) :
526     m_numValue(0),
527     m_str_value(value),
528     m_type(type::STRING)
529 {
530 }
531 
uwbParam(unsigned long value)532 uwbParam::uwbParam(unsigned long value) :
533     m_numValue(value),
534     m_type(type::NUMBER)
535 {
536 }
537 
uwbParam(vector<uint8_t> && value)538 uwbParam::uwbParam(vector<uint8_t> &&value) :
539     m_arrValue(move(value)),
540     m_type(type::BYTEARRAY)
541 {
542 }
543 
uwbParam(vector<string> && value)544 uwbParam::uwbParam(vector<string> &&value) :
545     m_arrStrValue(move(value)),
546     m_type(type::STRINGARRAY)
547 {
548 }
549 
550 
dump(const string & tag) const551 void uwbParam::dump(const string &tag) const
552 {
553     if (m_type == type::NUMBER) {
554         ALOGV(" - %s = 0x%lx", tag.c_str(), m_numValue);
555     } else if (m_type == type::STRING) {
556         ALOGV(" - %s = %s", tag.c_str(), m_str_value.c_str());
557     } else if (m_type == type::BYTEARRAY) {
558         stringstream ss_hex;
559         ss_hex.fill('0');
560         for (auto b : m_arrValue) {
561             ss_hex << setw(2) << hex << (int)b << " ";
562         }
563         ALOGV(" - %s = { %s}", tag.c_str(), ss_hex.str().c_str());
564     } else if (m_type == type::STRINGARRAY) {
565         stringstream ss;
566         for (auto s : m_arrStrValue) {
567             ss << "\"" << s << "\", ";
568         }
569         ALOGV(" - %s = { %s}", tag.c_str(), ss.str().c_str());
570     }
571 }
572 /*******************************************************************************/
573 class RegionCodeMap {
574 public:
loadMapping(const char * filepath)575     void loadMapping(const char *filepath) {
576         CUwbNxpConfig config(filepath);
577         if (!config.isValid()) {
578             ALOGW("Region mapping was not provided.");
579             return;
580         }
581 
582         ALOGI("Region mapping was provided by %s", filepath);
583         auto &all_params = config.get_data();
584         for (auto &it : all_params) {
585             const auto &region_str = it.first;
586             const uwbParam *param = &it.second;
587 
588             // split space-separated strings into set
589             stringstream ss(param->str_value());
590             string cc;
591             unordered_set<string> cc_set;
592             while (ss >> cc) {
593               if (cc.length() == 2 && isupper(cc[0]) && isupper(cc[1])) {
594                 cc_set.emplace(move(cc));
595               }
596             }
597             auto result = m_map.try_emplace(region_str, move(cc_set));
598             if (!result.second) {
599               // region conlifct : merge
600               result.first->second.merge(move(cc_set));
601             }
602         }
603         m_config = move(config);
604     }
xlateCountryCode(const char country_code[2])605     string xlateCountryCode(const char country_code[2]) {
606         string code{country_code[0], country_code[1]};
607         if (m_config.isValid()) {
608             for (auto &it : m_map) {
609                 const auto &region_str = it.first;
610                 const auto &cc_set = it.second;
611                 if (cc_set.find(code) != cc_set.end()) {
612                     ALOGV("map country code %c%c --> %s",
613                             country_code[0], country_code[1], region_str.c_str());
614                     return region_str;
615                 }
616             }
617         }
618         return code;
619     }
reset()620     void reset() {
621         m_config.reset();
622         m_map.clear();
623     }
dump()624     void dump() {
625         ALOGV("Region mapping dump:");
626         for (auto &entry : m_map) {
627             const auto &region_str = entry.first;
628             const auto &cc_set = entry.second;
629             stringstream ss;
630             for (const auto s : cc_set) {
631                 ss << "\"" << s << "\", ";
632             }
633             ALOGV("- %s = { %s}", region_str.c_str(), ss.str().c_str());
634         }
635     }
636 private:
637     CUwbNxpConfig m_config;
638     unordered_map<string, unordered_set<string>> m_map;
639 };
640 
641 /*******************************************************************************/
642 class CascadeConfig {
643 public:
644     CascadeConfig();
645 
646     void init(const char *main_config);
647     void deinit();
648     bool setCountryCode(const char country_code[2]);
649 
650     const uwbParam* find(const char *name)  const;
651     bool    getValue(const char* name, char* pValue, size_t len) const;
652     bool    getValue(const char* name, unsigned long& rValue) const;
653     bool    getValue(const char* name, uint8_t* pValue, size_t len, size_t* readlen) const;
654 private:
655     // default_nxp_config_path
656     CUwbNxpConfig mMainConfig;
657 
658     // uci config
659     CUwbNxpConfig mUciConfig;
660 
661     // EXTRA_CONF_PATH[N]
662     std::vector<std::pair<string, CUwbNxpConfig>> mExtraConfig;
663 
664     // [COUNTRY_CODE_CAP_FILE_LOCATION]/country_code_config_name
665     CUwbNxpConfig mCapsConfig;
666 
667     // Region Code mapping
668     RegionCodeMap mRegionMap;
669 
670     // current set of specifiers for EXTRA_CONF_PATH[]
671     struct ExtraConfPathSpecifiers {
672         string mCurSku;
673         string mCurExtid;
674         string mCurRegionCode;
675         string mCurRevision;
reset__anonc2d6b5820111::CascadeConfig::ExtraConfPathSpecifiers676         void reset() {
677             mCurSku.clear();
678             mCurExtid.clear();
679             mCurRegionCode.clear();
680             mCurRevision.clear();
681         }
682     };
683     ExtraConfPathSpecifiers mExtraConfSpecifiers;
684 
685     // Re-evaluate filepaths of mExtraConfig with mExtraConfSpecifiers, and re-load them.
686     // returns true if any of entries were updated.
687     bool evaluateExtraConfPaths();
688 
dump()689     void dump() {
690         mMainConfig.dump();
691         mUciConfig.dump();
692 
693         for (const auto &[filename, config] : mExtraConfig)
694             config.dump();
695 
696         mCapsConfig.dump();
697         mRegionMap.dump();
698     }
699 };
700 
CascadeConfig()701 CascadeConfig::CascadeConfig()
702 {
703 }
704 
evaluateExtraConfPaths()705 bool CascadeConfig::evaluateExtraConfPaths()
706 {
707     bool updated = false;
708 
709     for (auto& [filename, config] : mExtraConfig) {
710         std::string new_filename(filename);
711 
712         auto posSku = filename.find(sku_specifier);
713         if (posSku != std::string::npos && !mExtraConfSpecifiers.mCurSku.empty()) {
714             new_filename.replace(posSku, strlen(sku_specifier), mExtraConfSpecifiers.mCurSku);
715         }
716 
717         auto posExtid = filename.find(extid_specifier);
718         if (posExtid != std::string::npos && !mExtraConfSpecifiers.mCurExtid.empty()) {
719             new_filename.replace(posExtid, strlen(extid_specifier), mExtraConfSpecifiers.mCurExtid);
720         }
721 
722         auto posCountry = filename.find(country_code_specifier);
723         if (posCountry != std::string::npos && !mExtraConfSpecifiers.mCurRegionCode.empty()) {
724             new_filename.replace(posCountry, strlen(country_code_specifier), mExtraConfSpecifiers.mCurRegionCode);
725         }
726 
727         auto posRevision = filename.find(revision_specifier);
728         if (posRevision != std::string::npos && !mExtraConfSpecifiers.mCurRevision.empty()) {
729             new_filename.replace(posRevision, strlen(revision_specifier), mExtraConfSpecifiers.mCurRevision);
730         }
731 
732         // re-open the file if filepath got re-evaluated.
733         if (new_filename != config.getFilePath()) {
734             config.open(new_filename.c_str());
735             updated = true;
736         }
737     }
738     return updated;
739 }
740 
init(const char * main_config)741 void CascadeConfig::init(const char *main_config)
742 {
743     ALOGV("CascadeConfig initialize with %s", main_config);
744 
745     // Main config file
746     CUwbNxpConfig config(main_config);
747     if (!config.isValid()) {
748         ALOGW("Failed to load main config file");
749         return;
750     }
751     mMainConfig = move(config);
752 
753     {
754         // UCI config file
755         std::string uciConfigFilePath = default_uci_config_path;
756         uciConfigFilePath += nxp_uci_config_file;
757 
758         CUwbNxpConfig config(uciConfigFilePath.c_str());
759         if (!config.isValid()) {
760             ALOGW("Failed to load uci config file:%s",
761                     uciConfigFilePath.c_str());
762         } else {
763             mUciConfig = move(config);
764         }
765     }
766 
767     char sku_value[PROPERTY_VALUE_MAX];
768     char revision_value[PROPERTY_VALUE_MAX];
769     property_get(prop_name_calsku, sku_value, prop_default_calsku);
770     property_get(prop_name_revision, revision_value, prop_default_revision);
771 
772     // Read EXTRA_CONF_PATH[N]
773     for (int i = 1; i <= 10; i++) {
774         char key[32];
775         snprintf(key, sizeof(key), "EXTRA_CONF_PATH_%d", i);
776         const uwbParam *param = mMainConfig.find(key);
777         if (!param)
778             continue;
779 
780         std::string filename(param->str_value());
781 
782         auto entry = std::make_pair(param->str_value(), CUwbNxpConfig(filename.c_str()));
783         mExtraConfig.emplace_back(std::move(entry));
784     }
785 
786     // evaluate <sku> and <revision>
787     mExtraConfSpecifiers.mCurSku = sku_value;
788     mExtraConfSpecifiers.mCurRevision = revision_value;
789     evaluateExtraConfPaths();
790 
791     // re-evaluate with "<extid>"
792     char extid_value[PROPERTY_VALUE_MAX];
793     if (!NxpConfig_GetStr(extid_config_name, extid_value, sizeof(extid_value))) {
794         strcpy(extid_value, extid_default_value);
795     }
796     mExtraConfSpecifiers.mCurExtid = extid_value;
797     evaluateExtraConfPaths();
798 
799     ALOGI("Provided specifiers: sku=[%s] revision=[%s] extid=[%s]", sku_value, revision_value, extid_value);
800 
801     // Pick one libuwb-countrycode.conf with the highest VERSION number
802     // from multiple directories specified by COUNTRY_CODE_CAP_FILE_LOCATION
803     size_t arrLen = 0;
804     if (NxpConfig_GetStrArrayLen(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, &arrLen) && arrLen > 0) {
805         constexpr size_t loc_max_len = 260;
806         auto loc = make_unique<char[]>(loc_max_len);
807         int version, max_version = -1;
808         string strPickedPath;
809         bool foundCapFile = false;
810         CUwbNxpConfig pickedConfig;
811 
812         for (int i = 0; i < arrLen; i++) {
813             if (!NxpConfig_GetStrArrayVal(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, i, loc.get(), loc_max_len)) {
814                 continue;
815             }
816             string strPath(loc.get());
817             strPath += country_code_config_name;
818 
819             ALOGV("Try to load %s", strPath.c_str());
820 
821             CUwbNxpConfig config(strPath.c_str());
822 
823             const uwbParam *param = config.find(NAME_NXP_COUNTRY_CODE_VERSION);
824             version = param ? atoi(param->str_value()) : -2;
825             if (version > max_version) {
826                 foundCapFile = true;
827                 pickedConfig = move(config);
828                 strPickedPath = move(strPath);
829                 max_version = version;
830             }
831         }
832         if (foundCapFile) {
833             mCapsConfig = move(pickedConfig);
834             ALOGI("CountryCodeCaps file %s loaded with VERSION=%d", strPickedPath.c_str(), max_version);
835         } else {
836             ALOGI("No CountryCodeCaps specified");
837         }
838     } else {
839         ALOGI(NAME_COUNTRY_CODE_CAP_FILE_LOCATION " was not specified, skip loading CountryCodeCaps");
840     }
841 
842     // Load region mapping
843     const uwbParam *param = find(NAME_REGION_MAP_PATH);
844     if (param) {
845         mRegionMap.loadMapping(param->str_value());
846     }
847 
848     ALOGD("CascadeConfig initialized");
849 
850     dump();
851 }
852 
deinit()853 void CascadeConfig::deinit()
854 {
855     mMainConfig.reset();
856     mExtraConfig.clear();
857     mCapsConfig.reset();
858     mRegionMap.reset();
859     mUciConfig.reset();
860     mExtraConfSpecifiers.reset();
861 }
862 
setCountryCode(const char country_code[2])863 bool CascadeConfig::setCountryCode(const char country_code[2])
864 {
865     string strRegion = mRegionMap.xlateCountryCode(country_code);
866 
867     if (strRegion == mExtraConfSpecifiers.mCurRegionCode) {
868         ALOGI("Same region code(%c%c --> %s), per-country configuration not updated.",
869               country_code[0], country_code[1], strRegion.c_str());
870         return false;
871     }
872 
873     ALOGI("Apply country code %c%c --> %s\n", country_code[0], country_code[1], strRegion.c_str());
874     mExtraConfSpecifiers.mCurRegionCode = strRegion;
875 
876     return evaluateExtraConfPaths();
877 }
878 
find(const char * name) const879 const uwbParam* CascadeConfig::find(const char *name) const
880 {
881     const uwbParam* param = NULL;
882 
883     param = mCapsConfig.find(name);
884     if (param)
885       return param;
886 
887     for (auto it = mExtraConfig.rbegin(); it != mExtraConfig.rend(); it++) {
888         auto &config = it->second;
889         param = config.find(name);
890         if (param)
891             break;
892     }
893     if (!param) {
894         param = mMainConfig.find(name);
895     }
896     if (!param) {
897         param = mUciConfig.find(name);
898     }
899     return param;
900 }
901 
902 // TODO: move these getValue() helpers out of the class
getValue(const char * name,char * pValue,size_t len) const903 bool CascadeConfig::getValue(const char* name, char* pValue, size_t len) const
904 {
905     const uwbParam *param = find(name);
906     if (!param)
907         return false;
908     if (param->getType() != uwbParam::type::STRING)
909         return false;
910     if (len < (param->str_len() + 1))
911         return false;
912 
913     strncpy(pValue, param->str_value(), len);
914     return true;
915 }
916 
getValue(const char * name,uint8_t * pValue,size_t len,size_t * readlen) const917 bool CascadeConfig::getValue(const char* name, uint8_t* pValue, size_t len, size_t* readlen) const
918 {
919     const uwbParam *param = find(name);
920     if (!param)
921         return false;
922     if (param->getType() != uwbParam::type::BYTEARRAY)
923         return false;
924     if (len < param->arr_len())
925         return false;
926     memcpy(pValue, param->arr_value(), param->arr_len());
927     if (readlen)
928         *readlen = param->arr_len();
929     return true;
930 }
931 
getValue(const char * name,unsigned long & rValue) const932 bool CascadeConfig::getValue(const char* name, unsigned long& rValue) const
933 {
934     const uwbParam *param = find(name);
935     if (!param)
936         return false;
937     if (param->getType() != uwbParam::type::NUMBER)
938         return false;
939 
940     rValue = param->numValue();
941     return true;
942 }
943 
944 }   // namespace
945 
946 static CascadeConfig gConfig;
947 
NxpConfig_Init(void)948 void NxpConfig_Init(void)
949 {
950     gConfig.init(default_nxp_config_path);
951 }
952 
NxpConfig_Deinit(void)953 void NxpConfig_Deinit(void)
954 {
955     gConfig.deinit();
956 }
957 
958 // return true if new per-country configuration file was load.
959 //        false if it can stay at the current configuration.
NxpConfig_SetCountryCode(const char country_code[2])960 bool NxpConfig_SetCountryCode(const char country_code[2])
961 {
962     return gConfig.setCountryCode(country_code);
963 }
964 
965 /*******************************************************************************
966 **
967 ** Function:    NxpConfig_GetStr
968 **
969 ** Description: API function for getting a string value of a setting
970 **
971 ** Returns:     True if found, otherwise False.
972 **
973 *******************************************************************************/
NxpConfig_GetStr(const char * name,char * pValue,size_t len)974 bool NxpConfig_GetStr(const char* name, char* pValue, size_t len)
975 {
976     return gConfig.getValue(name, pValue, len);
977 }
978 
979 /*******************************************************************************
980 **
981 ** Function:    NxpConfig_GetByteArray()
982 **
983 ** Description: Read byte array value from the config file.
984 **
985 ** Parameters:
986 **              name    - name of the config param to read.
987 **              pValue  - pointer to input buffer.
988 **              bufflen - input buffer length.
989 **              len     - out parameter to return the number of bytes read from config file,
990 **                        return -1 in case bufflen is not enough.
991 **
992 ** Returns:     TRUE[1] if config param name is found in the config file, else FALSE[0]
993 **
994 *******************************************************************************/
NxpConfig_GetByteArray(const char * name,uint8_t * pValue,size_t bufflen,size_t * len)995 bool NxpConfig_GetByteArray(const char* name, uint8_t* pValue, size_t bufflen, size_t* len)
996 {
997     return gConfig.getValue(name, pValue, bufflen, len);
998 }
999 
1000 /*******************************************************************************
1001 **
1002 ** Function:    NxpConfig_GetNum
1003 **
1004 ** Description: API function for getting a numerical value of a setting
1005 **
1006 ** Returns:     true, if successful
1007 **
1008 *******************************************************************************/
NxpConfig_GetNum(const char * name,void * pValue,size_t len)1009 bool NxpConfig_GetNum(const char* name, void* pValue, size_t len)
1010 {
1011     if ((name == nullptr) || (pValue == nullptr)){
1012         ALOGE("[%s] Invalid arguments", __func__);
1013         return false;
1014     }
1015     const uwbParam* pParam = gConfig.find(name);
1016 
1017     if ((pParam == nullptr) || (pParam->getType() != uwbParam::type::NUMBER)) {
1018         ALOGE("Config:%s not found in the config file", name);
1019         return false;
1020     }
1021 
1022     unsigned long v = pParam->numValue();
1023     switch (len)
1024     {
1025     case sizeof(unsigned long):
1026         *(static_cast<unsigned long*>(pValue)) = (unsigned long)v;
1027         break;
1028     case sizeof(unsigned short):
1029         *(static_cast<unsigned short*>(pValue)) = (unsigned short)v;
1030         break;
1031     case sizeof(unsigned char):
1032         *(static_cast<unsigned char*> (pValue)) = (unsigned char)v;
1033         break;
1034     default:
1035         ALOGE("[%s] unsupported length:%zu", __func__, len);
1036         return false;
1037     }
1038     return true;
1039 }
1040 
1041 // Get the length of a 'string-array' type parameter
NxpConfig_GetStrArrayLen(const char * name,size_t * pLen)1042 bool NxpConfig_GetStrArrayLen(const char* name, size_t* pLen)
1043 {
1044     const uwbParam* param = gConfig.find(name);
1045     if (!param || param->getType() != uwbParam::type::STRINGARRAY)
1046         return false;
1047 
1048     *pLen = param->str_arr_len();
1049     return true;
1050 }
1051 
1052 // Get a string value from 'string-array' type parameters, index zero-based
NxpConfig_GetStrArrayVal(const char * name,int index,char * pValue,size_t len)1053 bool NxpConfig_GetStrArrayVal(const char* name, int index, char* pValue, size_t len)
1054 {
1055     const uwbParam* param = gConfig.find(name);
1056     if (!param || param->getType() != uwbParam::type::STRINGARRAY)
1057         return false;
1058     if (index < 0 || index >= param->str_arr_len())
1059         return false;
1060 
1061     if (len < param->str_arr_elem_len(index) + 1)
1062         return false;
1063     strncpy(pValue, param->str_arr_elem(index), len);
1064     return true;
1065 }
1066