Основы NFC

В этом документе описаны основные задачи NFC, которые вы выполняете в Android. В нем объясняется, как отправлять и получать данные NFC в виде сообщений NDEF, и описываются API-интерфейсы платформы Android, поддерживающие эти функции. Дополнительную информацию, в том числе о работе с данными, не относящимися к NDEF, можно найти в статье Расширенные возможности NFC.

Чтение данных NDEF из NFC-метки обрабатывается системой диспетчеризации меток, которая анализирует обнаруженные NFC-метки, соответствующим образом классифицирует данные и запускает приложение, заинтересованное в классифицированных данных. Приложение, которое хочет обработать отсканированную NFC-метку, может объявить фильтр интентов и запросить обработку данных.

Система отправки тегов

Устройства Android обычно ищут NFC-метки, когда экран разблокирован, если только NFC не отключен в меню настроек устройства. Когда устройство Android обнаруживает NFC-метку, наиболее подходящее действие должно обработать интент, не спрашивая пользователя, какое приложение использовать. Поскольку устройства сканируют NFC-теги на очень коротком расстоянии, необходимость вручную выбирать действие может заставить пользователей отодвинуть устройство от тега и прервать соединение. Настройте обработку только тех NFC-меток, которые нужны для работы вашего Activity, чтобы избежать появления окна выбора действий.

Чтобы помочь вам в этом, Android предоставляет специальную систему диспетчеризации тегов, которая анализирует отсканированные NFC-метки, анализирует их и пытается найти приложения, которые заинтересованы в отсканированных данных. Это делается следующим образом:

  1. Анализ NFC-тега и определение MIME-типа или URI, который идентифицирует полезную нагрузку данных в теге.
  2. Инкапсуляция MIME-типа или URI и полезной нагрузки в намерение. Эти два шага описаны в разделе Как NFC-метки сопоставляются с MIME-типами и URI.
  3. Запускает активность на основе намерения. Это описано в разделе Как NFC-метки отправляются в приложения.

Как NFC-метки сопоставляются с MIME-типами и URI

Прежде чем приступить к написанию приложений NFC, важно понять, какие существуют типы тегов NFC, как система диспетчеризации тегов анализирует теги NFC и какую специальную работу выполняет система диспетчеризации тегов при обнаружении сообщения NDEF. NFC-метки создаются на основе разных технологий, и данные на них можно записывать разными способами. Android лучше всего поддерживает стандарт NDEF, определенный NFC Forum.

Данные NDEF инкапсулируются в сообщение (NdefMessage), которое содержит одну или несколько записей (NdefRecord). Каждая запись NDEF должна быть правильно сформирована в соответствии со спецификацией типа записи, которую вы хотите создать. Android также поддерживает другие типы тегов, которые не содержат данные NDEF. С ними можно работать, используя классы в пакете android.nfc.tech. Подробнее об этих технологиях рассказывается в разделе Расширенные возможности NFC. Для работы с другими типами тегов необходимо написать собственный стек протоколов для связи с тегами, поэтому мы рекомендуем по возможности использовать NDEF для простоты разработки и максимальной поддержки устройств на базе Android.

Примечание. Чтобы скачать полные спецификации NDEF, перейдите на сайт NFC Forum Specifications & Application Documents. Примеры создания записей NDEF можно найти в статье Создание распространенных типов записей NDEF.

Теперь, когда у вас есть некоторые знания о NFC-метках, в следующих разделах более подробно описывается, как Android обрабатывает NFC-метки в формате NDEF. Когда устройство Android сканирует NFC-метку, содержащую данные в формате NDEF, оно анализирует сообщение и пытается определить MIME-тип данных или идентифицирующий URI. Для этого система считывает первый элемент NdefRecord внутри NdefMessage, чтобы определить, как интерпретировать все сообщение NDEF (сообщение NDEF может содержать несколько записей NDEF). В правильно сформированном сообщении NDEF первая запись NdefRecord содержит следующие поля:

