Visão geral avançada da NFC

Este documento descreve tópicos avançados de NFC, como trabalhar com várias tecnologias de etiqueta, gravação em etiquetas NFC e expedição em primeiro plano que permite que um aplicativo em primeiro plano processe intents mesmo quando outros aplicativos aplicam filtros para os mesmos intents.

Trabalhar com tecnologias de etiqueta compatíveis

Ao trabalhar com etiquetas NFC e dispositivos com tecnologia Android, o formato principal usado para ler e gravar dados em etiquetas é o NDEF. Quando um dispositivo faz a leitura de uma etiqueta com dados NDEF, o Android fornece suporte para analisar a mensagem e exibi-la em um NdefMessage, quando possível. No entanto, há casos em que é feita a leitura de uma etiqueta que não contém dados NDEF ou quando os dados NDEF não puderam ser mapeados para um tipo MIME ou URI. Nesses casos, é preciso abrir a comunicação diretamente com a etiqueta e ler e gravar nela usando um protocolo próprio (em bytes brutos). O Android fornece compatibilidade genérica nesses casos de uso com o pacote android.nfc.tech, que é descrito na Tabela 1. O getTechList() pode ser usado para determinar as tecnologias compatíveis com a etiqueta e criar o objeto TagTechnology correspondente com uma das classes fornecidas por android.nfc.tech

Tabela 1. Tecnologias de etiqueta compatíveis

Classe Descrição
TagTechnology A interface que todas as classes de tecnologia de etiquetas precisam implementar.
NfcA Fornece acesso às propriedades NFC-A (ISO 14443-3A) e operações de E/S.
NfcB Fornece acesso às propriedades NFC-B (ISO 14443-3B) e operações de E/S.
NfcF Fornece acesso às propriedades da NFC-F (JIS 6319-4) e operações de E/S.
NfcV Fornece acesso às propriedades NFC-V (ISO 15693) e operações de E/S.
IsoDep Fornece acesso às propriedades ISO-DEP (ISO 14443-4) e operações de E/S.
Ndef Fornece acesso a dados e operações NDEF em etiquetas NFC formatadas como NDEF.
NdefFormatable Fornece operações de formato para etiquetas que podem ser formatadas como NDEF.

As tecnologias de etiqueta a seguir não precisam ser compatíveis com dispositivos com tecnologia Android.

Tabela 2. Tecnologias de etiqueta compatíveis opcionais

Classe Descrição
MifareClassic Fornece acesso a propriedades MIFARE Classic e operações de E/S, se este dispositivo Android for compatível com MIFARE.
MifareUltralight Fornece acesso a propriedades MIFARE Ultralight e operações de E/S, se este dispositivo Android for compatível com MIFARE.

Trabalhando com tecnologias de etiqueta e com o intent ACTION_TECH_DISCOVERED

Quando um dispositivo faz a leitura de uma etiqueta que contém dados NDEF, mas não pôde ser mapeada para um MIME ou URI, o sistema de expedição de etiquetas tenta iniciar uma atividade com o intent ACTION_TECH_DISCOVERED. O ACTION_TECH_DISCOVERED também é usado quando uma etiqueta com dados não formatos como-NDEF é lida. Esse substituto permite trabalhar diretamente com os dados da etiqueta se o sistema de expedição de etiquetas não puder analisá-los. As etapas básicas ao trabalhar com tecnologias de etiquetas são as seguintes:

  1. Aplique um filtro para um intent ACTION_TECH_DISCOVERED que especifique as tecnologias de etiqueta que você quer processar. Consulte Como aplicar filtros para intents NFC para ver mais informações. Em geral, o sistema de expedição de etiquetas tenta iniciar um intent ACTION_TECH_DISCOVERED quando uma mensagem NDEF não pode ser mapeada para um tipo MIME ou URI, ou se a etiqueta lida não continha dados NDEF. Para mais informações sobre como isso é determinado, consulte Sistema de expedição de etiquetas .
  2. Quando o aplicativo receber o intent, pegue o objeto Tag do intent:

    Kotlin

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

    Java

        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        
  3. Uma instância de TagTechnology é obtida, chamando um dos métodos de fábrica get das classes no pacote android.nfc.tech. Tecnologias compatíveis com a etiqueta podem ser enumeradas chamando getTechList() antes de chamar um método de fábrica get. Por exemplo, para obter uma instância de MifareUltralight de um Tag, faça o seguinte:

    Kotlin

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

    Java

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

Ler e gravar em etiquetas

Ler e gravar em uma etiqueta NFC envolve obter a etiqueta do intent e abrir a comunicação com a etiqueta. Uma pilha de protocolos própria precisa ser definida para ler e gravar dados na etiqueta. No entanto, lembre-se que é possível ler e gravar dados NDEF ao trabalhar diretamente com uma etiqueta. Cabe a você decidir como quer estruturar as coisas. O exemplo a seguir mostra como trabalhar com uma 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;
        }
    }
    

Usar o sistema de expedição em primeiro plano

O sistema de expedição em primeiro plano permite que uma atividade intercepte um intent e reivindique a prioridade sobre outras atividades que processam o mesmo intent. Usar esse sistema envolve a construção de algumas estruturas de dados para que o sistema Android possa enviar os intents apropriados para o aplicativo. Para ativar o sistema de expedição em primeiro plano:

  1. Adicione o código a seguir no método onCreate() da atividade:
      .
    1. Crie um objeto PendingIntent para que o sistema Android possa preenchê-lo com os detalhes da etiqueta quando ela for lida.

      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. Declare os filtros de intent para processar os intents que você quer interceptar. O sistema de expedição em primeiro plano verifica os filtros de intent especificados com o intent recebido quando o dispositivo faz a leitura de uma etiqueta. Se corresponderem, o aplicativo processará o intent. Se não houver correspondência, o sistema de expedição em primeiro plano volta para o sistema de expedição de intents. Especificar uma matriz null de filtros de intent e de tecnologia significa que você quer aplicar um filtro para todas as etiquetas que usam o intent TAG_DISCOVERED como substituto. O snippet de código abaixo processa todos os tipos MIME para NDEF_DISCOVERED. Recomenda-se processar apenas os que você precisa.

      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. Configurar uma matriz de tecnologias de etiqueta que o aplicativo quer processar. Chame o método Object.class.getName() para obter a classe da tecnologia à qual quer oferecer compatibilidade.

      Kotlin

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

      Java

          techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
          
  2. Modifique os seguintes callbacks do ciclo de vida da atividade e adicione lógica para ativar e desativar a expedição em primeiro plano quando a atividade perder (onPause()) e recuperar o foco (onResume()). enableForegroundDispatch() precisa ser chamado na linha de execução principal e apenas quando a atividade estiver em primeiro plano (chamar onResume() garante isso). Também é necessário implementar o callback onNewIntent para processar os dados da etiqueta NFC lida.
  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
        }
        

Veja o exemplo ForegroundDispatch nas demonstrações da API para a demonstração completa.