eclipse-keyple / keyple-java-example

Eclipse Keyple™ Project: use case examples of the Keyple Java components for end users
https://keyple.org
Eclipse Public License 2.0
7 stars 4 forks source link

READ and WRITE #12

Closed androuino closed 1 year ago

androuino commented 2 years ago

Hi,

Greate library btw. However, could you help me on how to implement the read and write to an NFC smart card reader?

My implementation of your library is like the below example

    fun onKeyple() {
        // Get the instance of the SmartCardService (singleton pattern)
        smartCardService = SmartCardServiceProvider.getService()

        // Register the PcscPlugin with the SmartCardService, get the corresponding generic plugin in
        // return.
        plugin = smartCardService?.registerPlugin(PcscPluginFactoryBuilder.builder().build())

        // We add an observer to each plugin (only one in this example) the readers observers will be
        // added dynamically upon plugin events notification. Nevertheless, here we provide the plugin
        // observer with the readers already present at startup in order to assign them a reader
        // observer.
        Log.i("Add observer PLUGINNAME = $", plugin?.name)
        val pluginObserver = PluginObserver(plugin!!, smartCardService!!)
        (plugin as ObservablePlugin).setPluginObservationExceptionHandler(pluginObserver)
        (plugin as ObservablePlugin).addObserver(pluginObserver)

        Log.i("Wait for reader or card insertion/removal")

        // Wait indefinitely. CTRL-C to exit.
        synchronized(waitForEnd) { waitForEnd }

        Log.i("Exit program.")
    }

then my PluginOberser.kt class example is here

import org.calypsonet.terminal.reader.ObservableCardReader
import org.eclipse.keyple.core.service.*
import org.eclipse.keyple.core.service.spi.PluginObservationExceptionHandlerSpi
import org.eclipse.keyple.core.service.spi.PluginObserverSpi
import org.eclipse.keyple.core.util.protocol.ContactCardCommonProtocol
import org.eclipse.keyple.plugin.pcsc.PcscReader
import org.eclipse.keyple.plugin.pcsc.PcscSupportedContactProtocol

class PluginObserver(plugin: Plugin, smartCardService: SmartCardService) : PluginObserverSpi, PluginObservationExceptionHandlerSpi {
    private var readerObserver: ReaderObserver? = null

    init {
        readerObserver = ReaderObserver(plugin, smartCardService)
        for (reader in plugin.readers) {
            if (reader is ObservableReader) {
                addObserver(reader)
            }
        }
    }

    /**
     * {@inheritDoc}
     *
     * @since 2.0.0
     */
    override fun onPluginEvent(event: PluginEvent) {
        when (event.type) {
            PluginEvent.Type.READER_CONNECTED -> {
                for (readerName in event.readerNames) {
                    // We retrieve the reader object from its name.
                    val reader: Reader = SmartCardServiceProvider.getService()
                        .getPlugin(event.pluginName)
                        .getReader(readerName)
                    Log.i(
                        "PluginEvent: PLUGINNAME = %s, READERNAME = %s, Type = %s",
                        event.pluginName,
                        readerName,
                        event.type
                    )
                    // We are informed here of a connection of a reader. We add an observer to this reader if
                    // this is possible.
                    Log.i("New reader! READERNAME = %s", readerName)

                    // Configure the reader with parameters suitable for contactless operations.
                    setupReader(reader)
                    if (reader is ObservableReader) {
                        addObserver(reader)
                    }
                }
            }
            PluginEvent.Type.READER_DISCONNECTED -> {
                if (event.readerNames.isNotEmpty()) {
                    for (readerName in event.readerNames) {
                        // We retrieve the reader object from its name.
                        val reader: Reader = SmartCardServiceProvider.getService()
                            .getPlugin(event.pluginName)
                            .getReader(readerName)
                        Log.i(
                            "PluginEvent: PLUGINNAME = %s, READERNAME = %s, Type = %s",
                            event.pluginName,
                            readerName,
                            event.type
                        )
                        // We are informed here of a disconnection of a reader. The reader object still exists but
                        // will be removed from the reader list right after. Thus, we can properly remove the
                        // observer attached to this reader before the list update.
                        Log.i("Reader removed. READERNAME = %s", readerName)
                        if (reader is ObservableReader) {
                            Log.i("Clear observers of READERNAME = %s", readerName)
                            reader.clearObservers()
                        }
                    }
                }
            }
            else -> Log.i("Unexpected reader event. EVENT = %s", event.type.name)
        }
    }

    /**
     * {@inheritDoc}
     *
     * @since 2.0.0
     */
    override fun onPluginObservationError(pluginName: String?, e: Throwable?) {
        Log.e("An exception occurred in plugin %s, %s", pluginName, e)
    }

