我正在开发一个应用程序,用于从NFC读取器(ACR122U-A9)设备读取NFC标签UID。 我使用JAVA和javax.smartcardio API来检测NFC阅读器和阅读NFC标签。
应用程序的功能是在NFC读取器设备与PC连接或断开连接时显示通知。然后,如果设备已连接并且呈现NFC标签,则显示提供NFC标签的通知。 我试图找到基于事件的api来实现上述功能,但我找不到,所以我使用Java Timer和Polling作为NFC读卡器设备和NFC标签。
以下是我的示例JAVA代码,用于轮询NFC设备和标记。
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");
}
}
}
}
以上代码在Windows操作系统中运行良好,但是当我在MAC OS上运行时,应用程序运行完美5-10秒,然后突然崩溃,出现以下内存错误。
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
我在互联网上搜索,找不到任何有关上述内存错误的信息。此外,我还包括内存管理代码,以便在计时器中使用NULL值时释放该对象。
我使用http://ludovicrousseau.blogspot.com/2010/06/pcsc-sample-in-java.html作为参考
答案 0 :(得分:2)
我认为这是我在OS X上尝试使用64位Java上的libj2pcsc.dylib来查找错误时遇到的错误之一。另请参阅smartcardio thread on discussions.apple.com和我的email to security-dev。基本上,问题是DWORD*
应该是OS X上32位数字的指针,但是Sun的库假设它是指向64位数字的指针。然后它取消引用该值并尝试malloc一个大小的缓冲区,该缓冲区可以包含高32位的垃圾。请参阅source of pcsc.c
Java_sun_security_smartcardio_PCSC_SCardListReaders
潜在的解决方法:
Terminals.list()
(间歇性崩溃)的调用非常保守,并且不相信Terminal.isCardPresent()
,Terminals.waitForChange(long)
或CardTerminal.waitForCard(boolean, long)
的结果。我的同事意识到他可以使用反射来呼叫TerminalImpl.SCardGetStatusChange(long, long, int[], String[])
以获得正确的结果。这就是我们以前做的事情。非常痛苦!javax.smartcardio
的其他实现。我知道两个:我自己的jnasmartcardio和intarsys/smartcard-io。但是,我没有在NFC卡上尝试过自己的库,但我欢迎任何错误报告和补丁。