Conceptos básicos de NFC

Este documento describe las tareas básicas de NFC que realizas en Android. Explica cómo enviar y recibir datos NFC en forma de mensajes NDEF y describe las API de marco de trabajo de Android que son compatibles con estas características. Para obtener información sobre temas más avanzados, incluida un debate sobre cómo trabajar con datos que no son NDEF, consulta conceptos avanzados de NFC.

Hay dos casos prácticos importantes para tener en cuenta al trabajar con datos NDEF y Android:

  • Lectura de datos NDEF de una etiqueta NFC
  • Transmisión de mensajes NDEF de un dispositivo a otro con Android Beam™

La lectura de los datos NDEF de una etiqueta NFC se realiza con el sistema de envío de etiquetas, que analiza las etiquetas NFC detectadas, categoriza los datos de forma apropiada y, luego, inicia una aplicación que está interesada en los datos categorizados. Una aplicación que desea controlar la etiqueta NFC escaneada puede declarar un filtro de intent y solicitar el manejo de los datos.

La función Android Beam™ permite que un dispositivo envíe un mensaje NDEF a otro cuando los dispositivos están físicamente en contacto. Esta interacción proporciona una forma más sencilla de enviar datos que otras tecnologías inalámbricas, como Bluetooth, ya que con NFC no se requiere la sincronización ni la detección manual de dispositivos. Se inicia automáticamente la conexión cuando dos dispositivos entran en rango. Android Beam está disponible a través de un conjunto de API de NFC, por lo que cualquier aplicación puede transmitir información entre dispositivos. Por ejemplo, las aplicaciones de Contactos, Navegador y YouTube usan Android Beam para compartir contactos, páginas web y videos con otros dispositivos.

Sistema de envío de etiquetas

Por lo general, los dispositivos con tecnología Android buscan etiquetas NFC cuando la pantalla está desbloqueada, a menos que la NFC esté inhabilitada en el menú Configuración del dispositivo. Cuando un dispositivo con Android descubre una etiqueta NFC, el comportamiento deseado es hacer que la actividad más adecuada maneje el intent sin preguntarle al usuario qué aplicación usar. Como los dispositivos escanean las etiquetas NFC en un rango muy corto, es probable que hacer que los usuarios seleccionen manualmente una actividad los obligue a alejar el dispositivo de la etiqueta y romper la conexión. Debes desarrollar tu actividad para manejar solo las etiquetas NFC que le interesan a tu actividad para evitar que aparezca el Selector de actividad.

Para ayudarte con este objetivo, Android proporciona un sistema especial de envío de etiquetas que analiza las etiquetas NFC escaneadas y trata de ubicar las aplicaciones que están interesadas en los datos escaneados haciendo lo siguiente:

  1. Analizando la etiqueta NFC y averiguando el tipo MIME o un URI que identifica la carga útil de datos en la etiqueta
  2. Encapsulando el tipo MIME o URI y la carga útil en un intent. Estos dos primeros pasos se describen en Cómo se asignan las etiquetas NFC a los tipos MIME y los URI.
  3. Iniciando una actividad basada en el intent. Esto se describe en Cómo se distribuyen las etiquetas NFC a las aplicaciones.

Cómo se asignan las etiquetas NFC a los tipos MIME y URI

Antes de comenzar a escribir tus aplicaciones NFC, es importante que entiendas cuáles son los diferentes tipos de etiquetas NFC, cómo el sistema de envío de etiquetas analiza las etiquetas NFC y también el trabajo especial que realiza el sistema de envío de etiquetas cuando detecta un mensaje NDEF. Las etiquetas NFC vienen en una amplia gama de tecnologías y también pueden tener datos escritos de muchas maneras diferentes. Android tiene la mayor compatibilidad con el estándar NDEF, que se define en el Foro NFC.

Los datos NDEF se encapsulan dentro de un mensaje (NdefMessage) que contiene uno o más registros (NdefRecord). Cada registro NDEF debe tener el formato indicado por la especificación del tipo de registro que deseas crear. Android también admite otros tipos de etiquetas que no contienen datos NDEF, con los que puedes trabajar utilizando las clases incluidas en el paquete android.nfc.tech. Para obtener más información sobre estas tecnologías, consulta el tema Conceptos avanzados de NFC. Trabajar con estos otros tipos de etiquetas implica escribir tu propia pila de protocolos para poder comunicarte con ellas, por lo que te recomendamos usar NDEF siempre que sea posible, ya que de esa forma facilitas el desarrollo y la compatibilidad máxima para los dispositivos con Android.

