update the latest source
[platform/core/connectivity/smartcard-service.git] / server / ServerResource.cpp
1 /*
2 * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
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 /* standard library header */
19 #include <stdio.h>
20 #include <string.h>
21 #include <dlfcn.h>
22 #include <errno.h>
23 #include <dirent.h>
24
25 /* SLP library header */
26
27 /* local header */
28 #include "Debug.h"
29 #include "ServerResource.h"
30 #include "TerminalInterface.h"
31 #include "APDUHelper.h"
32 #include "SignatureHelper.h"
33 #include "GPSEACL.h"
34
35 namespace smartcard_service_api
36 {
37         unsigned int IntegerHandle::newHandle = 0;
38         set<unsigned int> IntegerHandle::setHandles;
39         PMutex IntegerHandle::mutexLock;
40
41         unsigned int IntegerHandle::assignHandle()
42         {
43                 SCOPE_LOCK(mutexLock)
44                 {
45                         pair<set<unsigned int>::iterator, bool> result;
46
47                         do
48                         {
49                                 newHandle++;
50                                 if (newHandle == (unsigned int)-1)
51                                 {
52                                         newHandle = 1;
53                                 }
54
55                                 result = setHandles.insert(newHandle);
56
57                         }
58                         while (!result.second);
59                 }
60
61                 SCARD_DEBUG("assign handle : newHandle [%d]", newHandle);
62
63                 return newHandle;
64         }
65
66         void IntegerHandle::releaseHandle(unsigned int handle)
67         {
68                 SCARD_DEBUG("will be released : Handle [%d]", handle);
69
70                 SCOPE_LOCK(mutexLock)
71                 {
72                         setHandles.erase(handle);
73                 }
74         }
75
76 #define OMAPI_SE_PATH "/usr/lib/se"
77
78         ServerResource::ServerResource()
79         {
80                 SCARD_BEGIN();
81
82                 serverIPC = ServerIPC::getInstance();
83                 serverDispatcher = ServerDispatcher::getInstance();
84
85 #if 1
86                 loadSecureElements();
87 #endif
88                 SCARD_END();
89         }
90
91         ServerResource::~ServerResource()
92         {
93         }
94
95         ServerResource &ServerResource::getInstance()
96         {
97                 static ServerResource serverResource;
98
99                 return serverResource;
100         }
101
102         bool ServerResource::createClient(void *ioChannel, int socket, int watchID, int state, int pid)
103         {
104                 bool result = false;
105
106                 if (getClient(socket) == NULL)
107                 {
108                         ClientInstance *instance = new ClientInstance(ioChannel, socket, watchID, state, pid);
109                         if (instance != NULL)
110                         {
111                                 mapClients.insert(make_pair(socket, instance));
112                                 result = true;
113                         }
114                         else
115                         {
116                                 SCARD_DEBUG_ERR("alloc failed");
117                         }
118                 }
119                 else
120                 {
121                         SCARD_DEBUG_ERR("client already exist [%d]", socket);
122                 }
123
124                 return result;
125         }
126
127         ClientInstance *ServerResource::getClient(int socket)
128         {
129                 ClientInstance *result = NULL;
130                 map<int, ClientInstance *>::iterator item;
131
132                 if ((item = mapClients.find(socket)) != mapClients.end())
133                 {
134                         result = item->second;
135                 }
136
137                 return result;
138         }
139
140         void ServerResource::setPID(int socket, int pid)
141         {
142                 map<int, ClientInstance *>::iterator item;
143
144                 if ((item = mapClients.find(socket)) != mapClients.end())
145                 {
146                         if (item->second->getPID() < 0)
147                                 item->second->setPID(pid);
148                 }
149         }
150
151         void ServerResource::removeClient(int socket)
152         {
153                 map<int, ClientInstance *>::iterator item;
154
155                 if ((item = mapClients.find(socket)) != mapClients.end())
156                 {
157                         ServerIPC::getInstance()->releaseClient(item->second->getIOChannel(), item->second->getSocket(), item->second->getWatchID());
158
159                         delete item->second;
160                         mapClients.erase(item);
161                 }
162                 else
163                 {
164                         SCARD_DEBUG("client exists already [%d]", socket);
165                 }
166         }
167
168         void ServerResource::removeClients()
169         {
170                 map<int, ClientInstance *>::iterator item;
171
172                 for (item = mapClients.begin(); item != mapClients.end(); item++)
173                 {
174                         ServerIPC::getInstance()->releaseClient(item->second->getIOChannel(), item->second->getSocket(), item->second->getWatchID());
175
176                         delete item->second;
177                 }
178
179                 mapClients.clear();
180         }
181
182         bool ServerResource::createService(int socket, unsigned int context)
183         {
184                 bool result = false;
185                 ClientInstance *instance = NULL;
186
187                 if ((instance = getClient(socket)) != NULL)
188                 {
189                         if ((result = instance->createService(context)) == false)
190                         {
191                                 SCARD_DEBUG_ERR("ClientInstance::createService failed [%d] [%d]", socket, context);
192                         }
193                 }
194                 else
195                 {
196                         SCARD_DEBUG_ERR("client doesn't exist [%d]", socket);
197                 }
198
199                 return result;
200         }
201
202         ServiceInstance *ServerResource::getService(int socket, unsigned int context)
203         {
204                 ServiceInstance *result = NULL;
205                 ClientInstance *instance = NULL;
206
207                 if ((instance = getClient(socket)) != NULL)
208                 {
209                         result = instance->getService(context);
210                 }
211                 else
212                 {
213                         SCARD_DEBUG_ERR("client doesn't exist [%d]", socket);
214                 }
215
216                 return result;
217         }
218
219         void ServerResource::removeService(int socket, unsigned int context)
220         {
221                 ClientInstance *instance = NULL;
222
223                 if ((instance = getClient(socket)) != NULL)
224                 {
225                         instance->removeService(context);
226                 }
227                 else
228                 {
229                         SCARD_DEBUG_ERR("client doesn't exist [%d]", socket);
230                 }
231         }
232
233         void ServerResource::removeServices(int socket)
234         {
235                 ClientInstance *instance = NULL;
236
237                 if ((instance = getClient(socket)) != NULL)
238                 {
239                         instance->removeServices();
240                 }
241                 else
242                 {
243                         SCARD_DEBUG_ERR("client doesn't exist [%d]", socket);
244                 }
245         }
246
247         Terminal *ServerResource::getTerminal(unsigned int terminalID)
248         {
249                 Terminal *result = NULL;
250                 map<unsigned int, Terminal *>::iterator item;
251
252                 if ((item = mapTerminals.find(terminalID)) != mapTerminals.end())
253                 {
254                         result = item->second;
255                 }
256                 else
257                 {
258                         SCARD_DEBUG_ERR("Terminal doesn't exist [%d]", terminalID);
259                 }
260
261                 return result;
262         }
263
264         Terminal *ServerResource::getTerminal(const char *name)
265         {
266                 Terminal *result = NULL;
267                 map<unsigned int, Terminal *>::iterator item;
268
269                 for (item = mapTerminals.begin(); item != mapTerminals.end(); item++)
270                 {
271                         if (strncmp(name, item->second->getName(), strlen(name)) == 0)
272                         {
273                                 result = item->second;
274                                 break;
275                         }
276                 }
277
278                 return result;
279         }
280
281         unsigned int ServerResource::createSession(int socket, unsigned int context, unsigned int terminalID, ByteArray packageCert, void *caller)
282         {
283                 unsigned int result = -1;
284                 Terminal *temp = NULL;
285                 ServiceInstance *instance = NULL;
286
287                 if ((instance = getService(socket, context)) != NULL)
288                 {
289                         if ((temp = getTerminal(terminalID)) != NULL)
290                         {
291                                 result = instance->openSession(temp, packageCert, caller);
292                         }
293                 }
294                 else
295                 {
296                         SCARD_DEBUG_ERR("getService doesn't exist : socket [%d], context [%d]", socket, context);
297                 }
298
299                 return result;
300         }
301
302         ServerSession *ServerResource::getSession(int socket, unsigned int context, unsigned int sessionID)
303         {
304                 ServerSession *result = NULL;
305                 ServiceInstance *instance = NULL;
306
307                 if ((instance = getService(socket, context)) != NULL)
308                 {
309                         result = instance->getSession(sessionID);
310                 }
311                 else
312                 {
313                         SCARD_DEBUG_ERR("Session doesn't exist : socket [%d], context [%d], handle [%d]", socket, context, sessionID);
314                 }
315
316                 return result;
317         }
318
319         unsigned int ServerResource::getChannelCount(int socket, unsigned int context, unsigned int sessionID)
320         {
321                 unsigned int result = -1;
322                 ServiceInstance *instance = NULL;
323
324                 if ((instance = getService(socket, context)) != NULL)
325                 {
326                         result = instance->getChannelCountBySession(sessionID);
327                 }
328                 else
329                 {
330                         SCARD_DEBUG_ERR("getService doesn't exist : socket [%d], context [%d]", socket, context);
331                 }
332
333                 return result;
334         }
335
336         void ServerResource::removeSession(int socket, unsigned int context, unsigned int sessionID)
337         {
338                 ServiceInstance *instance = NULL;
339
340                 if ((instance = getService(socket, context)) != NULL)
341                 {
342                         instance->closeSession(sessionID);
343                 }
344                 else
345                 {
346                         SCARD_DEBUG_ERR("getService doesn't exist : socket [%d], context [%d]", socket, context);
347                 }
348         }
349
350         unsigned int ServerResource::createChannel(int socket, unsigned int context, unsigned int sessionID, int channelType, ByteArray aid)
351         {
352                 unsigned int result = -1;
353                 ServiceInstance *client = NULL;
354
355                 if ((client = getService(socket, context)) != NULL)
356                 {
357                         if (client->isVaildSessionHandle(sessionID) == true)
358                         {
359                                 AccessControlList *acList = NULL;
360                                 ServerSession *session = NULL;
361                                 Terminal *terminal = NULL;
362
363                                 terminal = client->getTerminal(sessionID);
364                                 session = client->getSession(sessionID);
365                                 if (terminal != NULL && session != NULL)
366                                 {
367                                         int rv = 0;
368                                         int channelNum = 0;
369                                         ByteArray certHash;
370                                         ByteArray selectResponse;
371                                         ByteArray command;
372                                         char filename[1024] = { 0, };
373
374                                         /* check exceptional case */
375                                         SignatureHelper::getProcessName(client->getParent()->getPID(), filename, sizeof(filename));
376                                         if (strncmp(filename, "ozD3Dw1MZruTDKHWGgYaDib2B2LV4/nfT+8b/g1Vsk8=", sizeof(filename)) != 0)
377                                         {
378 #if 1
379                                                 certHash = session->packageCert;
380 #else
381                                                 certHash = client->getParent()->getCertificationHash();
382 #endif
383                                                 /* request open channel sequence */
384                                                 if ((acList = getAccessControlList(terminal)) == NULL)
385                                                 {
386                                                         SCARD_DEBUG_ERR("acList is null");
387
388                                                         return result;
389                                                 }
390
391                                                 if (acList->isAuthorizedAccess(aid, certHash) == false)
392                                                 {
393                                                         SCARD_DEBUG_ERR("unauthorized access, aid %s, hash %s", aid.toString(), certHash.toString());
394
395                                                         return result;
396                                                 }
397                                         }
398
399                                         if (channelType == 1)
400                                         {
401                                                 ByteArray response;
402
403                                                 /* open channel */
404                                                 command = APDUHelper::generateAPDU(APDUHelper::COMMAND_OPEN_LOGICAL_CHANNEL, 0, ByteArray::EMPTY);
405                                                 rv = terminal->transmitSync(command, response);
406
407                                                 if (rv == 0 && response.getLength() >= 2)
408                                                 {
409                                                         ResponseHelper resp(response);
410
411                                                         if (resp.getStatus() == 0)
412                                                         {
413                                                                 channelNum = resp.getDataField()[0];
414
415                                                                 SCARD_DEBUG("channelNum [%d]", channelNum);
416                                                         }
417                                                         else
418                                                         {
419                                                                 SCARD_DEBUG_ERR("status word [%d][ 0x%02X 0x%02X ]", resp.getStatus(), response[response.getLength() - 2], response[response.getLength() - 1]);
420
421                                                                 return result;
422                                                         }
423                                                 }
424                                                 else
425                                                 {
426                                                         SCARD_DEBUG_ERR("select apdu is failed, rv [%d], length [%d]", rv, response.getLength());
427
428                                                         return result;
429                                                 }
430                                         }
431
432                                         /* select aid */
433                                         command = APDUHelper::generateAPDU(APDUHelper::COMMAND_SELECT_BY_DF_NAME, channelNum, aid);
434                                         rv = terminal->transmitSync(command, selectResponse);
435                                         if (rv == 0 && selectResponse.getLength() >= 2)
436                                         {
437                                                 ResponseHelper resp(selectResponse);
438
439                                                 if (resp.getStatus() == 0)
440                                                 {
441                                                         result = client->openChannel(sessionID, channelNum);
442                                                         if (result != IntegerHandle::INVALID_HANDLE)
443                                                         {
444                                                                 ServerChannel *temp = (ServerChannel *)client->getChannel(result);
445                                                                 if (temp != NULL)
446                                                                 {
447                                                                         /* set select response */
448                                                                         temp->selectResponse = selectResponse;
449                                                                 }
450                                                                 else
451                                                                 {
452                                                                         SCARD_DEBUG_ERR("IS IT POSSIBLE??????????????????");
453                                                                 }
454                                                         }
455                                                         else
456                                                         {
457                                                                 SCARD_DEBUG_ERR("channel is null.");
458                                                         }
459                                                 }
460                                                 else
461                                                 {
462                                                         SCARD_DEBUG_ERR("status word [%d][ 0x%02X 0x%02X ]", resp.getStatus(), selectResponse[selectResponse.getLength() - 2], selectResponse[selectResponse.getLength() - 1]);
463                                                 }
464                                         }
465                                         else
466                                         {
467                                                 SCARD_DEBUG_ERR("select apdu is failed, rv [%d], length [%d]", rv, selectResponse.getLength());
468                                         }
469
470                                 }
471                                 else
472                                 {
473                                         SCARD_DEBUG_ERR("session is invalid [%d]", sessionID);
474                                 }
475                         }
476                         else
477                         {
478                                 SCARD_DEBUG_ERR("session is invalid [%d]", sessionID);
479                         }
480                 }
481                 else
482                 {
483                         SCARD_DEBUG_ERR("getClient is failed [%d] [%d]", socket, context);
484                 }
485
486                 return result;
487         }
488
489         Channel *ServerResource::getChannel(int socket, unsigned int context, unsigned int channelID)
490         {
491                 Channel *result = NULL;
492                 ServiceInstance *instance = NULL;
493
494                 if ((instance = getService(socket, context)) != NULL)
495                 {
496                         result = instance->getChannel(channelID);
497                 }
498                 else
499                 {
500                         SCARD_DEBUG_ERR("Channel doesn't exist : socket [%d], context [%d], handle [%d]", socket, context, channelID);
501                 }
502
503                 return result;
504         }
505
506         void ServerResource::removeChannel(int socket, unsigned int context, unsigned int channelID)
507         {
508                 ServiceInstance *instance = NULL;
509
510                 if ((instance = getService(socket, context)) != NULL)
511                 {
512                         instance->closeChannel(channelID);
513                 }
514                 else
515                 {
516                         SCARD_DEBUG_ERR("getService doesn't exist : socket [%d], context [%d]", socket, context);
517                 }
518         }
519
520         AccessControlList *ServerResource::getAccessControlList(Terminal *terminal)
521         {
522                 AccessControlList *result = NULL;
523                 map<Terminal *, AccessControlList *>::iterator item;
524
525                 if ((item = mapACL.find(terminal)) == mapACL.end())
526                 {
527                         ServerChannel *channel = new ServerChannel(NULL, NULL, 0, terminal);
528                         if (channel != NULL)
529                         {
530                                 /* load access control */
531                                 result = new GPSEACL(channel);
532                                 if (result != NULL)
533                                 {
534                                         result->loadACL();
535
536                                         mapACL.insert(make_pair(terminal, result));
537                                 }
538                                 else
539                                 {
540                                         SCARD_DEBUG_ERR("alloc failed");
541                                 }
542                         }
543                         else
544                         {
545                                 SCARD_DEBUG_ERR("alloc failed");
546                         }
547                 }
548                 else
549                 {
550                         result = item->second;
551                 }
552
553                 return result;
554         }
555
556         Terminal *ServerResource::createInstance(void *library)
557         {
558                 Terminal *terminal = NULL;
559                 terminal_create_instance_fn createInstance = NULL;
560
561                 /* create se instance */
562                 createInstance = (terminal_create_instance_fn)dlsym(library, "create_instance");
563                 if (createInstance != NULL)
564                 {
565                         terminal = (Terminal *)createInstance();
566                         if (terminal != NULL)
567                         {
568                                 SCARD_DEBUG("terminal [%p]", terminal);
569                         }
570                         else
571                         {
572                                 SCARD_DEBUG_ERR("terminal is null");
573                         }
574                 }
575                 else
576                 {
577                         SCARD_DEBUG_ERR("create_instance is null [%d]", errno);
578                 }
579
580                 return terminal;
581         }
582
583         bool ServerResource::appendSELibrary(char *library)
584         {
585                 void *libHandle = NULL;
586                 bool result = false;
587
588                 libHandle = dlopen(library, RTLD_LAZY);
589                 if (libHandle != NULL)
590                 {
591                         Terminal *terminal = NULL;
592
593                         terminal = createInstance(libHandle);
594                         if (terminal != NULL)
595                         {
596                                 unsigned int handle = IntegerHandle::assignHandle();
597
598                                 mapTerminals.insert(make_pair(handle, terminal));
599                                 libraries.push_back(libHandle);
600
601                                 terminal->setStatusCallback(&ServerResource::terminalCallback);
602
603                                 SCARD_DEBUG("register success [%s] [%p] [%s] [%p]", library, libHandle, terminal->getName(), terminal);
604
605                                 result = true;
606                         }
607                         else
608                         {
609                                 SCARD_DEBUG_ERR("terminal is null [%s]", library);
610
611                                 dlclose(libHandle);
612                         }
613                 }
614                 else
615                 {
616                         SCARD_DEBUG_ERR("it is not se file [%s] [%d]", library, errno);
617                 }
618
619                 return result;
620         }
621
622         int ServerResource::loadSecureElements()
623         {
624                 int result;
625                 void *libHandle;
626                 DIR *dir = NULL;
627                 struct dirent *entry = NULL;
628
629                 if ((dir = opendir(OMAPI_SE_PATH)) != NULL)
630                 {
631                         while ((entry = readdir(dir)) != NULL)
632                         {
633                                 if (strncmp(entry->d_name, ".", 1) != 0 && strncmp(entry->d_name, "..", 2) != 0)
634                                 {
635                                         char fullPath[1024] = { 0, };
636
637                                         /* TODO : need additional name rule :) */
638
639                                         /* open each files */
640                                         libHandle = NULL;
641
642                                         snprintf(fullPath, sizeof(fullPath), "%s/%s", OMAPI_SE_PATH, entry->d_name);
643
644                                         SCARD_DEBUG("se name [%s]", fullPath);
645
646                                         result = appendSELibrary(fullPath);
647                                 }
648                         }
649
650                         closedir(dir);
651
652                         result = 0;
653                 }
654                 else
655                 {
656                         result = -1;
657                 }
658
659                 return result;
660         }
661
662         void ServerResource::unloadSecureElements()
663         {
664                 size_t i;
665                 map<unsigned int, Terminal *>::iterator item;
666
667                 for (item = mapTerminals.begin(); item != mapTerminals.end(); item++)
668                 {
669                         item->second->finalize();
670
671                         IntegerHandle::releaseHandle(item->first);
672                 }
673
674                 mapTerminals.clear();
675
676                 for (i = 0; i < libraries.size(); i++)
677                 {
678                         if (libraries[i] != NULL)
679                                 dlclose(libraries[i]);
680                 }
681
682                 libraries.clear();
683         }
684
685         bool ServerResource::isValidReaderHandle(unsigned int reader)
686         {
687                 return (getTerminal(reader) != NULL);
688         }
689
690         bool ServerResource::isValidSessionHandle(int socket, unsigned int context, unsigned int session)
691         {
692                 ServiceInstance *instance = NULL;
693
694                 return (((instance = getService(socket, context)) != NULL) && (getService(socket, context)->isVaildSessionHandle(session)));
695         }
696
697         int ServerResource::getReadersInformation(ByteArray &info)
698         {
699                 int result = 0;
700                 unsigned char *buffer = NULL;
701                 unsigned int length = 0;
702                 unsigned int offset = 0;
703                 unsigned int nameLen = 0;
704
705                 if (mapTerminals.size() > 0)
706                 {
707                         map<unsigned int, Terminal *>::iterator item;
708
709                         for (item = mapTerminals.begin(); item != mapTerminals.end(); item++)
710                         {
711                                 if (item->second->isSecureElementPresence())
712                                 {
713                                         length += sizeof(nameLen) + strlen(item->second->getName()) + sizeof(unsigned int);
714                                         result++;
715                                 }
716                         }
717
718                         buffer = new unsigned char[length];
719                         if (buffer != NULL)
720                         {
721                                 memset(buffer, 0, length);
722
723                                 for (item = mapTerminals.begin(); item != mapTerminals.end(); item++)
724                                 {
725                                         if (item->second->isSecureElementPresence())
726                                         {
727                                                 nameLen = strlen(item->second->getName());
728
729                                                 memcpy(buffer + offset, &nameLen, sizeof(nameLen));
730                                                 offset += sizeof(nameLen);
731
732                                                 memcpy(buffer + offset, item->second->getName(), nameLen);
733                                                 offset += nameLen;
734
735                                                 memcpy(buffer + offset, &item->first, sizeof(unsigned int));
736                                                 offset += sizeof(unsigned int);
737                                         }
738                                 }
739
740                                 info.setBuffer(buffer, length);
741                                 delete []buffer;
742                         }
743                         else
744                         {
745                                 SCARD_DEBUG_ERR("alloc failed");
746                                 result = -1;
747                         }
748                 }
749                 else
750                 {
751                         SCARD_DEBUG("no secure element");
752                 }
753
754                 return result;
755         }
756
757         bool ServerResource::sendMessageToAllClients(Message &msg)
758         {
759                 bool result = true;
760                 map<int, ClientInstance *>::iterator item;
761
762                 for (item = mapClients.begin(); item != mapClients.end(); item++)
763                 {
764                         if (item->second->sendMessageToAllServices(item->second->getSocket(), msg) == false)
765                                 result = false;
766                 }
767
768                 return result;
769         }
770
771         void ServerResource::terminalCallback(void *terminal, int event, int error, void *user_param)
772         {
773                 SCARD_BEGIN();
774
775                 switch (event)
776                 {
777                 case Terminal::NOTIFY_SE_AVAILABLE :
778                         {
779                                 Message msg;
780
781                                 SCARD_DEBUG("[NOTIFY_SE_AVAILABLE]");
782
783                                 SCARD_DEBUG("terminal [%s], event [%d], error [%d], user_param [%p]", (char *)terminal, event, error, user_param);
784
785                                 /* send all client to refresh reader */
786                                 msg.message = msg.MSG_NOTIFY_SE_INSERTED;
787                                 msg.data.setBuffer((unsigned char *)terminal, strlen((char *)terminal) + 1);
788
789                                 ServerResource::getInstance().sendMessageToAllClients(msg);
790                         }
791                         break;
792
793                 case Terminal::NOTIFY_SE_NOT_AVAILABLE :
794                         {
795                                 Message msg;
796
797                                 SCARD_DEBUG("[NOTIFY_SE_NOT_AVAILABLE]");
798
799                                 SCARD_DEBUG("terminal [%s], event [%d], error [%d], user_param [%p]", (char *)terminal, event, error, user_param);
800
801                                 /* send all client to refresh reader */
802                                 msg.message = msg.MSG_NOTIFY_SE_REMOVED;
803                                 msg.data.setBuffer((unsigned char *)terminal, strlen((char *)terminal) + 1);
804
805                                 ServerResource::getInstance().sendMessageToAllClients(msg);
806                         }
807                         break;
808
809                 default :
810                         break;
811                 }
812
813                 SCARD_END();
814         }
815
816 } /* namespace smartcard_service_api */