Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- Fixed card removal wait for `INNOVATRON_B_PRIME` protocol cards: APDU polling is now bypassed in favor of the
standard PC/SC absence detection, and disconnection is unconditionally performed with `UNPOWER` regardless of the
configured disconnection mode.

## [2.6.2] - 2026-02-20
### Changed
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
group = org.eclipse.keyple
title = Keyple Plugin PCSC Java Lib
description = Keyple add-on to manage PC/SC readers
version = 2.6.2-SNAPSHOT
version = 2.6.3-SNAPSHOT

# Java Configuration
javaSourceLevel = 1.8
Expand Down
39 changes: 28 additions & 11 deletions src/main/java/org/eclipse/keyple/plugin/pcsc/PcscReaderAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,12 @@ public void closePhysicalChannel() throws ReaderIOException {
* Disconnects the current card and resets the context and reader state.
*
* <p>This method handles the disconnection of a card, taking into account the specific
* disconnection mode. If the card is an instance of {@link Smartcardio.JnaCard}, it disconnects
* disconnection mode. If the card uses the {@code INNOVATRON_B_PRIME} protocol, the disconnection
* mode is unconditionally overridden to {@link DisconnectionMode#UNPOWER}, regardless of the
* configured mode. If the card is an instance of {@link Smartcardio.JnaCard}, it disconnects
* using the extended mode specified by {@link #getDisposition(DisconnectionMode)} and resets the
* reader state to avoid incorrect card detection in subsequent operations. For other card types,
* it disconnects using the specified disconnection mode directly.
* it disconnects using the effective disconnection mode directly.
*
* <p>If a {@link CardException} occurs during the operation, a {@link ReaderIOException} is
* thrown with the associated error message.
Expand All @@ -337,13 +339,17 @@ public void closePhysicalChannel() throws ReaderIOException {
private void disconnect() throws ReaderIOException {
try {
if (card != null) {
DisconnectionMode effectiveMode =
isCurrentProtocol(PcscCardCommunicationProtocol.INNOVATRON_B_PRIME.name())
? DisconnectionMode.UNPOWER
: disconnectionMode;
if (card instanceof Smartcardio.JnaCard) {
// disconnect using the extended mode allowing UNPOWER
((Smartcardio.JnaCard) card).disconnect(getDisposition(disconnectionMode));
((Smartcardio.JnaCard) card).disconnect(getDisposition(effectiveMode));
// reset the reader state to avoid bad card detection next time
resetReaderState();
resetReaderState(effectiveMode);
} else {
card.disconnect(disconnectionMode == DisconnectionMode.RESET);
card.disconnect(effectiveMode == DisconnectionMode.RESET);
}
}
} catch (CardException e) {
Expand Down Expand Up @@ -377,14 +383,19 @@ private static int getDisposition(DisconnectionMode mode) {
/**
* Resets the state of the card reader.
*
* <p>This method attempts to reset the reader state based on the current disconnection mode. If
* the disconnection mode is set to UNPOWER, it reconnects to the terminal and then disconnects
* <p>This method attempts to reset the reader state based on the effective disconnection mode. If
* the effective mode is {@link DisconnectionMode#UNPOWER} (either configured or forced by the
* {@code INNOVATRON_B_PRIME} protocol), it reconnects to the terminal and then disconnects
* without powering off the reader. If any {@link CardException} occurs during this process, it is
* handled silently.
*
* @param effectiveMode The disconnection mode actually applied, which may differ from the
* configured {@link #disconnectionMode} when the card uses the {@code INNOVATRON_B_PRIME}
* protocol.
*/
private void resetReaderState() {
private void resetReaderState(DisconnectionMode effectiveMode) {
try {
if (disconnectionMode == DisconnectionMode.UNPOWER) {
if (effectiveMode == DisconnectionMode.UNPOWER) {
communicationTerminal.connect("*").disconnect(false);
}
} catch (CardException e) {
Expand Down Expand Up @@ -512,7 +523,8 @@ public void onUnregister() {
@Override
public void monitorCardPresenceDuringProcessing()
throws ReaderIOException, TaskCanceledException {
waitForCardRemoval();
doWaitForCardRemoval(
!isCurrentProtocol(PcscCardCommunicationProtocol.INNOVATRON_B_PRIME.name()));
}

/**
Expand All @@ -532,12 +544,17 @@ public void stopCardPresenceMonitoringDuringProcessing() {
*/
@Override
public void waitForCardRemoval() throws ReaderIOException, TaskCanceledException {
doWaitForCardRemoval(true);
}

private void doWaitForCardRemoval(boolean allowPolling)
throws ReaderIOException, TaskCanceledException {
if (logger.isTraceEnabled()) {
logger.trace("[readerExt={}] Starting waiting card removal", name);
}
loopWaitCardRemoval.set(true);
try {
if (disconnectionMode == DisconnectionMode.UNPOWER) {
if (allowPolling && disconnectionMode == DisconnectionMode.UNPOWER) {
waitForCardRemovalByPolling();
} else {
waitForCardRemovalStandard();
Expand Down
Loading