Чтение тегов NFC с Android —

Чтение тегов NFC с Android - NFC

Зачем? что нам это даёт?

Помимо уже очевидных сценариев: пропуска, оплата и проездные — есть приложения, которые умеют класть деньги на карту «Тройка» и другие транспортные карты.

Есть приложение — Считыватель банковских карт. Оно например может показать последние транзакции по карте. Не уверен, что это очень этично, но приложение лежит в Play Market.

Кстати, многих интересует, почему Google и Apple Pay не работают с картами Мир? Дело не в технических особенностях. Просто платежная система не договорилась с сервисами. Платить можно через свое приложение под Android — Мир Pay. Правда оно глючное, а под iPhone его вовсе нет!

Кстати, лайфхак. Если у вашего Android нет NFC, но платить очень хочется, что делать? Можно положить карточку под чехол. Обращайтесь. Правда толстые чехлы могут не пропускать волны даже встроенного NFC — так что проверяйте.

Мы уже поговорили про устройства, но есть вторая важная часть — это NFC метки. Они бывают двух видов.

  1. Те, на которые можно записывать информацию. Они выглядят как маленькие наклейки. Обычно доступный объем памяти — около 700 байт. Подобные выпускала компания Sony.


Тут можно хранить кучу всего, например:

Такую метку прочитает любой телефон с NFC.

Что делать, если у вас нет NFC меток? Их можно заказать, стоят копейки.

Но можно взять обычную банковскую карту или транспортную, вроде «Тройки». Это закрытые для записи метки. Типичный пример — ваша банковская карта. На них нельзя ничего записать.

Но ваш смартфон можно запрограммировать на любые действия, когда к нему приложат такую штуку.

Если у вас Android, можно поставить приложение например MacroDroid или NFC ReTag. В них можно назначать примерно такие же действия на NFC-теги. Включать/выключать Wi-Fi и звонок, запускать приложения, включать ночной режим. Например, можно сделать так, что когда вы кладете телефон на карту «Тройка», у вас автоматом открывается канал Droider. Рекомендую!

Кстати вот так выглядит содержимое «Тройки».

А еще можете почитать на

Введение в разработку nfc под android

Android поддерживает NFC с помощью двух пакетов: android.nfc и android.nfc.tech.

Основными классами в android.nfc являются:

NfcManager: Устройства под Android могут быть использованы для управления любыми обнаруженными NFC адаптерами, но поскольку большинство Android устройств поддерживают только один NFC адаптер, NfcManager обычно вызывается с getDefaultAdapter для доступа к конкретному адаптеру.

NfcAdapter работает как NFC агент, подобно сетевому адаптеру на ПК. С его помощью телефон получает доступ к аппаратной части NFC для инициализации NFC соединения.

NDEF: Стандарты NFC определяют общий формат данных, называемый NFC Data Exchange Format (NDEF), способный хранить и передавать различные типы объектов, начиная с MIME и заканчивая ультра-короткими RTD-документами, такими как URL. NdefMessage и NdefRecord – два типа NDEF для определенных NFC форумом форматов данных, которые будут использоваться в коде-примере.

Tag: Когда устройство Android обнаруживает пассивный объект типа ярлыка, карты и т.д., он создает объект типа «метка», помещая его далее в целевой объект и в заключении пересылая в соответствующий процесс.

Пакет android.nfc.tech также содержит множество важных подклассов. Эти подклассы обеспечивают доступ к функциям работы с метками, включающими в себя операции чтения и записи. В зависимости от используемого типа технологий, эти классы разбиты на различные категории, такие как NfcA, NfcB, NfcF, MifareClassic и так далее.

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

Здесь мы используем целевой фильтр для работы со всеми типами информации начиная с TECH_DISCOVERED до ACTION_TECH_DISCOVERED. Файл nfc_tech_filter.xml используется для всех типов, определенных в метке. Подробности можно найти в

. Рисунок ниже показывает схему действий при обнаружении метки.

Smart poster, что это?


Итак, Smart Poster это особый вид NFC записи, который может содержать в себе одновременно ссылку, текстовый заголовок (на нескольких языках), графические иконки в форматах

jpeg

или

png

и даже анимированную иконку в формате mpeg.

Помимо этого могут присутсвовать еще два поля:

Action

— подсказывает телефону какое приложение и как нужно открыть для обработки uriRecord

Size

— простое целочисленное число, отображающее размер загружаемого контента по ссылке.

Что такое nfc tag?

Чтение тегов NFC с Android -NFC Tag

— это и есть наша пассивная метка. На картинке показан внешний вид того как она может выглядеть, то есть, как правило, это наклейка из плотной бумаги, в которую встроен микрочип и антенна из фольги. NFC теги бывают нескольких типов, от типа также зависит максимальный допустимый размер данных. Я являюсь счастливым обладателем нескольких меток Type 2, 192 байта, привезенных с

Qt Developer Days 2021

. Чтож, 192 байта не густо, но для наших экспериментов хватит.

