使用JAVA智能卡API读取NFC标签不适用于MAC OS

我正在开发一个从NFC读卡器(ACR122U-A9)设备读取NFC标签UID的应用程序。 我使用JAVA和javax.smartcardio API来检测NFC阅读器和阅读NFC标签。

该应用程序的function是在NFC读取器设备与PC连接或断开连接时显示通知。 然后,如果设备已连接且显示NFC标签,则显示NFC标签显示的通知。 我试图找到基于事件的api来实现上述function但我找不到所以我使用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 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作为参考

我相信这是我在OS X上使用64位Java上的libj2pcsc.dylib来查找错误时遇到的错误之一。另请参阅discussion.apple.com上的smartcardio线程和我发送给security-dev的电子邮件 。 基本上,问题是DWORD*应该是OS X上32位数字的指针,但Sun的库假设它是一个指向64位数字的指针。 然后它取消引用该值并尝试malloc一个大小的缓冲区,该缓冲区可以包含高32位的垃圾。 请参阅Java_sun_security_smartcardio_PCSC_SCardListReaders 源代码中的Java_sun_security_smartcardio_PCSC_SCardListReaders

可能的解决方法:

  • 调用Terminals.list() (间歇性崩溃)非常保守,并且不信任Terminal.isCardPresent()Terminals.waitForChange(long)CardTerminal.waitForCard(boolean, long) 。 我的同事意识到他可以使用reflection调用TerminalImpl.SCardGetStatusChange(long, long, int[], String[])来获得正确的结果。 这就是我们以前做的事情。 很痛苦!
  • 修复libj2pcsc.dylib的头文件并重新编译OpenJDK。 这就是我们现在在公司所做的事情。
  • 切换到javax.smartcardio的不同实现。 我知道两个:我自己的jnasmartcardio和intarsys / smartcard-io 。 但是,我没有在NFC卡上尝试过我自己的库,但我欢迎任何错误报告和补丁。