1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <phNxpConfig.h> 18 #include <stdio.h> 19 #include <string> 20 #include <vector> 21 #include <list> 22 #include <sys/stat.h> 23 24 #include <phNxpLog.h> 25 26 #if GENERIC_TARGET 27 const char alternative_config_path[] = "/data/nfc/"; 28 #else 29 const char alternative_config_path[] = ""; 30 #endif 31 32 #if 1 33 const char transport_config_path[] = "/etc/"; 34 #else 35 const char transport_config_path[] = "res/"; 36 #endif 37 38 #define config_name "libnfc-nxp.conf" 39 #define extra_config_base "libnfc-nxp-" 40 #define extra_config_ext ".conf" 41 #define IsStringValue 0x80000000 42 43 const char config_timestamp_path[] = "/data/nfc/libnfc-nxpConfigState.bin"; 44 45 using namespace::std; 46 47 class CNxpNfcParam : public string 48 { 49 public: 50 CNxpNfcParam (); 51 CNxpNfcParam (const char* name, const string& value); 52 CNxpNfcParam (const char* name, unsigned long value); 53 virtual ~CNxpNfcParam (); 54 unsigned long numValue () const {return m_numValue;} 55 const char* str_value () const {return m_str_value.c_str();} 56 size_t str_len () const {return m_str_value.length();} 57 private: 58 string m_str_value; 59 unsigned long m_numValue; 60 }; 61 62 class CNxpNfcConfig : public vector<const CNxpNfcParam*> 63 { 64 public: 65 virtual ~CNxpNfcConfig (); 66 static CNxpNfcConfig& GetInstance (); 67 friend void readOptionalConfig (const char* optional); 68 int checkTimestamp (); 69 70 bool getValue (const char* name, char* pValue, size_t len) const; 71 bool getValue (const char* name, unsigned long& rValue) const; 72 bool getValue (const char* name, unsigned short & rValue) const; 73 bool getValue (const char* name, char* pValue, long len, long* readlen) const; 74 const CNxpNfcParam* find (const char* p_name) const; 75 void clean (); 76 private: 77 CNxpNfcConfig (); 78 bool readConfig (const char* name, bool bResetContent); 79 void moveFromList (); 80 void moveToList (); 81 void add (const CNxpNfcParam* pParam); 82 list<const CNxpNfcParam*> m_list; 83 bool mValidFile; 84 unsigned long m_timeStamp; 85 86 unsigned long state; 87 88 inline bool Is (unsigned long f) {return (state & f) == f;} 89 inline void Set (unsigned long f) {state |= f;} 90 inline void Reset (unsigned long f) {state &= ~f;} 91 }; 92 93 /******************************************************************************* 94 ** 95 ** Function: isPrintable() 96 ** 97 ** Description: determine if 'c' is printable 98 ** 99 ** Returns: 1, if printable, otherwise 0 100 ** 101 *******************************************************************************/ 102 inline bool isPrintable (char c) 103 { 104 return (c >= 'A' && c <= 'Z') || 105 (c >= 'a' && c <= 'z') || 106 (c >= '0' && c <= '9') || 107 c == '/' || c == '_' || c == '-' || c == '.'; 108 } 109 110 /******************************************************************************* 111 ** 112 ** Function: isDigit() 113 ** 114 ** Description: determine if 'c' is numeral digit 115 ** 116 ** Returns: true, if numerical digit 117 ** 118 *******************************************************************************/ 119 inline bool isDigit (char c, int base) 120 { 121 if ('0' <= c && c <= '9') 122 return true; 123 if (base == 16) 124 { 125 if (('A' <= c && c <= 'F') || 126 ('a' <= c && c <= 'f')) 127 return true; 128 } 129 return false; 130 } 131 132 /******************************************************************************* 133 ** 134 ** Function: getDigitValue() 135 ** 136 ** Description: return numerical value of a decimal or hex char 137 ** 138 ** Returns: numerical value if decimal or hex char, otherwise 0 139 ** 140 *******************************************************************************/ 141 inline int getDigitValue (char c, int base) 142 { 143 if ('0' <= c && c <= '9') 144 return c - '0'; 145 if (base == 16) 146 { 147 if ('A' <= c && c <= 'F') 148 return c - 'A' + 10; 149 else if ('a' <= c && c <= 'f') 150 return c - 'a' + 10; 151 } 152 return 0; 153 } 154 155 /******************************************************************************* 156 ** 157 ** Function: CNxpNfcConfig::readConfig() 158 ** 159 ** Description: read Config settings and parse them into a linked list 160 ** move the element from linked list to a array at the end 161 ** 162 ** Returns: 1, if there are any config data, 0 otherwise 163 ** 164 *******************************************************************************/ 165 bool CNxpNfcConfig::readConfig (const char* name, bool bResetContent) 166 { 167 enum { 168 BEGIN_LINE = 1, 169 TOKEN, 170 STR_VALUE, 171 NUM_VALUE, 172 BEGIN_HEX, 173 BEGIN_QUOTE, 174 END_LINE 175 }; 176 177 FILE* fd; 178 struct stat buf; 179 string token; 180 string strValue; 181 unsigned long numValue = 0; 182 CNxpNfcParam* pParam = NULL; 183 int i = 0; 184 int base = 0; 185 char c; 186 int bflag = 0; 187 state = BEGIN_LINE; 188 /* open config file, read it into a buffer */ 189 if ((fd = fopen (name, "rb")) == NULL) 190 { 191 ALOGE ("%s Cannot open config file %s\n", __func__, name); 192 if (bResetContent) 193 { 194 ALOGE ("%s Using default value for all settings\n", __func__); 195 mValidFile = false; 196 } 197 return false; 198 } 199 stat (name, &buf); 200 m_timeStamp = (unsigned long) buf.st_mtime; 201 202 mValidFile = true; 203 if (size() > 0) 204 { 205 if (bResetContent) 206 clean (); 207 else 208 moveToList (); 209 } 210 211 while (!feof (fd) && fread (&c, 1, 1, fd) == 1) 212 { 213 switch (state & 0xff) 214 { 215 case BEGIN_LINE: 216 if (c == '#') 217 { 218 state = END_LINE; 219 } 220 else if (isPrintable (c)) 221 { 222 i = 0; 223 token.erase (); 224 strValue.erase (); 225 state = TOKEN; 226 token.push_back (c); 227 } 228 break; 229 case TOKEN: 230 if (c == '=') 231 { 232 token.push_back ('\0'); 233 state = BEGIN_QUOTE; 234 } 235 else if (isPrintable (c)) 236 { 237 token.push_back (c); 238 } 239 else 240 { 241 state = END_LINE; 242 } 243 break; 244 case BEGIN_QUOTE: 245 if (c == '"') 246 { 247 state = STR_VALUE; 248 base = 0; 249 } 250 else if (c == '0') 251 { 252 state = BEGIN_HEX; 253 } 254 else if (isDigit (c, 10)) 255 { 256 state = NUM_VALUE; 257 base = 10; 258 numValue = getDigitValue (c, base); 259 i = 0; 260 } 261 else if (c == '{') 262 { 263 state = NUM_VALUE; 264 bflag = 1; 265 base = 16; 266 i = 0; 267 Set (IsStringValue); 268 } 269 else 270 state = END_LINE; 271 272 break; 273 case BEGIN_HEX: 274 if (c == 'x' || c == 'X') 275 { 276 state = NUM_VALUE; 277 base = 16; 278 numValue = 0; 279 i = 0; 280 break; 281 } 282 else if (isDigit (c, 10)) 283 { 284 state = NUM_VALUE; 285 base = 10; 286 numValue = getDigitValue (c, base); 287 break; 288 } 289 else if (c != '\n' && c != '\r') 290 { 291 state = END_LINE; 292 break; 293 } 294 // fall through to numValue to handle numValue 295 296 case NUM_VALUE: 297 if (isDigit (c, base)) 298 { 299 numValue *= base; 300 numValue += getDigitValue (c, base); 301 ++i; 302 } 303 else if(bflag == 1 && (c == ' ' || c == '\r' || c=='\n' || c=='\t')) 304 { 305 break; 306 } 307 else if (base == 16 && (c== ','|| c == ':' || c == '-' || c == ' ' || c == '}')) 308 { 309 310 if( c=='}' ) 311 { 312 bflag = 0; 313 } 314 if (i > 0) 315 { 316 int n = (i+1) / 2; 317 while (n-- > 0) 318 { 319 numValue = numValue >> (n * 8); 320 unsigned char c = (numValue) & 0xFF; 321 strValue.push_back (c); 322 } 323 } 324 325 Set (IsStringValue); 326 numValue = 0; 327 i = 0; 328 } 329 else 330 { 331 if (c == '\n' || c == '\r') 332 { 333 if (bflag == 0) 334 { 335 state = BEGIN_LINE; 336 } 337 } 338 else 339 { 340 if (bflag == 0) 341 { 342 state = END_LINE; 343 } 344 } 345 if (Is (IsStringValue) && base == 16 && i > 0) 346 { 347 int n = (i+1) / 2; 348 while (n-- > 0) 349 strValue.push_back (((numValue >> (n * 8)) & 0xFF)); 350 } 351 if (strValue.length() > 0) 352 pParam = new CNxpNfcParam (token.c_str (), strValue); 353 else 354 pParam = new CNxpNfcParam (token.c_str (), numValue); 355 356 add (pParam); 357 strValue.erase (); 358 numValue = 0; 359 } 360 break; 361 case STR_VALUE: 362 if (c == '"') 363 { 364 strValue.push_back('\0'); 365 state = END_LINE; 366 pParam = new CNxpNfcParam(token.c_str(), strValue); 367 add(pParam); 368 } 369 else if (isPrintable(c)) 370 { 371 strValue.push_back(c); 372 } 373 break; 374 case END_LINE: 375 if (c == '\n' || c == '\r') 376 { 377 state = BEGIN_LINE; 378 } 379 break; 380 default: 381 break; 382 } 383 } 384 385 fclose (fd); 386 387 moveFromList (); 388 return size () > 0; 389 } 390 391 /******************************************************************************* 392 ** 393 ** Function: CNxpNfcConfig::CNxpNfcConfig() 394 ** 395 ** Description: class constructor 396 ** 397 ** Returns: none 398 ** 399 *******************************************************************************/ 400 CNxpNfcConfig::CNxpNfcConfig () : 401 mValidFile (true), 402 m_timeStamp (0), 403 state (0) 404 { 405 } 406 407 /******************************************************************************* 408 ** 409 ** Function: CNxpNfcConfig::~CNxpNfcConfig() 410 ** 411 ** Description: class destructor 412 ** 413 ** Returns: none 414 ** 415 *******************************************************************************/ 416 CNxpNfcConfig::~CNxpNfcConfig () 417 { 418 } 419 420 /******************************************************************************* 421 ** 422 ** Function: CNxpNfcConfig::GetInstance() 423 ** 424 ** Description: get class singleton object 425 ** 426 ** Returns: none 427 ** 428 *******************************************************************************/ 429 CNxpNfcConfig& CNxpNfcConfig::GetInstance () 430 { 431 static CNxpNfcConfig theInstance; 432 433 if (theInstance.size () == 0 && theInstance.mValidFile) 434 { 435 string strPath; 436 if (alternative_config_path [0] != '\0') 437 { 438 strPath.assign (alternative_config_path); 439 strPath += config_name; 440 theInstance.readConfig (strPath.c_str (), true); 441 if (!theInstance.empty ()) 442 { 443 return theInstance; 444 } 445 } 446 strPath.assign (transport_config_path); 447 strPath += config_name; 448 theInstance.readConfig (strPath.c_str (), true); 449 } 450 451 return theInstance; 452 } 453 454 /******************************************************************************* 455 ** 456 ** Function: CNxpNfcConfig::getValue() 457 ** 458 ** Description: get a string value of a setting 459 ** 460 ** Returns: true if setting exists 461 ** false if setting does not exist 462 ** 463 *******************************************************************************/ 464 bool CNxpNfcConfig::getValue (const char* name, char* pValue, size_t len) const 465 { 466 const CNxpNfcParam* pParam = find (name); 467 if (pParam == NULL) 468 return false; 469 470 if (pParam->str_len () > 0) 471 { 472 memset (pValue, 0, len); 473 memcpy (pValue, pParam->str_value (), pParam->str_len ()); 474 return true; 475 } 476 return false; 477 } 478 479 bool CNxpNfcConfig::getValue (const char* name, char* pValue, long len,long* readlen) const 480 { 481 const CNxpNfcParam* pParam = find (name); 482 if (pParam == NULL) 483 return false; 484 485 if (pParam->str_len () > 0) 486 { 487 if(pParam->str_len () <= (unsigned long) len) 488 { 489 memset (pValue, 0, len); 490 memcpy (pValue, pParam->str_value (), pParam->str_len ()); 491 *readlen = pParam->str_len (); 492 } 493 else 494 { 495 *readlen = -1; 496 } 497 498 return true; 499 } 500 return false; 501 } 502 503 /******************************************************************************* 504 ** 505 ** Function: CNxpNfcConfig::getValue() 506 ** 507 ** Description: get a long numerical value of a setting 508 ** 509 ** Returns: true if setting exists 510 ** false if setting does not exist 511 ** 512 *******************************************************************************/ 513 bool CNxpNfcConfig::getValue (const char* name, unsigned long& rValue) const 514 { 515 const CNxpNfcParam* pParam = find (name); 516 if (pParam == NULL) 517 return false; 518 519 if (pParam->str_len () == 0) 520 { 521 rValue = static_cast<unsigned long> (pParam->numValue ()); 522 return true; 523 } 524 return false; 525 } 526 527 /******************************************************************************* 528 ** 529 ** Function: CNxpNfcConfig::getValue() 530 ** 531 ** Description: get a short numerical value of a setting 532 ** 533 ** Returns: true if setting exists 534 ** false if setting does not exist 535 ** 536 *******************************************************************************/ 537 bool CNxpNfcConfig::getValue (const char* name, unsigned short& rValue) const 538 { 539 const CNxpNfcParam* pParam = find (name); 540 if (pParam == NULL) 541 return false; 542 543 if (pParam->str_len () == 0) 544 { 545 rValue = static_cast<unsigned short> (pParam->numValue ()); 546 return true; 547 } 548 return false; 549 } 550 551 /******************************************************************************* 552 ** 553 ** Function: CNxpNfcConfig::find() 554 ** 555 ** Description: search if a setting exist in the setting array 556 ** 557 ** Returns: pointer to the setting object 558 ** 559 *******************************************************************************/ 560 const CNxpNfcParam* CNxpNfcConfig::find (const char* p_name) const 561 { 562 if (size () == 0) 563 return NULL; 564 565 for (const_iterator it = begin (), itEnd = end (); it != itEnd; ++it) 566 { 567 if (**it < p_name) 568 { 569 continue; 570 } 571 else if (**it == p_name) 572 { 573 if ((*it)->str_len () > 0) 574 { 575 NXPLOG_EXTNS_D ("%s found %s=%s\n", __func__, p_name, (*it)->str_value()); 576 } 577 else 578 { 579 NXPLOG_EXTNS_D ("%s found %s=(0x%lx)\n", __func__, p_name, (*it)->numValue()); 580 } 581 return *it; 582 } 583 else 584 break; 585 } 586 return NULL; 587 } 588 589 /******************************************************************************* 590 ** 591 ** Function: CNxpNfcConfig::clean() 592 ** 593 ** Description: reset the setting array 594 ** 595 ** Returns: none 596 ** 597 *******************************************************************************/ 598 void CNxpNfcConfig::clean () 599 { 600 if (size () == 0) 601 return; 602 603 for (iterator it = begin (), itEnd = end (); it != itEnd; ++it) 604 delete *it; 605 606 clear (); 607 } 608 609 /******************************************************************************* 610 ** 611 ** Function: CNxpNfcConfig::Add() 612 ** 613 ** Description: add a setting object to the list 614 ** 615 ** Returns: none 616 ** 617 *******************************************************************************/ 618 void CNxpNfcConfig::add (const CNxpNfcParam* pParam) 619 { 620 if (m_list.size () == 0) 621 { 622 m_list.push_back (pParam); 623 return; 624 } 625 for (list<const CNxpNfcParam*>::iterator it = m_list.begin (), itEnd = m_list.end (); it != itEnd; ++it) 626 { 627 if (**it < pParam->c_str ()) 628 continue; 629 630 m_list.insert (it, pParam); 631 return; 632 } 633 m_list.push_back (pParam); 634 } 635 636 /******************************************************************************* 637 ** 638 ** Function: CNxpNfcConfig::moveFromList() 639 ** 640 ** Description: move the setting object from list to array 641 ** 642 ** Returns: none 643 ** 644 *******************************************************************************/ 645 void CNxpNfcConfig::moveFromList () 646 { 647 if (m_list.size () == 0) 648 return; 649 650 for (list<const CNxpNfcParam*>::iterator it = m_list.begin (), itEnd = m_list.end (); it != itEnd; ++it) 651 push_back (*it); 652 653 m_list.clear (); 654 } 655 656 /******************************************************************************* 657 ** 658 ** Function: CNxpNfcConfig::moveToList() 659 ** 660 ** Description: move the setting object from array to list 661 ** 662 ** Returns: none 663 ** 664 *******************************************************************************/ 665 void CNxpNfcConfig::moveToList () 666 { 667 if (m_list.size () != 0) 668 m_list.clear (); 669 670 for (iterator it = begin (), itEnd = end (); it != itEnd; ++it) 671 m_list.push_back (*it); 672 673 clear(); 674 } 675 676 /******************************************************************************* 677 ** 678 ** Function: CNxpNfcConfig::checkTimestamp() 679 ** 680 ** Description: check if config file has modified 681 ** 682 ** Returns: 0 if not modified, 1 otherwise. 683 ** 684 *******************************************************************************/ 685 int CNxpNfcConfig::checkTimestamp () 686 { 687 FILE* fd; 688 struct stat st; 689 unsigned long value = 0; 690 int ret = 0; 691 692 if (stat(config_timestamp_path, &st) != 0) 693 { 694 ALOGD ("%s file %s not exist, creat it.\n", __func__, config_timestamp_path); 695 if ((fd = fopen (config_timestamp_path, "w+")) != NULL) 696 { 697 fwrite (&m_timeStamp, sizeof(unsigned long), 1, fd); 698 fclose (fd); 699 } 700 return 1; 701 } 702 else 703 { 704 fd = fopen (config_timestamp_path, "r+"); 705 if (fd == NULL) 706 { 707 ALOGE ("%s Cannot open file %s\n", __func__, config_timestamp_path); 708 return 1; 709 } 710 711 fread (&value, sizeof(unsigned long), 1, fd); 712 ret = (value != m_timeStamp); 713 if (ret) 714 { 715 fseek (fd, 0, SEEK_SET); 716 fwrite (&m_timeStamp, sizeof(unsigned long), 1, fd); 717 } 718 fclose (fd); 719 } 720 return ret; 721 } 722 723 /******************************************************************************* 724 ** 725 ** Function: CNxpNfcParam::CNxpNfcParam() 726 ** 727 ** Description: class constructor 728 ** 729 ** Returns: none 730 ** 731 *******************************************************************************/ 732 CNxpNfcParam::CNxpNfcParam () : 733 m_numValue (0) 734 { 735 } 736 737 /******************************************************************************* 738 ** 739 ** Function: CNxpNfcParam::~CNxpNfcParam() 740 ** 741 ** Description: class destructor 742 ** 743 ** Returns: none 744 ** 745 *******************************************************************************/ 746 CNxpNfcParam::~CNxpNfcParam () 747 { 748 } 749 750 /******************************************************************************* 751 ** 752 ** Function: CNxpNfcParam::CNxpNfcParam() 753 ** 754 ** Description: class copy constructor 755 ** 756 ** Returns: none 757 ** 758 *******************************************************************************/ 759 CNxpNfcParam::CNxpNfcParam (const char* name, const string& value) : 760 string (name), 761 m_str_value (value), 762 m_numValue (0) 763 { 764 } 765 766 /******************************************************************************* 767 ** 768 ** Function: CNxpNfcParam::CNxpNfcParam() 769 ** 770 ** Description: class copy constructor 771 ** 772 ** Returns: none 773 ** 774 *******************************************************************************/ 775 CNxpNfcParam::CNxpNfcParam (const char* name, unsigned long value) : 776 string (name), 777 m_numValue (value) 778 { 779 } 780 781 /******************************************************************************* 782 ** 783 ** Function: GetStrValue 784 ** 785 ** Description: API function for getting a string value of a setting 786 ** 787 ** Returns: True if found, otherwise False. 788 ** 789 *******************************************************************************/ 790 extern "C" int GetNxpStrValue (const char* name, char* pValue, unsigned long len) 791 { 792 CNxpNfcConfig& rConfig = CNxpNfcConfig::GetInstance (); 793 794 return rConfig.getValue (name, pValue, len); 795 } 796 797 /******************************************************************************* 798 ** 799 ** Function: GetByteArrayValue() 800 ** 801 ** Description: Read byte array value from the config file. 802 ** 803 ** Parameters: 804 ** name - name of the config param to read. 805 ** pValue - pointer to input buffer. 806 ** bufflen - input buffer length. 807 ** len - out parameter to return the number of bytes read from config file, 808 ** return -1 in case bufflen is not enough. 809 ** 810 ** Returns: TRUE[1] if config param name is found in the config file, else FALSE[0] 811 ** 812 *******************************************************************************/ 813 extern "C" int GetNxpByteArrayValue (const char* name, char* pValue, long bufflen, long *len) 814 { 815 CNxpNfcConfig& rConfig = CNxpNfcConfig::GetInstance (); 816 817 return rConfig.getValue (name, pValue, bufflen,len); 818 } 819 820 /******************************************************************************* 821 ** 822 ** Function: GetNumValue 823 ** 824 ** Description: API function for getting a numerical value of a setting 825 ** 826 ** Returns: true, if successful 827 ** 828 *******************************************************************************/ 829 extern "C" int GetNxpNumValue (const char* name, void* pValue, unsigned long len) 830 { 831 if (!pValue) 832 return false; 833 834 CNxpNfcConfig& rConfig = CNxpNfcConfig::GetInstance (); 835 const CNxpNfcParam* pParam = rConfig.find (name); 836 837 if (pParam == NULL) 838 return false; 839 840 unsigned long v = pParam->numValue (); 841 if (v == 0 && pParam->str_len () > 0 && pParam->str_len () < 4) 842 { 843 const unsigned char* p = (const unsigned char*) pParam->str_value (); 844 for (unsigned int i = 0 ; i < pParam->str_len (); ++i) 845 { 846 v *= 256; 847 v += *p++; 848 } 849 } 850 switch (len) 851 { 852 case sizeof(unsigned long): 853 *(static_cast<unsigned long*>(pValue)) = (unsigned long) v; 854 break; 855 case sizeof(unsigned short): 856 *(static_cast<unsigned short*>(pValue)) = (unsigned short) v; 857 break; 858 case sizeof(unsigned char): 859 *(static_cast<unsigned char*> (pValue)) = (unsigned char) v; 860 break; 861 default: 862 return false; 863 } 864 return true; 865 } 866 867 /******************************************************************************* 868 ** 869 ** Function: resetConfig 870 ** 871 ** Description: reset settings array 872 ** 873 ** Returns: none 874 ** 875 *******************************************************************************/ 876 extern "C" void resetNxpConfig () 877 { 878 CNxpNfcConfig& rConfig = CNxpNfcConfig::GetInstance (); 879 880 rConfig.clean (); 881 } 882 883 /******************************************************************************* 884 ** 885 ** Function: readOptionalConfig() 886 ** 887 ** Description: read Config settings from an optional conf file 888 ** 889 ** Returns: none 890 ** 891 *******************************************************************************/ 892 void readOptionalConfig (const char* extra) 893 { 894 string strPath; 895 strPath.assign (transport_config_path); 896 if (alternative_config_path [0] != '\0') 897 strPath.assign (alternative_config_path); 898 899 strPath += extra_config_base; 900 strPath += extra; 901 strPath += extra_config_ext; 902 CNxpNfcConfig::GetInstance ().readConfig (strPath.c_str (), false); 903 } 904 905 /******************************************************************************* 906 ** 907 ** Function: isNxpConfigModified() 908 ** 909 ** Description: check if config file has modified 910 ** 911 ** Returns: 0 if not modified, 1 otherwise. 912 ** 913 *******************************************************************************/ 914 extern "C" int isNxpConfigModified () 915 { 916 CNxpNfcConfig& rConfig = CNxpNfcConfig::GetInstance (); 917 return rConfig.checkTimestamp (); 918 } 919