Что такое nfc?

NFC — это сокращение от Near Field Communication . Это международный стандарт для бесконтактного обмена данными. В отличие от широкого спектра других технологий, таких как беспроводная локальная сеть и Bluetooth, максимальное расстояние между двумя устройствами составляет 10 см.

Разработка стандарта началась в 2002 году компаниями NXP Semiconductors и Sony.NFC Forum , консорциум из более чем 170 компаний и членов, в который входят Mastercard, NXP, Nokia, Samsung, Intel и Google, разрабатывает новые спецификации с 2004 года.

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

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

Feature detection #

Feature detection for hardware is different from what you’re probably used to. The presence of NDEFReader tells you that the browser supports Web NFC, but not whether the required hardware is present. In particular, if the hardware is missing, the promise returned by certain calls will reject. I’ll provide details when I describe NDEFReader.

if('NDEFReader'in window){/* Scan and write NFC tags */}

Label и textfield

Чтение тегов NFC с Android -

Для отображения текста можно использовать компонент

Label

— это не более чем стилизованная обертка над стандартным

Text

элементом.

Label { id: touchLabel
..... font.pixelSize: 60 text: qsTr("Touch a tag")
}

Чтение тегов NFC с Android -TextField — это продвинутая обертка над стандартным TextInput

TextField { id: textEdit
..... placeholderText: qsTr("Text") text: "yandex"
}

Listview

Чтение тегов NFC с Android -

На главном экране мы можем наблюдать список действий которые можно выполнить, подобный список делается так:

ListView { id: actionList
.... delegate: ListDelegate { anchors { left : parent.left leftMargin: 20 } onClicked: { pageStack.push(Qt.resolvedUrl(model.source)) } MoreIndicator { anchors { verticalCenter: parent.verticalCenter right: parent.right rightMargin: 30 } } } model: ListModel { ListElement { title: "Read Tag" subtitle: "" source: "ReadPage.qml" } ...... }
}

Элемент

ListView

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

delegate

— делегат для отрисовки одного элемента списка и

model

— модель данных для списка.


Пакет

com.nokia.extras

содержит уже готовый компонент

ListDelegate

для создания простого делегата. Элемент

ListModel

позволяет задать простую модель данных. А

ListElement

— ни что иное как одна запись этой модели.

Ndef обнаружил намерение

Как я упоминал ранее, у Обнаруженного Технического Намерения есть второй самый высокий приоритет. Однако, поскольку наше приложение будет поддерживать только NDEF, вместо этого мы можем использовать обнаруженное намерение NDEF, которое имеет более высокий приоритет. Мы можем снова удалить список технологий и заменить IntentFilter следующим.

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

Qmlregistertype

Как вы, конечно, помните после того как метка была прочтена мы испускаем сигнал содержащий объект с полученными данными. Для того чтобы этот объект стал доступен в QML мы должны зарегистрировать класс этого объекта в QML

qmlRegisterType<DataContainer>();
qmlRegisterType<UriDataContainer>();
qmlRegisterType<TextDataContainer>();
qmlRegisterType<SmartPosterDataContainer>();


Вставив этот код в

main.cpp

, мы регистрируем классы данных для всех типов имеющихся у нас данных.

Однако, запрещаем создавать подобные объекты напрямую из QML.

Read and write a mime type record #

The mediaType property of a MIME type record represents the MIME type of the NDEF record payload so that data can be properly decoded. For instance, use JSON.parse to decode JSON text and an Image element to decode image data.

functionreadMimeRecord(record){
console.assert(record.recordType ==="mime");
if(record.mediaType ==="application/json"){
const textDecoder =newTextDecoder();
console.log(`JSON: ${JSON.parse(decoder.decode(record.data))}`);
}
elseif(record.mediaType.startsWith('image/')){
const blob =newBlob([record.data],{ type: record.mediaType });
const img =newImage();
img.src =URL.createObjectURL(blob);
document.body.appendChild(img);
}
else{
// TODO: Handle other MIME types.
}
}

To write a MIME type record, pass an NDEF message dictionary to the NDEFReader write() method. The MIME type record contained in the NDEF message is defined as an object with a recordType key set to «mime», a mediaType key set to the actual MIME type of the content, and a data key set to an object that can be either an ArrayBuffer or provides a view on to an ArrayBuffer (e.g. Uint8Array, DataView).

const encoder =newTextEncoder();
const data ={
firstname:"François",
lastname:"Beaufort"
};
const jsonRecord ={
recordType:"mime",
mediaType:"application/json",
data: encoder.encode(JSON.stringify(data))
};

const imageRecord ={
recordType:"mime",
mediaType:"image/png",
data:await(awaitfetch("icon1.png")).arrayBuffer()
};

const ndef =newNDEFReader();
await ndef.write({ records:[jsonRecord, imageRecord]});

Read and write a smart poster record #

