Présentation de la technologie NFC avancée

Ce document décrit des sujets avancés liés à la technologie NFC, tels que l'utilisation de différentes technologies de taggage, l'écriture sur des tags NFC et la distribution de premier plan, qui permet à une application exécutée au premier plan de gérer les intents même lorsque d'autres applications filtrent les mêmes.

Utiliser les technologies de balises compatibles

Lorsque vous utilisez des tags NFC et des appareils Android, le principal format que vous utilisez pour lire et écrire des données sur des tags est le format NDEF. Lorsqu'un appareil analyse un tag avec des données NDEF, Android permet d'analyser le message et de le transmettre dans un NdefMessage lorsque cela est possible. Toutefois, il peut arriver que vous analysiez un tag qui ne contient pas de données NDEF ou que les données NDEF n'ont pas pu être mappées avec un type ou un URI MIME. Dans ce cas, vous devez établir la communication directement avec la balise, puis la lire et y écrire avec votre propre protocole (en octets bruts). Android fournit une compatibilité générique pour ces cas d'utilisation avec le package android.nfc.tech, comme décrit dans le Tableau 1. Vous pouvez utiliser la méthode getTechList() pour déterminer les technologies compatibles avec la balise et créer l'objet TagTechnology correspondant avec l'une des classes fournies par android.nfc.tech.

Tableau 1. Technologies de balises compatibles

Classe Description
TagTechnology Interface devant être implémentée par toutes les classes de technologie de balises.
NfcA Fournit l'accès aux propriétés NFC-A (ISO 14443-3A) et aux opérations d'E/S.
NfcB Fournit l'accès aux propriétés NFC-B (ISO 14443-3B) et aux opérations d'E/S.
NfcF Fournit l'accès aux propriétés NFC-F (JIS 6319-4) et aux opérations d'E/S.
NfcV Fournit l'accès aux propriétés NFC-V (ISO 15693) et aux opérations d'E/S.
IsoDep Fournit l'accès aux propriétés ISO-DEP (ISO 14443-4) et aux opérations d'E/S.
Ndef Fournit l'accès aux données et opérations NDEF sur les tags NFC mis en forme au format NDEF.
NdefFormatable Fournit des opérations de format pour les tags pouvant être formatables NDEF.

Les technologies de tag suivantes ne doivent pas nécessairement être compatibles avec les appareils Android.

Tableau 2. Technologies de balises compatibles facultatives

Classe Description
MifareClassic Fournit l'accès aux propriétés et opérations d'E/S de la version classique de MIFARE, si cet appareil Android est compatible avec MIFARE.
MifareUltralight Fournit l'accès aux propriétés Ultralight et aux opérations d'E/S de MIFARE, si cet appareil Android est compatible avec MIFARE.

Utiliser les technologies de balise et l'intent ACTION_TECH_DISCOVERED

Lorsqu'un appareil analyse une balise contenant des données NDEF, mais qui n'a pas pu être mappée avec un URI ou MIME, le système de distribution des balises tente de démarrer une activité avec l'intent ACTION_TECH_DISCOVERED. ACTION_TECH_DISCOVERED est également utilisé lorsqu'un tag contenant des données non-NDEF est analysé. Cet outil de remplacement vous permet d'utiliser directement les données de la balise si le système de distribution des balises n'a pas pu les analyser pour vous. Voici les principales étapes à suivre pour utiliser des technologies de balises:

  1. Filtrez les données en fonction d'un intent ACTION_TECH_DISCOVERED spécifiant les technologies de balises que vous souhaitez gérer. Pour en savoir plus, consultez la section Filtrer les intents NFC. En général, le système de distribution des tags tente de démarrer un intent ACTION_TECH_DISCOVERED lorsqu'un message NDEF ne peut pas être mappé sur un type ou un URI MIME, ou si le tag analysé ne contient pas de données NDEF. Pour en savoir plus sur la façon dont cela est déterminé, consultez la page consacrée au système de distribution des balises.
  2. Lorsque votre application reçoit l'intent, obtenez l'objet Tag à partir de l'intent:

    Kotlin

    var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Obtenez une instance de TagTechnology en appelant l'une des méthodes de fabrique get des classes du package android.nfc.tech. Vous pouvez énumérer les technologies compatibles du tag en appelant getTechList() avant d'appeler une méthode de fabrique get. Par exemple, pour obtenir une instance de MifareUltralight à partir d'un Tag, procédez comme suit:

    Kotlin

    MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
    

    Java

    MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
    

Lire et écrire dans des tags

La lecture et l'écriture dans un tag NFC impliquent de l'obtenir à partir de l'intent et d'ouvrir la communication avec le tag. Vous devez définir votre propre pile de protocoles pour lire et écrire des données dans la balise. Toutefois, n'oubliez pas que vous pouvez toujours lire et écrire des données NDEF lorsque vous travaillez directement avec une balise. C’est à vous de décider comment vous voulez structurer les choses. L'exemple suivant montre comment utiliser un tag MIFARE Ultralight.

Kotlin

package com.example.android.nfc
import android.nfc.Tag
import android.nfc.tech.MifareUltralight
import java.io.IOException
import java.nio.charset.Charset

class MifareUltralightTagTester {

