Descripción general de conceptos avanzados de NFC

En este documento, se describen temas avanzados de NFC, por ejemplo, cómo trabajar con varias tecnologías de etiquetas, escribir en etiquetas NFC y despachar en primer plano, lo que permite que una aplicación en primer plano maneje intentos 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 asistencia 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 no se pudieron asignar los datos NDEF a un tipo MIME o URI. En estos casos, debes abrir la comunicación directamente con la etiqueta, y leerla y escribirla con su propio protocolo (en bytes sin procesar). Android proporciona compatibilidad genérica para estos casos prácticos 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 los dispositivos con Android sean compatibles con las siguientes tecnologías de etiquetas.

Tabla 2: Tecnologías de etiquetas compatibles opcionales

Clase Descripción
MifareClassic Proporciona acceso a las propiedades y 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 puede 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 respaldo te permite trabajar con los datos directamente en la etiqueta si el sistema de envío de etiquetas no puede analizarlo en tu lugar. Los pasos básicos de trabajo con tecnologías de etiquetas son los siguientes:

  1. Filtra por un intent ACTION_TECH_DISCOVERED especificando las tecnologías de etiquetas que deseas manejar. Para obtener más información, consulta Filtrado de intents de NFC. En general, el sistema de envío de etiquetas trata de iniciar un intent ACTION_TECH_DISCOVERED cuando no se puede asignar un mensaje NDEF 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. Puedes enumerar las tecnologías compatibles de la etiqueta llamando 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 y escribir en una etiqueta NFC implica obtener la etiqueta del intent y abrir la comunicación con ella. Tienes que 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. El siguiente ejemplo 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 con respecto a 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 apropiados a tu aplicación. Para habilitar el sistema de envío en primer plano, haz lo siguiente:

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

      Kotlin

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

      Java

          PendingIntent pendingIntent = PendingIntent.getActivity(
              this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
          
    2. Declara filtros de intent para manejar los intents que deseas interceptar. El sistema de envío en primer plano verifica los filtros de intent 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 vuelve al sistema de envío del intent. Al especificar una matriz null de filtros de intent y filtros 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 necesites.

      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 deseas 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 pierde (onPause()) y recupera (onResume()) el enfoque. Se debe invocar enableForegroundDispatch() desde la conversación 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
        }
        

Consulta el ejemplo ForegroundDispatch de las demostraciones de la API para obtener la muestra completa.