See NFC reader "SELECT (by AID)" APDU is not routed to Android device on debugging and eventual results. TL;DR the reader might simply be defunct.
I have ACR122U reader connected to my Android device. My final goal is to use another Android device in HCE mode to enable data communication between the devices.
Currently I have a problem with integrating with ACR122U reader. I use lib acssmc-1.1.1.jar provider by the vendor. This is the code I currently have (omitting the example code of opening the reader).
mReader.power(slotNum, Reader.CARD_WARM_RESET);
mReader.setProtocol(slotNum, Reader.PROTOCOL_T0 | Reader.PROTOCOL_T1);
//FF 00 00 00 04 D4 4A 01 00 00 - Polling for the ISO14443-4 A ??
byte[] w = new byte[255];
int len = mReader.transmit(0, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, 9, w, w.length);
Log.e("Response", "r: " + NfcUtils.convertBinToASCII(w, 0, len));
//00 A4 04 00 07 F0 01 02 03 04 05 07 00 - Select AID APDU
w = new byte[255];
mReader.transmit(0, new byte[] {0x00, (byte) 0xA4, 0x04, 0x00, 7,
(byte)0xF0, 0x01, 0x02, 0x03, 0x04, (byte) 0x05, 0x07, 0}, 13, w, w.length);
Log.e("Response", "r: " + Arrays.toString (recvBuffer));
If I tap a tag to the NFC reader this is the output I get
r: D54B0101440004048853606A9000
r: lots of zeroes
When I tap an Android device this is the response:
r: D54B6300
r: lots of zeroes
basically it fails on the polling stage (?). On the Android device I have a basic HostAdpuService implementation:
public class MyHostApduService extends HostApduService {
#Override
public void onCreate() {
super.onCreate();
Log.e("APDU", "APDU service was created.");
}
#Override
public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
Log.e("APDU", "command apdu: " + Arrays.toString(apdu));
return new byte[2];
}
#Override
public void onDeactivated(int reason) {
Log.e("APDU", "ON DEACTIVATED.");
}
}
processCommandApdu never got called. Looking through the logs of an Android device I can't find anything relevant at the moment when I tap the device.
I went through several examples online and wasn't able to make it work on other (not Android) platforms (Windows, OSX) as well.
Related
I have this USB Relay and I'd like to control from an Android phone.
(There is a similar post here but it explains how do it from a Linux shell. By looking at that code i figured i'd be able to solve it - apparently not.)
The device lists in lsusb:
Bus 002 Device 011: ID 16c0:05df VOTI
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x16c0 VOTI
idProduct 0x05df
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 34
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 20mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 0 None
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.01
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 22
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 20
I find it strange that the end point is defined as "EP IN". In my mind it should be "EP OUT" since the direction should be "host->device".
Anyway, I use the Android USB manager to create a connection and then initialize the USbRequest for an interrupt based end point. Android permissions and all that is handled so the device is connected successfully.
Snippet for sending data. It runs in a separate thread following the Android guidelines:
UsbRequest request = new UsbRequest();
synchronized (mUsbLock) {
if (mUsbConnection != null && mUsbEndPointIn != null) {
if (!request.initialize(mUsbConnection, mUsbEndPointIn)) {
Log.e(TAG, "Unable to initialize UsbRequest. Thread exits");
return;
} else {
if (DEBUG) {
Log.d(TAG, String.format("Usb request is initialized"));
}
}
} else {
Log.e(TAG, "Usb communication is not up and running. The worker thread should never be started.");
return;
}
}
mRunning.set(true);
while (mRunning.get()) {
if (DEBUG) {
Log.d(TAG, String.format("Waiting for data to be sent to end point"));
}
WorkPackage wp = mWorkQueue.take();
// send the package.
byte[] data = wp.getData();
if (!request.queue(ByteBuffer.wrap(data), data.length)) {
Log.e(TAG, "Unable to queue to send data on UsbRequest.");
continue;
} else {
if (DEBUG) {
Log.d(TAG, String.format("Usb request is queued on end point, ep=%s", printUsbEndpoint(mUsbEndPointIn)));
}
}
}
It seems everything is fine, no errors occur and the request is queued on the end point but then nothing happens at all. I don't get any message back that the request has been handled.
Since the supplier won't release the on/off commands I tried variants based on the Linux post (above). None seem to work though. Supplier only release Windows binary lib.
Sending 8 byte packages (according to max package):
public static byte[] SET_RELAY_ON = {(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
public static byte[] SET_RELAY_OFF = {(byte) 0xfd, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
Help appreciated.
So, i figured out how to do this. I will write the solution here in case someone else is having a similar problem.
I had to snoop the USB traffic to understand what to actually send. It turned out that the defined interrupt end point was not used at all. So, the communication with the device was not interrupt based but instead using the control transfer type on end point 0.
So, using the Android USB API this translates into:
Open device (device->host direction):
int r = mUsbConnection.controlTransfer(0xa1, 0x01, 0x0300, 0x00, buffer, buffer.length, 500);
Open relay '1' (host->device direction).
byte[] buffer = {(byte) 0xff, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);
Note, the second parameter in the buffer (0x01) is the relay number (in case you have >1 relay on the board). I only had one.
close relay '1' (host->device direction):
byte[] buffer = {(byte) 0xfd, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);
I have found the project in Github which is similar with this it (for who is looking it)
https://github.com/gigacycle/AndroidHidUsbRelayControl
I have tested this code and I can confirm that it works well with the USB relay board.
When I try to transceive commands for NFC-V Tag-it HF-I Plus Inlay tag I get a TagLostException for most of the commands.
From the links I have gone through this exception may be caused by incorrect commands.
How can I create correct ISO15693 command byte[] for Nfc V Tag-it HF-I Plus Inlay?
The datasheet shows the supported commands but how can I create correct commands to read NFC-V tags?
The commands in the document are:
The tag that I'm trying to read is:
Code:
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Log.i(TAG, " tag "+tag );
if (tag != null) {
NfcV tech = NfcV.get(tag);
Log.i(TAG, " tech "+tech );
if (tech != null) {
try {
tech.connect();
Log.i(TAG, " on connect" );
byte[] data = tech.transceive(Nfcv.InventoryRequest());
Log.i(TAG, "resp data " + data);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.length; i++) {
byte b = data[i];
System.out.println(b);
sb.append(String.format("%02X ", b));
}
System.out.println("response: " + sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
tech.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I have gone through the following:
NfcV Transceive command throws tag lost exception with TI HF-I plus tag(ISO15693) in android
Transceive Failed on ISO15693 / Tag-it HF-I
Android NfcV Stay Quiet Command
Android NfcV (ISO 15693) tag
Connection error when reading Android NfcV tags
EDIT
Commands that I have tried:
public class Nfcv {
// texas get system info -> tag lost exception
public static byte[] GET_SYSTEM_INFO = ReadNfcActivity.hexStringToByteArray("010A00030418002B0000");
//read multiple blocks -> not working
byte[] read_multiple_blocks= ReadNfcActivity.hexStringToByteArray("010C00030418002301020000");
byte[] readSingleBlock = ReadNfcActivity.hexStringToByteArray("010B000304180020050000");
// readUID generic command -> not working
public static byte[] readUID = ReadNfcActivity.hexStringToByteArray("FFCA000000");
public static byte[] InventoryRequest(){
//working response: 00 00 3A E5 00 04 00 00 07 E0
// R/0 UID is E0 07 00 00 04 00 E5 3A 00 00 (reverse)
return new byte[] { (byte) 0x24, (byte) 0x01, (byte) 0x00};
}
//-> not working
private byte[] ReadSingleBlockUnadressed(byte blocknumber) {
return new byte[] {(byte) 0x00, (byte) 0x20, blocknumber};
}
//-> response 03
public static byte[] get_system_info = {0x00,(byte)0x2B};
}
The Android NFC stack automatically handles polling (searching for tags various tag technologies/protocols), anti-collision (enumeration of multiple tags within one tag technology/protocol) and activation (intitiating communication with one specific tag) for you. You should, therefore, never send commands used for anti-collision and activation yourself. The Inventory command is one such command (that is used to discover tags in range).
With regard to the Inventory command, there is typically no need to send this command. All the information that you would get from this command is already provided by the Android NFC API:
You can get the UID using tag.getId().
You can get the DSFID using tech.getDsfId().
Also, for your app to work reliable across different Android device platforms (= different NFC stacks), you should always use the addressed version of commands (i.e. Address_flag set and UID sent as part of request). See Android NfcV get information command returns only one byte.
If you want to read from/write to the tag, you could use the READ_SINGLE_BLOCK and WRITE_SINGLE_BLOCK commands:
byte[] tagUid = tag.getId(); // store tag UID for use in addressed commands
int blockAddress = 0; // block address that you want to read from/write to
READ_SINGLE_BLOCK:
byte[] cmd = new byte[] {
(byte)0x20, // FLAGS
(byte)0x20, // READ_SINGLE_BLOCK
0, 0, 0, 0, 0, 0, 0, 0,
(byte)(blockAddress & 0x0ff)
};
System.arraycopy(tagUid, 0, cmd, 2, 8);
byte[] response = tech.transceive(cmd);
WRITE_SINGLE_BLOCK:
byte[] cmd = new byte[] {
(byte)0x60, // FLAGS
(byte)0x21, // WRITE_SINGLE_BLOCK
0, 0, 0, 0, 0, 0, 0, 0,
(byte)(blockAddress & 0x0ff),
... // data block that you want to write (same length as the blocks that you read)
};
System.arraycopy(tagUid, 0, cmd, 2, 8);
byte[] response = tech.transceive(cmd);
I am trying to send some data to the Nexus 4 through NFC (i.e. the card emulation mode). I tried a number of the command APDUs such as writing and updating APDUs, but I couldn't get them to work.
What I am trying to say is, I want to send some data (that is not the AID) to the phone after the select APDU command.
Thanks in advance,
Bader
The HCE emulated card will understand exactly those commands that your HCE app's APDU service processes. So, for instance, if your HCE service's processCommandApdu() callback method looks like this:
final static byte[] SW_NO_ERROR = new byte[]{ (byte)0x90, (byte)0x00 };
final static byte[] SW_INCORRECT_P1P2 = new byte[]{ (byte)0x6A, (byte)0x86 };
final static byte[] SW_INS_NOT_SUPPORTED = new byte[]{ (byte)0x6D, (byte)0x00 };
final static byte[] SW_ERR_UNKNOWN = new byte[]{ (byte)0x6F, (byte)0x00 };
#Override
public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
if (apdu.length >= 4) {
if ((apdu[1] == (byte)0xA4) && (apdu[2] == (byte)0x04)) {
// SELECT APPLICATION
return SW_NO_ERROR;
} else if ((apdu[1] == (byte)0xCA) && (apdu[2] == (byte)0x02)) {
// GET DATA (SIMPLE TLV)
switch (apdu[3] & 0x0FF) {
case 0x001:
return new byte[]{ apdu[3], (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x90, (byte)0x00 };
case 0x002:
return new byte[]{ apdu[3], (byte)0x02, (byte)0x12, (byte)0x34, (byte)0x90, (byte)0x00 };
case 0x003:
return new byte[]{ apdu[3], (byte)0x06, (byte)0xAA, (byte)0xBB, (byte)0xCC, (byte)0xDD, (byte)0xEE, (byte)0xFF, (byte)0x90, (byte)0x00 };
default:
return SW_INCORRECT_P1P2;
}
} else {
return SW_INS_NOT_SUPPORTED;
}
}
return SW_ERR_UNKNOWN;
}
Your HCE app would understand the following command APDUs:
SELECT APPLICATION (by AID)
00 A4 04 xx ...
GET DATA for data object 0201
00 CA 02 01 00
GET DATA for data object 0202
00 CA 02 02 00
GET DATA for data object 0203
00 CA 02 03 00
Other commands will result in various errors.
I'm trying to read a smartcard via my LG P710 Optimus L7 2.
I'm following this tutorial
I can select the "1PAY.SYS.DDF01" directory
I can select the Application
But I can't perform an "GET PROCESSING OPTIONS"
It always result in an 6700 error (Lc or Le wrong)
here is my code
NfcAdapter mNFCAdapter;
Intent intent;
PendingIntent pendingIntent;
private TextView mTextView;
String[][] techList;
IntentFilter[] filters = new IntentFilter[3];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTextView = (TextView) findViewById(R.id.title);
mNFCAdapter = NfcAdapter.getDefaultAdapter(this);
intent = new Intent(getApplicationContext(), getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
techList = new String[][]{
new String[]
{ MifareClassic.class.getName() },
new String[]
{ IsoDep.class.getName() }
};
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
// add type of tag data you want to have - here ndef -> plain text
try {
filters[0].addDataType(MIME_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
e.printStackTrace();
}
filters[1] = new IntentFilter();
filters[1].addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
filters[1].addCategory(Intent.CATEGORY_DEFAULT);
filters[2] = new IntentFilter();
filters[2].addAction(NfcAdapter.ACTION_TECH_DISCOVERED);
filters[2].addCategory(Intent.CATEGORY_DEFAULT);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getAction();
mTextView.setText(action);
Toast.makeText(getApplicationContext(), action, Toast.LENGTH_SHORT).show();
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
IsoDep tagIsoDep;
if((tagIsoDep = IsoDep.get(tagFromIntent)) != null)
if(handleIsoDep(tagIsoDep))
return;
}
private boolean handleIsoDep(IsoDep tag){
try{
tag.connect();
tag.setTimeout(20);
byte[] responseAPDU;
//2PAY.SYS.DDF01
byte[] select_Dir = new byte[]{
(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x0e,
(byte)0x32, (byte)0x50, (byte)0x41, (byte)0x59, (byte)0x2e,
(byte)0x53, (byte)0x59, (byte)0x53, (byte)0x2e, (byte)0x44,
(byte)0x44, (byte)0x46, (byte)0x30, (byte)0x31
};
//Select CC Applet
byte[] select_Applet = new byte[]{
(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)7,
(byte)0xa0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04,
(byte)0x30, (byte)0x60
};
//Send GET PROCESSING OPTIONS command
byte[] Send_Get = new byte[]{
(byte)0x80,(byte)0xA8,(byte)0x00,(byte)0x00,(byte)0x02,
(byte)0x83,(byte)0x00,
(byte)0x00
};
responseAPDU = tag.transceive(select_Dir);
mTextView.setText(mTextView.getText() + handleResponse(responseAPDU));
this returns the APDU-Statusword 9000 -> success
responseAPDU = tag.transceive(select_Applet);
mTextView.setText(mTextView.getText() + handleResponse(responseAPDU));
this returns the APDU-Statusword 9000 -> success
responseAPDU = tag.transceive(Send_Get);
mTextView.setText(mTextView.getText() + handleResponse(responseAPDU));
and this one is making problems: it returns 6700 -> wrong Lc or Le
mTextView.setText(mTextView.getText() + "\n\nDone");
tag.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
The function handleResponse just parses the "responseAPDU" from Binary to Hex an highlights the Statusword
Can anybody tell my what is going wrong?
or just help me out?
PS sry for bad english ;)
As response to my application-select I get:
6f298407a0000000043060a51e50074d41455354524f5f2d046465656e9f38039f5c08bf0c059f4d020b0a9000
6F -> FCI Template 29
84 -> DF Name 07 A0 00 00 00 04 30 60
A5 -> FCI Properietary Template 1E
50 -> Application Lable 07 4D 41 45 53 54 52 4F 5F 2D 04 64 65 6E
9F38 -> PDOL 03 9F 5C 08
BF0C -> FCI Issuer Data 05
9F4D -> Log Entry 02 0B
0A Additional Issuer Data
But I don't know what ive to insert into the Data fild from the GET PROCESSING OPTIONS.
Iv'e red the guidelines in EMV Book 3, section "5.4 Rules for Using a Data Object List (DOL)".
So do I just have to set the data field 83 03 9F 5C 08
and Lc = 5?
In order to help you, the entire ADPU dialog (commands/responses) would be needed.
However, based on your code : hardcoding your select_Dir and select_Applet commands is correct, but you can't hardcode the GET PROCESSING OPTIONS command whose syntax depends on the response of the card (ICC) to your select_Applet command.
EMV 4.3 Book 1, "Table 45: SELECT Response Message Data Field (FCI) of an ADF", explains that a successful card response to the SELECT command contains a "Processing Options Data Object List" (PDOL, tag 9F38). That's the list of fields required by the card to process the transaction (ex : amount, ...). These fields values are to be returned to the card by the terminal (your phone) through the GET PROCESSING OPTIONS command data field (tag 83), as documented in EMV 4.3 book 3, section "6.5.8.3 Data Field Sent in the Command Message" :
The data field of the command message is a data object coded according to the PDOL provided by the ICC, as defined in section 5.4, and is introduced by the tag '83'. When the data object list is not provided by the ICC, the terminal sets the length field of the template to zero. Otherwise, the length field of the template is the total length of the value fields of the data objects transmitted to the ICC.
Knowing that :
Your selected AID (A0 00 00 00 04 30 60) is a Mastercard Maestro one, which is unlikely to have an empty PDOL
But your GET PROCESSING OPTIONS command does not list any value in its data field
You probably have a mismatch between the length of your GET PROCESSING OPTIONS data field and the total length of the fields asked by the card in the PDOL, hence the 6700 checking error returned by the card (EMV Book 1, "Table 33: GET RESPONSE Error Conditions").
You have identified the PDOL requested by the card as : 9F38 -> 03 9F 5C 08.
The 03 tells you the PDOL is 3 bytes long. 9F5C is the tag of the requested field, 08 is the length of the field value that is to be returned by the phone.
Tag 9F5C is defined in EMV Contactless 2.3 Book C2 kernel 2 specification, section "A.1.59 DS Requested Operator ID". The DS Requested Operator ID is defined as
Contains the Terminal determined operator identifier for data
storage. It is sent to the Card in the GET PROCESSING
OPTIONS command.
I'm not familiar with this tag, so I can't tell you what a proper value is.
However, here is what the data field of the GET PROCESSING OPTIONS command should look like, assuming a DS Requested Operator ID has value 01 02 03 04 05 06 07 08, and given the Data Object List formatting guidelines in EMV Book 3, section "5.4 Rules for Using a Data Object List (DOL)" :
83 08 01 02 03 04 05 06 07 08
and Lc = 10
I'm busy with an app to emulate normal APDU communication on a Nexus 7 with CM10.1 to an ACR122U102 reader/writer. I found this blog about software card emulation and wrote an app to make my device (the nexus) appear as a card. Now I'm trying to send messages back and forth between this device and the ACR122u. So far, I've only managed to communicate with the nexus 7 by sending D4 40 01 (InDataExchange page 127) APDU's. For the application I'm writing, this should be sufficient.
The problem lays in the answer I send from the device to the reader. Using the transcieve function (android.nfc.tech.IsoPcdA with reflection), I can reply with a byte array of length > 0. This would show up on the reader-end like a normal InDataExchange response (e.g: D5 41 00 01 02 03 with {01 02 03} being the byte array supplied to the transcieve function). But I can't control the status byte nor the SW bytes in the response (D5 41 XX and both SW's). There's no documentation to be found about this IsoPcdA class except the source code itself.
What I want to be able to do is change the XX to a byte of my choice and to send answers of length = 0 (e.g: D5 41 01 without any extra data). Is it possible?
I'm not exactly sure what you are trying to achieve here. Whatever you transceive with IsoPcdA's transceive method are complete APDUs (as defined in ISO/IEC 7816-4, or rather any PDU within the ISO-DEP transport protocol). So the return value of transceive is a full C-APDU (command APDU) and the byte array parameter of transceive is a full R-APDU (response APDU) including the two bytes of the status word (SW1 | SW2). Thus, the last two bytes of that parameter are the status word. In your example SW1 would be 02 and SW2 would be 03.
What you see as status byte in the InDataExchange command of the PN532 NFC controller is not the status word of the APDU but the status of the command execution within the PN532 NFC controller. This status byte gives you information about buffer overflows, communication timeouts, etc and is not something that is returned by the card side.
EDIT : Sample code + test commands:
Sample Code running on Galaxy Nexus (CM 10):
try {
Class isoPcdA = Class.forName("android.nfc.tech.IsoPcdA");
Method isoPcdA_get = isoPcdA.getDeclaredMethod("get", Tag.class);
final IsoPcdA techIsoPcdA = (IsoPcdA)isoPcdA_get.invoke(null, tag);
if (techIsoPcdA != null) {
if (mWorker != null) {
mInterrupt = true;
mWorker.interrupt();
try {
mWorker.join();
} catch (Exception e) {}
}
mInterrupt = false;
mWorker = new Thread(new Runnable() {
public void run () {
try {
techIsoPcdA.connect();
byte[] command = techIsoPcdA.transceive(new byte[]{ (byte)0x90, (byte)0x00 });
Log.d(CardEmulationTest.class.getName(), "Connected.");
while (!mInterrupt) {
Log.d(CardEmulationTest.class.getName(), "C-APDU=" + StringUtils.convertByteArrayToHexString(command));
command = techIsoPcdA.transceive(command);
}
} catch (Exception e) {
Log.e(CardEmulationTest.class.getName(), "Exception while communicating on IsoPcdA object", e);
} finally {
try {
techIsoPcdA.close();
} catch (Exception e) {}
}
}
});
mWorker.start();
}
} catch (Exception e) {
Log.e(CardEmulationTest.class.getName(), "Exception while processing IsoPcdA object", e);
}
Test (using ACR122U):
InListPassivTargets (1 target at 106kbps)
> FF00000004 D44A 0100 00
< D54B 010100046004088821310578338800 9000
InDataExchange with DATA = 0x01
> FF00000004 D440 01 01 00
< D541 00 01 9000
So we get an error code of 0x00 from the card reader (status of InDataExchange command; not part of the actual response APDU), we get 0x01 as the response (this is the IsoDepA response APDU) and we get 0x9000 as the status code for the card reader wrapper APDU (not part of the actual response APDU).
InDataExchange with DATA = 0x01 0x02
> FF00000005 D440 01 0102 00
< D541 00 0102 9000
So we get an error code of 0x00 from the card reader (status of InDataExchange command; not part of the actual response APDU), we get 0x01 0x02 as the response (this is the IsoDepA response APDU) and we get 0x9000 as the status code for the card reader wrapper APDU (not part of the actual response APDU).
InDataExchange with DATA = 0x01 0x02 0x03
> FF00000006 D440 01 010203 00
< D541 00 010203 9000
So we get an error code of 0x00 from the card reader (status of InDataExchange command; not part of the actual response APDU), we get 0x01 0x02 0x03 as the response (this is the IsoDepA response APDU) and we get 0x9000 as the status code for the card reader wrapper APDU (not part of the actual response APDU).
InDataExchange with DATA = 0x01 0x02 0x03 0x04
> FF00000007 D440 01 01020304 00
< D541 00 01020304 9000
So we get an error code of 0x00 from the card reader (status of InDataExchange command; not part of the actual response APDU), we get 0x01 0x02 0x03 0x04 as the response (this is the IsoDepA response APDU) and we get 0x9000 as the status code for the card reader wrapper APDU (not part of the actual response APDU).
Thus, we get exactly the data taht we send as command APDU as response APDU (note that none of these APDUs is formatted according to ISO 7816-4, but that doesnt matter as the IsoPcdA card emulation works with any ISO 14443-4 transport protocol format).
The status code of 0x9000 belongs to the card reader APDU encapsulation (CLA=FF INS=00 P1P2=0000 Lc [PN542 COMMAND] Le=00) that is required as the ACR122U's PN532 is accessed over the CCID (PC/SC) interface. These are pure reader command encapsulation and have nothing to do with the communication over ISO-DEP.
The D440 01 [DATA] is the PN532 command to exchange data (e.g. APDUs) over ISO-DEP and the D541 00 [DATA] is the associated response.