    fun writeTag(tag: Tag, tagText: String) {
        MifareUltralight.get(tag)?.use { ultralight ->
            ultralight.connect()
            Charset.forName("US-ASCII").also { usAscii ->
                ultralight.writePage(4, "abcd".toByteArray(usAscii))
                ultralight.writePage(5, "efgh".toByteArray(usAscii))
                ultralight.writePage(6, "ijkl".toByteArray(usAscii))
                ultralight.writePage(7, "mnop".toByteArray(usAscii))
            }
        }
    }

    fun readTag(tag: Tag): String? {
        return MifareUltralight.get(tag)?.use { mifare ->
            mifare.connect()
            val payload = mifare.readPages(4)
            String(payload, Charset.forName("US-ASCII"))
        }
    }
}

Java

package com.example.android.nfc;

import android.nfc.Tag;
import android.nfc.tech.MifareUltralight;
import android.util.Log;
import java.io.IOException;
import java.nio.charset.Charset;

public class MifareUltralightTagTester {

    private static final String TAG = MifareUltralightTagTester.class.getSimpleName();

    public void writeTag(Tag tag, String tagText) {
        MifareUltralight ultralight = MifareUltralight.get(tag);
        try {
            ultralight.connect();
            ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII")));
        } catch (IOException e) {
            Log.e(TAG, "IOException while writing MifareUltralight...", e);
        } finally {
            try {
                ultralight.close();
            } catch (IOException e) {
                Log.e(TAG, "IOException while closing MifareUltralight...", e);
            }
        }
    }

    public String readTag(Tag tag) {
        MifareUltralight mifare = MifareUltralight.get(tag);
        try {
            mifare.connect();
            byte[] payload = mifare.readPages(4);
            return new String(payload, Charset.forName("US-ASCII"));
        } catch (IOException e) {
            Log.e(TAG, "IOException while reading MifareUltralight message...", e);
        } finally {
            if (mifare != null) {
               try {
                   mifare.close();
               }
               catch (IOException e) {
                   Log.e(TAG, "Error closing tag...", e);
               }
            }
        }
        return null;
    }
}

Utiliser le système de répartition de premier plan

Le système de distribution de premier plan permet à une activité d'intercepter un intent et de revendiquer la priorité sur d'autres activités qui gèrent le même intent. L'utilisation de ce système implique de construire quelques structures de données pour qu'Android puisse envoyer les intents appropriés à votre application. Pour activer le système de répartition de premier plan:

  1. Ajoutez le code suivant dans la méthode onCreate() de votre activité :
    1. Créez un objet PendingIntent modifiable afin que le système Android puisse y insérer les détails de la balise lorsqu'elle est analysée.

      Kotlin

      val intent = Intent(this, javaClass).apply {
          addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
      }
      var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent,
              PendingIntent.FLAG_MUTABLE)
      

      Java

      PendingIntent pendingIntent = PendingIntent.getActivity(
          this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
          PendingIntent.FLAG_MUTABLE);
      
    2. Déclarez des filtres d'intent pour gérer les intents que vous souhaitez intercepter. Le système de distribution au premier plan vérifie les filtres d'intent spécifiés avec l'intent reçu lorsque l'appareil analyse une balise. Si tel est le cas, votre application gère l'intent. Si la correspondance n'est pas établie, le système de distribution de premier plan se rabat sur le système de distribution des intents. La spécification d'un tableau null de filtres d'intent et de filtres technologiques indique que vous souhaitez filtrer toutes les balises de remplacement à l'intent TAG_DISCOVERED. L'extrait de code ci-dessous gère tous les types MIME pour NDEF_DISCOVERED. Vous ne devez gérer que celles dont vous avez besoin.

      Kotlin

      val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
          try {
              addDataType("*/*")    /* Handles all MIME based dispatches.
                                       You should specify only the ones that you need. */
          } catch (e: IntentFilter.MalformedMimeTypeException) {
              throw RuntimeException("fail", e)
          }
      }
      
      intentFiltersArray = arrayOf(ndef)
      

      Java

      IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
          try {
              ndef.addDataType("*/*");    /* Handles all MIME based dispatches.
                                             You should specify only the ones that you need. */
          }
          catch (MalformedMimeTypeException e) {
              throw new RuntimeException("fail", e);
          }
         intentFiltersArray = new IntentFilter[] {ndef, };
      
    3. Configurez un ensemble de technologies de tag que votre application souhaite gérer. Appelez la méthode Object.class.getName() pour obtenir la classe de la technologie que vous souhaitez utiliser.

      Kotlin

      techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
      

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Ignorez les rappels de cycle de vie de l'activité suivants, et ajoutez une logique pour activer et désactiver la distribution au premier plan lorsque l'activité perd (onPause()) et récupère (onResume()) le focus. enableForegroundDispatch() doit être appelé à partir du thread principal et uniquement lorsque l'activité est au premier plan (l'appel de onResume() le garantit). Vous devez également implémenter le rappel onNewIntent pour traiter les données du tag NFC scanné.
  3. Kotlin

    public override fun onPause() {
        super.onPause()
        adapter.disableForegroundDispatch(this)
    }
    
    public override fun onResume() {
        super.onResume()
        adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
    }
    
    public override fun onNewIntent(intent: Intent) {
        val tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
        // do something with tagFromIntent
    }
    

    Java

    public void onPause() {
        super.onPause();
        adapter.disableForegroundDispatch(this);
    }
    
    public void onResume() {
        super.onResume();
        adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
    }
    
    public void onNewIntent(Intent intent) {
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        // do something with tagFromIntent
    }