使用JAVA智能卡API读取NFC标记在MAC OS上不起作用

使用JAVA智能卡API读取NFC标记在MAC OS上不起作用,java,nfc,smartcard,smartcard-reader,Java,Nfc,Smartcard,Smartcard Reader,我正在开发一个从NFC阅读器(ACR122U-A9)设备读取NFC标签UID的应用程序。 我使用JAVA和javax.smartcardio API检测NFC阅读器并读取NFC标记 应用程序的功能是在NFC阅读器设备与PC连接或断开连接时显示通知。然后,如果设备已连接且显示NFC标签,则显示显示NFC标签的通知。 我试图找到基于事件的api来实现上述功能,但找不到,所以我使用Java定时器和轮询来实现NFC阅读器设备和NFC标记 下面是用于轮询NFC设备和标记的示例JAVA代码 import j

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

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

下面是用于轮询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");
            }

        }
    }
}
我在网上搜索,找不到任何关于上述内存错误的信息。此外,我还包含了内存管理代码,当对象在计时器中使用时,通过为其分配空值来释放该对象


我曾参考过

我相信这是我在OS X上的64位Java上使用libj2pcsc.dylib查找bug时遇到的错误之一。另请参见和我的。基本上,问题在于,
DWORD*
应该是指向OS X上32位数字的指针,但Sun的库假设它是指向64位数字的指针。然后它取消对该值的引用,并尝试对该大小的缓冲区进行malloc,该缓冲区可能包含高32位的垃圾。请参阅中的
Java\u sun\u security\u smartcardio\u PCSC\u SCardListReaders

可能的解决办法:

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

在Windows上“完美”吗?我打赌它最终会爆炸。在这里,您在没有文档的情况下调用私有函数,您会惊讶于发生了可怕的事情?我正在调用哪个私有函数?我在阅读了使用反射时的线程后应用的修复程序。@b argulies你能回答上面代码中调用了哪个私有函数吗。Thanx Yonran,你能给我举个例子“我的同事意识到他可以调用TerminalImpl.SCardGetStatusChange(long,long,int[],String[])”使用反射来获得正确的结果。这是我们过去所做的。”解决方法,因为我需要知道如何使用反射来调用它。我还将尝试第三种变通方法,即使用备选库。@SwapnilTandel,实际上第一种是错误的选择。不要继续使用损坏的默认javax.smartcardio实现。很难看。此外,Terminals.list()仍将间歇性崩溃。我还在示例程序中尝试了intarsys/smartcard io,它在Windows和MAC中的初始测试中运行良好,因此我将此库升级到我的程序中,看看会发生什么
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