3-битный TNF (Type Name Format)
Указывает, как интерпретировать поле типа переменной длины. Допустимые значения перечислены в таблице 1.
Тип переменной длины
Описывает тип записи. Если вы используете TNF_WELL_KNOWN, укажите в этом поле определение типа записи (RTD). Допустимые значения RTD описаны в таблице 2.
Идентификатор переменной длины
Уникальный идентификатор записи. Это поле используется редко, но если вам нужно уникально идентифицировать тег, вы можете создать для него идентификатор.
Полезная нагрузка переменной длины
Фактические данные, которые вы хотите прочитать или записать. Сообщение NDEF может содержать несколько записей NDEF, поэтому не думайте, что вся полезная нагрузка находится в первой записи NDEF сообщения NDEF.

Система диспетчеризации тегов использует поля TNF и типа, чтобы сопоставить MIME-тип или URI с сообщением NDEF. Если все в порядке, то эта информация вместе с полезной нагрузкой инкапсулируется в намерение ACTION_NDEF_DISCOVERED. Однако в некоторых случаях система диспетчеризации тегов не может определить тип данных на основе первой записи NDEF. Это происходит, когда данные NDEF не могут быть сопоставлены с MIME-типом или URI или когда NFC-метка не содержит данных NDEF. В таких случаях объект Tag, содержащий информацию о технологиях тега и полезной нагрузке, инкапсулируется в намерение ACTION_TECH_DISCOVERED.

В таблице 1 показано, как система диспетчеризации тегов сопоставляет поля TNF и типа с MIME-типами или URI. В ней также описано, какие TNF нельзя сопоставить с MIME-типом или URI. В таких случаях система отправки тегов использует ACTION_TECH_DISCOVERED.

Например, если система диспетчеризации тегов обнаруживает запись типа TNF_ABSOLUTE_URI, она сопоставляет поле типа переменной длины этой записи с URI. Система отправки тегов инкапсулирует этот URI в поле данных намерения ACTION_NDEF_DISCOVERED вместе с другой информацией о теге, например полезной нагрузкой. Если же обнаруживается запись типа TNF_UNKNOWN, создается намерение, которое инкапсулирует технологии тега.

Таблица 1. Поддерживаемые TNF и их сопоставления

Формат названия типа (TNF) Сопоставление
TNF_ABSOLUTE_URI URI на основе поля type.
TNF_EMPTY Резервный параметр – ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE URI на основе URN в поле типа. URN кодируется в поле типа NDEF в сокращенной форме: <domain_name>:<service_name>. Android сопоставляет его с URI в формате vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA MIME-тип на основе поля type.
TNF_UNCHANGED Недопустимое значение в первой записи, поэтому используется значение по умолчанию – ACTION_TECH_DISCOVERED.
TNF_UNKNOWN Резервный параметр – ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN MIME-тип или URI в зависимости от определения типа записи (RTD), которое вы задаете в поле type. Дополнительную информацию о доступных RTD и их сопоставлениях можно найти в таблице 2.

Таблица 2. Поддерживаемые RTD для TNF_WELL_KNOWN и их сопоставления

Определение типа записи (RTD) Сопоставление
RTD_ALTERNATIVE_CARRIER Резервный параметр – ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER Резервный параметр – ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST Резервный параметр – ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT Резервный параметр – ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER URI, полученный в результате синтаксического анализа полезной нагрузки.
RTD_TEXT MIME-тип text/plain.
RTD_URI URI на основе полезной нагрузки.

Как NFC-метки отправляются в приложения