Nota: Para descargar las especificaciones completas de NDEF, ve al sitio de especificaciones del foro de NFC y documentos de la aplicación y consulta Cómo crear tipos comunes de registros de NDEF para ver ejemplos de cómo crear registros de NDEF.

Ahora que tienes más información sobre las etiquetas NFC, puedes consultar las siguientes secciones, que describen en mayor detalle cómo Android maneja las etiquetas con formato NDEF. Cuando un dispositivo con Android analiza una etiqueta NFC que contiene datos con formato NDEF, analiza el mensaje y trata de determinar el tipo MIME o URI de identificación de los datos. Para ello, el sistema lee el primer NdefRecord dentro del NdefMessage a fin de determinar cómo interpretar todo el mensaje NDEF (un mensaje NDEF puede tener varios registros NDEF). En un mensaje NDEF bien formado, el primer NdefRecord contiene los siguientes campos:

TNF (Formato de nombre de tipo) de 3 bits
Indica cómo interpretar el campo de tipo de longitud variable. Los valores válidos se describen en la tabla 1.
Tipo de longitud variable
Describe el tipo de registro. Si usas TNF_WELL_KNOWN, usa este campo para especificar la definición del tipo de registro (RTD). Los valores válidos de RTD se describen en la tabla 2.
ID de longitud variable
Un identificador único para el registro. No se utiliza con frecuencia este campo, pero si necesitas identificar una etiqueta de forma exclusiva, puedes crear un ID para ella.
Carga útil de longitud variable
La carga útil de datos reales que deseas leer o escribir. Un mensaje NDEF puede contener múltiples registros NDEF, así que no asumas que la carga útil completa está en el primer registro NDEF del mensaje NDEF.

El sistema de envío de etiquetas utiliza los campos TNF y de tipo para intentar asignar un tipo MIME o URI al mensaje NDEF. Si tiene éxito, encapsula esa información dentro de un intent ACTION_NDEF_DISCOVERED junto con la carga útil real. Sin embargo, hay casos en que el sistema de envío de etiquetas no puede determinar el tipo de datos en función del primer registro NDEF. Esto sucede cuando los datos NDEF no pueden asignarse a un tipo MIME o URI, o cuando la etiqueta NFC no contiene datos NDEF. En esos casos, se encapsula un objeto Tag que tenga información sobre las tecnologías de la etiqueta y la carga útil dentro de un intent ACTION_TECH_DISCOVERED.

La tabla 1 describe cómo el sistema de envío de etiquetas asigna TNF y campos de tipo a tipos MIME o URI. También describe qué TNF no se puede asignar a un tipo MIME o URI. En estos casos, el sistema de envío de etiquetas recurre a ACTION_TECH_DISCOVERED.

Por ejemplo, si el sistema de envío de etiquetas encuentra un registro de tipo TNF_ABSOLUTE_URI, asigna el campo de tipo de longitud variable de ese registro a un URI. El sistema de envío de etiquetas encapsula ese URI en el campo de datos de un intent ACTION_NDEF_DISCOVERED junto con otra información sobre la etiqueta, como la carga útil. Por otro lado, si encuentra un registro de tipo TNF_UNKNOWN, crea un intent que encapsula las tecnologías de la etiqueta.

Tabla 1: Archivos TNF compatibles y sus mapeos

Formato de nombre de tipo (TNF) Mapeo
TNF_ABSOLUTE_URI Este URI se basa en el campo de tipo.
TNF_EMPTY Recurre a ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE Este URI se basa en el URN en el campo de tipo. El URN está codificado en el campo de tipo NDEF en un formulario abreviado: <domain_name>:<service_name>. Android lo asigna a un URI en el formulario: vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA Este tipo de MIME se basa en el campo de tipo.
TNF_UNCHANGED No es válido en el primer registro, por lo que recurre a ACTION_TECH_DISCOVERED.
TNF_UNKNOWN Recurre a ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN Tipo MIME o URI según la definición de tipo de registro (RTD) que establezcas en el campo de tipo. Consulta la tabla 2 para obtener más información sobre las RTD disponibles y sus asignaciones.

