1 /*
2  * Copyright (C) 2012 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 /*
18  *  Import and export general routing data using a XML file.
19  */
20 
21 #include <android-base/stringprintf.h>
22 #include <base/logging.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 
26 /* NOTE:
27  * This has to be included AFTER the android-base includes since
28  * android-base/macros.h defines ATTRIBUTE_UNUSED, also used in the
29  * tiny XML library.
30  */
31 #include "RouteDataSet.h"
32 
33 #include "libxml/xmlmemory.h"
34 
35 using android::base::StringPrintf;
36 
37 extern std::string nfc_storage_path;
38 extern bool nfc_debug_enabled;
39 
40 /*******************************************************************************
41 **
42 ** Function:        AidBuffer
43 **
44 ** Description:     Parse a string of hex numbers.  Store result in an array of
45 **                  bytes.
46 **                  aid: string of hex numbers.
47 **
48 ** Returns:         None.
49 **
50 *******************************************************************************/
AidBuffer(std::string & aid)51 AidBuffer::AidBuffer(std::string& aid) : mBuffer(NULL), mBufferLen(0) {
52   unsigned int num = 0;
53   const char delimiter = ':';
54   std::string::size_type pos1 = 0;
55   std::string::size_type pos2 = aid.find_first_of(delimiter);
56 
57   // parse the AID string; each hex number is separated by a colon;
58   mBuffer = new uint8_t[aid.length()];
59   while (true) {
60     num = 0;
61     if (pos2 == std::string::npos) {
62       sscanf(aid.substr(pos1).c_str(), "%x", &num);
63       mBuffer[mBufferLen] = (uint8_t)num;
64       mBufferLen++;
65       break;
66     } else {
67       sscanf(aid.substr(pos1, pos2 - pos1 + 1).c_str(), "%x", &num);
68       mBuffer[mBufferLen] = (uint8_t)num;
69       mBufferLen++;
70       pos1 = pos2 + 1;
71       pos2 = aid.find_first_of(delimiter, pos1);
72     }
73   }
74 }
75 
76 /*******************************************************************************
77 **
78 ** Function:        ~AidBuffer
79 **
80 ** Description:     Release all resources.
81 **
82 ** Returns:         None.
83 **
84 *******************************************************************************/
~AidBuffer()85 AidBuffer::~AidBuffer() { delete[] mBuffer; }
86 
87 /*******************************************************************************/
88 /*******************************************************************************/
89 
90 const char* RouteDataSet::sConfigFile = "/param/route.xml";
91 
92 /*******************************************************************************
93 **
94 ** Function:        ~RouteDataSet
95 **
96 ** Description:     Release all resources.
97 **
98 ** Returns:         None.
99 **
100 *******************************************************************************/
~RouteDataSet()101 RouteDataSet::~RouteDataSet() { deleteDatabase(); }
102 
103 /*******************************************************************************
104 **
105 ** Function:        initialize
106 **
107 ** Description:     Initialize resources.
108 **
109 ** Returns:         True if ok.
110 **
111 *******************************************************************************/
initialize()112 bool RouteDataSet::initialize() {
113   DLOG_IF(INFO, nfc_debug_enabled)
114       << StringPrintf("%s: enter", "RouteDataSet::initialize");
115   // check that the libxml2 version in use is compatible
116   // with the version the software has been compiled with
117   LIBXML_TEST_VERSION
118   DLOG_IF(INFO, nfc_debug_enabled)
119       << StringPrintf("%s: exit; return=true", "RouteDataSet::initialize");
120   return true;
121 }
122 
123 /*******************************************************************************
124 **
125 ** Function:        deleteDatabase
126 **
127 ** Description:     Delete all routes stored in all databases.
128 **
129 ** Returns:         None.
130 **
131 *******************************************************************************/
deleteDatabase()132 void RouteDataSet::deleteDatabase() {
133   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
134       "%s: default db size=%zu; sec elem db size=%zu",
135       "RouteDataSet::deleteDatabase", mDefaultRouteDatabase.size(),
136       mSecElemRouteDatabase.size());
137   Database::iterator it;
138 
139   for (it = mDefaultRouteDatabase.begin(); it != mDefaultRouteDatabase.end();
140        it++)
141     delete (*it);
142   mDefaultRouteDatabase.clear();
143 
144   for (it = mSecElemRouteDatabase.begin(); it != mSecElemRouteDatabase.end();
145        it++)
146     delete (*it);
147   mSecElemRouteDatabase.clear();
148 }
149 
150 /*******************************************************************************
151 **
152 ** Function:        import
153 **
154 ** Description:     Import data from an XML file.  Fill the databases.
155 **
156 ** Returns:         True if ok.
157 **
158 *******************************************************************************/
import()159 bool RouteDataSet::import() {
160   static const char fn[] = "RouteDataSet::import";
161   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", fn);
162   bool retval = false;
163   xmlDocPtr doc;
164   xmlNodePtr node1;
165   std::string strFilename(nfc_storage_path);
166   strFilename += sConfigFile;
167 
168   deleteDatabase();
169 
170   doc = xmlParseFile(strFilename.c_str());
171   if (doc == NULL) {
172     DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: fail parse", fn);
173     goto TheEnd;
174   }
175 
176   node1 = xmlDocGetRootElement(doc);
177   if (node1 == NULL) {
178     LOG(ERROR) << StringPrintf("%s: fail root element", fn);
179     goto TheEnd;
180   }
181   DLOG_IF(INFO, nfc_debug_enabled)
182       << StringPrintf("%s: root=%s", fn, node1->name);
183 
184   node1 = node1->xmlChildrenNode;
185   while (node1)  // loop through all elements in <Routes ...
186   {
187     if (xmlStrcmp(node1->name, (const xmlChar*)"Route") == 0) {
188       xmlChar* value = xmlGetProp(node1, (const xmlChar*)"Type");
189       if (value &&
190           (xmlStrcmp(value, (const xmlChar*)"SecElemSelectedRoutes") == 0)) {
191         DLOG_IF(INFO, nfc_debug_enabled)
192             << StringPrintf("%s: found SecElemSelectedRoutes", fn);
193         xmlNodePtr node2 = node1->xmlChildrenNode;
194         while (node2)  // loop all elements in <Route
195                        // Type="SecElemSelectedRoutes" ...
196         {
197           if (xmlStrcmp(node2->name, (const xmlChar*)"Proto") == 0)
198             importProtocolRoute(node2, mSecElemRouteDatabase);
199           else if (xmlStrcmp(node2->name, (const xmlChar*)"Tech") == 0)
200             importTechnologyRoute(node2, mSecElemRouteDatabase);
201           node2 = node2->next;
202         }  // loop all elements in <Route Type="SecElemSelectedRoutes" ...
203       } else if (value &&
204                  (xmlStrcmp(value, (const xmlChar*)"DefaultRoutes") == 0)) {
205         DLOG_IF(INFO, nfc_debug_enabled)
206             << StringPrintf("%s: found DefaultRoutes", fn);
207         xmlNodePtr node2 = node1->xmlChildrenNode;
208         while (node2)  // loop all elements in <Route Type="DefaultRoutes" ...
209         {
210           if (xmlStrcmp(node2->name, (const xmlChar*)"Proto") == 0)
211             importProtocolRoute(node2, mDefaultRouteDatabase);
212           else if (xmlStrcmp(node2->name, (const xmlChar*)"Tech") == 0)
213             importTechnologyRoute(node2, mDefaultRouteDatabase);
214           node2 = node2->next;
215         }  // loop all elements in <Route Type="DefaultRoutes" ...
216       }
217       if (value) xmlFree(value);
218     }  // check <Route ...
219     node1 = node1->next;
220   }  // loop through all elements in <Routes ...
221   retval = true;
222 
223 TheEnd:
224   xmlFreeDoc(doc);
225   xmlCleanupParser();
226   DLOG_IF(INFO, nfc_debug_enabled)
227       << StringPrintf("%s: exit; return=%u", fn, retval);
228   return retval;
229 }
230 
231 /*******************************************************************************
232 **
233 ** Function:        saveToFile
234 **
235 ** Description:     Save XML data from a string into a file.
236 **                  routesXml: XML that represents routes.
237 **
238 ** Returns:         True if ok.
239 **
240 *******************************************************************************/
saveToFile(const char * routesXml)241 bool RouteDataSet::saveToFile(const char* routesXml) {
242   static const char fn[] = "RouteDataSet::saveToFile";
243   FILE* fh = NULL;
244   size_t actualWritten = 0;
245   bool retval = false;
246   std::string filename(nfc_storage_path);
247   int stat = 0;
248 
249   filename.append(sConfigFile);
250   fh = fopen(filename.c_str(), "w");
251   if (fh == NULL) {
252     LOG(ERROR) << StringPrintf("%s: fail to open file", fn);
253     return false;
254   }
255 
256   actualWritten = fwrite(routesXml, sizeof(char), strlen(routesXml), fh);
257   retval = actualWritten == strlen(routesXml);
258   fclose(fh);
259   DLOG_IF(INFO, nfc_debug_enabled)
260       << StringPrintf("%s: wrote %zu bytes", fn, actualWritten);
261   if (retval == false) LOG(ERROR) << StringPrintf("%s: error during write", fn);
262 
263   // set file permission to
264   // owner read, write; group read; other read
265   stat = chmod(filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
266   if (stat == -1) LOG(ERROR) << StringPrintf("%s: error during chmod", fn);
267   return retval;
268 }
269 
270 /*******************************************************************************
271 **
272 ** Function:        loadFromFile
273 **
274 ** Description:     Load XML data from file into a string.
275 **                  routesXml: string to receive XML data.
276 **
277 ** Returns:         True if ok.
278 **
279 *******************************************************************************/
loadFromFile(std::string & routesXml)280 bool RouteDataSet::loadFromFile(std::string& routesXml) {
281   FILE* fh = NULL;
282   size_t actual = 0;
283   char buffer[1024];
284   std::string filename(nfc_storage_path);
285 
286   filename.append(sConfigFile);
287   fh = fopen(filename.c_str(), "r");
288   if (fh == NULL) {
289     DLOG_IF(INFO, nfc_debug_enabled)
290         << StringPrintf("%s: fail to open file", "RouteDataSet::loadFromFile");
291     return false;
292   }
293 
294   while (true) {
295     actual = fread(buffer, sizeof(char), sizeof(buffer), fh);
296     if (actual == 0) break;
297     routesXml.append(buffer, actual);
298   }
299   fclose(fh);
300   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
301       "%s: read %zu bytes", "RouteDataSet::loadFromFile", routesXml.length());
302   return true;
303 }
304 
305 /*******************************************************************************
306 **
307 ** Function:        importProtocolRoute
308 **
309 ** Description:     Parse data for protocol routes.
310 **                  element: XML node for one protocol route.
311 **                  database: store data in this database.
312 **
313 ** Returns:         None.
314 **
315 *******************************************************************************/
importProtocolRoute(xmlNodePtr & element,Database & database)316 void RouteDataSet::importProtocolRoute(xmlNodePtr& element,
317                                        Database& database) {
318   const xmlChar* id = (const xmlChar*)"Id";
319   const xmlChar* secElem = (const xmlChar*)"SecElem";
320   const xmlChar* trueString = (const xmlChar*)"true";
321   const xmlChar* switchOn = (const xmlChar*)"SwitchOn";
322   const xmlChar* switchOff = (const xmlChar*)"SwitchOff";
323   const xmlChar* batteryOff = (const xmlChar*)"BatteryOff";
324   RouteDataForProtocol* data = new RouteDataForProtocol;
325   xmlChar* value = NULL;
326 
327   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
328       "%s: element=%s", "RouteDataSet::importProtocolRoute", element->name);
329   value = xmlGetProp(element, id);
330   if (value) {
331     if (xmlStrcmp(value, (const xmlChar*)"T1T") == 0)
332       data->mProtocol = NFA_PROTOCOL_MASK_T1T;
333     else if (xmlStrcmp(value, (const xmlChar*)"T2T") == 0)
334       data->mProtocol = NFA_PROTOCOL_MASK_T2T;
335     else if (xmlStrcmp(value, (const xmlChar*)"T3T") == 0)
336       data->mProtocol = NFA_PROTOCOL_MASK_T3T;
337     else if (xmlStrcmp(value, (const xmlChar*)"IsoDep") == 0)
338       data->mProtocol = NFA_PROTOCOL_MASK_ISO_DEP;
339     xmlFree(value);
340     DLOG_IF(INFO, nfc_debug_enabled)
341         << StringPrintf("%s: %s=0x%X", "RouteDataSet::importProtocolRoute", id,
342                         data->mProtocol);
343   }
344 
345   value = xmlGetProp(element, secElem);
346   if (value) {
347     data->mNfaEeHandle = strtol((char*)value, NULL, 16);
348     xmlFree(value);
349     data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE;
350     DLOG_IF(INFO, nfc_debug_enabled)
351         << StringPrintf("%s: %s=0x%X", "RouteDataSet::importProtocolRoute",
352                         secElem, data->mNfaEeHandle);
353   }
354 
355   value = xmlGetProp(element, switchOn);
356   if (value) {
357     data->mSwitchOn = (xmlStrcmp(value, trueString) == 0);
358     xmlFree(value);
359   }
360 
361   value = xmlGetProp(element, switchOff);
362   if (value) {
363     data->mSwitchOff = (xmlStrcmp(value, trueString) == 0);
364     xmlFree(value);
365   }
366 
367   value = xmlGetProp(element, batteryOff);
368   if (value) {
369     data->mBatteryOff = (xmlStrcmp(value, trueString) == 0);
370     xmlFree(value);
371   }
372   database.push_back(data);
373 }
374 
375 /*******************************************************************************
376 **
377 ** Function:        importTechnologyRoute
378 **
379 ** Description:     Parse data for technology routes.
380 **                  element: XML node for one technology route.
381 **                  database: store data in this database.
382 **
383 ** Returns:         None.
384 **
385 *******************************************************************************/
importTechnologyRoute(xmlNodePtr & element,Database & database)386 void RouteDataSet::importTechnologyRoute(xmlNodePtr& element,
387                                          Database& database) {
388   const xmlChar* id = (const xmlChar*)"Id";
389   const xmlChar* secElem = (const xmlChar*)"SecElem";
390   const xmlChar* trueString = (const xmlChar*)"true";
391   const xmlChar* switchOn = (const xmlChar*)"SwitchOn";
392   const xmlChar* switchOff = (const xmlChar*)"SwitchOff";
393   const xmlChar* batteryOff = (const xmlChar*)"BatteryOff";
394   RouteDataForTechnology* data = new RouteDataForTechnology;
395   xmlChar* value = NULL;
396 
397   DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
398       "%s: element=%s", "RouteDataSet::importTechnologyRoute", element->name);
399   value = xmlGetProp(element, id);
400   if (value) {
401     if (xmlStrcmp(value, (const xmlChar*)"NfcA") == 0)
402       data->mTechnology = NFA_TECHNOLOGY_MASK_A;
403     else if (xmlStrcmp(value, (const xmlChar*)"NfcB") == 0)
404       data->mTechnology = NFA_TECHNOLOGY_MASK_B;
405     else if (xmlStrcmp(value, (const xmlChar*)"NfcF") == 0)
406       data->mTechnology = NFA_TECHNOLOGY_MASK_F;
407     xmlFree(value);
408     DLOG_IF(INFO, nfc_debug_enabled)
409         << StringPrintf("%s: %s=0x%X", "RouteDataSet::importTechnologyRoute",
410                         id, data->mTechnology);
411   }
412 
413   value = xmlGetProp(element, secElem);
414   if (value) {
415     data->mNfaEeHandle = strtol((char*)value, NULL, 16);
416     xmlFree(value);
417     data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE;
418     DLOG_IF(INFO, nfc_debug_enabled)
419         << StringPrintf("%s: %s=0x%X", "RouteDataSet::importTechnologyRoute",
420                         secElem, data->mNfaEeHandle);
421   }
422 
423   value = xmlGetProp(element, switchOn);
424   if (value) {
425     data->mSwitchOn = (xmlStrcmp(value, trueString) == 0);
426     xmlFree(value);
427   }
428 
429   value = xmlGetProp(element, switchOff);
430   if (value) {
431     data->mSwitchOff = (xmlStrcmp(value, trueString) == 0);
432     xmlFree(value);
433   }
434 
435   value = xmlGetProp(element, batteryOff);
436   if (value) {
437     data->mBatteryOff = (xmlStrcmp(value, trueString) == 0);
438     xmlFree(value);
439   }
440   database.push_back(data);
441 }
442 
443 /*******************************************************************************
444 **
445 ** Function:        deleteFile
446 **
447 ** Description:     Delete route data XML file.
448 **
449 ** Returns:         True if ok.
450 **
451 *******************************************************************************/
deleteFile()452 bool RouteDataSet::deleteFile() {
453   static const char fn[] = "RouteDataSet::deleteFile";
454   std::string filename(nfc_storage_path);
455   filename.append(sConfigFile);
456   int stat = remove(filename.c_str());
457   DLOG_IF(INFO, nfc_debug_enabled)
458       << StringPrintf("%s: exit %u", fn, stat == 0);
459   return stat == 0;
460 }
461 
462 /*******************************************************************************
463 **
464 ** Function:        getDatabase
465 **
466 ** Description:     Obtain a database of routing data.
467 **                  selection: which database.
468 **
469 ** Returns:         Pointer to database.
470 **
471 *******************************************************************************/
getDatabase(DatabaseSelection selection)472 RouteDataSet::Database* RouteDataSet::getDatabase(DatabaseSelection selection) {
473   switch (selection) {
474     case DefaultRouteDatabase:
475       return &mDefaultRouteDatabase;
476     case SecElemRouteDatabase:
477       return &mSecElemRouteDatabase;
478   }
479   return NULL;
480 }
481 
482 /*******************************************************************************
483 **
484 ** Function:        printDiagnostic
485 **
486 ** Description:     Print some diagnostic output.
487 **
488 ** Returns:         None.
489 **
490 *******************************************************************************/
printDiagnostic()491 void RouteDataSet::printDiagnostic() {
492   static const char fn[] = "RouteDataSet::printDiagnostic";
493   Database* db = getDatabase(DefaultRouteDatabase);
494 
495   DLOG_IF(INFO, nfc_debug_enabled)
496       << StringPrintf("%s: default route database", fn);
497   for (Database::iterator iter = db->begin(); iter != db->end(); iter++) {
498     RouteData* routeData = *iter;
499     switch (routeData->mRouteType) {
500       case RouteData::ProtocolRoute: {
501         RouteDataForProtocol* proto = (RouteDataForProtocol*)routeData;
502         DLOG_IF(INFO, nfc_debug_enabled)
503             << StringPrintf("%s: ee h=0x%X; protocol=0x%X", fn,
504                             proto->mNfaEeHandle, proto->mProtocol);
505       } break;
506       case RouteData::TechnologyRoute: {
507         RouteDataForTechnology* tech = (RouteDataForTechnology*)routeData;
508         DLOG_IF(INFO, nfc_debug_enabled)
509             << StringPrintf("%s: ee h=0x%X; technology=0x%X", fn,
510                             tech->mNfaEeHandle, tech->mTechnology);
511       } break;
512     }
513   }
514 
515   DLOG_IF(INFO, nfc_debug_enabled)
516       << StringPrintf("%s: sec elem route database", fn);
517   db = getDatabase(SecElemRouteDatabase);
518   for (Database::iterator iter2 = db->begin(); iter2 != db->end(); iter2++) {
519     RouteData* routeData = *iter2;
520     switch (routeData->mRouteType) {
521       case RouteData::ProtocolRoute: {
522         RouteDataForProtocol* proto = (RouteDataForProtocol*)routeData;
523         DLOG_IF(INFO, nfc_debug_enabled)
524             << StringPrintf("%s: ee h=0x%X; protocol=0x%X", fn,
525                             proto->mNfaEeHandle, proto->mProtocol);
526       } break;
527       case RouteData::TechnologyRoute: {
528         RouteDataForTechnology* tech = (RouteDataForTechnology*)routeData;
529         DLOG_IF(INFO, nfc_debug_enabled)
530             << StringPrintf("%s: ee h=0x%X; technology=0x%X", fn,
531                             tech->mNfaEeHandle, tech->mTechnology);
532       } break;
533     }
534   }
535 }
536