Когда система диспетчеризации тегов завершает создание интента, который инкапсулирует NFC-метку и ее идентифицирующую информацию, она отправляет интент заинтересованному приложению, которое фильтрует интент. Если интент может обработать несколько приложений, пользователю предлагается выбрать Activity. Система диспетчеризации тегов определяет три намерения, которые перечислены в порядке от наивысшего приоритета к наименьшему:

  1. ACTION_NDEF_DISCOVERED – используется для запуска Activity, когда сканируется тег, содержащий полезную нагрузку NDEF и относящийся к распознанному типу. Это намерение с наивысшим приоритетом, и система отправки тегов пытается запустить Activity с этим интентом перед любым другим интентом, когда это возможно.

    Примечание. Начиная с Android 16, при сканировании NFC-меток, в которых хранятся веб-ссылки (то есть схема URI – http:// или https://), активируется намерение ACTION_VIEW, а не ACTION_NDEF_DISCOVERED. Начиная с Android 17, при сканировании такого тега появляется уведомление "Открыть ссылку", требующее явного взаимодействия с пользователем для запуска намерения ACTION_VIEW.

  2. ACTION_TECH_DISCOVERED. Если для обработки намерения ACTION_NDEF_DISCOVERED не зарегистрировано никаких действий, система отправки тегов пытается запустить приложение с этим намерением. Это намерение также запускается напрямую (без предварительного запуска ACTION_NDEF_DISCOVERED), если отсканированный тег содержит данные NDEF, которые нельзя сопоставить с MIME-типом или URI, или если тег не содержит данных NDEF, но относится к известной технологии тегов.
  3. ACTION_TAG_DISCOVERED. Это намерение запускается, если никакие действия не обрабатывают намерения ACTION_NDEF_DISCOVERED или ACTION_TECH_DISCOVERED.

Базовый принцип работы системы отправки тегов выглядит следующим образом:

  1. Попробуйте запустить Activity с интентом, созданным системой диспетчеризации тегов при синтаксическом анализе NFC-метки (ACTION_NDEF_DISCOVERED или ACTION_TECH_DISCOVERED).
  2. Если ни одно действие не отфильтровано для этого интента, попробуйте запустить Activity с интентом следующего самого низкого приоритета (ACTION_TECH_DISCOVERED или ACTION_TAG_DISCOVERED), пока приложение не отфильтрует интент или пока система отправки тегов не попробует все возможные интенты.
  3. Если ни одно приложение не фильтрует ни одно из намерений, ничего не делайте.
Рисунок 1. Система отправки тегов

По возможности работайте с сообщениями NDEF и намерением ACTION_NDEF_DISCOVERED, поскольку оно является наиболее конкретным из трех. Это намерение позволяет запустить приложение в более подходящее время, чем два других, что делает его более удобным для пользователя.

Запросите доступ к NFC в манифесте Android

Прежде чем вы сможете получить доступ к оборудованию NFC устройства и правильно обрабатывать намерения NFC, объявите эти элементы в файле AndroidManifest.xml:

  • Элемент NFC <uses-permission> для доступа к оборудованию NFC:
    <uses-permission android:name="android.permission.NFC" />
  • Начиная с Android 17 (уровень API 37), чтобы активити отправлялся NFC-интент, приложение, предназначенное для SDK > Build.VERSION_CODES.BAKLAVA, должно быть защищено разрешением android.permission.DISPATCH_NFC_MESSAGE. Это гарантирует, что только системный сервис NFC может отправлять намерения в ваше действие. Кроме того, система не будет отправлять намерения NFC приложениям, которые находятся в остановленном состоянии (например, если приложение никогда не запускалось пользователем или было принудительно остановлено).
    <activity
        android:name=".MyActivity"
        android:exported="true"
        android:permission="android.permission.DISPATCH_NFC_MESSAGE">
        ...
    </activity>
  • Минимальная версия SDK, которую может поддерживать ваше приложение. Уровень API 9 поддерживает только ограниченную отправку тегов через ACTION_TAG_DISCOVERED и предоставляет доступ к сообщениям NDEF только через дополнительный параметр EXTRA_NDEF_MESSAGES. Другие свойства тегов и операции ввода-вывода недоступны. Уровень API 10 включает полную поддержку чтения и записи, а также отправку NDEF на переднем плане, а уровень API 14 предоставляет дополнительные удобные методы для создания записей NDEF.
    <uses-sdk android:minSdkVersion="10"/>
  • Элемент uses-feature, чтобы ваше приложение показывалось в Google Play только для устройств с NFC-модулем:
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    Если в вашем приложении используется функция NFC, но она не является критически важной, вы можете опустить элемент uses-feature и проверять доступность NFC во время выполнения, проверяя, является ли getDefaultAdapter() null.

Фильтр для намерений NFC

Чтобы приложение запускалось при сканировании NFC-метки, которую вы хотите обработать, оно может фильтровать одно, два или все три намерения NFC в манифесте Android. Однако обычно лучше использовать фильтр для намерения ACTION_NDEF_DISCOVERED, чтобы лучше контролировать запуск приложения. Намерение ACTION_TECH_DISCOVERED является резервным для ACTION_NDEF_DISCOVERED, когда ни одно приложение не фильтрует ACTION_NDEF_DISCOVERED или когда полезная нагрузка не является NDEF. Фильтрация по категории "ACTION_TAG_DISCOVERED" обычно слишком общая. Многие приложения фильтруют ACTION_NDEF_DISCOVERED или ACTION_TECH_DISCOVERED перед ACTION_TAG_DISCOVERED, поэтому вероятность запуска вашего приложения низкая. ACTION_TAG_DISCOVERED доступен только в крайнем случае, когда нет других приложений, которые могут обработать намерение ACTION_NDEF_DISCOVERED или ACTION_TECH_DISCOVERED.

Поскольку развертывание NFC-тегов может быть разным и часто не зависит от вас, это не всегда возможно. Поэтому при необходимости вы можете использовать два других намерения. Если вы можете контролировать типы тегов и записываемых данных, рекомендуется использовать формат NDEF. В разделах ниже описано, как фильтровать данные по каждому типу намерения.

ACTION_NDEF_DISCOVERED

Чтобы отфильтровать интенты ACTION_NDEF_DISCOVERED, объявите фильтр интентов вместе с типом данных, которые вы хотите отфильтровать. В следующем примере показано, как отфильтровать намерения ACTION_NDEF_DISCOVERED с MIME-типом text/plain:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

В следующем примере фильтр применяется к URI в формате https://developer.android.com/index.html.

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

ACTION_TECH_DISCOVERED

Если ваше действие фильтрует intent ACTION_TECH_DISCOVERED, вам необходимо создать XML-файл ресурсов, в котором в наборе tech-list будут указаны технологии, поддерживаемые вашим действием. Действие считается соответствующим, если набор tech-list является подмножеством технологий, поддерживаемых тегом, которые можно получить, вызвав getTechList().

Например, если отсканированный тег поддерживает MifareClassic, NdefFormatable и NfcA, ваш набор tech-list должен указывать все три, две или одну из технологий (и ничего больше), чтобы ваше действие соответствовало.

В примере ниже определены все технологии. Удалите те, которые не поддерживаются вашей NFC-меткой. Сохраните этот файл в папке <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>

Вы также можете указать несколько наборов tech-list. Каждый из наборов tech-list рассматривается отдельно. Ваше действие считается соответствующим, если любой из наборов tech-list является подмножеством технологий, возвращаемых getTechList(). Это обеспечивает семантику AND и OR для технологий сопоставления. В следующем примере сопоставляются теги, которые могут поддерживать технологии NfcA и Ndef или могут поддерживать технологии NfcB и 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>

В файле AndroidManifest.xml укажите только что созданный файл ресурсов в элементе <meta-data> внутри элемента <activity>, как показано в примере ниже.

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>

Дополнительную информацию о работе с технологиями тегов и намерением ACTION_TECH_DISCOVERED можно найти в разделе Работа с поддерживаемыми технологиями тегов документа Advanced NFC.

ACTION_TAG_DISCOVERED

Примечание. ACTION_TAG_DISCOVERED не поддерживается начиная с Android 17 (уровень API 37). Используйте ACTION_NDEF_DISCOVERED или ACTION_TECH_DISCOVERED.

Чтобы отфильтровать ACTION_TAG_DISCOVERED, используйте следующий фильтр intent:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.TAG_DISCOVERED"/>
    </intent-filter>
</activity>

ACTION_VIEW

Начиная с Android 16 сканирование NFC-меток, в которых хранятся веб-ссылки (то есть схема URI – http:// или https://), запускает намерение ACTION_VIEW вместо ACTION_NDEF_DISCOVERED. Начиная с Android 17, при сканировании такого тега появляется уведомление "Открыть ссылку", и для запуска намерения ACTION_VIEW требуется явное действие пользователя. Чтобы отфильтровать данные по параметру ACTION_VIEW, ознакомьтесь со статьей this. Используйте Android app links, чтобы открывать приложение по URL.

Получение информации из намерений

Если действие начинается из-за намерения NFC, вы можете получить информацию об отсканированном теге NFC из намерения. В зависимости от отсканированного тега намерения могут содержать следующие дополнительные данные:

  • EXTRA_TAG (обязательный) – объект Tag, представляющий отсканированный тег.
  • EXTRA_NDEF_MESSAGES (необязательно) – массив сообщений NDEF, полученных из тега. Это дополнение обязательно для ACTION_NDEF_DISCOVERED намерений.
  • EXTRA_ID (необязательно) – низкоуровневый идентификатор тега.

Чтобы получить эти дополнительные данные, проверьте, было ли ваше действие запущено с одним из намерений NFC, чтобы убедиться, что тег был отсканирован, а затем получите дополнительные данные из намерения. В следующем примере проверяется намерение ACTION_NDEF_DISCOVERED и извлекаются сообщения NDEF из дополнительных данных намерения.

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

Вы также можете получить объект Tag из намерения, который будет содержать полезную нагрузку и позволит вам перечислить технологии тега:

Kotlin

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

Java

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

Создание распространенных типов записей NDEF

В этом разделе описывается, как создавать распространенные типы записей NDEF, чтобы помочь вам при записи в NFC-метки. Начиная с Android 4.0 (уровень API 14) доступен метод createUri(), который позволяет автоматически создавать записи URI. Начиная с Android 4.1 (уровень API 16) createExternal() и createMime() помогают создавать записи NDEF с MIME и внешними типами. По возможности используйте эти вспомогательные методы, чтобы избежать ошибок при создании записей NDEF вручную.

В этом разделе также описывается, как создать соответствующий фильтр намерений для записи. Все эти примеры записей NDEF должны находиться в первой записи NDEF сообщения NDEF, которое вы записываете в тег.

TNF_ABSOLUTE_URI

Примечание. Рекомендуем использовать тип RTD_URI вместо TNF_ABSOLUTE_URI, поскольку он более эффективен.

Создать запись NDEF TNF_ABSOLUTE_URI можно следующим образом:

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

Фильтр интентов для предыдущей записи NDEF будет выглядеть следующим образом:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

TNF_MIME_MEDIA

Создать запись NDEF TNF_MIME_MEDIA можно следующими способами:

При использовании метода 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")));

Создание NdefRecord вручную:

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

Фильтр интентов для предыдущей записи NDEF будет выглядеть следующим образом:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

TNF_WELL_KNOWN с RTD_TEXT

Создать запись NDEF TNF_WELL_KNOWN можно следующим образом:

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

Фильтр интентов для предыдущей записи NDEF будет выглядеть следующим образом:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

TNF_WELL_KNOWN с RTD_URI

Создать запись NDEF TNF_WELL_KNOWN можно следующими способами:

При использовании метода createUri(String):

Kotlin

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

Java

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

При использовании метода 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);

Создание NdefRecord вручную:

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

Фильтр интентов для предыдущей записи NDEF будет выглядеть следующим образом:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

TNF_EXTERNAL_TYPE

Создать запись NDEF TNF_EXTERNAL_TYPE можно следующими способами:

При использовании метода 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);

