Fundamentos de NFC

Este documento descreve as tarefas básicas de NFC que são realizadas no Android. O documento explica como enviar e receber dados NFC no formato de mensagens NDEF e descreve as APIs do framework do Android compatíveis com esses recursos. Para tópicos mais avançados, incluindo uma discussão sobre como trabalhar com dados que não são NDEF, consulte NFC avançada.

Há dois casos de uso principais ao trabalhar com dados NDEF e Android:

  • Leitura de dados NDEF de uma etiqueta NFC
  • Envio de mensagens NDEF de um dispositivo para outro com o Android Beam™

A leitura de dados NDEF de uma etiqueta NFC é processada com o sistema de expedição de etiquetas, que analisa etiquetas NFC descobertas, categoriza os dados de forma adequada e inicia um aplicativo interessado nos dados categorizados. Um aplicativo que queira processar a etiqueta NFC lida pode declarar um filtro de intent e solicitar o processamento dos dados.

O recurso Android Beam™ permite que um dispositivo envie uma mensagem NDEF para outro por meio da aproximação física dos dispositivos. Essa interação oferece uma maneira mais fácil de enviar dados do que outras tecnologias sem fio, como Bluetooth, já que, com NFC, não é necessário parear ou descobrir manualmente outro dispositivo. A conexão é iniciada automaticamente quando dois dispositivos entram no alcance. O Android Beam está disponível por meio de um conjunto de APIs NFC, de modo que qualquer aplicativo pode transmitir informações entre dispositivos. Por exemplo, os aplicativos Contatos, Navegador e YouTube usam o Android Beam para compartilhar contatos, páginas da Web e vídeos com outros dispositivos.

O sistema de expedição de etiquetas

Dispositivos com tecnologia Android geralmente procuram etiquetas NFC quando a tela está desbloqueada, a não ser que a NFC esteja desativada no menu "Configurações" do dispositivo. Quando um dispositivo com tecnologia Android descobre uma etiqueta NFC, o comportamento esperado é fazer com que a atividade mais apropriada processe o intent sem perguntar ao usuário qual aplicativo usar. Como os dispositivos fazem a leitura das etiquetas NFC em alcances muito curtos, pedir aos usuários que selecionem manualmente uma atividade pode fazer com que afastem o dispositivo da etiqueta, interrompendo a conexão. A atividade precisa ser desenvolvida para processar somente as etiquetas NFC relevantes para sua atividade para impedir que o seletor de atividades seja exibido.

Para ajudar com esse objetivo, o Android fornece um sistema de expedição de etiquetas especial que analisa etiquetas NFC lidas, categoriza-as e tenta localizar aplicativos que têm interesse nos dados digitalizados. Ele faz isso da seguinte maneira:

  1. Analisa a etiqueta NFC e descobrindo o tipo MIME ou um URI que identifica o payload na etiqueta.
  2. Encapsula o tipo MIME ou o URI e o payload em um intent. Estas duas primeiras etapas são descritas em Como as etiquetas NFC são mapeadas em tipos MIME e URIs .
  3. Inicia uma atividade com base no intent. Isso é descrito em Como etiquetas NFC são expedidas para aplicativos.

Como etiquetas NFC são mapeadas em tipos MIME e URIs

Antes de começar a escrever aplicativos NFC, é importante entender os diferentes tipos de etiquetas NFC, a análise de etiquetas NFC feita pelo sistema de expedição de etiquetas e o trabalho especial que o sistema de expedição de etiquetas executa quando detecta uma mensagem NDEF. As etiquetas NFC são compatíveis com uma vasta gama de tecnologias, e é possível gravar dados nelas de diversas maneiras. O Android tem mais compatibilidade com o padrão NDEF, que é definido pelo NFC Forum (link em inglês).

Os dados NDEF são encapsulados em uma mensagem (NdefMessage) que contém um ou mais registros (NdefRecord). Cada registro NDEF precisa ser formatado corretamente de acordo com a especificação do tipo de registro que você quer criar. O Android também é compatível com outros tipos de etiquetas que não contêm dados NDEF, e você pode trabalhar com eles usando as classes do pacote android.nfc.tech. Para saber mais sobre essas tecnologias, consulte o tópico NFC avançada. Trabalhar com esses outros tipos de etiquetas envolve criar uma pilha de protocolo própria para se comunicar com as etiquetas. Por isso, recomendamos o uso do NDEF, quando possível, para facilitar o desenvolvimento e oferecer compatibilidade máxima para dispositivos com tecnologia Android.

Observação: para fazer o download das especificações completas de NDEF, acesse o site Documentos de especificações e aplicações do NFC Forum (link em inglês) e veja Como criar tipos comuns de registros NDEF para ver exemplos de como construir esses registros.

