Tutorial

/*
 * Copyright (c) 2014, Andreas Fagschlunger. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package at.o2xfs.emv.demo.icc;

import java.io.IOException;
import java.util.List;

import at.o2xfs.common.Hex;
import at.o2xfs.emv.icc.CAPDU;
import at.o2xfs.emv.icc.ICReader;
import at.o2xfs.emv.icc.RAPDU;
import at.o2xfs.emv.icc.atr.ATR;
import at.o2xfs.emv.icc.atr.ATRException;
import at.o2xfs.log.Logger;
import at.o2xfs.log.LoggerFactory;
import at.o2xfs.xfs.XfsException;
import at.o2xfs.xfs.idc.ChipIOStruct;
import at.o2xfs.xfs.idc.IDCChipProtocol;
import at.o2xfs.xfs.idc.IDCTrack;
import at.o2xfs.xfs.idc.WFSIDCCARDDATA;
import at.o2xfs.xfs.service.XfsServiceManager;
import at.o2xfs.xfs.service.XfsServiceManagerException;
import at.o2xfs.xfs.service.idc.IDCService;
import at.o2xfs.xfs.service.idc.cmd.ChipIOCommand;
import at.o2xfs.xfs.service.idc.cmd.ReadCardCommand;
import at.o2xfs.xfs.service.idc.cmd.ReadCardListener;

public class XfsICReader implements ICReader, ReadCardListener {

	private static final Logger LOG = LoggerFactory.getLogger(XfsICReader.class);

	private final XfsServiceManager serviceManager;

	private final IDCService service;

	private boolean running = true;

	private byte[] atr = null;

	private byte[] track2 = null;

	private byte[] track3 = null;

	private IDCChipProtocol chipProtocol = IDCChipProtocol.T0;

	public XfsICReader() throws XfsServiceManagerException {
		serviceManager = XfsServiceManager.getInstance();
		service = serviceManager.getService(IDCService.class);
	}

	@Override
	public byte[] reset() throws IOException {
		final String method = "reset()";
		ReadCardCommand readCardCommand = new ReadCardCommand(service);
		readCardCommand.addCommandListener(this);
		readCardCommand.addReadData(IDCTrack.TRACK2);
		readCardCommand.addReadData(IDCTrack.TRACK3);
		readCardCommand.addReadData(IDCTrack.CHIP);
		readCardCommand.execute();
		synchronized (this) {
			try {
				while (running) {
					wait();
				}
			} catch (InterruptedException e) {
				LOG.error(method, "Interrupted", e);
			}
		}
		try {
			ATR newATR = new ATR(atr);
			if (newATR.containsTD(1)) {
				int protocol = newATR.getTD(1) & 0xF;
				switch (protocol) {
					case 1:
						chipProtocol = IDCChipProtocol.T1;
						break;
				}
			}
		} catch (ATRException e) {
			LOG.error(method, "ATR Error: " + Hex.encode(atr), e);
		}
		return atr;
	}

	@Override
	public void commandCancelled() {
		synchronized (this) {
			running = false;
			notifyAll();
		}
	}

	@Override
	public void commandFailed(Exception e) {
		final String method = "commandFailed(Exception)";
		LOG.error(method, "", e);
		synchronized (this) {
			running = false;
			notifyAll();
		}
	}

	@Override
	public void commandSuccessful() {
		synchronized (this) {
			running = false;
			notifyAll();
		}
	}

	@Override
	public void cardInserted() {
		final String method = "cardInserted()";
		LOG.info(method, "");
	}

	@Override
	public void cardInvalid() {
		final String method = "cardInvalid()";
		LOG.info(method, "");
	}

	@Override
	public void cardRead(List<WFSIDCCARDDATA> cardData) {
		final String method = "cardRead(List<WFSIDCCARDDATA>)";
		for (WFSIDCCARDDATA each : cardData) {
			LOG.info(method, each);
			switch (each.getDataSource()) {
				case CHIP:
					atr = each.getData();
					break;
				case TRACK2:
					track2 = each.getData();
					break;
				case TRACK3:
					track3 = each.getData();
					break;
				default:
					break;
			}
		}
	}

	@Override
	public RAPDU transmit(CAPDU command) throws IOException {
		return transmit(command.getBytes());
	}

	@Override
	public RAPDU transmit(byte[] command) throws IOException {
		try {
			if (atr == null) {
				reset();
			}
			ChipIOStruct in = new ChipIOStruct(command);
			in.setChipProtocol(chipProtocol);
			ChipIOStruct result = new ChipIOCommand(service, in).call();
			return new RAPDU(result.getChipData());
		} catch (XfsException e) {
			throw new IOException(e);
		}
	}

	public byte[] getTrack2() {
		return track2;
	}

	public byte[] getTrack3() {
		return track3;
	}

}