Создание NdefRecord вручную:

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

Фильтр интентов для предыдущей записи NDEF будет выглядеть следующим образом:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

Используйте TNF_EXTERNAL_TYPE для более общих развертываний NFC-меток, чтобы обеспечить лучшую поддержку устройств как на базе Android, так и на других платформах.

Примечание. URN для TNF_EXTERNAL_TYPE имеют канонический формат urn:nfc:ext:example.com:externalType, однако в спецификации NFC Forum RTD указано, что часть urn:nfc:ext: URN должна быть исключена из записи NDEF. Вам нужно указать только домен (example.com в примере) и тип (externalType в примере), разделенные двоеточием. При отправке TNF_EXTERNAL_TYPE Android преобразует URN urn:nfc:ext:example.com:externalType в URI vnd.android.nfc://ext/example.com:externalType, который объявляется фильтром интентов в примере.

Записи приложений для Android

Запись Android Application Record (AAR), появившаяся в Android 4.0 (уровень API 14), повышает вероятность того, что приложение для Android запустится при сканировании NFC-метки. В AAR содержится название пакета приложения, встроенное в запись NDEF. Вы можете добавить запись AAR к любой записи NDEF в сообщении NDEF, поскольку Android ищет записи AAR по всему сообщению NDEF. Если запись AAR найдена, запускается приложение, название пакета которого указано в этой записи. Если приложение не установлено на устройстве, откроется Google Play, где его можно будет скачать.

