I am trying to create a simple message exchange between a Nexus 3 phone and a raspberry Pi with an adafruit NFC breakboard (PN532 chip).
I have compiled the latest libnfc1.71 library and ran the examples:
pi#raspberrypi ~/libnfc1.7/libnfc-1.7.1/examples $ ./nfc-dep-initiator
NFC device: pn532_uart:/dev/ttyAMA0
openedD.E.P. (212 kbpspassive mode) target:
NFCID3: 01 fe ec d8 f7 03 c5 2d 00 00
BS: 00
BR: 00
TO: 08
PP: 32
General Bytes: 46 66 6d 01 01 11 03 02 00 13 04 01 96
Sending: Hello World!
nfc_initiator_transceive_bytes: RF Transmission Error
The error is thrown when I remove the phone from the NFC board, otherwise it just does nothing.
pi#raspberrypi ~/libnfc1.7/libnfc-1.7.1/examples $ ./nfc-dep-target
NFC device: pn532_uart:/dev/ttyAMA0 opened
NFC device will now act as: D.E.P. (undefined baud ratepassive mode) target:
NFCID3: 12 34 56 78 9a bc de ff 00 00
BS: 00
BR: 00
TO: 00
PP: 01
General Bytes: 12 34 56 78
Waiting for initiator request...
Initiator request received. Waiting for data...
nfc_target_receive_bytes: Target Released
The code from the android device:
MainActivity extends ActionBarActivity implements NfcAdapter.CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback{
private NfcAdapter mNfcAdapter = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null) {
Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
finish();
return;
}
mNfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback()
{
#Override
public NdefMessage createNdefMessage(NfcEvent event)
{
String text = ("Beam me up, Android!\n\n" +
"Beam Time: " + System.currentTimeMillis());
NdefMessage msg = new NdefMessage(
new NdefRecord[] { createMime(
"application/vnd.com.example.android.beam", text.getBytes())
});
return msg;
}
}, this, this);
// mNfcAdapter.setNdefPushMessageCallback(this, this);
mNfcAdapter.setOnNdefPushCompleteCallback(this,this);
}
#Override
public void onResume() {
super.onResume();
// Check to see that the Activity started due to an Android Beam
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
processIntent(getIntent());
}
}
#Override
public void onNewIntent(Intent intent) {
// onResume gets called after this to handle the intent
setIntent(intent);
}
/**
* Parses the NDEF Message from the intent and prints to the TextView
*/
void processIntent(Intent intent) {
EditText textView = (EditText) findViewById(R.id.editText);
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
// only one message sent during the beam
NdefMessage msg = (NdefMessage) rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
textView.setText(new String(msg.getRecords()[0].getPayload()));
}
public void CloseApp(View view) {
this.finish();
}
#Override
public void onNdefPushComplete(NfcEvent nfcEvent) {
EditText editText = (EditText) findViewById(R.id.editText);
editText.setText("onNdefPushComplete");
}
processIntent never gets called, neither onNdefPushComplete or createNdefMessage
The peer-to-peer examples that you are using are transmitting data at the NFCIP-1 (NFC-DEP) layer. An Android device supports only exchange of NDEF messages through SNEP/NPP, so you would need to implement the missing layers between NFC-DEP and SNEP in order to get P2P communication with an Android device running. The layers are like this (you can get all the specifications from the NFC Forum's website:
+------------------------------------------------+
| NFC Data Exchange Format (NDEF) |
+------------------------------------------------+
| Simple NDEF Exchange Protocol (SNEP) |
+------------------------------------------------+
| NFC Logical Link Control Protocol (LLCP) |
+------------------------------------------------+
| NFC Data Exchange Protocol (NFC-DEP/NFCIP-1) |
+------------------------------------------------+
So you have to add the LLCP, SNEP and NDEF layers.
Related
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.
Hi I'm trying to connect an Android app to an Eddystone UID Beacon I've created using Bluez 5.23 at a RaspberryPi 3.
The beacon was created using the following command:
sudo hciconfig hci0 up
sudo hciconfig hci0 leadv 3
sudo hcitool -i hci0 cmd 0x08 0x0008 1F 02 01 06 03 03 AA FE 17 16 AA FE 00 E7 01 02 03 04 05 06 07 08 09 0A 01 02 03 04 05 06 00 00
This beacon is apparently working properly cause I can see it in an Android device using some store app like Beacon Toy.
I am using the following code inside the Main Activity in order to discover the beacon:
public class MainActivity extends AppCompatActivity implements BeaconConsumer, MonitorNotifier {
...
#Override
public void onResume() {
super.onResume();
beaconManager = BeaconManager.getInstanceForApplication(this.getApplicationContext());
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
beaconManager.bind(this);
}
#Override
public void onBeaconServiceConnect() {
Region region = new Region("my-beacon-region", null, null, null);
beaconManager.addMonitorNotifier(this);
try {
beaconManager.startMonitoringBeaconsInRegion(region);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void didEnterRegion(Region region) {
Log.d(TAG, "I detected a beacon in the region with namespace id " + region.getId1() +
" and instance id: " + region.getId2());
}
}
Anyone knows what might be happening? The method didEnterRegion is never called. I also put a 'didDetermineStateForRegion' method inside the class but I always receive OUTSIDE in the state param meaning that I'm not in the region.
I guess you did not request or did not approve the Bluetooth and Location permissions for your application. Look at: https://altbeacon.github.io/android-beacon-library/requesting_permission.html.
If you have the correct permissions already then few questions to be sure:
Is the beacon recognized as Eddystone UID by the Beacon Toy app?
Is onBeaconServiceConnect called?
A small hint: I recommend you to initialize the BeaconManager in onCreate method instead of onResume. You are adding new parser whenever your app is resumed right now. I am actually doing this in application object as even onCreate of activity can be called multiple times.
How could I play a wave file in Android using libpd? I can do this with soundpool.play, but I'd like to try libpd. I followed this tutorial to implement libpd, but it's not working. What can be wrong? The code or the PD patch?
This is my activity code:
public class MainActivity extends ActionBarActivity implements OnTouchListener {
private PdUiDispatcher dispatcher;
private void initPD() throws IOException {
int sampleRate = AudioParameters.suggestSampleRate();
PdAudio.initAudio(sampleRate, 0, 2, 8, true);
dispatcher = new PdUiDispatcher();
PdBase.setReceiver(dispatcher);
}
private void loadPDPatch() throws IOException {
File dir = getFilesDir();
IoUtils.extractZipResource(getResources().openRawResource(R.raw.playaudio), dir, true);
File pdPatch = new File(dir, "playaudio.pd");
PdBase.openPatch(pdPatch.getAbsolutePath());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bangButton = (Button) findViewById(R.id.bangButton);
bangButton.setOnTouchListener(this);
try {
initPD();
loadPDPatch();
PdAudio.startAudio(this);
} catch (IOException e) {
finish();
}
}
#Override
protected void onResume() {
super.onResume();
PdAudio.startAudio(this);
}
#Override
protected void onPause() {
super.onPause();
PdAudio.stopAudio();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
if(v.getId() == R.id.bangButton) {
PdBase.sendBang("mybang");
}
return false;
}
}
This is my pd patch:
#N canvas 0 22 902 577 24;
#X obj 46 24 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1
;
#X obj 47 248 dac~;
#X obj 48 193 readsf~;
#X obj 49 118 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
-1;
#X msg 42 61 \; pd dsp \$1;
#X msg 50 155 open myfile.wav \, 1;
#X connect 0 0 4 0;
#X connect 2 0 1 0;
#X connect 3 0 5 0;
#X connect 5 0 2 0;
The error is in the Pd-patch:
You are sending a bang to the mybang symbol within the Pd-patch.
However, there doesn't seem to be a receiver attached to that name in the patch, so the even never triggers anything within Pd.
You have another [send mybang] that is triggered by the bng object (but that will also just send to the void).
The [bng] object might have a receive-label set, but
according to the screenshot it has no receive-label set at all (the inlet would vanish if so; but it's a bit hard to read).
If you did have a receive-label mybang, then clicking on [bng] would trigger an infinite recursion ([bng] => [s mybang] -> [bng] ...)
general errors
output of [bng]: you should never have a fan-out of messages (where you connect a single outlet to multiple message inlets) as this results in undefined order of execution; use [trigger] in these cases.
last outlet of [readsf~]: [readsf~] is a mono player by default; the last outlet of this object gives you a bang whenever the soundfile has finished playing; sending a bang to the 2nd inlet of [dac~] (expecting a signal) is an error.
If you want a stereo soundfile player, use [readsf~ 2]
solution
So the patch should instead look like:
[bng]
|
[s mybang]
[r mybang]
|
[open myfile.wav, 1(
|
[readsf~ 2]
| |
[dac~ ]
Did the patch alone work, before integrating to android? If so, try enabling the DSP ON via android with another button and send it to patch or remove DSP ON toggle box permanently and make DSP ON.
In the book "Making Musical Apps" by Peter Brinkmann
he suggests:
"If your patch uses additional resources, such as wav files or abstractions, then it is good practice to package those resources with your patch and to refer to them by relative paths only."
In other words:
Compress all your folder content in one zip file, so when this is uncompressed in Android, all the resources will be on the same folder, and the pd patch will be able to find your .wav or other files.
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.