Tabla 2: RTD admitidas para TNF_WELL_KNOWN y sus mapeos

Definición de tipo de registro (RTD) Mapeo
RTD_ALTERNATIVE_CARRIER Recurre a ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER Recurre a ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST Recurre a ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT Recurre a ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER Este URI se basa en el análisis de la carga útil.
RTD_TEXT Es el tipo MIME de text/plain.
RTD_URI Este tipo de URI se basa en la carga útil.

Cómo se envían las etiquetas NFC a las aplicaciones

Cuando el sistema de envío de etiquetas termina de crear un intent que encapsula la etiqueta NFC y su información de identificación, envía el intent a una aplicación interesada que lo filtra. Si más de una aplicación puede manejar el intent, se muestra el Selector de actividad para que el usuario pueda seleccionar la actividad. El sistema de envío de etiquetas define tres intents, que se enumeran en orden de mayor a menor prioridad:

  1. ACTION_NDEF_DISCOVERED: Este intent se utiliza para iniciar una actividad cuando se analiza una etiqueta que contiene una carga útil NDEF y es de un tipo reconocido. Este es el intent de mayor prioridad y, siempre que sea posible, el sistema de envío de etiquetas intenta iniciar una actividad con él antes que con cualquier otro.
  2. ACTION_TECH_DISCOVERED: Si no hay ninguna actividad registrada para manejar el intent ACTION_NDEF_DISCOVERED, el sistema de envío de etiquetas intenta iniciar una aplicación con este intent. Se inicia directamente este intent (sin iniciar ACTION_NDEF_DISCOVERED primero) si la etiqueta que se escanea contiene datos NDEF que no pueden asignarse a un tipo MIME o URI, o si la etiqueta no contiene datos NDEF, pero contiene una tecnología de etiqueta conocida.
  3. ACTION_TAG_DISCOVERED: se inicia este intent si ninguna actividad maneja los intents ACTION_NDEF_DISCOVERED o ACTION_TECH_DISCOVERED.

La forma básica en que funciona el sistema de envíos de etiquetas es la siguiente:

  1. Intenta iniciar una actividad con el intent creado por el sistema de envío de etiquetas analizando la etiqueta NFC (ya sea ACTION_NDEF_DISCOVERED o ACTION_TECH_DISCOVERED).
  2. Si ninguna actividad filtra ese intent, trata de iniciar una actividad con la siguiente prioridad más baja (ACTION_TECH_DISCOVERED o ACTION_TAG_DISCOVERED) hasta que una aplicación filtre por el intent o hasta que el sistema de envío de etiquetas pruebe todos los intents posibles.
  3. Si no se filtran aplicaciones para ninguno de los intents, no hagas nada.
Figura 1: Sistema de envío de etiquetas

Siempre que sea posible, trabaja con mensajes NDEF y el intent ACTION_NDEF_DISCOVERED, porque es el más específico de los tres. Este intent te permite iniciar tu aplicación en un momento más apropiado que los otros dos, lo que le brinda al usuario una mejor experiencia.

Cómo solicitar acceso NFC en el manifiesto de Android

Para poder acceder al hardware NFC de un dispositivo y manejar adecuadamente los intents NFC, declara estos elementos en tu archivo AndroidManifest.xml:

  • El elemento NFC <uses-permission> para acceder al hardware NFC:
        <uses-permission android:name="android.permission.NFC" />
        
  • La versión mínima de SDK con la que es compatible tu aplicación. La API nivel 9 solo es compatible con el envío limitado de etiquetas a través de ACTION_TAG_DISCOVERED, y da acceso únicamente a los mensajes NDEF a través de EXTRA_NDEF_MESSAGES extra. No se puede acceder a ninguna otra propiedad de etiqueta ni a operaciones de E/S. La API nivel 10 incluye soporte integral de lectura/escritura, así como la implementación de cambios NDEF en primer plano, y la API nivel 14 proporciona una manera más fácil de enviar mensajes NDEF a otros dispositivos con Android Beam y también métodos de conveniencia adicionales para crear registros NDEF.
        <uses-sdk android:minSdkVersion="10"/>
        
  • El elemento uses-feature para que tu aplicación aparezca en Google Play solo para dispositivos que tienen hardware NFC:
        <uses-feature android:name="android.hardware.nfc" android:required="true" />
        

    Si tu aplicación usa NFC, pero no es una funcionalidad esencial para ella, puedes omitir el elemento uses-feature y verificar la disponibilidad de NFC durante el tiempo de ejecución verificando si getDefaultAdapter() es null.