Это полезно, если вы хотите, чтобы другие приложения не могли фильтровать по тому же намерению и обрабатывать определенные теги, которые вы развернули. Из-за ограничений, связанных с названием пакета, AAR поддерживаются только на уровне приложения, а не на уровне Activity, как в случае с фильтрацией интентов. Если вы хотите обрабатывать интент на уровне Activity, используйте фильтры интентов.

Если тег содержит AAR, система отправки тегов работает следующим образом:

  1. Попробуйте запустить Activity, используя фильтр интентов, как обычно. Если Activity, соответствующее намерению, также соответствует AAR, запустите Activity.
  2. Если объект Activity, который фильтрует intent, не соответствует AAR, если несколько объектов Activity могут обработать intent или если ни один объект Activity не обрабатывает intent, запустите приложение, указанное в AAR.
  3. Если ни одно приложение не может запуститься с помощью AAR, перейдите в Google Play и скачайте приложение, основанное на AAR.

Примечание. Вы можете переопределить записи AAR и систему диспетчеризации намерений с помощью системы диспетчеризации переднего плана, которая позволяет действию переднего плана иметь приоритет при обнаружении NFC-метки. При этом приложение должно быть активным, чтобы переопределить AAR и систему диспетчеризации намерений.

Если вы по-прежнему хотите отфильтровать отсканированные теги, которые не содержат AAR, вы можете объявить фильтры интентов как обычно. Это полезно, если ваше приложение работает с другими тегами, которые не содержат AAR. Например, вы можете гарантировать, что ваше приложение обрабатывает как собственные теги, которые вы развертываете, так и общие теги, развернутые третьими лицами. Помните, что записи AAR поддерживаются только на устройствах с Android 4.0 и более поздними версиями. Поэтому при развертывании тегов лучше использовать комбинацию записей AAR и MIME-типов/URI, чтобы обеспечить поддержку как можно большего количества устройств. Кроме того, при развертывании NFC-меток подумайте, как вы хотите их записать, чтобы обеспечить поддержку большинства устройств (на базе Android и других). Для этого задайте относительно уникальный MIME-тип или URI, чтобы приложениям было проще различать их.

