/****************************************************************************
* Copyright (C) 2014-2015 TU Darmstadt.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.scio;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import org.openecard.common.apdu.common.CardCommandAPDU;
import org.openecard.common.apdu.common.CardResponseAPDU;
import org.openecard.common.ifd.scio.SCIOChannel;
import org.openecard.common.ifd.scio.SCIOException;
import static org.openecard.scio.PCSCExceptionExtractor.getCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* PC/SC channel implementation of the SCIOChannel.
*
* @author Wael Alkhatib
* @author Tobias Wich
*/
public class PCSCChannel implements SCIOChannel {
private static final Logger logger = LoggerFactory.getLogger(PCSCChannel.class);
private final PCSCCard card;
private final CardChannel channel;
private final int channelNum;
PCSCChannel(@Nonnull PCSCCard card, @Nonnull CardChannel channel) {
this.card = card;
this.channel = channel;
// pretend channel num = 0 in case there is something really fucked up during init of the card
int num = 0;
try {
num = channel.getChannelNumber();
} catch (IllegalStateException ex) {
// very unlikely event, that the card is removed during the connect phase
String msg = "Card disconnected during connect phase, pretending to be channel 0 regardless of what it is.";
logger.error(msg);
}
this.channelNum = num;
}
@Override
public PCSCCard getCard() {
return card;
}
@Override
public int getChannelNumber() {
return channelNum;
}
@Override
public boolean isBasicChannel() {
return channel.getChannelNumber() == 0;
}
@Override
public boolean isLogicalChannel() {
return ! isBasicChannel();
}
@Override
public CardResponseAPDU transmit(byte[] command) throws SCIOException {
try {
CommandAPDU convertCommand = new CommandAPDU(command);
ResponseAPDU response = channel.transmit(convertCommand);
return new CardResponseAPDU(response.getBytes());
} catch (CardException ex) {
String msg = "Failed to transmit APDU to the card in terminal '%s'.";
throw new SCIOException(String.format(msg, card.getTerminal().getName()), getCode(ex), ex);
}
}
@Override
public CardResponseAPDU transmit(CardCommandAPDU apdu) throws SCIOException {
return transmit(apdu.toByteArray());
}
@Override
public int transmit(ByteBuffer command, ByteBuffer response) throws SCIOException {
try {
return channel.transmit(command, response);
} catch (CardException ex) {
String msg = "Failed to transmit APDU to the card in terminal '%s'.";
throw new SCIOException(String.format(msg, card.getTerminal().getName()), getCode(ex), ex);
}
}
@Override
public void close() throws SCIOException {
// only close logical channels
if (isLogicalChannel()) {
try {
channel.close();
} catch (CardException ex) {
String msg = "Failed to close channel to card in terminal '%s'.";
throw new SCIOException(String.format(msg, card.getTerminal().getName()), getCode(ex), ex);
}
}
}
}