FileDocCategorySizeDatePackage
Simulator.javaAPI DocphoneME MR2 API (J2ME)23099Wed May 02 18:00:38 BST 2007 com.sun.midp.io.j2me.apdu

Simulator

public class Simulator extends com.sun.cardreader.CardDevice
This class provides virtual device driving the simulator.

Fields Summary
public static final String
PROP_NAME
Configuration property name.
private static String[]
hosts
Host names.
private static int[]
ports
Port numbers.
private InputStream[]
in
Input streams.
private OutputStream[]
out
Output streams.
private com.sun.midp.io.j2me.socket.Protocol[]
conn
Socket connection to connect with the Java Card reference implementation.
private int
slotCount
Number of slots supported by the simulator.
private int
currentSlot
Number of the current slot for operations.
private boolean
cardChanged
Is the card changed since last reset.
private TLP224Message
responseMsg
The response message received from the card.
private TLP224Message
commandMsg
Message sent to the card.
private byte[]
header
Byte array containing current APDU header.
private byte[]
command
Current command APDU data.
private int
Lc
Lc value for the current APDU (data size).
private int
Le
Le value for the current APDU (length of expected data).
private int
sw1
1st byte of APDU status word.
private int
sw2
2nd byte of APDU status word.
private byte[]
output
Data received from the card during APDU exchange.
private int
dataSize
Current size of received data.
Constructors Summary
Methods Summary
public voidclose()
Closes the device. No exceptions thrown to avoid mess in exception handlers trying to clean up and close the device.

        for (int i = 0; i < this.slotCount; i++) {
            try {
                closeSlot(i);
            }
            catch (IOException e) { // ignored
            }
        }
    
public voidcloseSlot(int slot)
Close the specified slot.

param
slot Slot number
throws
IOException If slot close failed.

        String msg = "";
        if (slot >= 0 && slot <= this.slotCount) {
            
            try {
                if (this.in[slot] != null) {
                    InputStream tmp = this.in[slot];
                    this.in[slot] = null;
                    tmp.close();
                }
            } catch (IOException e) {
                msg += e.getMessage();
            }
            
            try {
                if (this.out[slot] != null) {
                    OutputStream tmp = this.out[slot];
                    this.out[slot] = null;
                    tmp.close();
                }
            } catch (IOException e) {
                msg += e.getMessage();
            }
            
            try {
                if (this.conn[slot] != null) {
                    com.sun.midp.io.j2me.socket.Protocol tmp = this.conn[slot];
                    this.conn[slot] = null;
                    tmp.close();
                }
            } catch (IOException e) {
                msg += e.getMessage();
            }
            if (msg.length() != 0) {
                throw new IOException(msg);
            }
        } else {
            throw new IOException("Invalid slot number");
        }
    
public voidcmdPowerDown()
Performs 'POWER DOWN' command.

throws
IOException If a reset failed.

        byte[] data = commandMsg.getData();
        int atrLen;

        data[0] = TLP224Message.ACK;
        data[1] = 1;
        data[2] = 0x4D; // 'POWER_DOWN'
        data[3] = commandMsg.computeLRC(3);
        commandMsg.setLength(4);
        sendTLP224Message(commandMsg);
        receiveTLP224Message(responseMsg);
    
public intcmdReset(byte[] atr)
Performs reset of device. Returns ATR into provided buffer

param
atr Buffer for ATR bytes
return
Length of ATR
throws
IOException If a reset failed.

        byte[] data = commandMsg.getData();
        int atrLen;

        data[0] = TLP224Message.ACK;
        data[1] = 4;
        data[2] = TLP224Message.POWER_UP;
        data[3] = 0;
        data[4] = 0;
        data[5] = 0;
        data[6] = commandMsg.computeLRC(6);
        commandMsg.setLength(7);
        sendTLP224Message(commandMsg);
        receiveTLP224Message(responseMsg);
        
        data = responseMsg.getData();
        if (data[2] == TLP224Message.STATUS_CARD_REMOVED) {
            // the card has been just inserted, try again
            sendTLP224Message(commandMsg);
            receiveTLP224Message(responseMsg);
        }
        if (data[2] != 0) {
            throw new IOException("TLP224Error " + (data[2] & 0xff));
        }
        if (data[1] < 5 || (data[1] == 1 && data[2] == 0)) {
            throw new IOException("CREF is not ready");
        }
        // card on-line
        atrLen = data[5];
        if (atrLen < 0 || atrLen > atr.length) {
            throw new IOException("Invalid length of ATR (" + atrLen + ")");
        }
        System.arraycopy(data, 6, atr, 0, atrLen);

        return atrLen;
    