Cómo filtrar por intents de NFC

Para iniciar tu aplicación cuando se escanea una etiqueta NFC que deseas manejar, tu aplicación puede filtrar uno, dos o los tres intents de NFC en el manifiesto de Android. Sin embargo, generalmente, deseas filtrar el intent ACTION_NDEF_DISCOVERED para obtener el mayor control de cuándo se inicia tu aplicación. El intent ACTION_TECH_DISCOVERED es una alternativa para ACTION_NDEF_DISCOVERED cuando no se filtran aplicaciones para ACTION_NDEF_DISCOVERED o cuando la carga útil no es NDEF. Usualmente, filtrar por ACTION_TAG_DISCOVERED es demasiado general para filtrar una categoría. Muchas aplicaciones se filtrarán por ACTION_NDEF_DISCOVERED o ACTION_TECH_DISCOVERED antes de ACTION_TAG_DISCOVERED, por lo que tu aplicación tiene una baja probabilidad de iniciarse. ACTION_TAG_DISCOVERED solo está disponible como último recurso a fin de que las aplicaciones se filtren en aquellos casos en que no se instalan otras aplicaciones para manejar el intent ACTION_NDEF_DISCOVERED o ACTION_TECH_DISCOVERED.

Como las implementaciones de etiquetas NFC varían y, muchas veces, no están bajo tu control, esto no siempre es posible, por lo que puedes recurrir a los otros dos intents cuando sea necesario. Cuando tienes control sobre los tipos de etiquetas y datos escritos, te recomendamos usar NDEF para dar formato a tus etiquetas. Las siguientes secciones describen cómo filtrar para cada tipo de intent.

ACTION_NDEF_DISCOVERED

Para filtrar por intents ACTION_NDEF_DISCOVERED, declara el filtro del intent junto con el tipo de datos que deseas filtrar. El siguiente ejemplo filtra para intents ACTION_NDEF_DISCOVERED con un 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>
    

El siguiente ejemplo filtra un URI en el formato 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

Si tu actividad filtra el intent ACTION_TECH_DISCOVERED, debes crear un archivo de recursos XML que especifique las tecnologías que admite tu actividad dentro de un conjunto tech-list. Tu actividad se considera una coincidencia si un conjunto tech-list es un subconjunto de las tecnologías admitidas por la etiqueta, que puedes obtener llamando getTechList().

Por ejemplo, si la etiqueta escaneada admite MifareClassic, NdefFormatable y NfcA, tu conjunto tech-list debe especificar tres, dos o una de las tecnologías (y nada más) para que coincida tu actividad.

Los siguientes ejemplos definen todas las tecnologías. Puedes quitar las que no necesites. Guarda este archivo (puedes nombrarlo como desees) en la carpeta <project-root>/res/xml.

    <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>
    

También puedes especificar múltiples conjuntos tech-list. Se considera de forma independiente cada uno de los conjuntos tech-list, y tu actividad se considera una coincidencia si cualquier conjunto tech-list es un subconjunto de las tecnologías que devuelve getTechList(). Este proceso proporciona una semántica AND y OR para las tecnologías de concordancia. El siguiente ejemplo coincide con las etiquetas que son compatibles con las tecnologías NfcA y Ndef, o que pueden ser compatibles con las tecnologías NfcB y 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>
    

En tu archivo AndroidManifest.xml, especifica el archivo de recursos que acabas de crear en el elemento <meta-data> dentro del elemento <activity>, como se muestra en el siguiente ejemplo:

    <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 obtener más información sobre cómo trabajar con tecnologías de etiquetas y el intent ACTION_TECH_DISCOVERED, consulta Cómo trabajar con tecnologías de etiquetas compatibles en el documento de conceptos avanzados de NFC.