В Android есть простой API для создания AAR: createApplicationRecord(). Вам нужно лишь встроить AAR в любое место в NdefMessage. Не используйте первую запись NdefMessage, если только автоматическое применение рекомендаций не является единственной записью в NdefMessage. Это связано с тем, что система Android проверяет первую запись NdefMessage, чтобы определить MIME-тип или URI тега, который используется для создания намерения, фильтруемого приложениями. В следующем коде показано, как создать 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")}
        );
)

Белый список приложений для сканирования NFC-меток

Начиная с Android 16 пользователи получают уведомление, когда приложение впервые запрашивает сканирование NFC-меток. В уведомлении пользователю предлагается запретить приложению сканировать NFC-метки.

  • Приложения могут проверять, разрешил ли пользователь сканировать NFC-метки, с помощью NfcAdapter.isTagIntentAllowed().
  • Приложения могут предлагать пользователю разрешить сканирование NFC-меток, отправляя намерение ACTION_CHANGE_TAG_INTENT_PREFERENCE.

Примечание. Белый список приложений для сканирования NFC-меток доступен в разделе Settings > Apps > Special app access > Launch via NFC.

Этот механизм был добавлен, чтобы решить проблему, на которую жаловались пользователи: некоторые приложения, в которых были зарегистрированы фильтры интентов для NFC-меток, постоянно открывались, когда телефон находился рядом с NFC-меткой (кредитной картой, другим телефоном или часами и т. д.).