Agora que você tem alguma experiência com etiquetas NFC, as seções a seguir descrevem mais detalhadamente como o Android processa etiquetas formatadas em NDEF. Quando um dispositivo Android faz a leitura de uma etiqueta NFC com dados formatados como NDEF, ele analisa a mensagem e tenta descobrir o tipo MIME dos dados ou identificar o URI. Para fazer isso, o sistema faz a leitura do primeiro NdefRecord dentro do NdefMessage para determinar como interpretar toda a mensagem NDEF, já que uma mensagem NDEF pode ter vários registros NDEF. Em uma mensagem NDEF formatada corretamente, o primeiro NdefRecord contém os seguintes campos:

Formato de nome de tipo (TNF, na sigla em inglês) de 3 bits
Indica como interpretar o campo de tipo de comprimento variável. Os valores válidos são descritos na Tabela 1.
Tipo de comprimento variável
Descreve o tipo do registro. Se estiver utilizando TNF_WELL_KNOWN, use este campo para especificar a definição de tipo de registro (RTD, na sigla em inglês). Os valores de RTD válidos são descritos na Tabela 2.
ID de comprimento variável
Um identificador exclusivo para o registro. Este campo não é usado com frequência, mas se você precisar identificar uma etiqueta especificamente, poderá criar um ID para ela.
Payload de comprimento variável
O payload real que você quer ler ou gravar. Uma mensagem NDEF pode conter vários registros NDEF, portanto, não presuma que o payload inteiro esteja no primeiro registro da mensagem.

O sistema de expedição de etiquetas usa os campos TNF e de tipo para tentar mapear um tipo MIME ou URI para a mensagem NDEF. Se bem-sucedido, ele encapsula essas informações dentro de um intent ACTION_NDEF_DISCOVERED com o payload real. No entanto, há casos em que o sistema de expedição de etiquetas não pode determinar o tipo de dados com base no primeiro registro NDEF. Isso acontece quando os dados NDEF não podem ser mapeados para um tipo MIME ou URI ou quando a etiqueta NFC não contém dados NDEF. Nesses casos, um objeto Tag com informações sobre as tecnologias e o payload da etiqueta é encapsulado dentro de um intent ACTION_TECH_DISCOVERED.

A Tabela 1 descreve como o sistema de expedição de etiquetas mapeia campos TNF e de tipo para tipos MIME ou URIs. Ela também descreve quais TNFs não podem ser mapeados para um tipo MIME ou URI. Nesses casos, o sistema de expedição de etiquetas volta para ACTION_TECH_DISCOVERED.

Por exemplo, se o sistema de expedição de etiquetas encontrar um registro do tipo TNF_ABSOLUTE_URI, ele mapeará o campo de tipo de comprimento variável desse registro em um URI. O sistema de expedição de etiquetas encapsula esse URI no campo de dados de um intent ACTION_NDEF_DISCOVERED com outras informações sobre a etiqueta, como o payload. Por outro lado, se encontrar um registro do tipo TNF_UNKNOWN, ele criará um intent que encapsula as tecnologias da etiqueta.

Tabela 1. TNFs compatíveis e seus mapeamentos

Formato de nome de tipo (TNF) Mapeamento
TNF_ABSOLUTE_URI URI com base no campo de tipo.
TNF_EMPTY Volta para ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE URI com base no URN no campo de tipo. O URN é codificado no campo de tipo NDEF em uma forma abreviada: <domain_name>:<service_name>. O Android mapeia isso para um URI no formato: vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA Tipo MIME com base no campo de tipo.
TNF_UNCHANGED Quando inválido no primeiro registro, volta para ACTION_TECH_DISCOVERED.
TNF_UNKNOWN Volta para ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN Tipo MIME ou URI, dependendo da definição de tipo de registro (RTD, na sigla em inglês), que você define no campo de tipo. Consulte a Tabela 2 para ver mais informações sobre RTDs disponíveis e seus mapeamentos.

Tabela 2. RTDs suportados para TNF_WELL_KNOWN e seus mapeamentos

Definição de tipo de registro (RTD) Mapeamento
RTD_ALTERNATIVE_CARRIER Volta para ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER Volta para ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST Volta para ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT Volta para ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER URI com base na análise do payload.
RTD_TEXT Tipo MIME de text/plain.
RTD_URI URI com base no payload.

Como etiquetas NFC são expedidas para aplicativos

Quando o sistema de expedição de etiquetas termina de criar um intent que encapsula a etiqueta NFC e as informações de identificação dela, ele o envia para um aplicativo interessado que aplica um filtro para esse intent. Se mais de um aplicativo puder processar o intent, o seletor de atividades será apresentado para que o usuário possa selecionar a atividade. O sistema de expedição de etiquetas define três intents, que estão listados em ordem de prioridade, da maior para a menor:

  1. ACTION_NDEF_DISCOVERED: este intent é usado para iniciar uma atividade quando uma etiqueta que contém um payload NDEF é lida e é de um tipo reconhecido. Este é o intent de prioridade mais alta, e o sistema de expedição de etiquetas tenta iniciar uma atividade com ele antes de qualquer outro, sempre que possível.
  2. ACTION_TECH_DISCOVERED: se nenhuma atividade estiver registrada para processar o intent ACTION_NDEF_DISCOVERED, o sistema de expedição de etiquetas tentará iniciar um aplicativo com este intent. Esse intent também será iniciado diretamente (sem ter que iniciar ACTION_NDEF_DISCOVERED primeiro) se a etiqueta lida contiver dados NDEF que não podem ser mapeados para um tipo MIME ou URI ou se não contiver dados NDEF, mas tiver uma tecnologia de etiqueta conhecida.
  3. ACTION_TAG_DISCOVERED: este intent será iniciado se nenhuma atividade processar os intents ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED.

