Основы NFC

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

Существует два основных варианта использования при работе с данными NDEF и Android:

  • Чтение данных NDEF из метки NFC
  • Передача сообщений NDEF с одного устройства на другое с помощью Android Beam™

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

Функция Android Beam™ позволяет устройству передавать сообщение NDEF на другое устройство путем физического соединения устройств друг с другом. Такое взаимодействие обеспечивает более простой способ отправки данных, чем другие беспроводные технологии, такие как Bluetooth, поскольку благодаря NFC не требуется ручное обнаружение или сопряжение устройств. Соединение автоматически запускается, когда два устройства входят в зону действия. Android Beam доступен через набор API-интерфейсов NFC, поэтому любое приложение может передавать информацию между устройствами. Например, приложения «Контакты», «Браузер» и YouTube используют Android Beam для обмена контактами, веб-страницами и видео с другими устройствами.

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

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

Чтобы помочь вам в достижении этой цели, 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» и ознакомьтесь с примерами создания записей NDEF в разделе «Создание общих типов записей NDEF».

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

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

Система отправки тегов использует поля TNF и type, чтобы попытаться сопоставить тип 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 на основе поля типа.
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 на основе поля типа.
TNF_UNCHANGED Недопустимо в первой записи, поэтому возвращается к ACTION_TECH_DISCOVERED .
TNF_UNKNOWN Возвращается к ACTION_TECH_DISCOVERED .
TNF_WELL_KNOWN Тип MIME или URI в зависимости от определения типа записи (RTD), которое вы установили в поле типа. См. Таблицу 2 для получения дополнительной информации о доступных RTD и их сопоставлениях.

Таблица 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 и его идентификационную информацию, она отправляет намерение заинтересованному приложению, которое фильтрует это намерение. Если с намерением могут справиться несколько приложений, отображается окно выбора действия, позволяющее пользователю выбрать действие. Система отправки тегов определяет три намерения, которые перечислены в порядке от высшего к низшему приоритету:

  1. ACTION_NDEF_DISCOVERED : это намерение используется для запуска действия, когда тег, содержащий полезную нагрузку NDEF, сканируется и имеет распознанный тип. Это намерение с наивысшим приоритетом, и система отправки тегов пытается запустить действие с этим намерением раньше, чем с любым другим намерением, когда это возможно.
  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. Если никакие действия не фильтруются для этого намерения, попробуйте запустить действие со следующим намерением с наименьшим приоритетом ( 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" />
    
  • Минимальная версия SDK, которую может поддерживать ваше приложение. Уровень API 9 поддерживает только ограниченную отправку тегов через ACTION_TAG_DISCOVERED и предоставляет доступ к сообщениям NDEF только через дополнительную опцию EXTRA_NDEF_MESSAGES . Никакие другие свойства тега или операции ввода-вывода недоступны. Уровень API 10 включает в себя комплексную поддержку чтения/записи, а также отправку NDEF на переднем плане, а уровень API 14 обеспечивает более простой способ отправки сообщений NDEF на другие устройства с помощью Android Beam и дополнительные удобные методы для создания записей 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 :

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

В следующем примере фильтруется URI в форме 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

Если ваша деятельность фильтрует намерение 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>
...
<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 используйте следующий фильтр намерений:

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

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

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

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

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

Котлин

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

Ява

@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 из намерения, который будет содержать полезную нагрузку и позволит вам перечислить технологии тега:

Котлин

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

Ява

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

Создание общих типов записей NDEF.

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

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

TNF_ABSOLUTE_URI

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

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

Котлин

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

Ява

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 будет выглядеть следующим образом:

<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

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

Используя метод createMime() :

Котлин

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

Ява

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

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

Котлин

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

Ява

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 будет выглядеть следующим образом:

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

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

Котлин

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

Ява

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 будет выглядеть следующим образом:

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

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

Используя метод createUri(String) :

Котлин

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

Ява

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

Используя метод createUri(Uri) :

Котлин

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

Ява

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

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

Котлин

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)

Ява

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 будет выглядеть следующим образом:

<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

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

Используя метод createExternal() :

Котлин

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)

Ява

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 вручную:

Котлин

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

Ява

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

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

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

Используйте 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 (AAR), представленная в Android 4.0 (уровень API 14), обеспечивает большую уверенность в том, что ваше приложение запускается при сканировании метки NFC. AAR содержит имя пакета приложения, встроенное в запись NDEF. Вы можете добавить AAR к любой записи NDEF вашего сообщения NDEF, поскольку Android ищет AAR во всем сообщении NDEF. Если он находит AAR, он запускает приложение на основе имени пакета внутри AAR. Если приложение отсутствует на устройстве, запускается Google Play для его загрузки.

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

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

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

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

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

Android предоставляет простой API для создания AAR — createApplicationRecord() . Все, что вам нужно сделать, это встроить AAR в любое место вашего NdefMessage . Вы не хотите использовать первую запись вашего NdefMessage , если только AAR не является единственной записью в NdefMessage . Это связано с тем, что система Android проверяет первую запись NdefMessage чтобы определить тип MIME или URI тега, который используется для создания намерения приложений фильтровать. Следующий код показывает, как создать AAR:

Котлин

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

Ява

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

Передача сообщений NDEF на другие устройства

Android Beam обеспечивает простой одноранговый обмен данными между двумя устройствами под управлением Android. Приложение, которое хочет передать данные на другое устройство, должно быть на переднем плане, а устройство, получающее данные, не должно быть заблокировано. Когда передающее устройство вступает в достаточно тесный контакт с принимающим устройством, на передающем устройстве отображается пользовательский интерфейс «Touch to Beam». Затем пользователь может выбрать, передавать ли сообщение на принимающее устройство или нет.

Примечание. Отправка NDEF на передний план была доступна на уровне API 10, который обеспечивает функциональность, аналогичную Android Beam. Эти API с тех пор устарели, но доступны для поддержки старых устройств. Дополнительную информацию см. enableForegroundNdefPush() .

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

  • setNdefPushMessage() : принимает NdefMessage для установки в качестве сообщения для передачи. Автоматически передает сообщение, когда два устройства находятся достаточно близко.
  • setNdefPushMessageCallback() : принимает обратный вызов, содержащий createNdefMessage() , которая вызывается, когда устройство находится в зоне действия для передачи данных. Обратный вызов позволяет создавать сообщение NDEF только при необходимости.

Действие может одновременно отправлять только одно сообщение NDEF, поэтому setNdefPushMessageCallback() имеет приоритет над setNdefPushMessage() если установлены оба. Для использования Android Beam необходимо соблюдать следующие общие правила:

  • Действие, передающее данные, должно быть на переднем плане. Экраны обоих устройств должны быть разблокированы.
  • Вы должны инкапсулировать данные, которые вы передаете, в объект NdefMessage .
  • Устройство NFC, получающее передаваемые данные, должно поддерживать протокол com.android.npp NDEF push или SNEP (простой протокол обмена NDEF) NFC Forum. Протокол com.android.npp необходим для устройств с уровнем API 9 (Android 2.3) до уровня API 13 (Android 3.2). com.android.npp и SNEP необходимы на уровне API 14 (Android 4.0) и более поздних версиях.

Примечание. Если ваше действие включает Android Beam и находится на переднем плане, стандартная система отправки намерений отключена. Однако если ваша деятельность также включает приоритетную диспетчеризацию , она все равно может сканировать теги, соответствующие фильтрам намерений, установленным в приоритетной диспетчеризации.

Чтобы включить Android Beam:

  1. Создайте NdefMessage , содержащий NdefRecord , который вы хотите отправить на другое устройство.
  2. Вызовите setNdefPushMessage() с помощью NdefMessage или вызовите setNdefPushMessageCallback , передав объект NfcAdapter.CreateNdefMessageCallback в методе onCreate() вашей активности. Для этих методов требуется как минимум одно действие, которое вы хотите включить с помощью Android Beam, а также необязательный список других действий для активации.

    В общем, вы обычно используете setNdefPushMessage() если вашему действию нужно только постоянно отправлять одно и то же сообщение NDEF, когда два устройства находятся в пределах досягаемости для связи. Вы используете setNdefPushMessageCallback когда ваше приложение заботится о текущем контексте приложения и хочет отправить сообщение NDEF в зависимости от того, что пользователь делает в вашем приложении.

В следующем примере показано, как простое действие вызывает NfcAdapter.CreateNdefMessageCallback в методе onCreate() действия (полный пример см. в разделе AndroidBeamDemo ). В этом примере также есть методы, которые помогут вам создать запись MIME:

Котлин

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

Ява

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

Обратите внимание, что этот код закомментирует AAR, который вы можете удалить. Если вы включите AAR, приложение, указанное в AAR, всегда будет получать сообщение Android Beam. Если приложение отсутствует, запускается Google Play для его загрузки. Таким образом, следующий фильтр намерений не является технически необходимым для устройств Android 4.0 или более поздней версии, если используется AAR:

<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.example.android.beam теперь можно запускать, когда оно сканирует тег NFC или получает Android Beam с AAR типа com.example.android.beam , или когда сообщение в формате NDEF содержит MIME-запись типа application/vnd.com.example.android.beam .

Несмотря на то, что AAR гарантируют, что приложение запускается или загружается, рекомендуется использовать фильтры намерений, поскольку они позволяют вам запускать действие по вашему выбору в вашем приложении вместо того, чтобы всегда запускать основное действие в пакете, указанном AAR. AAR не имеют детализации уровня активности. Кроме того, поскольку некоторые устройства под управлением Android не поддерживают AAR, вам также следует на всякий случай встроить идентифицирующую информацию в первую запись NDEF ваших сообщений NDEF и отфильтровать ее. Дополнительную информацию о том, как создавать записи, см. в разделе «Создание общих типов записей NDEF» .