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

时间:2013-11-19 12:25:48

标签: java nfc smartcard smartcard-reader

我正在开发一个应用程序,用于从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作为参考

1 个答案:

答案 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[])以获得正确的结果。这就是我们以前做的事情。非常痛苦!
  • 修复libj2pcsc.dylib的头文件并重新编译OpenJDK。这就是我们现在在公司所做的事情。
  • 切换到javax.smartcardio的其他实现。我知道两个:我自己的jnasmartcardiointarsys/smartcard-io。但是,我没有在NFC卡上尝试过自己的库,但我欢迎任何错误报告和补丁。