O funcionamento básico do sistema de expedição de etiquetas é o seguinte:

  1. Ele tenta iniciar uma atividade com o intent que foi criado pelo sistema de expedição ao analisar a etiqueta NFC (ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED).
  2. Se nenhuma atividade aplicar um filtro para esse intent, o sistema tentará iniciar uma atividade com o próximo intent de prioridade mais baixa (ACTION_TECH_DISCOVERED ou ACTION_TAG_DISCOVERED) até que um aplicativo aplique o filtro ou até que o sistema de expedição de etiquetas tente todos os intents possíveis.
  3. Se nenhum aplicativo aplicar um filtro para nenhum dos intents, nada acontecerá.
Figura 1. Sistema de expedição de etiquetas

Sempre que possível, trabalhe com mensagens NDEF e o intent ACTION_NDEF_DISCOVERED, porque ele é o mais específico dos três. Esse intent permite que o aplicativo seja iniciado em um momento mais oportuno que os outros dois intents, proporcionando uma experiência melhor ao usuário.

Solicitar acesso a NFC no manifesto do Android

Antes de acessar o hardware NFC de um dispositivo e processar corretamente os intents NFC, declare os seguintes itens no seu arquivo AndroidManifest.xml:

  • O elemento NFC <uses-permission> para acessar o hardware NFC:
        <uses-permission android:name="android.permission.NFC" />
        
  • A versão mínima do SDK compatível com o aplicativo. A API de nível 9 só é compatível com expedição limitada de etiquetas via ACTION_TAG_DISCOVERED e só dá acesso a mensagens NDEF por meio do item extra EXTRA_NDEF_MESSAGES. Nenhuma outra propriedade da etiqueta ou operação de E/S é acessível. A API de nível 10 inclui suporte abrangente de leitura/gravação, além de envio de NDEF em primeiro plano. E a API de nível 14 fornece uma maneira mais fácil de enviar mensagens NDEF para outros dispositivos com Android Beam e métodos adicionais de conveniência para criar registros NDEF.
        <uses-sdk android:minSdkVersion="10"/>
        
  • O elemento uses-feature para que o aplicativo seja exibido no Google Play apenas para dispositivos com hardware NFC:
        <uses-feature android:name="android.hardware.nfc" android:required="true" />
        

    Se o aplicativo usa a funcionalidade NFC mas essa funcionalidade não é crucial, o elemento uses-feature pode ser omitido e a disponibilidade NFC pode ser verificada no ambiente de execução, verificando se getDefaultAdapter() é null.

Filtro para intents NFC

O aplicativo pode aplicar um filtro para um, dois ou todos os três intents NFC no manifesto do Android para iniciar quando for lida uma etiqueta NFC que você queira processar. No entanto, o recomendado é aplicar um filtro para o intent ACTION_NDEF_DISCOVERED para ter mais controle de quando o aplicativo é iniciado. O intent ACTION_TECH_DISCOVERED é um substituto para ACTION_NDEF_DISCOVERED quando nenhum aplicativo aplica um filtro para ACTION_NDEF_DISCOVERED ou para quando o payload não é NDEF. Geralmente, um filtro para ACTION_TAG_DISCOVERED é uma categoria muito genérica de filtro. Muitos aplicativos aplicam o filtro para ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED antes de ACTION_TAG_DISCOVERED, de modo que seu aplicativo terá uma baixa probabilidade de ser iniciado. ACTION_TAG_DISCOVERED está disponível apenas como último recurso de filtro, nos casos em que não há outros aplicativos instalados para processar os intents ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED.

Como as implantações de etiquetas NFC variam e muitas vezes não estão sob seu controle, isso nem sempre é possível. Por isso, é possível substituí-lo pelos outros dois intents quando necessário. Quando tiver controle sobre os tipos de etiquetas e dados gravados, é recomendável usar o NDEF para formatar as etiquetas. As seções a seguir descrevem como aplicar um filtro para cada tipo de intent.

ACTION_NDEF_DISCOVERED

Para aplicar um filtro para intents ACTION_NDEF_DISCOVERED, declare o filtro de intent junto com o tipo de dados que quer filtrar. O exemplo a seguir aplica um filtro para intents ACTION_NDEF_DISCOVERED com um tipo MIME de text/plain:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
    

