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

Cad

public class Cad extends Object
The Cad class maintains the context for the client (terminal) side of the terminal CAD connection. This class is responsible for sending APDUs to the card, receiving back the responses and automatically receiving a response if status '61 XX' is received from the card.

Fields Summary
private boolean
basicChannelInUse
Since at any time once channel always has to be open with the card and that channel is generally the basic channel. If the basic channel is not in use, the APDU manager can select the next application on that channel. This variable shows if basic channel is in use currently or not.
boolean
SIMPresent
Indicates if slot is the SAT slot and a SIM is installed on it. This flag is set in Cad.selectApplication. It is cleared when Cad.isAlive detects the death of connection or Cad.getATR is not able to reset the card.
private byte[]
getChannelAPDU
APDU for logical channel allocation.
private byte[]
closeChannelAPDU
APDU for closing a logical channel.
private byte[]
getResponseAPDU
APDU for GET RESPONSE command.
private byte[]
case2APDU
APDU for ISO7816-4 'case 2' command. Length is 5 bytes.
private byte[]
FCI
FCI information received from selected applications.
private CardSlot
cardSlot
CardSlot created for Cad.
private TempByteArray
resp_buffer
Stuff buffer for received data.
private TempByteArray
output_buffer
Stuff buffer for output data.
private int
slotN
Slot number
Constructors Summary
Cad(int slot, com.sun.midp.security.SecurityToken securityToken)
Constructor for CADClient.

param
slot Global slot number
param
securityToken Security token for this class
throws
IOException if there any problems in opening the socket or streams or powering up the card.


                                         
          
        
        try {
            cardSlot = SlotFactory.getCardSlot(slot, securityToken);
            if (cardSlot == null) {
                throw new IOException("Slot factory could not create a slot");
            }
        }
        catch (CardDeviceException e) { 
            throw new IOException("Config error: " + e.getMessage());
        }
        slotN = slot;
        getChannelAPDU = new byte[] {0, 0x70, 0, 0, 1};
        closeChannelAPDU = new byte[] {0, 0x70, (byte) 0x80, 0};
        getResponseAPDU = new byte[] {0, (byte)0xC0, 0, 0, 0};
        case2APDU = new byte[5];
    
Methods Summary
voidclean()
Does cleaning of the object.

        basicChannelInUse = false;
        SIMPresent = false;
        FCI = null;
    
voidcloseChannel(int channel)
This method is used to close connection with the card. If the channel number passed to this method is for basic channel then the basic channel is marked as available and nothing is communicated with the card. If the channel is not the basic channel, a request to close the channel is sent to the card.

param
channel channel number
exception
IOException if a communication error happens

        
        if (channel == 0) {
            basicChannelInUse = false;
            return;
        }

        try {
            closeChannelAPDU[3] = (byte) channel;
            exchangeApdu(null, closeChannelAPDU);
        } catch (IOException ioException) {
            throw new IOException("Error closing connection");
        }
    
byte[]exchangeApdu(Handle h, byte[] commandAPDU)
Exchange an Apdu with a CAD.

param
h Handle of the connection.
param
commandAPDU APDU data in byte array form.
return
response APDU data in byte array form.
exception
InterruptedIOException if connection was closed in the other thread.
exception
IOException if a communication error happens while communicating with the CAD.

        byte[] result;
        int result_length = 0;
        
        int Lc, Le;
        
        Lc = 0;
        Le = commandAPDU.length == 4 ? -1 : commandAPDU[4] & 0xFF; 

        if (commandAPDU.length > 5) {

            Lc = Le;

            if (5 + Lc > commandAPDU.length) {
                throw new IllegalArgumentException("Malformed APDU");
            }

            Le = 5 + Lc == commandAPDU.length ?
                    -1 : commandAPDU[5 + Lc] & 0xFF;
        }
        
        if (Le == 0) {
            Le = 256;
        }
        if (Le == -1) {
            Le = 0; 
        }
        
        int cla = commandAPDU[0] & 0xf0;
        int channel = cla != 0 && (cla < 0x80 || cla > 0xA0) ?
                      0 : commandAPDU[0] & 3;

        cardSlot.lockSlot();
        if (Lc == 0 && commandAPDU.length > 5) { // (case 4 & Lc==0) ==> case 2
            System.arraycopy(commandAPDU, 0, case2APDU, 0, 4);
            case2APDU[4] = commandAPDU[5];
            commandAPDU = case2APDU;
        }
        while (true) {
            int bytes_read;
            int sw1, sw2;
            
            bytes_read = 
                cardSlot.xferData(commandAPDU, resp_buffer.ensure(Le + 2));
            if (bytes_read < 2) {
                cardSlot.unlockSlot();
                throw new IOException("Bad response");
            }
            if (h != null && !h.opened) {
                cardSlot.unlockSlot();
                throw new InterruptedIOException("Connection closed");
            }
            sw1 = resp_buffer.val(bytes_read - 2) & 0xFF;
            sw2 = resp_buffer.val(bytes_read - 1) & 0xFF;
            
            // Correct wrong Le
             /* IMPL_NOTE: We shell make sure that Le!=sw2 but that is out of spec */
            if (bytes_read == 2 &&  
                    sw1 == 0x6C && sw2 != 0x00 &&
                    Le != 0) {
                
                Le = sw2;
                if (commandAPDU.length == 4) { // case 1
                    System.arraycopy(commandAPDU, 0, case2APDU, 0, 
                                     commandAPDU.length);
                    case2APDU[4] = (byte)(Le & 0xFF);
                    commandAPDU = case2APDU;
                } else 
                if (commandAPDU.length == 5) { // case 2
                    if (commandAPDU != case2APDU) {
                        System.arraycopy(commandAPDU, 0, case2APDU, 0, 4);
                        commandAPDU = case2APDU;
                    }
                    case2APDU[4] = (byte)(Le & 0xFF);
                } else {
                    if (commandAPDU.length == 5 + Lc) { // case 3
                        byte[] temp = new byte[5 + Lc + 1];
                        System.arraycopy(commandAPDU, 0, temp, 0, 5 + Lc);
                        commandAPDU = temp;
                    }
                    commandAPDU[5 + Lc] = (byte)(Le & 0xFF);
                }
                result_length = 0;
                continue;
            }
            if (result_length >= 2) {
                result_length -= 2; // Delete previous status bytes
            }
            System.arraycopy(resp_buffer.data(), 0, 
                output_buffer.expand(result_length + bytes_read), 
                    result_length, bytes_read);
            result_length += bytes_read;
            
            if (Le == 0 || (sw1 != 0x61 &&
                (channel != 0 || !SIMPresent ||
                 (sw1 != 0x62 && sw1 != 0x63 &&
                  sw1 != 0x9E && sw1 != 0x9F)))) {
                break;
            }

            Le = sw1 == 0x62 || sw1 == 0x63 ? 0 : sw2;
            
            commandAPDU = getResponseAPDU;
            commandAPDU[0] = (byte) channel;
            commandAPDU[4] = (byte)Le;
            if (Le == 0) {
                Le = 256;
            }
        }
        cardSlot.unlockSlot();
        result = new byte[result_length];
        System.arraycopy(output_buffer.data(), 0, result, 0, result_length);
        return result;
    