A smart poster record (used in magazine advertisements, fliers, billboards, etc.), describes some web content as an NDEF record that contains an NDEF message as its payload. Call record.toRecords() to transform data to a list of records contained in the smart poster record.

Проблемы NFC:  оплата моб телефона мтс банковской картой через интернет без комиссии на телефон

It should have a URL record, a text record for the title, a MIME type record for the image, and some custom local type records such as «:t», «:act», and «:s» respectively for the type, action, and size of the smart poster record.

Local type records are unique only within the local context of the containing NDEF record. Use them when the meaning of the types doesn’t matter outside of the local context of the containing record and when storage usage is a hard constraint. Local type record names always start with :

functionreadSmartPosterRecord(smartPosterRecord){
console.assert(record.recordType ==="smart-poster");
let action, text, url;

for(const record of smartPosterRecord.toRecords()){
if(record.recordType =="text"){
const decoder =newTextDecoder(record.encoding);
text = decoder.decode(record.data);
}elseif(record.recordType =="url"){
const decoder =newTextDecoder();
url = decoder.decode(record.data);
}elseif(record.recordType ==":act"){
action = record.data.getUint8(0);
}else{
// TODO: Handle other type of records such as `:t`, `:s`.
}
}

switch(action){
case0:
// Do the action
break;
case1:
// Save for later
break;
case2:
// Open for editing
break;
}
}

To write a smart poster record, pass an NDEF message to the NDEFReader write() method. The smart poster record contained in the NDEF message is defined as an object with a recordType key set to «smart-poster» and a data key set to an object that represents (once again) an NDEF message contained in the smart poster record.

Read and write a text record #

The text record data can be decoded with a TextDecoder instantiated with the record encoding property. Note that the language of the text record is available through its lang property.

functionreadTextRecord(record){
console.assert(record.recordType ==="text");
const textDecoder =newTextDecoder(record.encoding);
console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
}

To write a simple text record, pass a string to the NDEFReader write() method.

const ndef =newNDEFReader();
await ndef.write("Hello World");

Read and write a url record #

Use TextDecoder to decode the record’s data.

functionreadUrlRecord(record){
console.assert(record.recordType ==="url");
const textDecoder =newTextDecoder();
console.log(`URL: ${textDecoder.decode(record.data)}`);
}

To write a URL record, pass an NDEF message dictionary to the NDEFReader write() method. The URL record contained in the NDEF message is defined as an object with a recordType key set to «url» and a data key set to the URL string.

Read and write an absolute-url record #

The absolute-URL record data can be decoded with a simple TextDecoder.

functionreadAbsoluteUrlRecord(record){
console.assert(record.recordType ==="absolute-url");
const textDecoder =newTextDecoder();
console.log(`Absolute URL: ${textDecoder.decode(record.data)}`);
}

To write an absolute URL record, pass an NDEF message dictionary to the NDEFReader write() method. The absolute-URL record contained in the NDEF message is defined as an object with a recordType key set to «absolute-url» and a data key set to the URL string.

Read and write an empty record #

An empty record has no payload.

To write an empty record, pass an NDEF message dictionary to the NDEFReader write() method. The empty record contained in the NDEF message is defined as an object with a recordType key set to «empty».

const emptyRecord ={
recordType:"empty"
};

const ndef =newNDEFReader();
await ndef.write({ records:[emptyRecord]});

Setcontextproperty

Для того, чтобы наш QML код мог видеть наш класс для управления чтением и записью, мы должны сообщить декларативному движку о существовании объекта этого класса, поэтому в main.cpp мы пишем:

NfcManager *nfcManager = new NfcManager();
viewer->rootContext()->setContextProperty("NfcManager", nfcManager);

То есть мы создаем объект NfcManager и указываем движку, что мы должны иметь доступ к нему из QML.

Кстати в последнем обновлении QtSDK что-то сломали, и для того, чтобы этот код корректно заработал, надо применить workaround описанный в багтрекере.

Suggested use cases #

Web NFC is limited to NDEF because the security properties of reading and writing NDEF data are more easily quantifiable. Low-level I/O operations (e.g. ISO-DEP, NFC-A/B, NFC-F), Peer-to-Peer communication mode and Host-based Card Emulation (HCE) are not supported.

Examples of sites that may use Web NFC include:

Phone scanning several NFC tags
NFC inventory management illustrated

Terminology #

An NFC tag is a passive NFC device, meaning that is powered by magnetic induction when an active NFC device (.e.g a phone) is in proximity. NFC tags come in many forms and fashions, as stickers, credit cards, arm wrists, etc.

Photo of a transparent NFC tag
A transparent NFC tag

The NDEFReader object is the entry point in Web NFC that exposes functionality for preparing reading and/or writing actions that are fulfilled when an NDEF tag comes in proximity. The NDEF in NDEFReader stands for NFC Data Exchange Format, a lightweight binary message format standardized by the NFC Forum.

The NDEFReader object is for acting on incoming NDEF messages from NFC tags and for writing NDEF messages to NFC tags within range.