O exemplo a seguir aplica filtro para um URI na forma de https://developer.android.com/index.html.

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
       <data android:scheme="http"
                  android:host="developer.android.com"
                  android:pathPrefix="/index.html" />
    </intent-filter>
    

ACTION_TECH_DISCOVERED

Se a atividade aplicar filtro para o intent ACTION_TECH_DISCOVERED, é preciso criar um arquivo de recurso XML que especifique as tecnologias compatíveis com a atividade dentro de um conjunto tech-list. A atividade será considerada uma correspondência se um conjunto tech-list for um subconjunto das tecnologias compatíveis com a etiqueta, que pode ser obtido chamando getTechList().

Por exemplo, se a etiqueta lida for compatível com MifareClassic, NdefFormatable e NfcA, o conjunto tech-list precisará especificar todas as três, duas ou uma das tecnologias (e nada mais) para que a atividade seja correspondida.

O exemplo a seguir define todas as tecnologias. É possível remover aquelas que não são necessárias. Salve este arquivo na pasta <project-root>/res/xml. O arquivo pode ser nomeado da maneira que você quiser.

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.IsoDep</tech>
            <tech>android.nfc.tech.NfcA</tech>
            <tech>android.nfc.tech.NfcB</tech>
            <tech>android.nfc.tech.NfcF</tech>
            <tech>android.nfc.tech.NfcV</tech>
            <tech>android.nfc.tech.Ndef</tech>
            <tech>android.nfc.tech.NdefFormatable</tech>
            <tech>android.nfc.tech.MifareClassic</tech>
            <tech>android.nfc.tech.MifareUltralight</tech>
        </tech-list>
    </resources>
    

Também é possível especificar vários conjuntos tech-list. Cada um dos conjuntos tech-list é considerado de forma independente, e a atividade é considerada uma correspondência se qualquer conjunto tech-list for um subconjunto das tecnologias retornadas por getTechList(). Isso fornece semânticas AND e OR para tecnologias compatíveis. O exemplo a seguir combina etiquetas que podem ser compatíveis com as tecnologias NfcA e Ndef ou com as tecnologias NfcB e Ndef:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.NfcA</tech>
            <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
        <tech-list>
            <tech>android.nfc.tech.NfcB</tech>
            <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
    </resources>
    

No arquivo AndroidManifest.xml, especifique o arquivo de recurso que acabou de criar no elemento <meta-data> dentro do elemento <activity>, como no exemplo a seguir:

    <activity>
    ...
    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED"/>
    </intent-filter>

    <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />
    ...
    </activity>
    

Para ver mais informações sobre como trabalhar com tecnologias de etiqueta e o intent ACTION_TECH_DISCOVERED, consulte Trabalhando com tecnologias de etiqueta compatíveis no documento de NFC avançada.

ACTION_TAG_DISCOVERED

Para aplicar um filtro para ACTION_TAG_DISCOVERED use o seguinte filtro de intent:

<intent-filter>
        <action android:name="android.nfc.action.TAG_DISCOVERED"/>
    </intent-filter>
    

Receber informações de intents

Se uma atividade iniciar devido a um intent NFC, é possível obter informações sobre a etiqueta NFC lida pelo intent. Os intents podem conter os seguintes extras, dependendo da etiqueta que for lida:

Para ter esses extras, verifique se a atividade foi iniciada com um dos intents NFC para garantir que uma etiqueta foi lida e, em seguida, pegue os extras a partir do intent. O exemplo a seguir verifica o intent ACTION_NDEF_DISCOVERED e recebe as mensagens NDEF de um intent extra.

Kotlin

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
                val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
                // Process the messages array.
                ...
            }
        }
    }
    

Java

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMessages =
                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                }
                // Process the messages array.
                ...
            }
        }
    }
    

Como alternativa, é possível obter um objeto Tag do intent, que irá conter o payload e permitirá a enumeração das tecnologias da etiqueta:

Kotlin

    val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

Java

    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    

Criar tipos comuns de registros NDEF

Essa seção descreve como criar tipos comuns de registros NDEF para ajudar você na gravação de etiquetas NFC ou no envio de dados pelo Android Beam. A partir do Android 4.0 (API de nível 14) o método createUri() está disponível para ajudar você a criar registros de URI automaticamente. A partir do Android 4.1 (API de nível 16) createExternal() e createMime() estão disponíveis para ajudar você a criar registros NDEF de tipo MIME e externo. Use esses métodos auxiliares sempre que possível para evitar erros ao criar registros NDEF manualmente.

Esta seção também descreve como criar o filtro de intent correspondente para o registro. Todos esses exemplos de registro NDEF precisam estar no primeiro registro da mensagem NDEF que você está gravando em uma etiqueta ou enviando.

TNF_ABSOLUTE_URI

Observação: recomendamos o uso do tipo RTD_URI em vez de TNF_ABSOLUTE_URI porque é mais eficiente.

Um registro NDEF TNF_ABSOLUTE_URI pode ser criado da seguinte maneira:

Kotlin

    val uriRecord = ByteArray(0).let { emptyByteArray ->
        NdefRecord(
                TNF_ABSOLUTE_URI,
                "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")),
                emptyByteArray,
                emptyByteArray
        )
    }
    

Java

    NdefRecord uriRecord = new NdefRecord(
        NdefRecord.TNF_ABSOLUTE_URI ,
        "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
        new byte[0], new byte[0]);
    

O filtro de intent do registro NDEF anterior ficaria assim:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http"
            android:host="developer.android.com"
            android:pathPrefix="/index.html" />
    </intent-filter>
    

TNF_MIME_MEDIA

Um registro NDEF TNF_MIME_MEDIA pode ser criado das seguintes maneiras:

Usando o método createMime():

Kotlin

    val mimeRecord = NdefRecord.createMime(
            "application/vnd.com.example.android.beam",
            "Beam me up, Android".toByteArray(Charset.forName("US-ASCII"))
    )
    

Java

    NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
        "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
    

Criando o NdefRecord manualmente:

Kotlin

    val mimeRecord = Charset.forName("US-ASCII").let { usAscii ->
        NdefRecord(
                NdefRecord.TNF_MIME_MEDIA,
                "application/vnd.com.example.android.beam".toByteArray(usAscii),
                ByteArray(0),
                "Beam me up, Android!".toByteArray(usAscii)
        )
    }
    

Java

    NdefRecord mimeRecord = new NdefRecord(
        NdefRecord.TNF_MIME_MEDIA ,
        "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
        new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
    

O filtro de intent do registro NDEF anterior ficaria assim:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="application/vnd.com.example.android.beam" />
    </intent-filter>
    

TNF_WELL_KNOWN com RTD_TEXT

Um registro NDEF TNF_WELL_KNOWN pode ser criado da seguinte maneira:

Kotlin

    fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord {
        val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII"))
        val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16")
        val textBytes = payload.toByteArray(utfEncoding)
        val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7
        val status = (utfBit + langBytes.size).toChar()
        val data = ByteArray(1 + langBytes.size + textBytes.size)
        data[0] = status.toByte()
        System.arraycopy(langBytes, 0, data, 1, langBytes.size)
        System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size)
        return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data)
    }
    

Java

    public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
        byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
        Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
        byte[] textBytes = payload.getBytes(utfEncoding);
        int utfBit = encodeInUtf8 ? 0 : (1 << 7);
        char status = (char) (utfBit + langBytes.length);
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        data[0] = (byte) status;
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
        NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
        NdefRecord.RTD_TEXT, new byte[0], data);
        return record;
    }
    

O filtro de intent do registro NDEF anterior ficaria assim:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    

TNF_WELL_KNOWN com RTD_URI

Um registro NDEF TNF_WELL_KNOWN pode ser criado das seguintes maneiras:

Usando o método createUri(String):

Kotlin

    val rtdUriRecord1 = NdefRecord.createUri("http://example.com")
    

Java

    NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
    

Usando o método createUri(Uri):

Kotlin

    val rtdUriRecord2 = Uri.parse("http://example.com").let { uri ->
        NdefRecord.createUri(uri)
    }
    

Java

    Uri uri = Uri.parse("http://example.com");
    NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
    

Criando o NdefRecord manualmente:

Kotlin

    val uriField = "example.com".toByteArray(Charset.forName("US-ASCII"))
    val payload = ByteArray(uriField.size + 1)                   //add 1 for the URI Prefix
    payload [0] = 0x01                                           //prefixes http://www. to the URI
    System.arraycopy(uriField, 0, payload, 1, uriField.size)     //appends URI to payload
    val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)
    