    /**
     * Configure the reader to handle ISO14443-4 contactless cards
     *
     * @param reader The reader.
     */
    private fun setupReader(reader: Reader) {
        reader
            .getExtension(PcscReader::class.java)
            .setContactless(true)
            .setIsoProtocol(PcscReader.IsoProtocol.T1)
            .setSharingMode(PcscReader.SharingMode.SHARED)

        // Activate the ISO14443 card protocol.
        /*(reader as ConfigurableReader)
            .activateProtocol(
                PcscSupportedContactlessProtocol.ISO_14443_4.name,
                ContactlessCardCommonProtocol.ISO_14443_4.name
            )*/
        (reader as ConfigurableReader).activateProtocol(PcscSupportedContactProtocol.ISO_7816_3.name, ContactCardCommonProtocol.ISO_7816_3.name)
    }

    /**
     * Add the unique observer to the provided observable reader.
     *
     * @param reader An observable reader
     */
    private fun addObserver(reader: Reader) {
        Log.i("Add observer READERNAME = %s", reader.name)
        (reader as ObservableReader).setReaderObservationExceptionHandler(readerObserver)
        reader.addObserver(readerObserver)
        reader.startCardDetection(ObservableCardReader.DetectionMode.REPEATING)
    }
}

and my ReaderObserver.kt is

import org.calypsonet.terminal.reader.CardReaderEvent
import org.calypsonet.terminal.reader.spi.CardReaderObservationExceptionHandlerSpi
import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi
import org.eclipse.keyple.core.service.*
import org.eclipse.keyple.plugin.pcsc.PcscReader

class ReaderObserver(private val plugin: Plugin, private val smartCardService: SmartCardService) : CardReaderObserverSpi, CardReaderObservationExceptionHandlerSpi {

    override fun onReaderEvent(event: CardReaderEvent) {
        /* just log the event */
        Log.i(
            "Event: PLUGINNAME = %s, READERNAME = %s, EVENT = %s",
            (event as ReaderEvent).pluginName,
            event.getReaderName(),
            event.getType().name
        )
        when (event.type) {
            CardReaderEvent.Type.CARD_MATCHED -> {
            }
            CardReaderEvent.Type.CARD_INSERTED -> {
                Log.i("Card is inserted %s", "")
                val readerName: String = plugin.readerNames.first()
                val reader = plugin.getReader(readerName)
                val cardSelectionManager = smartCardService.createCardSelectionManager()
                val selectionResult = cardSelectionManager.processCardSelectionScenario(reader)
                val smartCard = selectionResult.activeSmartCard
                Log.i("Response ${smartCard.selectApplicationResponse}")
                reader.getExtension(PcscReader::class.java).setContactless(false)

                (SmartCardServiceProvider.getService()
                    .getPlugin(event.pluginName)
                    .getReader(event.getReaderName()) as ObservableReader)
                    .finalizeCardProcessing()
            }
            CardReaderEvent.Type.CARD_REMOVED -> {
                var scsp = (SmartCardServiceProvider.getService()
                    .getPlugin(event.pluginName)
                    .getReader(event.getReaderName()) as ObservableReader)
                    .finalizeCardProcessing()
            }
            else -> {}
        }
    }

    override fun onReaderObservationError(pluginName: String?, readerName: String?, e: Throwable?) {
        Log.i("An exception occurred in plugin '%s', reader '%s'. exception %s", pluginName, readerName, e?.message)
    }
}
jeanpierrefortune commented 2 years ago

Hi Androuino,

Could you please be more specific about the problem you are experiencing?

androuino commented 2 years ago

Hi, I hope I was able to get a response in a timely manner but anyway, I was trying to use your library but I couldn't find the example to write something to an NFC tag. Do you have somehow an example of how to write bytes to an NFC card? Thanks.

jeanpierrefortune commented 2 years ago

Hi, The Keyple library is currently oriented towards ISO14443-4 cards, in particular Calyspo cards through the Calypso extension but also with any other ISO Type 4 card with the generic extension. Depending on the type of card you want to use, you will need to know the set of commands available for this card. Maybe you are thinking of Mifare or NFC Tag cards, in this case you will have to look for the proprietary APDU commands provided by the PC/SC reader manufacturer and use the generic extension to send the appropriate commands. What type of card and reader do you want to use? Regards.

androuino commented 2 years ago

Hello, Thank you for the response. I actually using NFC Tag cards now and I have also the APDU commands for the cards. I would like to ask on how I can send those commands and be able to write or read the cards?

Thanks.

jeanpierrefortune commented 2 years ago

Hello,

Have you read this https://keyple.org/learn/user-guide/standalone-application/?

This example shows how to make a basic card selection and send an APDU to the selected card: https://github.com/eclipse/keyple-java-example/blob/main/Example_Service/src/main/java/org/eclipse/keyple/core/service/example/UseCase1_BasicSelection/Main_BasicSelection_Pcsc.java I would start from here to validate the feasibility and progressively add the features needed for the final application (such as reader event observation, etc.). It should work with a contactless EMV banking card. In this example, the card must be in the RF field of the reader before the program starts.

Keep us informed of your progress!

Regards.

androuino commented 2 years ago

Thanks, @jeanpierrefortune, the information that you gave is very helpful, I'll carefully study the examples and will keep you posted on my development.

Thank you very much.