Conceitos básicos da NFC

Este documento descreve as tarefas básicas de NFC que são realizadas no Android. Ele explica como enviar e receber dados NFC na forma 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 nã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 tag NFC
  • Envio de mensagens NDEF de um dispositivo para outro com o Android BeamTM

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

O recurso Android BeamTM permite que um dispositivo envie uma mensagem NDEF para outro, tocando fisicamente os dispositivos juntos. Essa interação oferece uma maneira mais fácil de enviar dados do que outras tecnologias sem fio, como Bluetooth, porque, com a NFC, não é necessário descobrir ou parear manualmente o dispositivo. A conexão será iniciada automaticamente quando dois dispositivos entrarem ao alcance. O Android Beam está disponível por meio de um conjunto de APIs NFC, de modo que qualquer app 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 tags NFC quando a tela está desbloqueada, a menos que a NFC esteja desativada no menu "Configurações" do dispositivo. Quando um dispositivo com tecnologia Android descobre uma tag NFC, o comportamento esperado é fazer com que a atividade mais apropriada processe a intent sem perguntar ao usuário qual aplicativo usar. Como os dispositivos leem tags NFC em um alcance muito curto, é provável que fazer com que os usuários selecionem manualmente uma atividade os force a mover o dispositivo para longe da etiqueta e interromper a conexão. A atividade precisa ser desenvolvida para processar apenas as tags NFC importantes para sua atividade, para impedir que o Seletor de atividade seja exibido.

Para ajudar com esse objetivo, o Android oferece um sistema especial de expedição de etiquetas que analisa as etiquetas NFC lidas, as analisa e tenta localizar aplicativos interessados nos dados lidos. Ele faz isso:

  1. Analisa a tag 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. Essas duas primeiras etapas são descritas em Como tags NFC são mapeadas para tipos MIME e URIs.
  3. Inicia uma atividade com base no intent. Isso é descrito em Como tags NFC são expedidas para aplicativos.

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

Antes de começar a programar seus aplicativos de NFC, é importante entender os diferentes tipos de etiquetas NFC, como o sistema de expedição de etiquetas analisa as etiquetas NFC e o trabalho especial que o sistema de expedição de etiquetas faz quando detecta uma mensagem NDEF. As etiquetas NFC apresentam uma ampla gama de tecnologias e também podem ter dados gravados nelas de várias maneiras diferentes. 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 bem formado de acordo com a especificação do tipo de registro que você quer criar. O Android também é compatível com outros tipos de tags que não contêm dados NDEF, com que você pode trabalhar usando as classes no 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 escrever sua própria pilha de protocolo para se comunicar com as etiquetas. Por isso, recomendamos usar o NDEF quando possível para facilitar o desenvolvimento e oferecer suporte máximo para dispositivos Android.

Observação:para fazer o download das especificações completas de NDEF, acesse o site Especificações do fórum de NFC e documentos de inscrição e consulte Como criar tipos comuns de registros NDEF para exemplos de como construir registros NDEF.

Agora que você tem algum conhecimento sobre tags NFC, as seções a seguir descrevem em mais detalhes como o Android processa tags formatadas como NDEF. Quando um dispositivo Android faz a leitura de uma tag NFC contendo dados formatados como NDEF, ele analisa a mensagem e tenta descobrir o tipo MIME dos dados ou identificar a URI. Para fazer isso, o sistema lê o primeiro NdefRecord dentro do NdefMessage para determinar como interpretar toda a mensagem NDEF. Uma mensagem NDEF pode ter vários registros NDEF. Em uma mensagem NDEF bem formada, 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 estão descritos na Tabela 1.
Tipo de comprimento variável
Descreve o tipo do registro. Se estiver usando TNF_WELL_KNOWN, use esse 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. Esse campo não é usado com frequência, mas se você precisar identificar uma tag de maneira exclusiva, crie 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 completo esteja no primeiro registro NDEF 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 consegue 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 tag NFC não contém dados NDEF. Nesses casos, um objeto Tag com informações sobre as tecnologias da tag e o payload é encapsulado dentro de uma intent ACTION_TECH_DISCOVERED.

A Tabela 1 descreve como o sistema de expedição de tags 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 tags 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 tags encapsula esse URI no campo de dados de uma intent ACTION_NDEF_DISCOVERED, além de outras informações sobre a tag, como o payload. Por outro lado, se encontrar um registro do tipo TNF_UNKNOWN, ele vai criar uma intent que encapsula as tecnologias da tag.

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 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 mais informações sobre RTDs disponíveis e os mapeamentos deles.