Java

    byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
    byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
    payload[0] = 0x01;                                           //prefixes http://www. to the URI
    System.arraycopy(uriField, 0, payload, 1, uriField.length);  //appends URI to payload
    NdefRecord rtdUriRecord = new NdefRecord(
        NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
    

O filtro de intent do registro NDEF anterior ficaria assim:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http"
            android:host="example.com"
            android:pathPrefix="" />
    </intent-filter>
    

TNF_EXTERNAL_TYPE

Um registro NDEF TNF_EXTERNAL_TYPE pode ser criado das seguintes maneiras:

Usando o método createExternal():

Kotlin

    var payload: ByteArray //assign to your data
    val domain = "com.example" //usually your app's package name
    val type = "externalType"
    val extRecord = NdefRecord.createExternal(domain, type, payload)
    

Java

    byte[] payload; //assign to your data
    String domain = "com.example"; //usually your app's package name
    String type = "externalType";
    NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
    

Criando o NdefRecord manualmente:

Kotlin

    var payload: ByteArray
    ...
    val extRecord = NdefRecord(
            NdefRecord.TNF_EXTERNAL_TYPE,
            "com.example:externalType".toByteArray(Charset.forName("US-ASCII")),
            ByteArray(0),
            payload
    )
    

Java

    byte[] payload;
    ...
    NdefRecord extRecord = new NdefRecord(
        NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")),
        new byte[0], payload);
    

O filtro de intent do registro NDEF anterior ficaria assim:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="vnd.android.nfc"
            android:host="ext"
            android:pathPrefix="/com.example:externalType"/>
    </intent-filter>
    

Use TNF_EXTERNAL_TYPE para implantações mais genéricas de etiquetas NFC para oferecer melhor compatibilidade para dispositivos com tecnologia Android e sem.

Observação: URNs para TNF_EXTERNAL_TYPE têm um formato canônico de urn:nfc:ext:example.com:externalType, mas a especificação RTD do NFC Forum declara que a parte urn:nfc:ext: da URN precisa ser omitida do registro NDEF. Portanto, tudo o que é preciso fornecer é o domínio (example.com no exemplo) e o tipo (externalType no exemplo), separados por dois pontos. Ao emitir TNF_EXTERNAL_TYPE, o Android converte o URN urn:nfc:ext:example.com:externalType para um URI vnd.android.nfc://ext/example.com:externalType, que é o que o filtro de intent no exemplo declara.

Registros do aplicativo para Android

Introduzido no Android 4.0 (API de nível 14), um Android Application Record (AAR) fornece uma garantia maior de que o aplicativo será iniciado quando uma etiqueta NFC for lida. Um AAR tem o nome do pacote de um aplicativo incorporado em um registro NDEF. É possível adicionar um AAR a qualquer registro da mensagem NDEF porque o Android procura AARs por toda a mensagem. Se encontrar um AAR, ele iniciará o aplicativo com base no nome do pacote dentro do AAR. Se o aplicativo não estiver presente no dispositivo, o Google Play será iniciado para fazer o download do aplicativo.

Os AARs são úteis se você quiser impedir que outros aplicativos apliquem um filtro para o mesmo intent, possivelmente processando etiquetas específicas que você implantou. Os AARs são suportados apenas no nível do aplicativo (por causa da restrição do nome do pacote) e não no nível da atividade como filtros de intents. Se quiser processar um intent no nível da atividade, use os filtros de intent.

Se uma etiqueta contiver um AAR, o sistema de expedição de etiquetas fará a expedição da seguinte maneira:

  1. Ele tentará iniciar uma atividade usando um filtro de intent normalmente. Se a atividade que corresponde ao intent também corresponder ao AAR, ele iniciará a atividade.
  2. Se a atividade que aplica o filtro para o intent não corresponder ao AAR, se várias atividades puderem processar o intent ou se nenhuma atividade processar o intent, ele iniciará o aplicativo especificado pelo AAR.
  3. Se nenhum aplicativo for iniciado com o AAR, ele acessará o Google Play para fazer o download do aplicativo baseado no AAR.

Observação: você pode substituir os AARs e o sistema de expedição de intents pelo sistema de expedição em primeiro plano, o que permite que uma atividade em primeiro plano tenha prioridade quando uma etiqueta NFC é descoberta. Com esse método, a atividade precisa estar em primeiro plano para modificar os AARs e o sistema de expedição de intents.

Se ainda quiser aplicar filtros para as etiquetas lidas que não contêm um AAR, é possível declarar os filtros de intent normalmente. Isso será útil o aplicativo estiver interessado em outras etiquetas que não contenham um AAR. Por exemplo, talvez você queira garantir que o aplicativo processe etiquetas reservadas implantadas por você, além de etiquetas gerais implantadas por terceiros. Não esqueça que os AARs são específicos para dispositivos Android 4.0 ou posteriores. Portanto, ao implantar etiquetas, é recomendável usar uma combinação de AARs e tipos MIME/URI para garantir a compatibilidade com uma maior gama de dispositivos. Além disso, ao implantar etiquetas NFC, pense em como você quer criar etiquetas NFC para oferecer compatibilidade com a maioria dos dispositivos (com tecnologia Android e outros dispositivos). É possível fazer isso definindo um tipo MIME ou URI relativamente único para facilitar a diferenciação pelos aplicativos.

O Android fornece uma API simples para criar um AAR, createApplicationRecord(). Tudo o que precisa fazer é incorporar o AAR em qualquer lugar no NdefMessage. Não use o primeiro registro do NdefMessage, a menos que o AAR seja o único registro em NdefMessage. Isso porque o sistema Android verifica o primeiro registro de um NdefMessage para determinar o tipo MIME ou URI da etiqueta, que é usado para criar um intent para que os aplicativos filtrem. O código a seguir mostra como criar um AAR:

Kotlin

    val msg = NdefMessage(
            arrayOf(
                    ...,
                    NdefRecord.createApplicationRecord("com.example.android.beam")
            )
    )
    

Java

    NdefMessage msg = new NdefMessage(
            new NdefRecord[] {
                ...,
                NdefRecord.createApplicationRecord("com.example.android.beam")}
            );
    )
    

Enviar mensagens NDEF para outros dispositivos

