Keyple Service C++ Library - 3.3.5
Component of the Keyple C++ middleware
LocalReaderAdapter.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/core/service/LocalReaderAdapter.hpp"
15
16#include <memory>
17#include <regex>
18#include <sstream>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "keyple/core/plugin/CardIOException.hpp"
24#include "keyple/core/plugin/ReaderIOException.hpp"
25#include "keyple/core/plugin/spi/reader/AutonomousSelectionReaderSpi.hpp"
26#include "keyple/core/plugin/spi/reader/ConfigurableReaderSpi.hpp"
27#include "keyple/core/service/CardSelectionResponseAdapter.hpp"
28#include "keyple/core/util/ApduUtil.hpp"
29#include "keyple/core/util/HexUtil.hpp"
30#include "keyple/core/util/KeypleAssert.hpp"
31#include "keyple/core/util/cpp/Arrays.hpp"
32#include "keyple/core/util/cpp/KeypleStd.hpp"
33#include "keyple/core/util/cpp/System.hpp"
34#include "keyple/core/util/cpp/exception/Exception.hpp"
35#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
36#include "keyple/core/util/cpp/exception/RuntimeException.hpp"
37#include "keypop/card/CardBrokenCommunicationException.hpp"
38#include "keypop/card/ReaderBrokenCommunicationException.hpp"
39#include "keypop/card/UnexpectedStatusWordException.hpp"
40#include "keypop/reader/ReaderCommunicationException.hpp"
41#include "keypop/reader/ReaderProtocolNotSupportedException.hpp"
42
43namespace keyple {
44namespace core {
45namespace service {
46
47using keyple::core::plugin::CardIOException;
48using keyple::core::plugin::ReaderIOException;
49using keyple::core::plugin::spi::reader::AutonomousSelectionReaderSpi;
50using keyple::core::plugin::spi::reader::ConfigurableReaderSpi;
51using keyple::core::util::ApduUtil;
52using keyple::core::util::Assert;
53using keyple::core::util::HexUtil;
54using keyple::core::util::cpp::Arrays;
55using keyple::core::util::cpp::System;
56using keyple::core::util::cpp::exception::Exception;
57using keyple::core::util::cpp::exception::IllegalStateException;
58using keyple::core::util::cpp::exception::RuntimeException;
59using keypop::card::CardBrokenCommunicationException;
60using keypop::card::ReaderBrokenCommunicationException;
61using keypop::card::UnexpectedStatusWordException;
62using keypop::reader::ReaderCommunicationException;
63using keypop::reader::ReaderProtocolNotSupportedException;
64
65/* LOCAL READER ADAPTER
66 * ------------------------------------------------------------------------- */
67
68const int LocalReaderAdapter::SW_6100 = 0x6100;
69
70const int LocalReaderAdapter::SW_6C00 = 0x6C00;
71
72const int LocalReaderAdapter::SW1_MASK = 0xFF00;
73const int LocalReaderAdapter::SW2_MASK = 0x00FF;
74
75LocalReaderAdapter::LocalReaderAdapter(
76 std::shared_ptr<ReaderSpi> readerSpi, const std::string& pluginName)
77: AbstractReaderAdapter(
78 readerSpi->getName(),
79 std::dynamic_pointer_cast<KeypleReaderExtension>(readerSpi),
80 pluginName)
81, mReaderSpi(readerSpi)
82, mBefore(0)
83, mIsLogicalChannelOpen(false)
84, mUseDefaultProtocol(false)
85, mCurrentLogicalProtocolName("")
86, mCurrentPhysicalProtocolName("")
87, mProtocolAssociations({})
88{
89}
90
91void
92LocalReaderAdapter::computeCurrentProtocol()
93{
94 mCurrentLogicalProtocolName = "";
95 mCurrentPhysicalProtocolName = "";
96
97 if (mProtocolAssociations.empty()) {
98 mUseDefaultProtocol = true;
99 } else {
100 mUseDefaultProtocol = false;
101
102 const auto configurable
103 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
104 for (const auto& entry : mProtocolAssociations) {
105 if (configurable->isCurrentProtocol(entry.first)) {
106 mCurrentLogicalProtocolName = entry.second;
107 mCurrentPhysicalProtocolName = entry.first;
108 }
109 }
110 }
111}
112
113const std::string&
114LocalReaderAdapter::getCurrentPhysicalProtocolName() const
115{
116 return mCurrentPhysicalProtocolName;
117}
118
119void
120LocalReaderAdapter::closeLogicalChannel()
121{
122 mLogger->trace("Reader [%] closes logical channel\n", getName());
123
124 auto reader
125 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
126 if (reader) {
127 /* AutonomousSelectionReader have an explicit method for closing
128 * channels */
129 reader->closeLogicalChannel();
130 }
131
132 mIsLogicalChannelOpen = false;
133 mLogger->trace("Logical channel closed\n");
134}
135
136uint8_t
137LocalReaderAdapter::computeSelectApplicationP2(
138 const FileOccurrence fileOccurrence,
139 const FileControlInformation fileControlInformation)
140{
141 uint8_t p2;
142
143 switch (fileOccurrence) {
144 case FileOccurrence::FIRST:
145 p2 = 0x00;
146 break;
147 case FileOccurrence::LAST:
148 p2 = 0x01;
149 break;
150 case FileOccurrence::NEXT:
151 p2 = 0x02;
152 break;
153 case FileOccurrence::PREVIOUS:
154 p2 = 0x03;
155 break;
156 default:
157 std::stringstream ss;
158 ss << fileOccurrence;
159 throw IllegalStateException("Unexpected value: " + ss.str());
160 }
161
162 switch (fileControlInformation) {
163 case FileControlInformation::FCI:
164 p2 |= 0x00;
165 break;
166 case FileControlInformation::FCP:
167 p2 |= 0x04;
168 break;
169 case FileControlInformation::FMD:
170 p2 |= 0x08;
171 break;
172 case FileControlInformation::NO_RESPONSE:
173 p2 |= 0x0C;
174 break;
175 default:
176 std::stringstream ss;
177 ss << fileControlInformation;
178 throw IllegalStateException("Unexpected value: " + ss.str());
179 }
180
181 return p2;
182}
183
184std::shared_ptr<ApduResponseAdapter>
185LocalReaderAdapter::processExplicitAidSelection(
186 std::shared_ptr<InternalIsoCardSelector> cardSelector)
187{
188 const std::vector<uint8_t>& aid = cardSelector->getAid();
189
190 mLogger->debug(
191 "Reader [%] selects application with AID [%]\n",
192 getName(),
193 HexUtil::toHex(aid));
194
195 /*
196 * Build a get response command the actual length expected by the card in
197 * the get response command is handled in transmitApdu
198 *
199 * RL-SEL-CLA.1
200 * RL-SEL-P2LC.1
201 */
202 std::vector<uint8_t> selectApplicationCommand(6 + aid.size());
203 selectApplicationCommand[0] = 0x00; /* CLA */
204 selectApplicationCommand[1] = 0xA4; /* INS */
205 selectApplicationCommand[2] = 0x04; /* P1: select by name */
206 /*
207 * P2: b0,b1 define the File occurrence, b2,b3 define the File control
208 * information we use the bitmask defined in the respective enums
209 */
210 selectApplicationCommand[3] = computeSelectApplicationP2(
211 cardSelector->getFileOccurrence(),
212 cardSelector->getFileControlInformation());
213 selectApplicationCommand[4] = static_cast<uint8_t>(aid.size()); /* Lc */
214 System::arraycopy(
215 aid, 0, selectApplicationCommand, 5, static_cast<int>(aid.size()));
216 selectApplicationCommand[5 + aid.size()] = 0x00; /* Le */
217
218 auto apduRequest = std::shared_ptr<ApduRequest>(
219 new ApduRequest(selectApplicationCommand));
220 apduRequest->setInfo("Internal Select Application");
221
222 return processApduRequest(apduRequest);
223}
224
225std::shared_ptr<ApduResponseAdapter>
226LocalReaderAdapter::selectByAid(
227 std::shared_ptr<InternalIsoCardSelector> cardSelector)
228{
229 std::shared_ptr<ApduResponseAdapter> fciResponse = nullptr;
230
231 /*
232 * RL-SEL-P2LC.1
233 * RL-SEL-DFNAME.1
234 */
235 Assert::getInstance().isInRange(
236 cardSelector->getAid().size(), 0, 16, "aid");
237
238 auto reader
239 = std::dynamic_pointer_cast<AutonomousSelectionReaderSpi>(mReaderSpi);
240 if (reader) {
241 const std::vector<uint8_t>& aid = cardSelector->getAid();
242 const uint8_t p2 = computeSelectApplicationP2(
243 cardSelector->getFileOccurrence(),
244 cardSelector->getFileControlInformation());
245 const std::vector<uint8_t> selectionDataBytes
246 = reader->openChannelForAid(aid, p2);
247 fciResponse = std::make_shared<ApduResponseAdapter>(selectionDataBytes);
248 } else {
249 fciResponse = processExplicitAidSelection(cardSelector);
250 }
251
252 return fciResponse;
253}
254
255bool
256LocalReaderAdapter::checkPowerOnData(
257 const std::string& powerOnData,
258 std::shared_ptr<InternalCardSelector> cardSelector)
259{
260 const std::string& powerOnDataRegex = cardSelector->getPowerOnDataRegex();
261
262 /* Check the power-on data */
263 if (powerOnData != "" && powerOnDataRegex != ""
264 && !std::regex_match(powerOnData, std::regex(powerOnDataRegex))) {
265 mLogger->trace(
266 "Power-on data didn't match (powerOnData: %, powerOnDataRegex: %\n",
267 getName(),
268 powerOnDataRegex);
269
270 /* The power-on data have been rejected */
271 return false;
272 } else {
273 /* The power-on data have been accepted */
274 return true;
275 }
276}
277
278std::shared_ptr<LocalReaderAdapter::SelectionStatus>
279LocalReaderAdapter::processSelection(
280 std::shared_ptr<CardSelectorBase> cardSelector,
281 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest)
282{
283 try {
284 /* RL-CLA-CHAAUTO.1 */
285 std::string powerOnData = "";
286 std::shared_ptr<ApduResponseAdapter> fciResponse = nullptr;
287 bool hasMatched = true;
288
289 auto internalSelector
290 = std::dynamic_pointer_cast<InternalCardSelector>(cardSelector);
291 if (!internalSelector) {
292 throw RuntimeException(
293 "cardSelector is not of type InternalCardSelector.");
294 }
295
296 const std::string& logicalProtocolName
297 = internalSelector->getLogicalProtocolName();
298 if (logicalProtocolName != "" && mUseDefaultProtocol) {
299 throw IllegalStateException(
300 "Protocol " + logicalProtocolName
301 + " not associated to a reader protocol.");
302 }
303
304 /* Check protocol if enabled */
305 if (logicalProtocolName == ""
306 || logicalProtocolName == mCurrentLogicalProtocolName) {
307 /*
308 * Protocol check succeeded, check power-on data if enabled
309 * RL-ATR-FILTER
310 * RL-SEL-USAGE.1
311 */
312 powerOnData = mReaderSpi->getPowerOnData();
313 if (checkPowerOnData(powerOnData, internalSelector)) {
314 /* No power-on data filter or power-on data check succeeded,
315 * select by AID if enabled */
316 const auto internalIsoCardSelector
317 = std::dynamic_pointer_cast<InternalIsoCardSelector>(
318 cardSelector);
319 if (internalIsoCardSelector
320 && internalIsoCardSelector->getAid().size() != 0) {
321 fciResponse = selectByAid(internalIsoCardSelector);
322 const std::vector<int>& statusWords
323 = cardSelectionRequest
324 ->getSuccessfulSelectionStatusWords();
325 hasMatched = std::find(
326 statusWords.begin(),
327 statusWords.end(),
328 fciResponse->getStatusWord())
329 != statusWords.end();
330 } else {
331 fciResponse = nullptr;
332 }
333 } else {
334 /* Check failed */
335 hasMatched = false;
336 fciResponse = nullptr;
337 }
338 } else {
339 /* Protocol failed */
340 powerOnData = "";
341 fciResponse = nullptr;
342 hasMatched = false;
343 }
344
345 return std::make_shared<SelectionStatus>(
346 powerOnData, fciResponse, hasMatched);
347
348 } catch (const ReaderIOException& e) {
349 throw ReaderBrokenCommunicationException(
350 std::make_shared<CardResponseAdapter>(
351 std::vector<std::shared_ptr<ApduResponseApi>>({}), false),
352 false,
353 e.getMessage(),
354 std::make_shared<ReaderIOException>(e));
355
356 } catch (const CardIOException& e) {
357 throw CardBrokenCommunicationException(
358 std::make_shared<CardResponseAdapter>(
359 std::vector<std::shared_ptr<ApduResponseApi>>({}), false),
360 false,
361 e.getMessage(),
362 std::make_shared<CardIOException>(e));
363 }
364}
365
366std::shared_ptr<CardSelectionResponseApi>
367LocalReaderAdapter::processCardSelectionRequest(
368 std::shared_ptr<CardSelectorBase> cardSelector,
369 std::shared_ptr<CardSelectionRequestSpi> cardSelectionRequest,
370 const ChannelControl channelControl)
371{
372 mIsLogicalChannelOpen = false;
373
374 std::shared_ptr<SelectionStatus> selectionStatus(
375 processSelection(cardSelector, cardSelectionRequest));
376 if (!selectionStatus->mHasMatched) {
377 /* The selection failed, return an empty response having the selection
378 * status */
379 return std::make_shared<CardSelectionResponseAdapter>(
380 selectionStatus->mPowerOnData,
381 selectionStatus->mSelectApplicationResponse,
382 false,
383 std::make_shared<CardResponseAdapter>(
384 std::vector<std::shared_ptr<ApduResponseApi>>({}), false));
385 }
386
387 mIsLogicalChannelOpen = true;
388
389 std::shared_ptr<CardResponseAdapter> cardResponse = nullptr;
390
391 if (cardSelectionRequest->getCardRequest() != nullptr) {
392 cardResponse
393 = std::dynamic_pointer_cast<CardResponseAdapter>(processCardRequest(
394 cardSelectionRequest->getCardRequest(), channelControl));
395 } else {
396 cardResponse = nullptr;
397 }
398
399 return std::make_shared<CardSelectionResponseAdapter>(
400 selectionStatus->mPowerOnData,
401 selectionStatus->mSelectApplicationResponse,
402 true,
403 cardResponse);
404}
405
406std::shared_ptr<ApduResponseAdapter>
407LocalReaderAdapter::processApduRequest(
408 const std::shared_ptr<ApduRequestSpi> apduRequest)
409{
410 std::shared_ptr<ApduResponseAdapter> apduResponse = nullptr;
411
412 uint64_t timeStamp = System::nanoTime();
413 uint64_t elapsed10ms = (timeStamp - mBefore) / 100000;
414 mBefore = timeStamp;
415
416 mLogger->debug(
417 "Reader [%] --> apduRequest: %, elapsed % ms\n",
418 getName(),
419 apduRequest,
420 elapsed10ms / 10.0);
421
422 apduResponse = std::make_shared<ApduResponseAdapter>(
423 mReaderSpi->transmitApdu(apduRequest->getApdu()));
424
425 timeStamp = System::nanoTime();
426 elapsed10ms = (timeStamp - mBefore) / 100000;
427 mBefore = timeStamp;
428
429 mLogger->debug(
430 "Reader [%] <-- apduResponse: %, elapsed % ms\n",
431 getName(),
432 apduResponse,
433 elapsed10ms / 10.0);
434
435 if (apduResponse->getDataOut().size() == 0) {
436 if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6100) {
437 /*
438 * RL-SW-61XX.1
439 * Build a GetResponse APDU command with the provided "le"
440 */
441 const uint8_t le = apduResponse->getStatusWord() & SW2_MASK;
442 const std::vector<uint8_t> getResponseApdu
443 = {0x00, 0xC0, 0x00, 0x00, le};
444
445 /* Execute APDU */
446 auto adapter = std::shared_ptr<ApduRequest>(
447 new ApduRequest(getResponseApdu));
448 adapter->setInfo("Internal Get Response");
449 apduResponse = processApduRequest(adapter);
450
451 } else if ((apduResponse->getStatusWord() & SW1_MASK) == SW_6C00) {
452 /*
453 * RL-SW-6CXX.1
454 * Update the last command with the provided "le"
455 */
456 std::vector<std::uint8_t> apdu = apduRequest->getApdu();
457 apdu[apduRequest->getApdu().size() - 1]
458 = (apduResponse->getStatusWord() & SW2_MASK);
459 apduRequest->setApdu(apdu);
460
461 /* Replay the last command APDU */
462 apduResponse = processApduRequest(apduRequest);
463
464 } else if (
465 ApduUtil::isCase4(apduRequest->getApdu())
466 && Arrays::contains(
467 apduRequest->getSuccessfulStatusWords(),
468 apduResponse->getStatusWord())) {
469 /*
470 * RL-SW-ANALYSIS.1
471 * RL-SW-CASE4.1 (SW=6200 not taken into account here)
472 * Build a GetResponse APDU command with the original "le"
473 */
474 const uint8_t le
475 = apduRequest->getApdu()[apduRequest->getApdu().size() - 1];
476 const std::vector<uint8_t> getResponseApdu
477 = {0x00, 0xC0, 0x00, 0x00, le};
478
479 /* Execute GetResponse APDU */
480 auto adapter = std::shared_ptr<ApduRequest>(
481 new ApduRequest(getResponseApdu));
482 adapter->setInfo("Internal Get Response");
483 apduResponse = processApduRequest(adapter);
484 }
485 }
486
487 return apduResponse;
488}
489
490void
491LocalReaderAdapter::releaseChannel()
492{
493 checkStatus();
494
495 try {
496 mReaderSpi->closePhysicalChannel();
497 } catch (const ReaderIOException& e) {
498 throw ReaderBrokenCommunicationException(
499 nullptr,
500 false,
501 "Failed to release the physical channel",
502 std::make_shared<ReaderIOException>(e));
503 }
504}
505
506void
507LocalReaderAdapter::deactivateReaderProtocol(const std::string& readerProtocol)
508{
509 /* RL-CL-PROTOCOL.1 */
510 checkStatus();
511 Assert::getInstance().notEmpty(readerProtocol, "readerProtocol");
512
513 mProtocolAssociations.erase(readerProtocol);
514
515 const auto configurable
516 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
517 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
518 throw ReaderProtocolNotSupportedException(readerProtocol);
519 }
520
521 configurable->deactivateProtocol(readerProtocol);
522}
523
524void
525LocalReaderAdapter::activateReaderProtocol(
526 const std::string& readerProtocol, const std::string& applicationProtocol)
527{
528 /* RL-CL-PROTOCOL.1 */
529 checkStatus();
530 Assert::getInstance()
531 .notEmpty(readerProtocol, "readerProtocol")
532 .notEmpty(applicationProtocol, "applicationProtocol");
533
534 const auto configurable
535 = std::dynamic_pointer_cast<ConfigurableReaderSpi>(mReaderSpi);
536 if (!configurable || !configurable->isProtocolSupported(readerProtocol)) {
537 throw ReaderProtocolNotSupportedException(readerProtocol);
538 }
539
540 configurable->activateProtocol(readerProtocol);
541
542 mProtocolAssociations.insert({readerProtocol, applicationProtocol});
543}
544
545bool
546LocalReaderAdapter::isCardPresent()
547{
548 /*
549 * RL-DET-PCRQ.1
550 * RL-DET-PCAPDU.1
551 */
552 checkStatus();
553
554 try {
555 return mReaderSpi->checkCardPresence();
556 } catch (const ReaderIOException& e) {
557 throw ReaderCommunicationException(
558 "An exception occurred while checking the card presence", e);
559 }
560}
561
562bool
563LocalReaderAdapter::isContactless()
564{
565 return mReaderSpi->isContactless();
566}
567
568std::shared_ptr<CardResponseApi>
569LocalReaderAdapter::processCardRequest(
570 const std::shared_ptr<CardRequestSpi> cardRequest,
571 const ChannelControl channelControl)
572{
573 checkStatus();
574
575 /* Proceeds with the APDU requests present in the CardRequest */
576 std::vector<std::shared_ptr<ApduResponseApi>> apduResponses;
577
578 for (const auto& apduRequest : cardRequest->getApduRequests()) {
579 try {
580 const std::shared_ptr<ApduResponseAdapter> apduResponse(
581 processApduRequest(apduRequest));
582 apduResponses.push_back(apduResponse);
583 if (cardRequest->stopOnUnsuccessfulStatusWord()
584 && !Arrays::contains(
585 apduRequest->getSuccessfulStatusWords(),
586 apduResponse->getStatusWord())) {
587 if (channelControl == ChannelControl::CLOSE_AFTER) {
588 closeLogicalAndPhysicalChannelsSilently();
589 }
590 throw UnexpectedStatusWordException(
591 std::make_shared<CardResponseAdapter>(apduResponses, false),
592 cardRequest->getApduRequests().size()
593 == apduResponses.size(),
594 "Unexpected status word");
595 }
596
597 } catch (const ReaderIOException& e) {
598 closeLogicalAndPhysicalChannelsSilently();
599 throw ReaderBrokenCommunicationException(
600 std::make_shared<CardResponseAdapter>(apduResponses, false),
601 false,
602 "Reader communication failure while transmitting a card "
603 "request",
604 std::make_shared<ReaderIOException>(e));
605
606 } catch (const CardIOException& e) {
607 closeLogicalAndPhysicalChannelsSilently();
608 throw CardBrokenCommunicationException(
609 std::make_shared<CardResponseAdapter>(apduResponses, false),
610 false,
611 "Card communication failure while transmitting a card request",
612 std::make_shared<CardIOException>(e));
613 }
614 }
615
616 /* Close the channel if requested */
617 if (channelControl == ChannelControl::CLOSE_AFTER) {
618 releaseChannel();
619 }
620
621 return std::make_shared<CardResponseAdapter>(
622 apduResponses, mIsLogicalChannelOpen);
623}
624
625std::vector<std::shared_ptr<CardSelectionResponseApi>>
626LocalReaderAdapter::processCardSelectionRequests(
627 const std::vector<std::shared_ptr<CardSelectorBase>>& cardSelectors,
628 const std::vector<std::shared_ptr<CardSelectionRequestSpi>>&
629 cardSelectionRequests,
630 const MultiSelectionProcessing multiSelectionProcessing,
631 const ChannelControl channelControl)
632{
633 checkStatus();
634
635 /* Open the physical channel, determine the current protocol */
636 if (!mReaderSpi->isPhysicalChannelOpen()) {
637 try {
638 mReaderSpi->openPhysicalChannel();
639 computeCurrentProtocol();
640 } catch (const ReaderIOException& e) {
641 throw ReaderBrokenCommunicationException(
642 nullptr,
643 false,
644 "Reader communication failure while opening physical channel",
645 std::make_shared<ReaderIOException>(e));
646 } catch (const CardIOException& e) {
647 throw CardBrokenCommunicationException(
648 nullptr,
649 false,
650 "Card communication failure while opening physical channel",
651 std::make_shared<CardIOException>(e));
652 }
653 }
654
655 std::vector<std::shared_ptr<CardSelectionResponseApi>>
656 cardSelectionResponses;
657
658 /* Loop over all CardRequest provided in the list */
659 for (auto p
660 = std::make_pair(cardSelectors.begin(), cardSelectionRequests.begin());
661 p.second != cardSelectionRequests.end()
662 && p.first != cardSelectors.end();
663 ++p.first, ++p.second) {
664 /* Process the CardRequest and append the CardResponse list */
665 const auto cardSelectionResponse
666 = processCardSelectionRequest(*p.first, *p.second, channelControl);
667 cardSelectionResponses.push_back(cardSelectionResponse);
668
669 if (multiSelectionProcessing == MultiSelectionProcessing::PROCESS_ALL) {
670 /*
671 * Multi CardRequest case: just close the logical channel and go on
672 * with the next selection.
673 */
674 closeLogicalChannel();
675 } else {
676 if (mIsLogicalChannelOpen) {
677 /* The logical channel being open, we stop here */
678 break; /* Exit for loop */
679 }
680 }
681 }
682
683 /* Close the channel if requested */
684 if (channelControl == ChannelControl::CLOSE_AFTER) {
685 releaseChannel();
686 }
687
688 return cardSelectionResponses;
689}
690
691void
692LocalReaderAdapter::doUnregister()
693{
694 try {
695 mReaderSpi->closePhysicalChannel();
696 } catch (const Exception& e) {
697 mLogger->error(
698 "Error closing physical channel on reader [%] - %\n", getName(), e);
699 }
700
701 try {
702 mReaderSpi->onUnregister();
703 } catch (const Exception& e) {
704 mLogger->error(
705 "Error unregistering reader extension of reader [%]: % - %\n",
706 getName(),
707 e.getMessage(),
708 e);
709 }
710
711 AbstractReaderAdapter::doUnregister();
712}
713
714void
715LocalReaderAdapter::closeLogicalAndPhysicalChannelsSilently()
716{
717 closeLogicalChannel();
718
719 /* Closes the physical channel and resets the current protocol info */
720 mCurrentLogicalProtocolName = "";
721 mUseDefaultProtocol = false;
722
723 try {
724 mReaderSpi->closePhysicalChannel();
725 } catch (const ReaderIOException& e) {
726 mLogger->error(
727 "Error closing physical channel on reader [%]: % - %\n",
728 getName(),
729 e.getMessage(),
730 e);
731 }
732}
733
734bool
735LocalReaderAdapter::isLogicalChannelOpen() const
736{
737 return mIsLogicalChannelOpen;
738}
739
740std::shared_ptr<ReaderSpi>
741LocalReaderAdapter::getReaderSpi() const
742{
743 return mReaderSpi;
744}
745
746/* SELECTION STATUS
747 * -----------------------------------------------------------------------------
748 */
749
750LocalReaderAdapter::SelectionStatus::SelectionStatus(
751 const std::string& powerOnData,
752 const std::shared_ptr<ApduResponseAdapter> selectApplicationResponse,
753 const bool hasMatched)
754: mPowerOnData(powerOnData)
755, mSelectApplicationResponse(selectApplicationResponse)
756, mHasMatched(hasMatched)
757{
758}
759
760/* APDU REQUEST
761 * ---------------------------------------------------------------------------------
762 */
763
764const int LocalReaderAdapter::ApduRequest::DEFAULT_SUCCESSFUL_CODE = 0x9000;
765
766LocalReaderAdapter::ApduRequest::ApduRequest(const std::vector<uint8_t>& apdu)
767: mApdu(apdu)
768, mSuccessfulStatusWords({DEFAULT_SUCCESSFUL_CODE})
769{
770}
771
772LocalReaderAdapter::ApduRequest&
773LocalReaderAdapter::ApduRequest::setInfo(const std::string& info)
774{
775 mInfo = info;
776
777 return *this;
778}
779
780const std::vector<uint8_t>&
781LocalReaderAdapter::ApduRequest::getApdu() const
782{
783 return mApdu;
784}
785
786void
787LocalReaderAdapter::ApduRequest::setApdu(const std::vector<std::uint8_t>& apdu)
788{
789 mApdu = apdu;
790}
791
792const std::vector<int>&
793LocalReaderAdapter::ApduRequest::getSuccessfulStatusWords() const
794{
795 return mSuccessfulStatusWords;
796}
797
798const std::string&
799LocalReaderAdapter::ApduRequest::getInfo() const
800{
801 return mInfo;
802}
803
804std::ostream&
805operator<<(std::ostream& os, LocalReaderAdapter::ApduRequest& ar)
806{
807 os << "APDU_REQUEST: {"
808 << "APDU: " << ar.getApdu() << ", "
809 << "SUCCESSFUL_STATUS_WORDS: " << ar.getSuccessfulStatusWords() << ", "
810 << "INFO: " << ar.getInfo() << "}";
811
812 return os;
813}
814
815std::ostream&
817 std::ostream& os, const std::shared_ptr<LocalReaderAdapter::ApduRequest> ar)
818{
819 if (ar == nullptr) {
820 os << "APDU_REQUEST: null";
821 } else {
822 os << *ar;
823 }
824
825 return os;
826}
827
828std::ostream&
830 std::ostream& os,
831 const std::vector<std::shared_ptr<LocalReaderAdapter::ApduRequest>>& ars)
832{
833 os << "APDU_REQUESTS: {";
834
835 for (auto it = ars.begin(); it != ars.end(); it++) {
836 if (it != ars.begin()) {
837 os << ", ";
838 }
839 os << *it;
840 }
841
842 os << "}";
843
844 return os;
845}
846
847} /* namespace service */
848} /* namespace core */
849} /* namespace keyple */
std::ostream & operator<<(std::ostream &os, const std::vector< std::shared_ptr< LocalReaderAdapter::ApduRequest > > &ars)