nfc - Android HCE issue -
i'm working on nfc-hce project android. stuck problem app doesn't recognize apdu select query.
i'm using acl122u reader powered chrome-nfc forum type 4 support , apdu_select query goes 00a4040007f039414848483500. checked many time, there can't error in aid. tried change several times, found working app on net working when changed aid in chrome-nfc. guess there manifest problem or service launching problem. appreciate help.
androidmanifest.xml
<service android:name=".myhostapduservice" android:exported="true" android:permission="android.permission.bind_nfc_service"> <intent-filter> <action android:name="android.nfc.cardemulation.action.host_apdu_service"/> </intent-filter> <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice"/> </service>
apduservice.xml
<?xml version="1.0" encoding="utf-8"?> <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/servicedesc" android:requiredeviceunlock="false"> <aid-group android:description="@string/aiddescription" android:category="other"> <aid-filter android:name="f0394148484835"/> </aid-group> </host-apdu-service>
myhostapduservice.java
public class myhostapduservice extends hostapduservice { private static final string tag = "myhostapduservice"; // private static final byte[] apdu_select = { (byte)0x00, // cla - class - class of instruction (byte)0xa4, // ins - instruction - instruction code (byte)0x04, // p1 - parameter 1 - instruction parameter 1 (byte)0x00, // p2 - parameter 2 - instruction parameter 2 (byte)0x07, // lc field - number of bytes present in data field of command (byte)0xf0, (byte)0x39, (byte)0x41, (byte)0x48, (byte)0x48, (byte)0x48, (byte)0x35, // ndef tag application name (byte)0x00 // le field - maximum number of bytes expected in data field of response command }; private static final byte[] capability_container = { (byte)0x00, // cla - class - class of instruction (byte)0xa4, // ins - instruction - instruction code (byte)0x00, // p1 - parameter 1 - instruction parameter 1 (byte)0x0c, // p2 - parameter 2 - instruction parameter 2 (byte)0x02, // lc field - number of bytes present in data field of command (byte)0xe1, (byte)0x03 // file identifier of cc file }; private static final byte[] read_capability_container = { (byte)0x00, // cla - class - class of instruction (byte)0xb0, // ins - instruction - instruction code (byte)0x00, // p1 - parameter 1 - instruction parameter 1 (byte)0x00, // p2 - parameter 2 - instruction parameter 2 (byte)0x0f // lc field - number of bytes present in data field of command }; // in scenario have done cc read, same byte[] match // readbinary trigger , don't want in succession private boolean read_capability_container_check = false; private static final byte[] read_capability_container_response = { (byte)0x00, (byte)0x0f, // cclen length of cc file (byte)0x20, // mapping version 2.0 (byte)0x00, (byte)0x3b, // mle maximum 59 bytes r-apdu data size (byte)0x00, (byte)0x34, // mlc maximum 52 bytes c-apdu data size (byte)0x04, // t field of ndef file control tlv (byte)0x06, // l field of ndef file control tlv (byte)0xe1, (byte)0x04, // file identifier of ndef file (byte)0x00, (byte)0x32, // maximum ndef file size of 50 bytes (byte)0x00, // read access without security (byte)0x00, // write access without security (byte)0x90, (byte)0x00 // a_okay }; private static final byte[] ndef_select = { (byte)0x00, // cla - class - class of instruction (byte)0xa4, // instruction byte (ins) select command (byte)0x00, // parameter byte (p1), select identifier (byte)0x0c, // parameter byte (p1), select identifier (byte)0x02, // lc field - number of bytes present in data field of command (byte)0xe1, (byte)0x04 // file identifier of ndef file retrieved cc file }; private static final byte[] ndef_read_binary_nlen = { (byte)0x00, // class byte (cla) (byte)0xb0, // instruction byte (ins) readbinary command (byte)0x00, (byte)0x00, // parameter byte (p1, p2), offset inside cc file (byte)0x02 // le field }; private static final byte[] ndef_read_binary_get_ndef = { (byte)0x00, // class byte (cla) (byte)0xb0, // instruction byte (ins) readbinary command (byte)0x00, (byte)0x00, // parameter byte (p1, p2), offset inside cc file (byte)0x0f // le field }; private static final byte[] a_okay = { (byte)0x90, // sw1 status byte 1 - command processing status (byte)0x00 // sw2 status byte 2 - command processing qualifier }; private static final byte[] ndef_id = { (byte)0xe1, (byte)0x04 }; private ndefrecord ndef_uri = new ndefrecord( ndefrecord.tnf_well_known, ndefrecord.rtd_text, ndef_id, "hello world, bitch!".getbytes(charset.forname("utf-8")) ); private byte[] ndef_uri_bytes = ndef_uri.tobytearray(); private byte[] ndef_uri_len = biginteger.valueof(ndef_uri_bytes.length).tobytearray(); @override public int onstartcommand(intent intent, int flags, int startid) { if (intent.hasextra("ndefmessage")) { ndef_uri = new ndefrecord( ndefrecord.tnf_well_known, ndefrecord.rtd_text, ndef_id, intent.getstringextra("ndefmessage").getbytes(charset.forname("utf-8")) ); ndef_uri_bytes = ndef_uri.tobytearray(); ndef_uri_len = biginteger.valueof(ndef_uri_bytes.length).tobytearray(); context context = getapplicationcontext(); charsequence text = "your ndef text has been set!"; int duration = toast.length_short; toast toast = toast.maketext(context, text, duration); toast.setgravity(gravity.center, 0, 0); toast.show(); } log.i(tag, "onstartcommand() | ndef" + ndef_uri.tostring()); return 0; } @override public byte[] processcommandapdu(byte[] commandapdu, bundle extras) { // // following flow based on appendix e "example of mapping version 2.0 command flow" // in nfc forum specification // log.i(tag, "processcommandapdu() | incoming commandapdu: " + utils.bytestohex(commandapdu)); // // first command: ndef tag application select (section 5.5.2 in nfc forum spec) // if (utils.isequal(apdu_select, commandapdu)) { log.i(tag, "apdu_select triggered. our response: " + utils.bytestohex(a_okay)); return a_okay; } // // second command: capability container select (section 5.5.3 in nfc forum spec) // if (utils.isequal(capability_container, commandapdu)) { log.i(tag, "capability_container triggered. our response: " + utils.bytestohex(a_okay)); return a_okay; } // // third command: readbinary data cc file (section 5.5.4 in nfc forum spec) // if (utils.isequal(read_capability_container, commandapdu) && !read_capability_container_check) { log.i(tag, "read_capability_container triggered. our response: " + utils.bytestohex(read_capability_container_response)); read_capability_container_check = true; return read_capability_container_response; } // // fourth command: ndef select command (section 5.5.5 in nfc forum spec) // if (utils.isequal(ndef_select, commandapdu)) { log.i(tag, "ndef_select triggered. our response: " + utils.bytestohex(a_okay)); return a_okay; } // // fifth command: readbinary, read nlen field // if (utils.isequal(ndef_read_binary_nlen, commandapdu)) { byte[] start = { (byte)0x00 }; // build our response byte[] response = new byte[start.length + ndef_uri_len.length + a_okay.length]; system.arraycopy(start, 0, response, 0, start.length); system.arraycopy(ndef_uri_len, 0, response, start.length, ndef_uri_len.length); system.arraycopy(a_okay, 0, response, start.length + ndef_uri_len.length, a_okay.length); log.i(tag, response.tostring()); log.i(tag, "ndef_read_binary_nlen triggered. our response: " + utils.bytestohex(response)); return response; } // // sixth command: readbinary, ndef data // if (utils.isequal(ndef_read_binary_get_ndef, commandapdu)) { log.i(tag, "processcommandapdu() | ndef_read_binary_get_ndef triggered"); byte[] start = { (byte)0x00 }; // build our response byte[] response = new byte[start.length + ndef_uri_len.length + ndef_uri_bytes.length + a_okay.length]; system.arraycopy(start, 0, response, 0, start.length); system.arraycopy(ndef_uri_len, 0, response, start.length, ndef_uri_len.length); system.arraycopy(ndef_uri_bytes, 0, response, start.length + ndef_uri_len.length, ndef_uri_bytes.length); system.arraycopy(a_okay, 0, response, start.length + ndef_uri_len.length + ndef_uri_bytes.length, a_okay.length); log.i(tag, ndef_uri.tostring()); log.i(tag, "ndef_read_binary_get_ndef triggered. our response: " + utils.bytestohex(response)); context context = getapplicationcontext(); charsequence text = "ndef text has been sent reader!"; int duration = toast.length_short; toast toast = toast.maketext(context, text, duration); toast.setgravity(gravity.center, 0, 0); toast.show(); read_capability_container_check = false; return response; } // // we're doing outside our scope // log.wtf(tag, "processcommandapdu() | don't know what's going on!!!."); return "can you?".getbytes(); } @override public void ondeactivated(int reason) { log.i(tag, "ondeactivated() fired! reason: " + reason); } }
sendovernfcactivity.java
public class sendovernfcactivity extends activity{ private static final string tag = "sendovernfcactivity"; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.sendnfc); button setndef = (button) findviewbyid(r.id.set_ndef_button); setndef.setonclicklistener(new view.onclicklistener() { @override public void onclick(view view) { // // technically, if past our byte limit, // cause issues. // // todo: add validation // textview getndefstring = (textview) findviewbyid(r.id.ndef_text); string test = getndefstring.gettext().tostring(); intent intent = new intent(getapplicationcontext(), myhostapduservice.class); intent.putextra("ndefmessage", test); startservice(intent); } } ); } @override public void onresume() { super.onresume(); } @override public void onpause() { super.onpause(); } @override public void onstop() { super.onstop(); } @override public void ondestroy() { super.ondestroy(); }
the problem public byte[] processcommandapdu(byte[] commandapdu, bundle extras)
never runs , app sending 6a82 every time in response mean file/app not found. totally can't understand why happens if aid correct.
Comments
Post a Comment