Reading NFC Tag using JAVA Smart Card API not work

2020-07-24 04:50发布

问题:

I am developing an application to read a NFC Tag UID from NFC Reader (ACR122U-A9) device. I used JAVA and javax.smartcardio API to detect the NFC Reader and Reading NFC Tag.

The functionality of the application is to display notification when the NFC Reader device is connect or disconnect from PC. Then if the device is connected and NFC Tag is presented then display the notification that NFC Tag is presented. I tried to find the Event based api to implement above functionality but I cannot find so I used Java Timer and Polling for the NFC Reader Device and NFC Tag.

Following is my sample JAVA code that used for Polling for NFC device and Tag.

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");
            }

        }
    }
}

Above code is working fine in Windows OS but when I run it on MAC OS then application runs for 5-10 seconds perfectly but then it suddenly crash with the following memory error.

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

I searched on internet and cannot find anything regarding above memory error. Also I included the code for memory management to release the object when it is used in timer by assigning NULL value to it.

I have used http://ludovicrousseau.blogspot.com/2010/06/pcsc-sample-in-java.html for reference

回答1:

I believe this was one of the errors I was getting when trying to track down the bugs with libj2pcsc.dylib on 64-bit Java on OS X. See also smartcardio thread on discussions.apple.com and my email to security-dev. Basically, the problem is that DWORD* should be a pointer to a 32-bit number on OS X, but Sun’s library assumed that it was a pointer to a 64-bit number. Then it dereferences that value and tries to malloc a buffer of that size, which can contain junk in the upper 32 bits. See Java_sun_security_smartcardio_PCSC_SCardListReaders in the source of pcsc.c

Potential workarounds:

  • Be very conservative with calls to Terminals.list() (which crashes intermittently), and don’t trust the results of Terminal.isCardPresent(), Terminals.waitForChange(long), or CardTerminal.waitForCard(boolean, long). My co-worker realized that he could call TerminalImpl.SCardGetStatusChange(long, long, int[], String[]) using reflection to get the right results. This is what we used to do. Very painful!
  • Fix the header files for libj2pcsc.dylib and recompile OpenJDK. This is what we do at my company right now.
  • Switch to a different implementation of javax.smartcardio. I know of two: my own jnasmartcardio and intarsys/smartcard-io. I have not tried my own library on NFC cards, however, but I welcome any bug reports and patches.