O Android Beam permite a troca de dados peer-to-peer simples entre dois dispositivos com tecnologia Android. O aplicativo que quer enviar dados para outro dispositivo precisa estar em primeiro plano, e o dispositivo que recebe os dados não pode estar bloqueado. Quando o dispositivo emissor entra no alcance suficientemente próximo do dispositivo receptor, o dispositivo emissor exibe a IU "Toque para transferir". O usuário pode escolher se quer ou não enviar a mensagem para o dispositivo de destino.

Observação: o envio de NDEF em primeiro plano estava disponível na API de nível 10, que fornece funcionalidade similar ao Android Beam. Essas APIs tornaram-se obsoletas desde então, mas estão disponíveis para manter a compatibilidade com dispositivos mais antigos. Consulte enableForegroundNdefPush() para mais informações.

É possível ativar o Android Beam para o aplicativo chamando um destes dois métodos:

  • setNdefPushMessage(): aceita um NdefMessage para definir a mensagem a ser enviada. Envia automaticamente a mensagem quando dois dispositivos estão próximos o suficiente.
  • setNdefPushMessageCallback(): aceita um callback que contém um createNdefMessage() que é chamado quando um dispositivo está no alcance em que os dados serão enviados. O callback permite criar a mensagem NDEF somente quando necessário.

Uma atividade só pode enviar uma mensagem NDEF de cada vez, por isso, setNdefPushMessageCallback() tem precedência sobre setNdefPushMessage() se ambos estiverem definidos. Para usar o Android Beam, as seguintes diretrizes gerais precisam ser seguidas:

  • A atividade que está enviando os dados precisa estar em primeiro plano. Os dois dispositivos precisam estar desbloqueados.
  • Os dados que serão enviados precisam ser encapsulados em um objeto NdefMessage.
  • O dispositivo NFC que recebe os dados enviados precisa ser compatível com o protocolo de envio com.android.npp NDEF ou o protocolo de troca de NDEF simples (SNEP, na sigla em inglês) do NFC Forum. O protocolo com.android.npp é obrigatório para dispositivos na API de nível 9 (Android 2.3) até a API de nível 13 (Android 3.2). com.android.npp e SNEP são obrigatórios na API de nível 14 (Android 4.0) ou posterior.

Observação: se a atividade ativar o Android Beam e estiver em primeiro plano, o sistema de expedição de intents padrão será desativado. No entanto, se a atividade também ativar expedição em primeiro plano, ela ainda poderá ler etiquetas que correspondam aos filtros de intent definidos na expedição em primeiro plano.

Para ativar o Android Beam:

  1. Crie um NdefMessage que contenha o NdefRecord que você quer enviar para outro dispositivo.
  2. Chame setNdefPushMessage() com um NdefMessage ou chame setNdefPushMessageCallback passando um objeto NfcAdapter.CreateNdefMessageCallback no método onCreate() da atividade. Esses métodos exigem pelo menos uma atividade que você queira ativar com o Android Beam, junto com uma lista opcional de outras atividades a serem ativadas.

    Em geral, use setNdefPushMessage() se a atividade precisar enviar somente a mesma mensagem NDEF sempre que dois dispositivos estiverem dentro do alcance para se comunicar. setNdefPushMessageCallback é usado quando o aplicativo se interessa pelo contexto atual do aplicativo e quer enviar uma mensagem NDEF, dependendo do que o usuário está fazendo no aplicativo.

O exemplo a seguir mostra como uma atividade simples chama NfcAdapter.CreateNdefMessageCallback no método onCreate() de uma atividade. Consulte AndroidBeamDemo para ver a demonstração completa. Este exemplo também contém métodos para ajudar você a criar um registro MIME:

Kotlin

    package com.example.android.beam

    import android.app.Activity
    import android.content.Intent
    import android.nfc.NdefMessage
    import android.nfc.NdefRecord
    import android.nfc.NfcAdapter
    import android.nfc.NfcAdapter.CreateNdefMessageCallback
    import android.nfc.NfcEvent
    import android.os.Bundle
    import android.os.Parcelable
    import android.widget.TextView
    import android.widget.Toast
    import java.nio.charset.Charset

    class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback {

        private var nfcAdapter: NfcAdapter? = null
        private lateinit var textView: TextView

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)
            textView = findViewById(R.id.textView)
            // Check for available NFC Adapter
            nfcAdapter = NfcAdapter.getDefaultAdapter(this)
            if (nfcAdapter == null) {
                Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show()
                finish()
                return
            }
            // Register callback
            nfcAdapter?.setNdefPushMessageCallback(this, this)
        }

        override fun createNdefMessage(event: NfcEvent): NdefMessage {
            val text = "Beam me up, Android!\n\n" +
                    "Beam Time: " + System.currentTimeMillis()
            return NdefMessage(
                    arrayOf(
                            createMime("application/vnd.com.example.android.beam", text.toByteArray())
                    )
                    /**
                     * The Android Application Record (AAR) is commented out. When a device
                     * receives a push with an AAR in it, the application specified in the AAR
                     * is guaranteed to run. The AAR overrides the tag dispatch system.
                     * You can add it back in to guarantee that this
                     * activity starts when receiving a beamed message. For now, this code
                     * uses the tag dispatch system.
                     *///,NdefRecord.createApplicationRecord("com.example.android.beam")
            )
        }

        override fun onResume() {
            super.onResume()
            // Check to see that the Activity started due to an Android Beam
            if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
                processIntent(intent)
            }
        }

        override fun onNewIntent(intent: Intent) {
            // onResume gets called after this to handle the intent
            setIntent(intent)
        }

        /**
         * Parses the NDEF Message from the intent and prints to the TextView
         */
        private fun processIntent(intent: Intent) {
            textView = findViewById(R.id.textView)
            // only one message sent during the beam
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs ->
                (rawMsgs[0] as NdefMessage).apply {
                    // record 0 contains the MIME type, record 1 is the AAR, if present
                    textView.text = String(records[0].payload)
                }
            }
        }
    }
    

