2

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

该应用程序的功能是在 NFC 阅读器设备与 PC 连接或断开连接时显示通知。然后,如果设备已连接并且出现 NFC 标签,则显示出现 NFC 标签的通知。我试图找到基于事件的 api 来实现上述功能,但我找不到,所以我对 NFC 阅读器设备和 NFC 标签使用了 Java 计时器和轮询。

以下是用于轮询 NFC 设备和标签的示例 JAVA 代码。

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 操作系统上运行它时,应用程序完美地运行了 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供参考

4

1 回答 1

2

我相信这是我在尝试使用 OS X 上的 64 位 Java 上的 libj2pcsc.dylib 查找错误时遇到的错误之一。另请参阅 Discussions.apple.com 上的smartcardio 线程和我发送给 security-dev的电子邮件。基本上,问题在于它DWORD*应该是指向 OS X 上的 32 位数字的指针,但 Sun 的库假定它是指向 64 位数字的指针。然后它取消引用该值并尝试分配该大小的缓冲区,该缓冲区可以包含高 32 位中的垃圾。参见pcsc.cJava_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 卡上尝试过我自己的库,但我欢迎任何错误报告和补丁。
于 2013-11-19T22:40:50.600 回答