Tabela 2. RTDs compatíveis com TNF_WELL_KNOWN e os mapeamentos deles

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 uma intent que encapsula a tag NFC e as informações de identificação, ele a envia para um aplicativo interessado que a filtra. Se mais de um app puder processar a 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, listadas em ordem de prioridade mais alta para a mais baixa:

  1. ACTION_NDEF_DISCOVERED: essa intent é usada para iniciar uma atividade quando uma etiqueta que contém um payload NDEF é lida e é de um tipo reconhecido. Essa é a intent de prioridade mais alta, e o sistema de expedição de tags tenta iniciar uma atividade com essa intent antes de qualquer outra, sempre que possível.
  2. ACTION_TECH_DISCOVERED: se nenhuma atividade for registrada para processar a intent ACTION_NDEF_DISCOVERED, o sistema de expedição de etiquetas tentará iniciar um aplicativo com essa intent. Essa intent também será iniciada diretamente (sem iniciar ACTION_NDEF_DISCOVERED primeiro) se a tag lida tiver dados NDEF que não podem ser mapeados para um tipo MIME ou URI ou se a etiqueta não contiver dados NDEF, mas for de uma tecnologia de etiqueta conhecida.
  3. ACTION_TAG_DISCOVERED: essa intent será iniciada se nenhuma atividade processar as intents ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED.

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

  1. Tenta iniciar uma atividade com a intent que foi criada pelo sistema de expedição ao analisar a tag NFC (ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED).
  2. Se nenhuma atividade filtrar para essa intent, tente iniciar uma atividade com a próxima intent de prioridade mais baixa (ACTION_TECH_DISCOVERED ou ACTION_TAG_DISCOVERED) até que um aplicativo filtre a intent 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 a intent ACTION_NDEF_DISCOVERED, porque ela é a mais específica dos três. Essa intent permite iniciar o aplicativo em um momento mais adequado do que as outras duas intents, proporcionando uma experiência melhor ao usuário.

Solicitar acesso a NFC no manifesto do Android

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

  • O elemento NFC <uses-permission> para acessar o hardware de NFC:
    <uses-permission android:name="android.permission.NFC" />
    
  • A versão mínima do SDK compatível com o aplicativo. O nível 9 da API só aceita o envio limitado de tags via ACTION_TAG_DISCOVERED e dá acesso somente a mensagens NDEF por meio do extra EXTRA_NDEF_MESSAGES. Nenhuma outra propriedade de tag ou operação de E/S está acessível. A API de nível 10 inclui suporte abrangente a leitor/gravador, bem como envio de NDEF em primeiro plano, e o nível 14 da API oferece uma maneira mais fácil de enviar mensagens NDEF para outros dispositivos com o Android Beam e outros métodos de conveniência para criar registros NDEF.
    <uses-sdk android:minSdkVersion="10"/>
    
  • O elemento uses-feature para que seu aplicativo apareça no Google Play somente para dispositivos que têm 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 para ele, é possível omitir o elemento uses-feature e verificar a disponibilidade de NFC no momento da execução verificando se getDefaultAdapter() é null.

Filtro para intents NFC

Para iniciar o aplicativo quando uma tag NFC que você quer processar for lida, ele poderá filtrar por uma, duas ou todas as três intents NFC no manifesto do Android. No entanto, o ideal é filtrar a intent ACTION_NDEF_DISCOVERED para ter mais controle de quando o aplicativo é iniciado. A intent ACTION_TECH_DISCOVERED é um substituto para ACTION_NDEF_DISCOVERED quando nenhum aplicativo filtrar por ACTION_NDEF_DISCOVERED ou quando o payload não for NDEF. Em geral, filtrar por ACTION_TAG_DISCOVERED é muito geral para uma categoria para filtrar. Muitos aplicativos filtram por ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED antes de ACTION_TAG_DISCOVERED, de modo que seu aplicativo terá baixa probabilidade de ser iniciado. O ACTION_TAG_DISCOVERED só está disponível como último recurso para filtragem de aplicativos nos casos em que nenhum outro aplicativo está instalado para processar a intent ACTION_NDEF_DISCOVERED ou ACTION_TECH_DISCOVERED.

Como as implantações de tags 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 você tem controle sobre os tipos de etiquetas e dados gravados, é recomendável usar o NDEF para formatar as tags. As seções a seguir descrevem como aplicar um filtro para cada tipo de intent.