ACTION_TAG_DISCOVERED

Para filtrar por ACTION_TAG_DISCOVERED, usa el siguiente filtro de intent:

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

Cómo obtener información de intents

Si se inicia una actividad debido a un intent NFC, puedes usar el intent para obtener información sobre la etiqueta NFC escaneada. Los intents pueden contener los siguientes servicios adicionales según la etiqueta escaneada:

Si deseas acceder a estos adicionales, verifica si se inició tu actividad con uno de los intents de NFC para asegurarte de que se escaneó una etiqueta y, luego, obtén los servicios adicionales por el intent. El siguiente ejemplo verifica el intent ACTION_NDEF_DISCOVERED y obtiene los mensajes NDEF de un adicional 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, puedes obtener un objeto Tag a partir del intent, que contendrá la carga útil y te permitirá enumerar las tecnologías de la etiqueta:

Kotlin

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

Java

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

Cómo crear tipos comunes de registros NDEF

Esta sección describe cómo crear tipos comunes de registros NDEF para ayudarte al momento de escribir en etiquetas NFC o enviar datos con Android Beam. A partir de Android 4.0 (API nivel 14), se lanzó el método createUri() para ayudarte a crear registros de URI automáticamente. A partir de Android 4.1 (API nivel 16), se lanzaron createExternal() y createMime() para ayudarte a crear registros MIME y de tipo externo NDEF. Usa estos métodos de ayuda cuando sea posible para evitar errores cuando creas registros NDEF de forma manual.

Esta sección también describe cómo crear el filtro de intents correspondiente para el registro. Todos estos ejemplos de registros NDEF deben estar en el primer registro NDEF del mensaje NDEF que estás escribiendo en una etiqueta o transmisión.

TNF_ABSOLUTE_URI

Nota: Te recomendamos utilizar el tipo RTD_URI en lugar de TNF_ABSOLUTE_URI, ya que es más eficiente.

Puedes crear un registro NDEF TNF_ABSOLUTE_URI de la siguiente manera:

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]);
    

El filtro de intents para el registro NDEF anterior se vería así:

    <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

Puedes crear un registro NDEF TNF_MIME_MEDIA de las siguientes maneras:

Con el 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")));
    

Creando el NdefRecord de forma manual:

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")));
    

El filtro de intents para el registro NDEF anterior se vería así:

    <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 con RTD_TEXT

Puedes crear un registro TNF_WELL_KNOWN NDEF de la siguiente manera:

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;
    }
    

El filtro de intents para el registro NDEF anterior se vería así:

    <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 con RTD_URI

Puedes crear un registro NDEF TNF_WELL_KNOWN de las siguientes maneras:

Con el método createUri(String):

Kotlin

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

Java

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

Con el 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);
    

Creando el NdefRecord de forma manual:

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);
    

El filtro de intents para el registro NDEF anterior se vería así:

    <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

Puedes crear un registro NDEF TNF_EXTERNAL_TYPE de las siguientes maneras:

Usando el 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);
    

Creando el NdefRecord de forma manual:

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);
    

El filtro de intents para el registro NDEF anterior se vería así:

    <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>
    

Usa TNF_EXTERNAL_TYPE para realizar implementaciones de etiquetas NFC más genéricas a fin de admitir mejor los dispositivos con tecnología de Android y sin ella.

Nota: los URN para TNF_EXTERNAL_TYPE tienen un formato canónico de urn:nfc:ext:example.com:externalType. Sin embargo, la especificación RTF de NFC Forum declara que debe omitirse del registro NDEF la porción urn:nfc:ext: del URN. Entonces, todo lo que necesitas proporcionar es el dominio (example.com en el ejemplo) y el tipo (externalType en el ejemplo) separados por dos puntos. Al enviar TNF_EXTERNAL_TYPE, Android convierte el URN urn:nfc:ext:example.com:externalType en un URI vnd.android.nfc://ext/example.com:externalType, que es lo que declara el filtro de intent en el ejemplo.

Registros de aplicaciones de Android