public intcmdXfer(byte[] commandAPDU, byte[] response)
Performs data transfer to the device.

param
commandAPDU Request bytes
param
response Response bytes
return
Length of response
throws
IOException If a data transfer failed.


        System.arraycopy(commandAPDU, 0, header, 0, 4);
    	command = commandAPDU;
   	    dataSize = 0;
   	    
    	if (command.length == 4) { // case 1 -> ISO_IN
    	    Lc = 0;
    	    Le = -1;
    	    isoIn();
    	} else
    	if (command.length == 5) { // case 2 -> ISO_OUT
    	    Lc = 0;
    	    Le = command[4] & 0xFF;
    	    isoOut();
	    } else
    	if (command.length == (command[4] & 0xFF) + 5) { // case 3 -> ISO_IN
    	    Lc = (command[4] & 0xFF);
    	    Le = -1;
    	    isoIn();
	    } else
    	if (command.length > (command[4] & 0xFF) + 5) { // case 4 -> ISO_IN
    	    Lc = (command[4] & 0xFF);
    	    Le = (command[Lc + 5] & 0xFF);
    	    isoIn();
	    } else {
    	    throw new IOException("Malformed APDU");
	    }
	    if (dataSize > 0) {
    	    System.arraycopy(output, 0, response, 0, dataSize);
	    }
	    response[dataSize] = (byte)sw1;
	    response[dataSize+1] = (byte)sw2;

	    return dataSize + 2;
    
public intgetSlotCount()
Gets number of slots on a device. Default implementation returns 1 which is ok for most devices.

return
Number of slots on a device

        return this.slotCount;
    
public voidinit()
Initializes the device.

throws
IOException If device initialization failed.
throws
CardDeviceException If device configuration is bad.


                          
          

        /* Read configuration, moved from Cad */
        String config = Configuration.getProperty(PROP_NAME);

        boolean ok;
        try {
            Vector list = new Vector();

            int index = config.indexOf(",");
            while (index != -1) {
                list.addElement(config.substring(0, index));
                config = config.substring(index + 1);
                index = config.indexOf(",");
            }
            list.addElement(config);

            this.slotCount = list.size();
            this.hosts = new String[this.slotCount];
            this.ports = new int[this.slotCount];
            this.in = new InputStream[this.slotCount];
            this.out = new OutputStream[this.slotCount];
            this.conn = 
                new com.sun.midp.io.j2me.socket.Protocol[this.slotCount];

            for (int i = 0; i < list.size(); i++) {
                String s = (String) list.elementAt(i);
                index = s.indexOf(":");
                hosts[i] = s.substring(0, index++).trim();
                ports[i] = Integer.parseInt(s.substring(index));
            }
            ok = list.size() > 0;
        } catch (NullPointerException npe) {
            ok = false;
        } catch (IndexOutOfBoundsException iobe) {
            ok = false;
        } catch (NumberFormatException nfe) {
            ok = false;
        }

        if (! ok) {
            throw new CardDeviceException("Simulator configuration is bad");
        }
        header = new byte[4];
        commandMsg = new TLP224Message();
        responseMsg = new TLP224Message();
        cardChanged = false;
    
public booleanisCardChanged()
Checks if the card in the selected slot was changed since last call or since last reset. Always called after any transfer operation, so implementation can check and store that status during this operation.

return
true if was changed, false otherwise.
throws
IOException If something fails.

        boolean retValue = cardChanged;
        cardChanged = false;
        return retValue;
    
public booleanisSatSlot(int slotNumber)
Checks if this slot is SAT slot.

param
slotNumber Slot number
return
true if the slot is dedicated for SAT, false otherwise
throws
IOException If an error occured.

        // IMPL_NOTE: This method must return always false if SAT support is disabled
        return slotNumber == 0;
    
