Cadpublic 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 | basicChannelInUseSince 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 | SIMPresentIndicates 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[] | getChannelAPDUAPDU for logical channel allocation. | private byte[] | closeChannelAPDUAPDU for closing a logical channel. | private byte[] | getResponseAPDUAPDU for GET RESPONSE command. | private byte[] | case2APDUAPDU for ISO7816-4 'case 2' command. Length is 5 bytes. | private byte[] | FCIFCI information received from selected applications. | private CardSlot | cardSlotCardSlot created for Cad. | private TempByteArray | resp_bufferStuff buffer for received data. | private TempByteArray | output_bufferStuff buffer for output data. | private int | slotNSlot number |
Constructors Summary |
---|
Cad(int slot, com.sun.midp.security.SecurityToken securityToken)Constructor for CADClient.
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 |
---|
void | clean()Does cleaning of the object.
basicChannelInUse = false;
SIMPresent = false;
FCI = null;
| void | closeChannel(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.
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.
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;
| void | freeSystemResources()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.
byte[] result;
if (!isAlive()) {
result = null;
} else {
result = cardSlot.getATR();
}
if (result == null) {
SIMPresent = false;
}
return result;
| public int | getCardSessionId()Returns the card session identifier.
This number is different for different card sessions.
return cardSlot.getCardSessionId();
| byte[] | getFCI()This method returns the FCI received from the card.
return FCI;
| void | initACL()Initializes ACL for the slot.
cardSlot.initACL();
| boolean | isAlive()Checks if the the connection is still live or not.
if (cardSlot.isAlive()) {
return true;
} else {
SIMPresent = false;
return false;
}
| int | selectApplication(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.
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;
|
|