Introducido en Android 4.0 (API nivel 14), un registro de aplicaciones para Android (AAR) proporciona una certeza más sólida de que tu aplicación se inicia cuando se escanea una etiqueta NFC. Un AAR tiene el nombre del paquete de una aplicación incorporada dentro de un registro NDEF. Puedes agregar un AAR a cualquier registro NDEF de tu mensaje NDEF, porque Android busca los AAR en todo el mensaje NDEF. Si encuentra un AAR, entonces, inicia la aplicación según el nombre del paquete dentro del AAR. Si la aplicación no está presente en el dispositivo, se inicia Google Play para descargarla.

Los AAR son útiles si deseas evitar que se filtren otras aplicaciones con el mismo intent y que, posiblemente, manejen etiquetas específicas que hayas implementado. Los AAR solo se admiten a nivel de la aplicación, debido a la restricción del nombre del paquete, y no a nivel de la Actividad, como ocurre con el filtro de intents. Si deseas manejar un intent al nivel de la Actividad, usa filtros de intents.

Si una etiqueta contiene un AAR, se despacha el sistema de envío de etiquetas de la siguiente manera:

  1. Intenta iniciar una Actividad usando un filtro de intents como de costumbre. Si la actividad que coincide con el intent también coincide con el AAR, iníciala.
  2. Si la actividad que filtra el intent no coincide con el AAR, si varias actividades pueden manejar el intent o si ninguna actividad maneja el intent, inicia la aplicación especificada por el AAR.
  3. Si no se inicia ninguna aplicación con el AAR, ve a Google Play para descargar la aplicación basada en el AAR.

Nota: Puedes anular los AAR y el sistema de envío de intents con el sistema de envío en primer plano, que permite que una actividad en primer plano tenga prioridad cuando se descubre una etiqueta NFC. Con este método, la actividad debe estar en primer plano para anular los AAR y el sistema de envío de intents.

Si aún deseas filtrar las etiquetas escaneadas que no contienen un AAR, puedes declarar los filtros de intents como normales. Esto es útil si tu aplicación está interesada en otras etiquetas que no contienen un AAR. Por ejemplo, tal vez desees garantizar que tu aplicación controle las etiquetas de propiedad que implementa así como también las etiquetas generales implementadas por terceros. Ten en cuenta que los AAR son específicos para dispositivos Android 4.0 o versiones posteriores, por lo que, cuando implementas etiquetas, lo más recomendable es utilizar una combinación de AAR y tipos/URI MIME para lograr ser compatible con la mayor cantidad de dispositivos posibles. Además, cuando implementes etiquetas NFC, piensa en cómo deseas escribir tus etiquetas NFC para que sean compatibles con la mayoría de los dispositivos (con Android y otros dispositivos). Para ello, define un URI o tipo de MIME relativamente único que facilite la distinción entre las aplicaciones.

Android proporciona una API simple para crear un AAR: createApplicationRecord(). Todo lo que necesitas hacer es incluir el AAR en cualquier parte de tu NdefMessage. No es aconsejable utilizar el primer registro de tu NdefMessage, a menos que el AAR sea el único registro en el NdefMessage. Esto se debe a que el sistema Android verifica el primer registro de un NdefMessage a fin de determinar el tipo MIME o URI de la etiqueta, que se utiliza para crear un intent para que las aplicaciones filtren. El siguiente código te muestra cómo crear un 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")}
            );
    )
    

Cómo transmitir mensajes NDEF a otros dispositivos

Android Beam permite el intercambio de datos de igual a igual entre dos dispositivos con Android. La aplicación que desea transferir datos a otro dispositivo debe estar en primer plano, y el dispositivo que recibe los datos no debe estar bloqueado. Cuando el dispositivo de transmisión está lo suficientemente cerca para entrar en contacto con un dispositivo receptor, muestra la IU "Tocar para transmitir". El usuario puede elegir si desea transmitir el mensaje al dispositivo receptor.

Nota: La implementación de cambios NDEF en primer plano estaba disponible en la API nivel 10, que proporciona una funcionalidad similar a Android Beam. Estas API son obsoletas, pero siguen disponibles para brindar compatibilidad con dispositivos más antiguos. Consulta enableForegroundNdefPush() para obtener más información.

