Search code examples
javawindows-8smartcardpcsc

Smartcard terminal removal : SCARD_E_NO_SERVICE CardException


I am working on an Java application which uses smartcardio to work with smartcard. It must be possible to have one removing its USB card reader and then inserting it again without starting again the applet.

I am using the terminals() and waitForChange() methods to detect terminal changes and it is working fine on Linux, MacOS and Win7.

But on Windows 8 (and Windows 8 only), after the removal of the last terminal, these methods throw a SCARD_E_NO_SERVICE CardException, and don't detect any more changes.

I'm not sure what "Service" is it talking about. But I think this is launched in my thread when I call TerminalFactory.getDefault() to have a TerminalFactory singleton. And I think this singleton may have a way to manage the underlayed service and this is what is broken.

Has anyone any lead on how to manage terminal disconnection with smartcardio on Windows 8 ?


Solution

  • This post is quite old, but it has been useful for me to fix the problem described on Windows 8.

    The solution from JR Utily did not work fully: in case of a reader unplugged then plugged again, there were errors on the CardTerminal instance.

    So I added some code to clear the terminals list, as you can see in the code below.

            Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals");
            Field contextId = pcscterminal.getDeclaredField("contextId");
            contextId.setAccessible(true);
    
            if(contextId.getLong(pcscterminal) != 0L)
            {
                // First get a new context value
                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[] { SCARD_SCOPE_USER.getInt(pcsc) }
                ));
                contextId.setLong(pcscterminal, newId);
    
    
                // Then clear the terminals in cache
                TerminalFactory factory = TerminalFactory.getDefault();
                CardTerminals terminals = factory.terminals();
                Field fieldTerminals = pcscterminal.getDeclaredField("terminals");
                fieldTerminals.setAccessible(true);
                Class classMap = Class.forName("java.util.Map");
                Method clearMap = classMap.getDeclaredMethod("clear");
    
                clearMap.invoke(fieldTerminals.get(terminals));
            }