ACTION_NDEF_DISCOVERED

Para filtrar intents ACTION_NDEF_DISCOVERED, declare o filtro de intent junto com o tipo de dados que você quer filtrar. O exemplo abaixo filtra 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 filtra 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="https"
              android:host="developer.android.com"
              android:pathPrefix="/index.html" />
</intent-filter>

ACTION_TECH_DISCOVERED

Se a atividade filtrar a intent ACTION_TECH_DISCOVERED, você precisará criar um arquivo de recurso XML que especifique as tecnologias com suporte da sua atividade em um conjunto tech-list. A atividade será considerada uma correspondência se um conjunto de tech-list for um subconjunto das tecnologias compatíveis com a tag, que você pode conseguir chamando getTechList().

Por exemplo, se a tag lida for compatível com MifareClassic, NdefFormatable e NfcA, o conjunto tech-list precisará especificar 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. Remova aqueles que não são compatíveis com sua tag NFC. Salve este arquivo na pasta <project-root>/res/xml. Você pode dar o nome que quiser a ele.

<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 de tech-list é considerado de forma independente, e a atividade é considerada uma correspondência se qualquer conjunto de tech-list for um subconjunto das tecnologias retornadas por getTechList(). Isso fornece semânticas AND e OR para tecnologias correspondentes. O exemplo a seguir corresponde tags 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 recursos que você 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 mais informações sobre como trabalhar com tecnologias de tag e a intent ACTION_TECH_DISCOVERED, consulte Como trabalhar com tecnologias de tag compatíveis no documento NFC avançada.

ACTION_TAG_DISCOVERED

Para filtrar 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 uma intent NFC, é possível acessar informações sobre a tag NFC lida pelo intent. Os intents podem conter os seguintes extras, dependendo da etiqueta que for lida:

Para ter acesso a esses extras, confira se a atividade foi iniciada com uma das intents NFC para garantir que uma tag foi lida e, em seguida, extraia os extras da intent. O exemplo a seguir verifica a intent ACTION_NDEF_DISCOVERED e recebe as mensagens NDEF de um extra de intent.

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, você pode conseguir um objeto Tag da intent, que conterá o payload e permitirá que você enumere as tecnologias da tag:

Kotlin

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

Java

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

Criar tipos comuns de registros NDEF

Esta seção descreve como criar tipos comuns de registros NDEF para ajudar você ao gravar em tags NFC ou enviar dados com o Android Beam. No Android 4.0 (nível 14 da API) e versões mais recentes, o método createUri() está disponível para ajudar você a criar registros de URI automaticamente. A partir do Android 4.1 (nível 16 da API), createExternal() e createMime() estão disponíveis para ajudar você a criar registros NDEF de tipo externo e MIME. 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 devem estar no primeiro registro da mensagem NDEF que você está gravando em uma tag ou enviando.

TNF_ABSOLUTE_URI

Observação:recomendamos usar o tipo RTD_URI em vez de TNF_ABSOLUTE_URI, porque ele é 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="https"
        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("https://example.com")

Java

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

Usando o método createUri(Uri):

Kotlin

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

Java

