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