Java

    package com.example.android.beam;

    import android.app.Activity;
    import android.content.Intent;
    import android.nfc.NdefMessage;
    import android.nfc.NdefRecord;
    import android.nfc.NfcAdapter;
    import android.nfc.NfcAdapter.CreateNdefMessageCallback;
    import android.nfc.NfcEvent;
    import android.os.Bundle;
    import android.os.Parcelable;
    import android.widget.TextView;
    import android.widget.Toast;
    import java.nio.charset.Charset;

    public class Beam extends Activity implements CreateNdefMessageCallback {
        NfcAdapter nfcAdapter;
        TextView textView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            TextView textView = (TextView) findViewById(R.id.textView);
            // Check for available NFC Adapter
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            if (nfcAdapter == null) {
                Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
                finish();
                return;
            }
            // Register callback
            nfcAdapter.setNdefPushMessageCallback(this, this);
        }

        @Override
        public NdefMessage createNdefMessage(NfcEvent event) {
            String text = ("Beam me up, Android!\n\n" +
                    "Beam Time: " + System.currentTimeMillis());
            NdefMessage msg = new NdefMessage(
                    new NdefRecord[] { createMime(
                            "application/vnd.com.example.android.beam", text.getBytes())
             /**
              * The Android Application Record (AAR) is commented out. When a device
              * receives a push with an AAR in it, the application specified in the AAR
              * is guaranteed to run. The AAR overrides the tag dispatch system.
              * You can add it back in to guarantee that this
              * activity starts when receiving a beamed message. For now, this code
              * uses the tag dispatch system.
              */
              //,NdefRecord.createApplicationRecord("com.example.android.beam")
            });
            return msg;
        }

        @Override
        public void onResume() {
            super.onResume();
            // Check to see that the Activity started due to an Android Beam
            if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
                processIntent(getIntent());
            }
        }

        @Override
        public void onNewIntent(Intent intent) {
            // onResume gets called after this to handle the intent
            setIntent(intent);
        }

        /**
         * Parses the NDEF Message from the intent and prints to the TextView
         */
        void processIntent(Intent intent) {
            textView = (TextView) findViewById(R.id.textView);
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                    NfcAdapter.EXTRA_NDEF_MESSAGES);
            // only one message sent during the beam
            NdefMessage msg = (NdefMessage) rawMsgs[0];
            // record 0 contains the MIME type, record 1 is the AAR, if present
            textView.setText(new String(msg.getRecords()[0].getPayload()));
        }
    }
    

Observe que esse código comenta um AAR, que pode ser removido. Se o AAR estiver ativado, o aplicativo especificado no AAR sempre receberá a mensagem do Android Beam. Se o aplicativo não estiver presente, o Google Play é iniciado para fazer o download do aplicativo. Portanto, o filtro de intent a seguir não é tecnicamente necessário para dispositivos Android 4.0 ou posteriores se o AAR for usado:

    <intent-filter>
      <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
      <category android:name="android.intent.category.DEFAULT"/>
      <data android:mimeType="application/vnd.com.example.android.beam"/>
    </intent-filter>
    

Com esse filtro de intent, o aplicativo com.example.android.beam pode ser iniciado quando faz a leitura de uma etiqueta NFC ou recebe um Android Beam com um AAR do tipo ou quando uma mensagem formatada como NDEF contém um registro MIME do tipo application/vnd.com.example.android.beam.

Embora os AARs garantam que um aplicativo seja iniciado ou que o download seja feito, os filtros de intent são recomendados porque permitem que uma atividade da sua escolha seja iniciada no aplicativo, em vez de sempre iniciar a atividade principal dentro do pacote especificado por um AAR. Os AARs não têm granularidade no nível da atividade. Além disso, como alguns dispositivos com tecnologia Android não são compatíveis com AARs, também é recomendado incorporar informações de identificação no primeiro registro NDEF das mensagens NDEF e aplicar os filtros correspondentes também, como precaução. Consulte Como criar tipos comuns de registros NDEF para ver mais informações sobre como criar registros.