Uri uri = Uri.parse("https://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 https://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 https://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="https"
        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 tags NFC para oferecer melhor compatibilidade com 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. No entanto, a especificação RTD do NFC Forum declara que a parte urn:nfc:ext: da URN precisa ser omitida do registro NDEF. Portanto, basta informar o domínio (example.com no exemplo) e o tipo (externalType no exemplo), separados por dois pontos. Ao enviar TNF_EXTERNAL_TYPE, o Android converte o URN urn:nfc:ext:example.com:externalType em um URI vnd.android.nfc://ext/example.com:externalType, que é declarado pelo filtro de intent no exemplo.

Registros do aplicativo para Android

Introduzido no Android 4.0 (nível 14 da API), um Android Application Record (AAR) fornece uma certeza mais forte de que o aplicativo será iniciado quando uma tag 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 NDEF da mensagem, já que o Android procura AARs em toda a mensagem. Se encontrar um AAR, ele vai 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 dele.

Os AARs são úteis para evitar que outros aplicativos filtrem a mesma intent e, potencialmente, processem tags específicas que você implantou. Os AARs só têm suporte no nível do aplicativo, devido à restrição do nome do pacote, e não no nível da atividade, como acontece com a filtragem 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 à intent também corresponder ao AAR, inicie a atividade.
  2. Se a atividade que filtra a intent não corresponder às AARs, se várias atividades puderem processar a intent ou se nenhuma atividade processar a intent, inicie o aplicativo especificado pelo AAR.
  3. Se nenhum aplicativo puder começar com o AAR, acesse o Google Play para fazer o download dele com base 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 tag NFC é descoberta. Com esse método, a atividade precisa estar em primeiro plano para substituir os AARs e o sistema de expedição de intents.

Se você ainda quiser filtrar as tags verificadas que não contêm um AAR, declare os filtros de intent normalmente. Isso é útil se o aplicativo estiver interessado em outras tags que não contenham um AAR. Por exemplo, é possível garantir que o aplicativo processe tags próprias implantadas por você e gerais implantadas por terceiros. Não esqueça que os AARs são específicos para dispositivos Android 4.0 ou versões mais recentes. Portanto, ao implantar tags, é recomendável usar uma combinação de AARs e tipos MIME/URIs para oferecer suporte à maior variedade de dispositivos. Além disso, ao implantar etiquetas NFC, pense em como escrevê-las para oferecer suporte à maioria dos dispositivos (com tecnologia Android e outros dispositivos). Para fazer isso, defina um tipo MIME ou URI relativamente exclusivo para facilitar a distinção dos aplicativos.

O Android oferece uma API simples para criar um AAR, createApplicationRecord(). Tudo o que você precisa fazer é incorporar o AAR em qualquer lugar no NdefMessage. Não use o primeiro registro da NdefMessage, a menos que o AAR seja o único registro no NdefMessage. Isso ocorre porque o sistema Android verifica o primeiro registro de uma NdefMessage para determinar o tipo MIME ou a URI da tag, que é usada para criar uma intent que será filtrada pelos aplicativos. 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 app 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 transmitido entra em contato suficiente com o dispositivo de destino, o dispositivo de transmissão exibe a interface "Toque para enviar". O usuário pode escolher se quer enviar a mensagem para o dispositivo de destino.

Observação:o envio de NDEF em primeiro plano estava disponível no nível 10 da API, que oferece funcionalidade semelhante ao Android Beam. Essas APIs foram descontinuadas, mas estão disponíveis para oferecer suporte a dispositivos mais antigos. Consulte os 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 como 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 para onde enviar dados. O callback permite criar a mensagem NDEF somente quando necessário.

Uma atividade só pode enviar uma mensagem NDEF por vez. Portanto, 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 desbloqueadas.
  • É necessário encapsular os dados enviados em um objeto NdefMessage.
  • O dispositivo NFC que recebe os dados enviados precisa ser compatível com o protocolo de push com.android.npp NDEF ou o protocolo de troca simples de NDEF (SNEP, na sigla em inglês) do NFC Forum. O protocolo com.android.npp é necessário para dispositivos com o nível 9 da API (Android 2.3) ao 13 da API (Android 3.2). com.android.npp e SNEP são necessários no nível 14 da API (Android 4.0) e versões mais recentes.

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 o envio em primeiro plano, ela ainda poderá verificar as tags que correspondem aos filtros de intent definidos nos envios em primeiro plano.

Para ativar o Android Beam:

  1. Crie um NdefMessage que contenha os NdefRecords que você quer enviar para o outro dispositivo.
  2. Chame setNdefPushMessage() com um NdefMessage ou setNdefPushMessageCallback transmitindo 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, o uso normal é setNdefPushMessage() quando a atividade precisa apenas enviar a mesma mensagem NDEF o tempo todo, quando dois dispositivos estão ao alcance para se comunicar. O setNdefPushMessageCallback é usado quando o aplicativo se preocupa com o contexto atual 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 o exemplo completo. 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 você ativar o AAR, o aplicativo especificado no AAR sempre receberá a mensagem do Android Beam. Se o aplicativo não estiver presente, o Google Play será iniciado para fazer o download dele. Portanto, o filtro de intent abaixo não é tecnicamente necessário para dispositivos com Android 4.0 ou versões mais recentes 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 agora pode ser iniciado quando faz a leitura de uma tag NFC ou recebe um Android Beam com um AAR do tipo com.example.android.beam 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 transferido por download, os filtros de intent são recomendados porque permitem iniciar uma atividade de sua escolha 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, você também precisa incorporar informações de identificação no primeiro registro NDEF das suas mensagens NDEF e filtrar isso também, por precaução. Consulte Como criar tipos comuns de registros NDEF para mais informações sobre como criar registros.