I am developing an application to read a NFC Tag UID from NFC Reader (ACR122U-A9) device. I used JAVA and javax.smartcardio API to detect the NFC Reader and Reading NFC Tag.
The functionality of the application is to display notification when the NFC Reader device is connect or disconnect from PC. Then if the device is connected and NFC Tag is presented then display the notification that NFC Tag is presented. I tried to find the Event based api to implement above functionality but I cannot find so I used Java Timer and Polling for the NFC Reader Device and NFC Tag.
Following is my sample JAVA code that used for Polling for NFC device and Tag.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.TerminalFactory;
/**
*
* @author sa
*/
public class NFC_Test {
/**
* @param args the command line arguments
*/
static Timer timer;
public static void main(String[] args) {
try {
timer = new Timer(); //At this line a new Thread will be created
timer.scheduleAtFixedRate(new NFC_Test.MyTask(), 0, 1000);
} catch (Exception ex) {
Logger.getLogger(NFC_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
static class MyTask extends TimerTask {
public void run() {
///////////////////This fix applied after reading thread at http://stackoverflow.com/a/16987873/1411888
try {
Class pcscterminal =
Class.forName("sun.security.smartcardio.PCSCTerminals");
Field contextId = pcscterminal.getDeclaredField("contextId");
contextId.setAccessible(true);
if (contextId.getLong(pcscterminal) != 0L) {
Class pcsc =
Class.forName("sun.security.smartcardio.PCSC");
Method SCardEstablishContext = pcsc.getDeclaredMethod(
"SCardEstablishContext", new Class[]{Integer.TYPE});
SCardEstablishContext.setAccessible(true);
Field SCARD_SCOPE_USER =
pcsc.getDeclaredField("SCARD_SCOPE_USER");
SCARD_SCOPE_USER.setAccessible(true);
long newId = ((Long) SCardEstablishContext.invoke(pcsc, new Object[]{Integer.valueOf(SCARD_SCOPE_USER.getInt(pcsc))})).longValue();
contextId.setLong(pcscterminal, newId);
}
} catch (Exception ex) {
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
TerminalFactory factory = null;
List<CardTerminal> terminals = null;
try {
factory = TerminalFactory.getDefault();
terminals = factory.terminals().list();
} catch (Exception ex) { //
Logger.getLogger(NFC_Test.class.getName()).log(Level.SEVERE,null, ex);
}
if (factory != null && factory.terminals() != null && terminals
!= null && terminals.size() > 0) {
try {
CardTerminal terminal = terminals.get(0);
if (terminal != null) {
System.out.println(terminal);
if (terminal.isCardPresent()) {
System.out.println("Card");
} else {
System.out.println("No Card");
}
} else {
System.out.println("No terminal");
}
terminal = null;
} catch (Exception e) {
Logger.getLogger(NFC_Test.class.getName()).log(Level.SEVERE,null, e);
}
factory = null;
terminals = null;
Runtime.getRuntime().gc();
} else {
System.out.println("No terminal");
}
}
}
}
Above code is working fine in Windows OS but when I run it on MAC OS then application runs for 5-10 seconds perfectly but then it suddenly crash with the following memory error.
java(921,0x10b0c3000) malloc: *** mmap(size=140350941302784) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
Java Result: 139
I searched on internet and cannot find anything regarding above memory error. Also I included the code for memory management to release the object when it is used in timer by assigning NULL value to it.
I have used http://ludovicrousseau.blogspot.com/2010/06/pcsc-sample-in-java.html for reference
I believe this was one of the errors I was getting when trying to track down the bugs with libj2pcsc.dylib on 64-bit Java on OS X. See also smartcardio thread on discussions.apple.com and my email to security-dev. Basically, the problem is that
DWORD*
should be a pointer to a 32-bit number on OS X, but Sun’s library assumed that it was a pointer to a 64-bit number. Then it dereferences that value and tries to malloc a buffer of that size, which can contain junk in the upper 32 bits. SeeJava_sun_security_smartcardio_PCSC_SCardListReaders
in the source of pcsc.cPotential workarounds:
Terminals.list()
(which crashes intermittently), and don’t trust the results ofTerminal.isCardPresent()
,Terminals.waitForChange(long)
, orCardTerminal.waitForCard(boolean, long)
. My co-worker realized that he could callTerminalImpl.SCardGetStatusChange(long, long, int[], String[])
using reflection to get the right results. This is what we used to do. Very painful!javax.smartcardio
. I know of two: my own jnasmartcardio and intarsys/smartcard-io. I have not tried my own library on NFC cards, however, but I welcome any bug reports and patches.