private voidisoIn()
Format and send an ISO_IN command to the CAD.

exception
InterruptedIOException if connection was closed in the other thread
exception
IOException if a communication error happens


        byte[] data = commandMsg.getData();
        int length = Lc;

        data[0] = TLP224Message.ACK;
        data[1] = (byte) (length + 6);
        data[2] = TLP224Message.ISO_INPUT;
        System.arraycopy(header, 0, data, 3, 4);
        data[7] = (byte) length;
        if (length > 0)
            System.arraycopy(command, 5, data, 8, length);
        data[length + 8] = commandMsg.computeLRC(length + 8);
        commandMsg.setLength(length + 9);

        sendTLP224Message(commandMsg);
        receiveTLP224Message(responseMsg);
        data = responseMsg.getData();
        sw1 = data[3] & 0xff;
        sw2 = data[4] & 0xff;

        byte status = data[2];
        if (status != TLP224Message.STATUS_SUCCESS &&
            status != TLP224Message.STATUS_CARD_ERROR &&
            status != TLP224Message.STATUS_INTERRUPTED_EXCHANGE) {
            throw new IOException("TLP224Error " + status);
        }
    
private voidisoOut()
Format and send an ISO_OUT command to the CAD.

exception
InterruptedIOException if connection was closed in the other thread
exception
IOException if a communication error happens


        byte[] data = commandMsg.getData();
        data[0] = TLP224Message.ACK;
        data[1] = 6;
        data[2] = TLP224Message.ISO_OUTPUT;
        System.arraycopy(header, 0, data, 3, 4);
        data[7] = (byte) Le;
        data[8] = commandMsg.computeLRC(8);
        commandMsg.setLength(9);

        sendTLP224Message(commandMsg);
        receiveTLP224Message(responseMsg);

        data = responseMsg.getData();

        int received = (data[1] == 3) ? 0 : responseMsg.getLength() - 6;

        if (received > 0) {
            if (output == null || output.length < received) {
                output = new byte[received];
            }
            System.arraycopy(data, 3, output, dataSize, received);
            dataSize = received;
        }

        sw1 = data[3 + received] & 0xff;
        sw2 = data[4 + received] & 0xff;

        byte status = data[2];
        if (status != TLP224Message.STATUS_SUCCESS &&
            status != TLP224Message.STATUS_CARD_ERROR &&
            status != TLP224Message.STATUS_INTERRUPTED_EXCHANGE) {
            throw new IOException("TLP224Error " + status);
        }
    
public voidlock()
Performs platform lock of the device. This is intended to make sure that no other native application uses the same device during a transaction.

throws
IOException If a device locking failed.

    
public voidopenSlot(int slot, SecurityToken token)
Open the specified slot.

param
slot Slot number
param
token Security token
throws
IOException If slot opening failed.

        // IMPL_NOTE: The CREF starts very slow. TCK's port checking is not enough:
        // the CREF can accept connection a little later than port 
        // is not available. Two seconds is enough on my computer
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ie) {} // ignored

        // Here we open socket to the simulator
        this.conn[slot] = new com.sun.midp.io.j2me.socket.Protocol();
        String url = "//" + this.hosts[slot] + ":" + this.ports[slot];
        try {
            this.conn[slot].openPrim(token, url);
            this.in[slot] = this.conn[slot].openInputStream();
            this.out[slot] = this.conn[slot].openOutputStream();
        } catch (IOException ie) {
            throw new IOException("Cannot open '" + url + "':" + ie);
        }
    
private voidreceiveTLP224Message(TLP224Message msg)
Receive a TLP224 formatted message from the input stream. This method reads bytes from the input stream until an EOT (0x03) character is received. The resulting message is decoded and stored in the TL224Message msg. In the event of a transmission error, this method will send attempt error recovery by sending a TLP224 NACK message to the sender. Up to 5 retries will be performed.