Puedes habilitar Android Beam para tu aplicación llamando uno de los dos métodos:

  • setNdefPushMessage(): Acepta configurar un NdefMessage como el mensaje para transmitir. Transmite automáticamente el mensaje cuando dos dispositivos están lo suficientemente cerca.
  • setNdefPushMessageCallback(): Acepta una devolución de llamada que contiene un createNdefMessage() que se llama cuando un dispositivo está dentro del alcance para transmitir datos. La devolución de llamada te permite crear el mensaje NDEF solo cuando es necesario.

Una actividad solo puede enviar un mensaje NDEF a la vez, por lo que setNdefPushMessageCallback() tiene prioridad sobre setNdefPushMessage() si ambos están configurados. Para usar Android Beam, se deben cumplir las siguientes pautas generales:

  • La actividad que transmite los datos debe estar en primer plano. Ambos dispositivos deben tener las pantallas desbloqueadas.
  • Debes encapsular los datos que estás transmitiendo en un objeto NdefMessage.
  • El dispositivo NFC que recibe los datos transmitidos debe ser compatible con el protocolo de inserción NDEF com.android.npp o el SNEP (Protocolo simple de intercambio NDEF) de NFC Forum. El protocolo com.android.npp es obligatorio para los dispositivos con API nivel 9 (Android 2.3) hasta API nivel 13 (Android 3.2). com.android.npp y SNEP son necesarios en las API nivel 14 (Android 4.0) y posteriores.

Nota: Si tu actividad habilita Android Beam y está en primer plano, el sistema estándar de envío de intents se inhabilita. Sin embargo, si tu actividad también permite el envío en primer plano, aún puedes escanear etiquetas que coincidan con los filtros de intents establecidos en el envío en primer plano.

Para habilitar Android Beam, haz lo siguiente:

  1. Crea un NdefMessage que contenga los NdefRecord que deseas insertar en el otro dispositivo.
  2. Llama a setNdefPushMessage() con NdefMessage o a setNdefPushMessageCallback pasando un objeto NfcAdapter.CreateNdefMessageCallback en el método onCreate() de tu actividad. Estos métodos requieren por lo menos una actividad que quieras habilitar con Android Beam, junto con una lista opcional de otras actividades para activar.

    En general, debes usar setNdefPushMessage() si tu Actividad solo necesita enviar el mismo mensaje NDEF en todo momento cuando dos dispositivos están dentro del alcance para comunicarse. Sin embargo, debes usar setNdefPushMessageCallbackando tu aplicación se ocupa del contexto en el que se encuentra la aplicación y desea enviar un mensaje NDEF según lo que el usuario esté haciendo en ella.

El siguiente ejemplo muestra cómo una actividad simple llama a NfcAdapter.CreateNdefMessageCallback en el método onCreate() de una actividad (consulta AndroidBeamDemo para obtener la muestra completa). Este ejemplo también tiene métodos para ayudarte a crear un 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()));
        }
    }
    

Ten en cuenta que este código comenta un AAR, que puedes quitar. Si habilitas el AAR, la aplicación especificada en él siempre recibe el mensaje de Android Beam. Si la aplicación no está presente, se inicia Google Play para descargar la aplicación. Por lo tanto, si se usa el AAR, el siguiente filtro de intent no es técnicamente necesario para dispositivos Android 4.0 o versiones posteriores:

    <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>
    

Con este filtro de intents, ahora se puede iniciar la aplicación com.example.android.beam cuando escanea una etiqueta NFC o recibe un Android Beam con un AAR de tipo o cuando un mensaje con formato NDEF contiene un registro MIME de tipo application/vnd.com.example.android.beam.

Aunque los AAR garantizan que se inicie o descargue una aplicación, se recomienda usar filtros de intent porque permiten iniciar una Actividad de tu elección, en lugar de iniciar siempre la Actividad principal dentro del paquete especificado por un AAR. Los AAR no tienen nivel de detalle de la actividad. Además, como algunos dispositivos con Android no son compatibles con el AAR, también debes incrustar información de identificación en el primer registro NDEF de tus mensajes NDEF y filtrar eso también, por si acaso. Consulta Cómo crear tipos comunes de registros NDEF para obtener más información sobre cómo crear registros.