An NFC tag that supports NDEF is like a post-it note. Anyone can read it, and unless it is read-only, anyone can write to it. It contains a single NDEF message which encapsulates one or more NDEF records. Each NDEF record is a binary structure that contains a data payload, and associated type information.

Diagram of an NDEF message
Diagram of an NDEF message

Toolbar

Чтение тегов NFC с Android -

Для различных действий у мобильного приложения также может присутствовать

Toolbar

с иконками, мое приложение простое и тулбар на внутренних страницах содержит только кнопку назад

Page { id: readPage
..... tools: ToolBarLayout { ToolIcon { iconId: "toolbar-back" onClicked: { pageStack.pop() } } }
.....

Для того чтобы подключить тулбар к странице его надо присвоить свойству

tools

, которое по умолчанию равно

null

Архитектура технологии nfc

NFC основана на RFID технологии с частотой 13.56 МГц и рабочей дистанцией до 10 см. Скорость обмена данными составляет до 424 кб/сек. По сравнению с другими коммуникационными технологиями, основным преимуществом NFC является быстрота и простота использования. На рисунке ниже видно расположение NFC среди других коммуникационных технологий.

Технология NFC имеет три режима: эмуляция NFC-карты, пиринговый режим и режим чтения/записи.

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

Взаимодействие

Когда пользователь попадает на страницу для записи или чтения метки, мы должны выполнить следующий код:

Для чтения

function tagWasRead(container) { NfcManager.stopDetection() readPage.dataContainer = container pageStack.push(Qt.resolvedUrl("ReadResultPage.qml"), {dataContainer: readPage.dataContainer})
}
function readError(string) { errorBanner.text = string errorBanner.show()
}
Component.onCompleted: { NfcManager.tagReadFinished.connect(readPage.tagWasRead) NfcManager.accessError.connect(readPage.readError) NfcManager.setReadMode() NfcManager.startDetection()
}

Метод

Component.onCompleted

выполняется когда страница полностью создана. В этом методе мы цепляем обработчики для ошибки и для успешного результата к нашим сигналам из NfcManager (обратите внимание на синтаксис подключения С сигнала к QML слоту)

После, мы выставляем режим для чтения и сообщаем нашему менеджеру, что следует ожидать прикладывания тега.

Также обратите внимание на вызов push

pageStack.push(Qt.resolvedUrl("ReadResultPage.qml"), {dataContainer: readPage.dataContainer})

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

Чтение тегов NFC с Android -

пример:

.....
Label { id: rawDataLabel width: parent.width font.pixelSize: 30 font.family: "Courier New" text: readPage.dataContainer.rawHexData() wrapMode: Text.WrapAnywhere
}
.....

Для записи

function tagWasWritten() {
.....
}
function writeError(string) {
.....
}
Component.onCompleted: { NfcManager.tagWriteFinished.connect(writePage.tagWasWritten) NfcManager.accessError.connect(writePage.writeError) NfcManager.setWriteMode() NfcManager.setDataForWrite(writePage.text, writePage.uri) NfcManager.startDetection()
}

Очень похоже, не правда ли? Единственным отличием является вызов метода

setDataForWrite

, который передает данные для записи.

Добавление поддержки nfc в приложение

Мы начинаем с нового проекта и пустой деятельности. Важно выбрать минимальную версию SDK уровня 10, потому что NFC поддерживается только после Android 2.3.3. Не забудьте выбрать собственное имя пакета. Я выбрал net.vrallev.android.nfc.demo , потому что vrallev.net является доменом моего сайта, а другая часть относится к теме этого приложения.

Макет по умолчанию, сгенерированный Eclipse, для нас почти достаточен. Я только добавил идентификатор в TextView и изменил текст.

Проблемы NFC:  Карты копировать nfc — купите карты копировать nfc с бесплатной доставкой на АлиЭкспресс version

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

MainActivity должен состоять только из метода onCreate (). Вы можете взаимодействовать с оборудованием через класс NfcAdapter. Важно выяснить, является ли NfcAdapter нулевым. В этом случае устройство Android не поддерживает NFC.

Если мы запустим наше приложение сейчас, мы увидим текст, включен ли NFC или нет.

Запись

void NfcManager::setDataForWrite(const QString &text, const QString &uri)
{ m_textForWrite = text; m_uriForWrite = uri;
}

Этот метод должен вызываться перед попыткой записи для того, чтобы установить новые значения Uri и/или Text. Если его не вызвать на тег будут записаны предыдущие данные (такой подход пригодится, если нужно записать много однотипных тегов)

void NfcManager::writeTarget(QNearFieldTarget *target)
{ if (m_textForWrite.isEmpty() && m_uriForWrite.isEmpty()) return; m_cachedTarget = target; QNdefMessage message; if (!m_textForWrite.isEmpty() && !m_uriForWrite.isEmpty()) { NdefNfcSmartPosterRecord smartPosterRecord; smartPosterRecord.setTitle(m_textForWrite); smartPosterRecord.setUri(QUrl(m_uriForWrite)); message.append(smartPosterRecord); } else if (!m_textForWrite.isEmpty()) { QNdefNfcTextRecord textRecord; textRecord.setText(m_textForWrite); message.append(textRecord); } else { QNdefNfcUriRecord uriRecord; uriRecord.setUri(QUrl(m_uriForWrite)); message.append(uriRecord); } connect(target, SIGNAL(error(QNearFieldTarget::Error,QNearFieldTarget::RequestId)), this, SLOT(errorHandler(QNearFieldTarget::Error,QNearFieldTarget::RequestId))); connect(target, SIGNAL(ndefMessagesWritten()), this, SIGNAL(tagWriteFinished())); target->writeNdefMessages(QList<QNdefMessage>() << message);
}

Главный метод записи не сложнее чем метод чтения. В блоке условий мы просто выбираем вид записи. Если присутствует только Uri или Text, то создается соответствующий тип, если же заполнены оба поля то создается запись типа Smart Poster.


После этого мы снова подключаем обработчик ошибок. Но, обратите внимание, поскольку в бэкенде нам не нужно никакой логики обработки успешного завершения чтения, мы пробрасываем сигнал на сигнал, который в дальнейшем поймаем в QML.

Зарядка

Если к этому моменты вы решили, что все знаете об NFC и устали от этих унылых применений. То вот вам кое-что бомбическое.

Есть такая организация NFC Forum, которая сертифицирует NFC. Вообще у каждой технологии есть такая организация, и хорошо если она одна.

И вот на днях они выложили очередной апдейт стандарта. И знаете что? Теперь NFC поддерживает беспроводную зарядку. Да, по сути, это четвёртый режим работы.

Как спросите вы? Электромагнитная индукция, помните? При помощи нее.

К слову Qi-зарядка работает точно по такому же принципу. Только там катушка побольше.

Но есть одна проблема. Катушка у NFC маленькая, а значит и мощность зарядки маленькая — всего 1 Ватт.

Можно ли зарядить смартфон с такой скоростью? Не стоит даже пробовать. Впрочем, функцию для этого и не придумывали.

Основное назначение ровно противоположное — зарядка смартфоном других устройств. Это вроде реверсивной зарядки в Galaxy и других смартфонах. Например, можно подпитать сами беспроводные наушники, а не кейс от них. По сути, перед нами очень дешевая беспроводная зарядка, которая есть в любом смартфоне и которую легко вставить в любое умное устройство.

Кстати 1 Ватт это не то чтобы слишком мало. Для сравнения со всеми iPhone кроме 11 Pro, кладут 5-ваттную зарядку. А мощность обратной беспроводной зарядки в современных флагманах колеблется на отметке 5 или 7 Вт.

Но есть одно но — на текущих моделях эта фича не заработает. Смартфоны с с такой фишкой скорее всего начнут появляться через год-полтора. Так что ждите рекламу этой штуки от Samsung.

Заставляем приложение перехватывать обработку nfc tags


Итак, для того чтобы начать обрабатывать теги нам потребуется объект класса

QNdefManager

NfcManager::NfcManager(QObject *parent) : QObject(parent), m_manager(new QNearFieldManager(this)), m_cachedTarget(0), m_mode(NfcManager::Read)
{ connect(m_manager, SIGNAL(targetDetected(QNearFieldTarget*)), this, SLOT(targetDetected(QNearFieldTarget*))); connect(m_manager, SIGNAL(targetLost(QNearFieldTarget*)), this, SLOT(targetLost(QNearFieldTarget*))); m_manager->setTargetAccessModes(QNearFieldManager::NdefReadTargetAccess | QNearFieldManager::NdefWriteTargetAccess);
}

Создадим его в конструкторе нашего класса

NfcManager

, который мы будем использовать для работы с NFC. Нам обязательно надо подключить сигналы

targetDetectedtargetLost

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

Как работает nfc?


Вы наверняка знаете, что NFC расшифровывается как Near Field Communication или по-русски — связь ближнего действия.

Но это не обычная передача данных по радиоволне. В отличие от Wi-Fi и Bluetooth NFC устроен хитрее. В основе лежит электромагнитная индукция. Это очень крутая штука из школьной программы, напомню.

Идея в том, что вы берете один проводник, в котором нет электричества. И кладете рядом с ним второй проводник, в котором есть электричество. И знаете, что? В первом проводнике, где электричества не было, начинает течь ток!

Круто, да?

Когда мы впервые про нее узнали, подумали, что такое невозможно! Серьезно? Вы гоните! Пошли играть в Counter Strike, пацаны.

Ну так вот, когда вы подносите смартфон к какой-нибудь NFC метке без питания, этого крошечного электромагнитного поля от смартфона достаточно, чтобы внутри метки побежали электроны, и заработали микросхемы внутри неё.

Ах да. В каждой метке есть крошечная микросхема. Например, в банковских картах микрочип запускает даже простенькую версию Java. Каково?

Может быть вы ещё слышали аббревиатуру RFID. Её разработали лет на 30 раньше. Она расшифровывается как радиочастотная идентификация. И по сути только для идентификации и подходит. Во многих офисных центрах пропуска до сих пор с RFID.

Так вот NFC является продвинутой веткой стандарта RFID и читает часть таких меток. Но главное отличие в том, что NFC умеет еще и передавать данные, в том числе зашифрованные.

NFC работает на частоте 13,56 МГц, что позволяет развить неплохую скорость от 106 до 424 Кбит/с. Так что mp3-файл скачается за пару минут, но только на расстоянии до 10 см.

Физически NFC — это маленькая катушка. Например в Pixel 4 прикреплена к крышке и выглядит вот так.

А так в Xiaomi Mi 10 Pro:

И тут как раз пора поговрить о том, что умеет делать NFC?

Работа этой технологии и смежных, вроде RFID, описаны в стандарте ISO 14443.  Там еще много чего свалено в кучу: например, итальянский протокол Mifare и VME — это в банковских картах.

NFC — это своего рода USB Type-C в мире беспроводных технологий, если вы понимаете, о чем я.

Но главное вот что. NFC может работать в трех режимах:

  1. Активный. Когда девайс считывает или записывает данные с метки или карточки. Кстати, да, данные на NFC метки можно и записывать.
  2. Передача между равноправными устройствами. Это когда вы подключаете к смартфону беспроводные наушники или используете Android Beam — помните такое. Там по NFC происходило подключение, а сама передача файла шла уже по Bluetooth.
  3. Пассивный. Когда наше устройство прикидывается чем-то пассивным: платежной картой или проездным.

Зачем NFC, если есть Bluetooth и Wi-Fi, ведь у них и скорость, и радиус действия больше.


Бонусы NFC вот в чем:

  1. Мгновенное подключение — одна десятая секунды.
  2. Низкое энергопотребление — 15 мА. У Bluetooth до 40 мА.
  3. Теги не требуют собственного питания.
  4. И не столь очевидное — малый радиус действия, что необходимо для безопасности и оплаты.

Есть правда еще Bluetooth Low Energy, но это отдельная история.

Метки

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

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

Некорректный код:

Давайте рассмотрим некоторые примеры кода для Android, который мы пытаемся взломать. Далее будет представлена часть кода, считывающая информацию из NFC метки и записывающая данные в файл, имя которого берется из считанной информации. Как вы видите, значение переменной strfile1 является первой записью NDEF, какой бы она ни была.

Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage msg = (NdefMessage) rawMsgs[0];
String payload = new String(msg.getRecords()[0].getPayload());

String strfile1 = getApplicationContext().getFilesDir().getAbsolutePath() payload ; //is this bad? 🙂
File f1 = new File(strfile1);
FileWriter filewriter = new FileWriter(f1);
BufferedWriter out = new BufferedWriter(filewriter);
out.write(msg.getRecords()[1].getPayload());
out.close();

Давайте представим, что приложение хранит текстовый файл со списком SSH хостов, к которым оно подключается. В этом случае мы создадим NFC метку, первой записью которой будет путь к файлу, к которому мы хотим получить доступ (“SSH.txt”). Второе значение будет содержать данные, которые необходимо записать в этот файл (ваш SSH MiTM прокси-сервер). После прочтения вашей метки пользовательское подключение будет перенаправлено на вас.

Удачного хака!

Передний план отправка

Обратите внимание, что остается одна проблема. Когда наше приложение уже открыто и мы снова прикрепляем тег, приложение открывается второй раз вместо прямой доставки тега. Это не наше предполагаемое поведение. Вы можете обойти проблему с помощью Foreground Dispatch.

Вместо того, чтобы система распространила Намерение, вы можете зарегистрировать свою активность, чтобы получить метку напрямую. Это важно для определенного рабочего процесса, где нет смысла открывать другое приложение.

Я вставил объяснения в соответствующие места в коде.

Теперь, когда вы прикрепляете тег и наше приложение уже открыто, вызывается onNewIntent, и новая активность не создается.

Перехватчик


Теперь рассмотрим описанные слоты:

void NfcManager::targetDetected(QNearFieldTarget *target)
{ if (m_cachedTarget) delete m_cachedTarget; m_cachedTarget = target; if (m_mode == Read) readTarget(m_cachedTarget); if (m_mode == Write) writeTarget(m_cachedTarget);
}

При обнаружении тега мы на всякий случай сохраняем указатель на объект типа

Проблемы NFC:  Что это и как пользоваться

QNearFieldTarget

, который является программным интерфейсом к самому тегу.


Далее идут два условия и в зависимости от режима (чтение или запись) вызывают соответствующие методы обработки. С точки зрения красивой архитектуры, это не самое лучшее решение, но я сделал это преднамеренно, чтобы не усложнять код.

void NfcManager::targetLost(QNearFieldTarget *target)
{ m_cachedTarget = 0; target->deleteLater();
}

При потере тега, мы просто освобождаем занятые ресурсы.

Пишем свой класс для smart poster

Ниже я расскажу как создать свой тип

NDEF

записи на примере создания типа для Smart Poster записи.


Сразу оговорюсь, что мой тип упрощен. Он не поддерживает ни Action ни Size ни даже иконки, но он позволяет одновременно хранить текст и ссылку.

Так выглядит объявление для класса нашего Smart Poster’а:

class NdefNfcSmartPosterRecord : public QNdefRecord
{
public: Q_DECLARE_NDEF_RECORD(NdefNfcSmartPosterRecord, QNdefRecord::NfcRtd, "Sp", QByteArray()) void setTitle(const QString &title, const QString &locale = "en"); void setUri(const QUrl &uri); QString title(const QString &locale = "en") const; QUrl uri() const; //TODO: Add icon, action and size fields support
private: RecordPart readPart(int &offset) const;
};
Q_DECLARE_ISRECORDTYPE_FOR_NDEF_RECORD(NdefNfcSmartPosterRecord, QNdefRecord::NfcRtd, "Sp")

Итак, разработчики Qt Mobility уже позаботились о том, чтобы нам было проще жить, и создали два специальных макроса, которые выполняют всю самую черновую работу.

Параметрами для макросов служат: имя класса, тип записи (для Smart Poster’а это QNdefRecord::NfcRtd) и «Имя типа» — аббревиатура для распознавания в теге. А также последний параметр в Q_DECLARE_NDEF_RECORD являются данные для первоначальной инициализации данных, в нашем случае это пустой массив байт.

Теперь посмотрим на реализацию методов чтения и записи.

Простая структура для хранения разобранной части записи:

struct RecordPart { enum Type { Uri, Text, Action, Icon, Size, Unknown }; Type type; QString text; QString locale; // For text type quint8 prefix; // For Uri type RecordPart() : type(Unknown), text(QString()), locale(QString()), prefix(0) { }
};

Для начала рассмотрим методы для чтения:

Пример 2. разработка nfc-приложения, использующего карты mifareclassic

В этом примере для чтения мы будем использовать карты MifareClassic и соответствующий им тип метки. Карты MifareClassic широко используются для различных нужд, таких как идентификация человека, автобусный билет и т.д. В традиционной карте MifareClassic область хранения разбита на 16 зон, в каждой зоне 4 блока, и каждый блок может хранить 16 байт данных.


Последний блок в зоне называется трейлером и используется обычно для хранения локального ключа чтения/записи. Он содержит два ключа, А и В, 6 байт длиной каждый, по умолчанию забитые 00 или FF, в зависимости от значения MifareClassic.KEY_DEFAULT.

Для записи на карту Mifare вы, прежде всего, должны иметь корректное значение ключа (что играет защитную роль), а также успешно пройти аутентификацию.

Про apple

Что делать, если у вас iPhone? Все думают, что доступ к NFC закрыт на iPhone, но это не так. Начиная с iOS 11, то есть с 2021 года Apple открыла доступ для разработчиков. И уже есть множество приложух таких же как на Android. Например, NFC Tools.

Правда там остаются ограничения: транспортные и банковские карты, например, не сканируются. Нужны специальные метки, о которых мы уже говорили.

Что делать? В iOS 13 появилась Функция Команды (Siri). И вот она как раз имеет доступ к любым NFC-меткам. Так что тут можно настроить запуск музыки по карте «Тройка». Или включить умную лампочку. Или еще кучу всего. Команды — реально бомбическая штука. Не понимаю, почему в Android такого до сих пор нет.

Режим эмуляции nfc карты

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

ЭБ подключается к NFC контроллеру посредством шины SWP (Single Wire Protocol) или DCLB (Digital Contactless Bridge). Стандарты NFC определяют логический интерфейс между хостом и контроллером, позволяя им взаимодействовать через RF-поле. ЭБ реализуется с помощью встроенного приложения или компонента ОС.

Существует три варианта реализации ЭБ: можно встроить его в SIM-карту, SD-карту или в NFC чип.

Операторы связи, такие как CMCC (China Mobile Communication Corporation), Vodafone или AT&T обычно используют решение на SIM-карте, поощряя своих абонентов бесплатной заменой старых SIM-карт на новые, оснащенные NFC.

Фильтр намерений nfc

Есть три разных фильтра для тегов:

  1. ACTION_NDEF_DISCOVERED
  2. ACTION_TECH_DISCOVERED
  3. ACTION_TAG_DISCOVERED

Список отсортирован от самого высокого до самого низкого приоритета.

Что происходит, когда к смартфону прикрепляется метка? Если система обнаруживает тег с поддержкой NDEF, запускается намерение. Намерение ACTION_TECH_DISCOVERED запускается, если ни одно действие из какого-либо приложения не зарегистрировано для намерения NDEF или если тег не поддерживает NDEF.

Таким образом, это означает, что каждое приложение должно фильтроваться после намерения с наивысшим приоритетом. В нашем случае это намерение NDEF. Сначала мы реализуем намерение ACTION_TECH_DISCOVERED, чтобы выделить разницу между приоритетами.

Чтение


Теперь рассмотрим методы чтения тега:

void NfcManager::readTarget(QNearFieldTarget *target)
{ connect(target, SIGNAL(error(QNearFieldTarget::Error,QNearFieldTarget::RequestId)), this, SLOT(errorHandler(QNearFieldTarget::Error,QNearFieldTarget::RequestId))); connect(target, SIGNAL(ndefMessageRead(QNdefMessage)), this, SLOT(readRecords(QNdefMessage))); target->readNdefMessages();
}

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

После этого мы просто вызываем метод для чтения:

void NfcManager::readRecords(const QNdefMessage &message)
{ if (message.isEmpty()) return; QNdefRecord record = message.at(0); // Read only first readRecord(record);
}


Если чтение прошло успешно, то мы попадем в данный слот, где получим первую запись из списка присутсвующих на теге записей.

Да-да, по спецификации на теге может быть несколько записей, но как говорит документация, для

SymbianHarmattan

доступно чтение и запись только одной записи.

void NfcManager::readRecord(const QtMobility::QNdefRecord &record)
{ DataContainer *result = 0; if (record.isRecordType<QNdefNfcUriRecord>()) { QNdefNfcUriRecord uriRecord(record); result = new UriDataContainer(uriRecord.payload(), uriRecord.uri().toString()); } else if (record.isRecordType<QNdefNfcTextRecord>()) { QNdefNfcTextRecord textRecord(record); result = new TextDataContainer(textRecord.payload(), textRecord.text()); } else if (record.isRecordType<NdefNfcSmartPosterRecord>()) { NdefNfcSmartPosterRecord smartPosterRecord(record); result = new SmartPosterDataContainer(smartPosterRecord.payload(), smartPosterRecord.uri().toString(), smartPosterRecord.title()); } else { result = new DataContainer(record.payload()); } emit tagReadFinished(result);
}

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


На данный момент

Qt Mobility

из коробки поддерживает только два вида записей это ссылки (

Uri

) и текст (

Text

), к третьему типу —

Smart Poster

мы еще вернемся ниже.


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

QML

В конце вызывается сигнал, содержащий объект с данными. В дальнейшем мы будем ловить этот сигнал в QML.

Чтение данных из тега ndef

Последний шаг — прочитать данные из тега. Объяснения вставляются в соответствующие места в коде еще раз. NdefReaderTask — это закрытый внутренний класс.

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

/**

    * Background task for reading the data.

    *

    * @author Ralf Wondratschek

    *

    */

   private class NdefReaderTask extends AsyncTask<Tag, Void, String> {

       @Override

       protected String doInBackground(Tag… params) {

           Tag tag = params[0];

           Ndef ndef = Ndef.get(tag);

           if (ndef == null) {

               // NDEF is not supported by this Tag.

               return null;

           }

           NdefMessage ndefMessage = ndef.getCachedNdefMessage();

           NdefRecord[] records = ndefMessage.getRecords();

           for (NdefRecord ndefRecord : records) {

               if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {

                   try {

                       return readText(ndefRecord);

                   } catch (UnsupportedEncodingException e) {

                       Log.e(TAG, «Unsupported Encoding», e);

                   }

               }

           }

           return null;

       }

       private String readText(NdefRecord record) throws UnsupportedEncodingException {

           /*

            * See NFC forum specification for «Text Record Type Definition» at 3.2.1

            *

            * http://www.nfc-forum.org/specs/

            *

            * bit_7 defines encoding

            * bit_6 reserved for future use, must be 0

            * bit_5..0 length of IANA language code

            */

           byte[] payload = record.getPayload();

           // Get the Text Encoding

           String textEncoding = ((payload[0] & 128) == 0) ?

           // Get the Language Code

           int languageCodeLength = payload[0] & 0063;

           // String languageCode = new String(payload, 1, languageCodeLength, «US-ASCII»);

           // eg «en»

           // Get the Text

           return new String(payload, languageCodeLength 1, payload.length — languageCodeLength — 1, textEncoding);

       }

       @Override

       protected void onPostExecute(String result) {

           if (result != null) {

               mTextView.setText(«Read content: » result);

           }

       }

   }

Приложение теперь успешно читает содержимое.

Вывод

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

Если вы хотите продолжить разработку Android, ознакомьтесь с огромным набором полезных шаблонов приложений для Android на Envato Market. Или нанять разработчика Android на Envato Studio.

Заключение


Таким образом мы получили простое, но функциональное приложение для платформы MeeGo Harmattan. Впрочем, минимальными усилиями можно превратить его в приложение для Symbian. Насколько мне известно, некоторые телефоны на Symbian (

С7

например) также имеют встроенный NFC чип.

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

Оцените статью
NFC в смартфонах