voidfreeSystemResources()
Frees up the resource that was used by this slot. This method is used only in case of error. IOException is ignored.

        try {
            cardSlot.closeSlot();
        }
        catch (IOException e) {}
        
    
byte[]getATR()
This method returns the ATR received from the card that this CadClient object is used to communicate with.

return
ATR information received from the card at startup or reset. In case of I/O troubles returns null.

        byte[] result;
        
        if (!isAlive()) {
            result = null;
        } else {
            result = cardSlot.getATR();
        }
        if (result == null) {
            SIMPresent = false;
        }
        return result;
    
public intgetCardSessionId()
Returns the card session identifier. This number is different for different card sessions.

return
the card session identifier

        return cardSlot.getCardSessionId();
    
byte[]getFCI()
This method returns the FCI received from the card.

return
FCI information received from the card at selectApplication.

        return FCI;
    
voidinitACL()
Initializes ACL for the slot.

        cardSlot.initACL();
    
booleanisAlive()
Checks if the the connection is still live or not.

return
true if the connection is alive

        if (cardSlot.isAlive()) {
            return true;
        } else {
            SIMPresent = false;
            return false;
        }
    
intselectApplication(boolean forSAT, byte[] selectAPDU)
This method is called when there is a connection creation is in progress and specifically card application selection is required. This method also does the channel management part. If basic channel is available for use, this method tries to select the target application on the basic channel. Otherwise, it requests the card for the channel and attempts to select the application on that particular channel. If the selection is successful, this method returns the logical channel reserved for communicating with the selected card application.

param
forSAT true if selection is made for SAT connection
param
selectAPDU byte encoded selection APDU
exception
ConnectionNotFoundException when selection is not successful
exception
IOException if no channel is available for communication establishment or there are communication problems.
exception
IllegalArgumentException if bad APDU provided.
return
response apdu from the select application request


        int channel;

        // Test if 'POWER UP' is needed or a card was changed
        if (!isAlive()) {
            if (cardSlot.isSAT()) {
                throw new ConnectionNotFoundException("SIM not found");
            } else {
                throw new ConnectionNotFoundException("SmartCard not found");
            }
        }

        if (cardSlot.initConnection()) {
            clean();
        }
        if (cardSlot.isSAT()) {
            SIMPresent = true;
        } else {
            SIMPresent = false;
        }
        if (!forSAT && (basicChannelInUse || SIMPresent)) {
            // get another channel for communication.
            byte[] response = exchangeApdu(null, getChannelAPDU);
            if (response.length  == 2) {
                // just got back the status word
                throw new IOException("No logical channel available");
            }
            // new channel number is in the first byte of response
            channel = response[0];
        } else {
            basicChannelInUse = true;
            channel = 0;
        }
        
        selectAPDU[0] = (byte)((selectAPDU[0] & 0xFC) | channel);

        byte[] result = exchangeApdu(null, selectAPDU);

        int sw1 = result[result.length - 2] & 0xFF;
        int sw2 = result[result.length - 1] & 0xFF;
        if ((sw1 << 8) + sw2 != 0x9000) {
            closeChannel(channel);
            throw new ConnectionNotFoundException(
                    "Card application selection failed");
        }
        FCI = result;
        return channel;