param
msg The object to store the received message in.
exception
IOException If an error occurs while reading from the input stream.


        byte[] data = msg.getData();
        int tries = 0;

        try {
            while (true) {
                // Only retry link level errors 5 times before giving up.
                if (tries++ > 5) {
                    throw new IOException("TLP224Error " + 1);
                }
    
                // loop reading characters until EOT is received.
                boolean messageTooLong = false;
                boolean xmitError = false;
                int got = 0;
    
                int hiNibble, lowNibble;
    
                while ((hiNibble = 
                        in[currentSlot].read()) != TLP224Message.EOT) {
    
                    if (hiNibble == -1) {
                        throw new EOFException();
                    }
    
                    if ((lowNibble = in[currentSlot].read()) == -1) {
                        throw new EOFException();
                    }
    
                    xmitError |= (hiNibble < '0" || hiNibble > 'F" ||
                                  (hiNibble > '9" && hiNibble < 'A")) ||
                                 (lowNibble < '0" || lowNibble > 'F" ||
                                  (lowNibble > '9" && lowNibble < 'A"));
    
                    if (lowNibble == TLP224Message.EOT) {
                        break;
                    }
    
                    if (xmitError)
                        continue;
    
                    hiNibble -= hiNibble > '9" ? 0x37 : 0x30;
                    lowNibble -= lowNibble > '9" ? 0x37 : 0x30;
    
                    try {
                        data[got++] = (byte) ((hiNibble << 4) | lowNibble);
                    } catch (ArrayIndexOutOfBoundsException e) {
                        messageTooLong = true;
                    }
                }
    
                if (xmitError || got < 3) {
                    transmissionError();
                    continue;
                }
    
                if (messageTooLong) {
                    statusResponse(TLP224Message.STATUS_MESSAGE_TOO_LONG);
                    continue;
                }
    
                if (data[got - 1] != msg.computeLRC(got - 1) ||
                    data[1] != (byte) (got - 3)) {
                    // the message must contain a valid LRC, the second byte
                    // of the message is the command length. The total message
                    // length includes the ACK/NACK, the length and the LRC
                    transmissionError();
                    continue;
                }
    
                // The first byte of the message must be either an ACK or a NACK
                if (data[0] != TLP224Message.ACK &&
                    data[0] != TLP224Message.NACK) {
                    statusResponse(TLP224Message.STATUS_PROTOCOL_ERROR);
                    continue;
                }
    
                msg.setLength(got);
                break;
            }
        } catch (IOException e) {
            cardChanged = true;
            throw e;
        }
    
public voidselectSlot(int slot)
Selects the current slot for the subsequent transfer operations. For the one-slot devices the default slot number is 0.

param
slot Slot number
throws
IOException If a slot selection failed.

        if (this.conn[slot] == null) {
            throw new IOException("Connection for simulator slot " +
                                  Integer.toString(slot) + " not open");
        }

        this.currentSlot = slot;
        cardChanged = false;
    
private voidsendTLP224Message(TLP224Message msg)
Formats a TLP224Message into it's ASCII representation and sends the message on the output stream.

param
msg TLP224 encoded message to be sent
exception
IOException is thrown in case there are any problems.

        byte[] data = msg.getData();

        try {
            for (int i = 0; i < msg.getLength(); i++) {
                int nibble = data[i] >> 4 & 0xf;
                out[currentSlot].write(nibble + (nibble < 10 ? 0x30 : 0x37));
                nibble = data[i] & 0xf;
                out[currentSlot].write(nibble + (nibble < 10 ? 0x30 : 0x37));
            }
    
            out[currentSlot].write(TLP224Message.EOT);
            out[currentSlot].flush();
        } catch (IOException e) {
            cardChanged = true;
            throw e;
        }
    
private voidstatusResponse(int code)
Send a one byte TLP224 status response.

param
code the status response
throws
IOException if IO error occurs

        TLP224Message msg = new TLP224Message();
        byte[] data = msg.getData();
        data[0] = TLP224Message.ACK;
        data[1] = 1;
        data[2] = (byte) code;
        data[3] = msg.computeLRC(3);
        msg.setLength(4);
        sendTLP224Message(msg);
    
private voidtransmissionError()
Send a TLP224 Transmission Error response.

throws
IOException if IO error occurs

        TLP224Message msg = new TLP224Message();
        byte[] data = msg.getData();
        data[0] = TLP224Message.NACK;
        data[1] = 0;
        data[2] = msg.computeLRC(2);
        msg.setLength(3);
        sendTLP224Message(msg);
    
public voidunlock()
Unlocks the device.

throws
IOException If a device unlocking failed.