/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.cardreader;
import java.io.IOException;
import java.io.InterruptedIOException;
import com.sun.midp.security.*;
/**
* This class represents card slot abstraction.
*/
public class CardSlot {
/**
* Device object.
*/
private CardDevice device;
/**
* Local slot number (inside the device).
*/
private int slotNumber;
/**
* Security token for this slot.
*/
SecurityToken securityToken;
/**
* Indicates that the slot has been opened.
*/
private boolean opened;
/** Unique card session identifier. */
private int cardSessionId;
/** This field is used to assign unique card session identifiers. */
private static int sessionId = 1;
/**
* ATR bytes kept since last reset.
*/
private byte[] ATR;
/**
* A flag which shows if the card was changed.
*/
private boolean cardChanged;
/**
* isAlive command APDU.
*/
private byte[] isAliveAPDU;
/**
* isAlive command response buffer.
*/
private byte[] isAliveResponse;
/**
* Creates card slot on the specified device.
*
* @param device Card device for which the slot is created
* @param slot Local device slot number
* @param token Security token for this slot
* @throws IOException If slot creation failed.
*/
public CardSlot(CardDevice device, int slot, SecurityToken token)
throws IOException {
this.device = device;
this.slotNumber = slot;
this.securityToken = token;
opened = false;
cardChanged = true;
isAliveAPDU = new byte[] {0, 0x70, (byte) 0x80, 0};
isAliveResponse = new byte[2];
}
/**
* Performs data transfer to the device slot.
* It must be called where slot is locked.
*
* @param request Request APDU bytes
* @param response Response bytes
* @return Length of response
* @throws IOException If a data transfer failed or
* the card was changed.
*/
public int xferData(byte[] request, byte[] response)
throws IOException {
return xferData(false, request, response);
}
/**
* Performs data transfer to the device slot.
* It must be called where slot is locked.
*
* @param aliveChecking true if isAlive command is performed,
* false otherwise
* @param request Request APDU bytes
* @param response Response bytes
* @return Length of response
* @throws IOException If a data transfer failed or
* the card was changed.
*/
private int xferData(boolean aliveChecking,
byte[] request, byte[] response)
throws IOException {
int bytes_read;
if (isCardChanged() && !aliveChecking) {
unlockSlot();
throw new InterruptedIOException("Card changed");
}
try {
bytes_read = device.xfer(request, response);
}
catch (IOException e) {
if (isCardChanged() && !aliveChecking) {
unlockSlot();
throw new InterruptedIOException("Card changed: " + e);
}
unlockSlot();
throw e;
}
catch (RuntimeException e) { // We must unlock slot
unlockSlot();
throw e;
}
if (isCardChanged() && !aliveChecking) {
unlockSlot();
throw new InterruptedIOException("Card changed");
}
return bytes_read;
}
/**
* Checks if the the connection is still live or not. It
* is a public wrapper for <code>doIsAlive</code>. Also
* checks if a card was changed. Slot must not be locked.
* @return <code>true</code> if the connection is alive
*/
public boolean isAlive() {
boolean alive = doIsAlive();
if (!alive) {
try {
if (!alive) {
lockSlot();
boolean changed = isCardChanged();
unlockSlot();
if (changed) {
alive = doIsAlive();
}
}
} catch (IOException e) {
alive = false;
}
}
return alive;
}
/**
* Checks if the the connection is still live or not by trying to
* send an APDU to close channel 0. If we get an IOException back
* that means a card or cref is gone.
* @return <code>true</code> if the connection is alive
*/
private boolean doIsAlive() {
try {
lockSlot();
xferData(true, isAliveAPDU, isAliveResponse);
unlockSlot();
return true;
} catch (IOException e) {
return false;
}
}
/**
* Gets ATR bytes from the device.
*
* @return ATR bytes.
*/
public byte[] getATR() {
byte[] result;
if (opened && ATR.length > 0) {
result = new byte[ATR.length];
System.arraycopy(ATR, 0, result, 0, ATR.length);
} else {
result = null;
}
return result;
}
/**
* Gets local device slot number.
*
* @return Local slot number.
*/
public int getSlotNumber() {
return slotNumber;
}
/**
* Returns the card session identifier.
* This number is different for different card sessions.
* @return the card session identifier or -1 when slot is closed
*/
public int getCardSessionId() {
return (opened && !cardChanged) ? cardSessionId : -1;
}
/**
* Starts operations with the slot.
* All the transfer operations (<code>xferData</code>)
* should be performed when the device is locked.
* If any problem occurs an unlock must be done.
*
* @throws IOException If something fails.
*/
public void lockSlot() throws IOException {
try {
device.lock();
open();
device.selectSlot(slotNumber);
}
catch (IOException e) {
unlockSlot();
throw e;
}
}
/**
* Ends operations with the slot.
*
* IOException is ignored.
*/
public void unlockSlot() {
try {
device.unlock();
}
catch (IOException e) {}
}
/**
* Makes sure that slot is ready for connection.
*
* @return true if this is first connection to inserted card
* @throws IOException If something fails.
*/
public boolean initConnection() throws IOException {
boolean prevCardChanged;
lockSlot();
prevCardChanged = isCardChanged();
unlockSlot();
cardChanged = false;
return prevCardChanged;
}
/**
* Initializes ACL for the slot.
*/
public void initACL() {
boolean changed;
try {
lockSlot();
changed = isCardChanged();
unlockSlot();
if (changed) {
com.sun.satsa.acl.AccessControlManager.init(slotNumber);
}
} catch (IOException e) {} // ignored
}
/**
* Closes the slot in case of error.
* <code>device.closeSlot</code> usually
* does nothing.
* Used for socket-like 'devices'.
*
* @throws IOException If something fails.
*/
public void closeSlot() throws IOException {
if (opened) {
device.closeSlot(slotNumber);
}
opened = false;
}
/**
* Gets device of this slot. For using in derived classes.
*
* @return Slot device
*/
protected CardDevice getDevice() {
return this.device;
}
/**
* Checks if this slot is SAT slot.
* @return SAT check result
* @throws IOException If an error occured.
*/
public boolean isSAT() throws IOException {
return device.isSatSlot(slotNumber);
}
/**
* Checks if the card was withdrawn or inserted.
* The device must be locked.
*
* @return true if a card was changed, false otherwise
* @throws IOException If something wrong.
*/
private boolean isCardChanged() throws IOException {
try {
if (device.isCardChanged()) {
closeSlot();
}
open();
} catch (IOException e) {
unlockSlot();
throw e;
}
if (cardChanged) {
return true;
}
return false;
}
/**
* Tries to open slot if it closed. Performes the 'RESET' command.
* The device must be locked.
*
* @throws IOException If open fails.
*/
private void open() throws IOException {
boolean openedSuccessfuly = false;
if (!opened) {
try {
device.openSlot(slotNumber, securityToken);
openedSuccessfuly = true;
device.selectSlot(slotNumber);
device.reset();
cardSessionId = sessionId++;
} catch (IOException ie) {
if (openedSuccessfuly) {
try {
device.closeSlot(slotNumber);
} catch (IOException ign) {} // ignored
}
throw new IOException("Cannot open slot #" +
slotNumber + ": " + ie);
}
ATR = device.getATR();
cardChanged = true;
opened = true;
}
}
}
|