Descripción general de conceptos avanzados de NFC

En este documento, se describen temas avanzados de NFC, como el trabajo con varias tecnologías de etiquetas, la escritura en etiquetas NFC y el despacho en primer plano, lo que permite que una aplicación en primer plano maneje intents incluso cuando otras aplicaciones filtran por los mismos.

Cómo trabajar con tecnologías de etiquetas compatibles

Cuando trabajas con etiquetas NFC y dispositivos con Android, el formato principal que usas para leer y escribir datos en etiquetas es NDEF. Cuando un dispositivo escanea una etiqueta con datos NDEF, Android proporciona compatibilidad para analizar el mensaje y entregarlo en un NdefMessage siempre que sea posible. Sin embargo, hay casos en los que escaneas una etiqueta que no contiene datos NDEF o cuando los datos NDEF no se pudieron asignar a un tipo MIME o URI. En estos casos, debes abrir la comunicación directamente con la etiqueta y leerla y escribirla con tu propio protocolo (en bytes sin procesar). Android proporciona compatibilidad genérica para estos casos de uso con el paquete android.nfc.tech, que se describe en la Tabla 1. Puedes usar el método getTechList() para determinar las tecnologías compatibles con la etiqueta y crear el objeto TagTechnology correspondiente con una de las clases proporcionadas por android.nfc.tech.

Tabla 1: Tecnologías de etiquetas compatibles

Clase Descripción
TagTechnology La interfaz que deben implementar todas las clases de tecnología de etiquetas.
NfcA Proporciona acceso a propiedades de NFC-A (ISO 14443-3A) y a las operaciones de E/S.
NfcB Proporciona acceso a las propiedades NFC-B (ISO 14443-3B) y a las operaciones de E/S.
NfcF Proporciona acceso a las propiedades NFC-F (JIS 6319-4) y a las operaciones de E/S.
NfcV Proporciona acceso a las propiedades NFC-V (ISO 15693) y a las operaciones de E/S.
IsoDep Proporciona acceso a las propiedades ISO-DEP (ISO 14443-4) y a las operaciones de E/S.
Ndef Proporciona acceso a datos y operaciones NDEF en etiquetas NFC que han sido formateadas como NDEF.
NdefFormatable Proporciona operaciones de formato para etiquetas que pueden tener formato NDEF.

No es necesario que las siguientes tecnologías de etiquetas sean compatibles con los dispositivos con tecnología Android.

Tabla 2: Tecnologías de etiquetas compatibles opcionales

Clase Descripción
MifareClassic Proporciona acceso a las propiedades y las operaciones de E/S de MIFARE Classic si el dispositivo Android es compatible con MIFARE.
MifareUltralight Proporciona acceso a las propiedades de MIFARE Ultralight y a las operaciones de E/S si el dispositivo Android es compatible con MIFARE.

Cómo trabajar con tecnologías de etiquetas y el intent ACTION_TECH_DISCOVERED

Cuando un dispositivo escanea una etiqueta que tiene datos NDEF, pero no se pudo asignar a un MIME o URI, el sistema de envío de etiquetas intenta iniciar una actividad con el intent ACTION_TECH_DISCOVERED. El ACTION_TECH_DISCOVERED también se usa cuando se escanea una etiqueta con datos que no son NDEF. Tener este resguardo te permite trabajar directamente con los datos de la etiqueta si el sistema de envío de etiquetas no pudo analizarlo por ti. Los pasos básicos para trabajar con tecnologías de etiquetas son los siguientes:

  1. Filtra por un intent ACTION_TECH_DISCOVERED y especifica las tecnologías de etiqueta que deseas controlar. Consulta Filtrado de intents de NFC para obtener más información. En general, el sistema de envío de etiquetas intenta iniciar un intent ACTION_TECH_DISCOVERED cuando un mensaje NDEF no se puede asignar a un tipo MIME o URI, o si la etiqueta escaneada no contenía datos NDEF. Para obtener más información sobre cómo se determina esto, consulta El sistema de envío de etiquetas.
  2. Cuando tu aplicación recibe el intent, obtén el objeto Tag del intent:

    Kotlin

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

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Obtén una instancia de un TagTechnology llamando a uno de los métodos de fábrica get de las clases en el paquete android.nfc.tech. Para enumerar las tecnologías compatibles de la etiqueta, llama a getTechList() antes de llamar a un método de fábrica get. Por ejemplo, para obtener una instancia de MifareUltralight desde un Tag, haz lo siguiente:

    Kotlin

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

    Java

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

Cómo leer y escribir en etiquetas

Leer una etiqueta NFC y escribir en ella implica obtener la etiqueta del intent y abrir la comunicación con ella. Debes definir tu propia pila de protocolos para leer y escribir datos en la etiqueta. Sin embargo, ten en cuenta que aún puedes leer y escribir datos NDEF cuando trabajes directamente con una etiqueta. Tú eliges cómo estructurar las cosas. En el siguiente ejemplo, se muestra cómo trabajar con una etiqueta 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;
    }
}

Cómo usar el sistema de envío en primer plano

El sistema de envío en primer plano permite que una actividad intercepte un intent y tenga prioridad sobre otras actividades que manejan el mismo intent. El uso de este sistema implica la construcción de algunas estructuras de datos para que el sistema Android pueda enviar los intents adecuados a tu aplicación. Para habilitar el sistema de envío en primer plano, haz lo siguiente:

  1. Agrega el siguiente código al método onCreate() de tu actividad:
    1. Crea un objeto PendingIntent mutable para que el sistema Android pueda propagarlo con los detalles de la etiqueta cuando se analice.

      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. Declara filtros de intent para manejar los intents que deseas interceptar. El sistema de envío en primer plano verifica los filtros de intents especificados con el intent que se recibe cuando el dispositivo escanea una etiqueta. Si coincide, entonces tu aplicación maneja el intent. De lo contrario, el sistema de envío en primer plano recurre al sistema de envío del intent. Al especificar un array null de filtros de intents y de tecnología, se especifica que deseas filtrar todas las etiquetas que recurren al intent TAG_DISCOVERED. El siguiente fragmento de código maneja todos los tipos MIME para NDEF_DISCOVERED. Solo debes administrar los que necesitas.

      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. Configura una variedad de tecnologías de etiquetas que tu aplicación quiera manejar. Llama al método Object.class.getName() para obtener la clase de la tecnología que quieres admitir.

      Kotlin

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

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Anula las siguientes devoluciones de llamada del ciclo de vida de la actividad y agrega lógica para habilitar o inhabilitar el envío en primer plano cuando la actividad pierda (onPause()) y recupere (onResume()) el enfoque. Se debe llamar a enableForegroundDispatch() desde el subproceso principal y solo cuando la actividad está en primer plano (llamar a onResume() garantiza esto). También debes implementar la devolución de llamada onNewIntent para procesar los datos de la etiqueta NFC escaneada.
  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
    }