Keyple Service C++ Library - 3.4.1
Component of the Keyple C++ middleware
ObservableLocalReaderAdapter.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/ObservableLocalReaderAdapter.hpp"
15
16#include <memory>
17#include <string>
18#include <vector>
19
20#include "keyple/core/plugin/CardIOException.hpp"
21#include "keyple/core/plugin/ReaderIOException.hpp"
22#include "keyple/core/service/ObservableReaderStateServiceAdapter.hpp"
23#include "keyple/core/service/ReaderEventAdapter.hpp"
24#include "keyple/core/service/ScheduledCardSelectionsResponseAdapter.hpp"
25#include "keyple/core/util/cpp/Arrays.hpp"
26#include "keyple/core/util/cpp/exception/Exception.hpp"
27#include "keypop/card/CardBrokenCommunicationException.hpp"
28#include "keypop/card/ReaderBrokenCommunicationException.hpp"
29#include "keypop/reader/ReaderCommunicationException.hpp"
30
31namespace keyple {
32namespace core {
33namespace service {
34
35using keyple::core::plugin::CardIOException;
36using keyple::core::plugin::ReaderIOException;
37using keyple::core::util::cpp::Arrays;
38using keyple::core::util::cpp::exception::Exception;
39using keypop::card::CardBrokenCommunicationException;
40using keypop::card::ReaderBrokenCommunicationException;
41using keypop::reader::ReaderCommunicationException;
42
43/* OBSERVABLE LOCAL READER ADAPTER JOB
44 * ---------------------------------------------------------- */
45
46ObservableLocalReaderAdapter::ObservableLocalReaderAdapterJob::
47 ObservableLocalReaderAdapterJob(
48 std::shared_ptr<CardReaderObserverSpi> observer,
49 const std::shared_ptr<CardReaderEvent> event,
50 ObservableLocalReaderAdapter* parent)
51: Job("ObservableLocalReaderAdapter")
52, mObserver(observer)
53, mEvent(event)
54, mParent(parent)
55{
56}
57
58void
59ObservableLocalReaderAdapter::ObservableLocalReaderAdapterJob::execute()
60{
61 mParent->notifyObserver(mObserver, mEvent);
62}
63
64/* OBSERVABLE LOCAL READER ADAPTER
65 * -------------------------------------------------------------- */
66
67const std::vector<uint8_t> ObservableLocalReaderAdapter::APDU_PING_CARD_PRESENCE
68 = {0x00, 0xC0, 0x00, 0x00, 0x00};
69const std::string ObservableLocalReaderAdapter::READER_MONITORING_ERROR
70 = "An error occurred while monitoring the reader.";
71
72ObservableLocalReaderAdapter::ObservableLocalReaderAdapter(
73 std::shared_ptr<ObservableReaderSpi> observableReaderSpi,
74 const std::string& pluginName)
75: LocalReaderAdapter(observableReaderSpi, pluginName)
76, mObservableReaderSpi(observableReaderSpi)
77, mStateService(std::make_shared<ObservableReaderStateServiceAdapter>(this))
78, mObservationManager(
79 std::make_shared<ObservationManagerAdapter<
80 CardReaderObserverSpi,
81 CardReaderObservationExceptionHandlerSpi>>(pluginName, getName()))
82{
83 auto asynchronousInsertion
84 = std::dynamic_pointer_cast<CardInsertionWaiterAsynchronousSpi>(
85 observableReaderSpi);
86 auto autonomousInsertion
87 = std::dynamic_pointer_cast<WaitForCardInsertionAutonomousSpi>(
88 observableReaderSpi);
89 if (asynchronousInsertion) {
90 asynchronousInsertion->setCallback(this);
91 } else if (autonomousInsertion) {
92 autonomousInsertion->connect(this);
93 }
94
95 auto asynchronousRemoval
96 = std::dynamic_pointer_cast<CardRemovalWaiterAsynchronousSpi>(
97 observableReaderSpi);
98 auto autonomousRemoval
99 = std::dynamic_pointer_cast<WaitForCardRemovalAutonomousSpi>(
100 observableReaderSpi);
101 if (asynchronousRemoval) {
102 asynchronousRemoval->setCallback(this);
103 } else if (autonomousRemoval) {
104 autonomousRemoval->connect(this);
105 }
106}
107
108std::shared_ptr<ObservableReaderSpi>
109ObservableLocalReaderAdapter::getObservableReaderSpi() const
110{
111 return mObservableReaderSpi;
112}
113
114std::shared_ptr<CardReaderObservationExceptionHandlerSpi>
115ObservableLocalReaderAdapter::getObservationExceptionHandler() const
116{
117 return mObservationManager->getObservationExceptionHandler();
118}
119
121ObservableLocalReaderAdapter::getDetectionMode() const
122{
123 return mDetectionMode;
124}
125
126MonitoringState
127ObservableLocalReaderAdapter::getCurrentMonitoringState() const
128{
129 return mStateService->getCurrentMonitoringState();
130}
131
132bool
133ObservableLocalReaderAdapter::isCardPresentPing()
134{
135 /* Transmits the APDU and checks for the IO exception */
136 try {
137 mObservableReaderSpi->transmitApdu(APDU_PING_CARD_PRESENCE);
138 } catch (const ReaderIOException& e) {
139 /* Notify the reader communication failure with the exception handler */
140 const auto rce = std::make_shared<ReaderCommunicationException>(
141 READER_MONITORING_ERROR, e);
142 getObservationExceptionHandler()->onReaderObservationError(
143 getPluginName(), getName(), rce);
144 return false;
145
146 } catch (const CardIOException&) {
147 return false;
148 }
149
150 return true;
151}
152
153std::shared_ptr<CardReaderEvent>
154ObservableLocalReaderAdapter::processCardInserted()
155{
156 /* RL-DET-INSNOTIF.1 */
157 mLogger->trace("[reader=%] Processing inserted card\n", getName());
158
159 mIsCardRemovedEventNotificationEnabled = true;
160
161 if (mCardSelectionScenario == nullptr) {
162 mLogger->trace(
163 "[reader=%] No card selection scenario defined. Notifying card "
164 "reader event [eventType=CARD_INSERTED]\n",
165 getName());
166
167 /* No default request is defined, just notify the card insertion */
168 return std::make_shared<ReaderEventAdapter>(
169 getPluginName(),
170 getName(),
171 CardReaderEvent::Type::CARD_INSERTED,
172 nullptr);
173 }
174
175 /*
176 * A card selection scenario is defined, send it and notify according to the
177 * notification mode and the selection status
178 */
179 try {
180 const std::vector<std::shared_ptr<CardSelectionResponseApi>>
181 cardSelectionResponses = transmitCardSelectionRequests(
182 mCardSelectionScenario->getCardSelectors(),
183 mCardSelectionScenario->getCardSelectionRequests(),
184 mCardSelectionScenario->getMultiSelectionProcessing(),
185 mCardSelectionScenario->getChannelControl());
186
187 if (hasACardMatched(cardSelectionResponses)) {
188 return std::make_shared<ReaderEventAdapter>(
189 getPluginName(),
190 getName(),
191 CardReaderEvent::Type::CARD_MATCHED,
192 std::make_shared<ScheduledCardSelectionsResponseAdapter>(
193 cardSelectionResponses));
194 }
195
196 if (mNotificationMode == NotificationMode::MATCHED_ONLY) {
197 /*
198 * Notify only if a card matched the selection, just ignore if not.
199 */
200 mLogger->trace(
201 "[reader=%] Selection hasn't matched. Event not notified "
202 "because the notification mode is MATCHED_ONLY\n",
203 getName());
204
205 return nullptr;
206 }
207
208 /*
209 * The card didn't match, notify an CARD_INSERTED event with the
210 * received response.
211 */
212 mLogger->debug(
213 "[reader=%] No selection cases matched [selectionCaseCount=%]\n",
214 getName(),
215 cardSelectionResponses.size());
216
217 return std::make_shared<ReaderEventAdapter>(
218 getPluginName(),
219 getName(),
220 CardReaderEvent::Type::CARD_INSERTED,
221 std::make_shared<ScheduledCardSelectionsResponseAdapter>(
222 cardSelectionResponses));
223
224 } catch (const ReaderBrokenCommunicationException& e) {
225 /* Notify the reader communication failure with the exception handler */
226 const auto rce = std::make_shared<ReaderCommunicationException>(
227 READER_MONITORING_ERROR, e);
228 getObservationExceptionHandler()->onReaderObservationError(
229 getPluginName(), getName(), rce);
230
231 } catch (const CardBrokenCommunicationException& e) {
232 /*
233 * The last transmission failed, close the logical and physical channels
234 */
235 closeLogicalAndPhysicalChannelsSilently();
236
237 /*
238 * The card was removed or not read correctly, no exception raising or
239 * event notification, just log.
240 */
241 mLogger->warn(
242 "[reader=%] Failed to process card selection scenario [reason=%]\n",
243 getName(),
244 e.getMessage());
245 }
246
247 /*
248 * Here we close the physical channel in case it was opened for a card
249 * excluded by the selection scenario.
250 */
251 try {
252 mObservableReaderSpi->closePhysicalChannel();
253
254 } catch (const ReaderIOException& e) {
255 /* Notify the reader communication failure with the exception handler */
256 const auto rce = std::make_shared<ReaderCommunicationException>(
257 READER_MONITORING_ERROR, e);
258 getObservationExceptionHandler()->onReaderObservationError(
259 getPluginName(), getName(), rce);
260 }
261
262 /* No event returned */
263 return nullptr;
264}
265
266bool
267ObservableLocalReaderAdapter::hasACardMatched(
268 const std::vector<std::shared_ptr<CardSelectionResponseApi>>&
269 cardSelectionResponses)
270{
271 if (std::any_of(
272 cardSelectionResponses.begin(),
273 cardSelectionResponses.end(),
274 [](const std::shared_ptr<CardSelectionResponseApi>&
275 cardSelectionResponse) {
276 return cardSelectionResponse != nullptr
277 && cardSelectionResponse->hasMatched();
278 })) {
279 mLogger->trace(
280 "[reader=%] A default selection case matched\n", getName());
281 return true;
282 }
283
284 return false;
285}
286
287void
288ObservableLocalReaderAdapter::processCardRemoved()
289{
290 /* RL-DET-REMNOTIF.1 */
291 closeLogicalAndPhysicalChannelsSilently();
292 if (mIsCardRemovedEventNotificationEnabled) {
293 notifyObservers(
294 std::make_shared<ReaderEventAdapter>(
295 getPluginName(),
296 getName(),
297 CardReaderEvent::Type::CARD_REMOVED,
298 nullptr));
299 }
300}
301
302void
303ObservableLocalReaderAdapter::switchState(const MonitoringState stateId)
304{
305 mStateService->switchState(stateId);
306}
307
308void
309ObservableLocalReaderAdapter::notifyObservers(
310 const std::shared_ptr<CardReaderEvent> event)
311{
312 mLogger->debug(
313 "[reader=%] Notifying observers [eventType=%, observerCount=%]\n",
314 getName(),
315 event->getType(),
316 countObservers());
317
318 for (const auto& observer : mObservationManager->getObservers()) {
319 notifyObserver(observer, event);
320 }
321
322 mLogger->debug("[reader=%] Observers notified\n", getName());
323}
324
325void
326ObservableLocalReaderAdapter::notifyObserver(
327 std::shared_ptr<CardReaderObserverSpi> observer,
328 const std::shared_ptr<CardReaderEvent> event)
329{
330 try {
331 observer->onReaderEvent(event);
332
333 } catch (const Exception& e) {
334 try {
335 mObservationManager->getObservationExceptionHandler()
336 ->onReaderObservationError(
337 getPluginName(), getName(), std::make_shared<Exception>(e));
338
339 } catch (const Exception& e2) {
340 mLogger->error(
341 "[reader=%] Failed to notify observer [reason=%]\n",
342 getName(),
343 e.getMessage());
344 mLogger->error(
345 "[reader=%] Failed to notify observation exception handler "
346 "[reason=%]\n",
347 getName(),
348 e2.getMessage());
349 }
350
351 } catch (const std::exception& e) {
352 try {
353 mObservationManager->getObservationExceptionHandler()
354 ->onReaderObservationError(
355 getPluginName(),
356 getName(),
357 std::make_shared<std::runtime_error>(e.what()));
358 } catch (const std::exception& e2) {
359 mLogger->error(
360 "Reader [%] failed to notify observer: %\n",
361 getName(),
362 e.what());
363 mLogger->error(
364 "Reader [%] failed to notify observation exception handler: "
365 "%\n",
366 getName(),
367 e2.what());
368 }
369 }
370}
371
372void
373ObservableLocalReaderAdapter::scheduleCardSelectionScenario(
374 std::shared_ptr<CardSelectionScenarioAdapter> cardSelectionScenario,
375 const NotificationMode notificationMode)
376{
377 mCardSelectionScenario = cardSelectionScenario;
378 mNotificationMode = notificationMode;
379}
380
381void
382ObservableLocalReaderAdapter::doUnregister()
383{
384 try {
385 stopCardDetection();
386 } catch (const Exception& e) {
387 mLogger->warn(
388 "[reader=%] Failed to stop card monitoring [reason=%]\n",
389 getName(),
390 e.getMessage());
391 }
392
393 /* Finally */
394 mStateService->shutdown();
395
396 notifyObservers(
397 std::make_shared<ReaderEventAdapter>(
398 getPluginName(),
399 getName(),
400 CardReaderEvent::Type::UNAVAILABLE,
401 nullptr));
402 clearObservers();
403 LocalReaderAdapter::doUnregister();
404}
405
406bool
407ObservableLocalReaderAdapter::isCardPresent()
408{
409 checkStatus();
410
411 if (LocalReaderAdapter::isCardPresent()) {
412 return true;
413 } else {
414 /*
415 * If the card is no longer present but one of the channels is still
416 * open, then the card removal sequence is initiated.
417 */
418 if (isLogicalChannelOpen()
419 || mObservableReaderSpi->isPhysicalChannelOpen()) {
420 processCardRemoved();
421 }
422
423 return false;
424 }
425}
426
427void
428ObservableLocalReaderAdapter::addObserver(
429 std::shared_ptr<CardReaderObserverSpi> observer)
430{
431 checkStatus();
432 mObservationManager->addObserver(observer);
433}
434
435void
436ObservableLocalReaderAdapter::removeObserver(
437 std::shared_ptr<CardReaderObserverSpi> observer)
438{
439 Assert::getInstance().notNull(observer, "observer");
440
441 if (Arrays::contains(mObservationManager->getObservers(), observer)) {
442 mObservationManager->removeObserver(observer);
443 }
444}
445
446int
447ObservableLocalReaderAdapter::countObservers() const
448{
449 return mObservationManager->countObservers();
450}
451
452void
453ObservableLocalReaderAdapter::clearObservers()
454{
455 mObservationManager->clearObservers();
456}
457
458void
459ObservableLocalReaderAdapter::startCardDetection(
460 const DetectionMode detectionMode)
461{
462 /* RL-DET-REMCTRL.1 */
463 checkStatus();
464
465 mLogger->info(
466 "[reader=%] Starting card monitoring [detectionMode=%]\n",
467 getName(),
468 detectionMode);
469
470 mDetectionMode = detectionMode;
471 mStateService->onEvent(InternalEvent::START_DETECT);
472}
473
474void
475ObservableLocalReaderAdapter::stopCardDetection()
476{
477 /* RL-DET-REMCTRL.1 */
478 mLogger->info("[reader=%] Stopping card monitoring\n", getName());
479
480 mStateService->onEvent(InternalEvent::STOP_DETECT);
481}
482
483void
484ObservableLocalReaderAdapter::finalizeCardProcessing()
485{
486 mLogger->info("[reader=%] Starting card removal sequence\n", getName());
487
488 mStateService->onEvent(InternalEvent::CARD_PROCESSED);
489}
490
491void
492ObservableLocalReaderAdapter::setReaderObservationExceptionHandler(
493 std::shared_ptr<CardReaderObservationExceptionHandlerSpi> exceptionHandler)
494{
495 checkStatus();
496 mObservationManager->setObservationExceptionHandler(exceptionHandler);
497}
498
499void
500ObservableLocalReaderAdapter::onCardInserted()
501{
502 mStateService->onEvent(InternalEvent::CARD_INSERTED);
503}
504
505void
506ObservableLocalReaderAdapter::onCardRemoved()
507{
508 mStateService->onEvent(InternalEvent::CARD_REMOVED);
509}
510
511} /* namespace service */
512} /* namespace core */
513} /* namespace keyple */
ObservableCardReader::DetectionMode DetectionMode