Статьи

NFC Android: чтение тега NDEF

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

  • Форум NFC известного типа [NFC RTD]
  • Тип носителя, как определено в RFC 2046
  • Абсолютный URI, как определено в RFC 3986
  • NFC Forum внешний тип [NFC RTD]

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

1
short tnf = record.getTnf();

Сравнивая tnf (Type Name Format) со всеми возможными комбинациями, мы можем узнать тип записи. В приведенном выше коде запись является экземпляром NdefRecord.

Структура записи NDEF

Прежде чем анализировать, как читать содержимое NDEF, важно знать структуру записей NDEF. На рисунке ниже показана структура:

NFC

Самый важный байт (7-й) — это байт «Начало сообщения» , этот байт равен 1, если это начальное сообщение, в противном случае он равен нулю. 6-й байт — это конец сообщения , этот байт равен 1, если эта запись является конечной записью, в противном случае — 0. SR — короткая запись, и это 1, если это короткая запись. Эта информация важна, если мы хотим правильно обрабатывать данные тега NDEF.

Мы знаем, что Android SDK предоставляет метод getPayload() который возвращает массив байтов, представляющих содержимое тега. Мы должны прочитать и проанализировать этот массив для извлечения информации.

Начнем с самой простой структуры записи: текстовая запись.

Хорошо известный тип: текстовая запись

Это самый простой тип записи, и мы начнем отсюда. Мы знаем из спецификации, как читать полезную нагрузку. Первое, что мы должны прочитать — это заголовок (payload [0]) и проанализировать его. Наиболее значимый байт (7-й) представляет кодировку текста:

1
2
3
4
5
6
7
byte status = payload[0];
int enc = status & 0x80; // Bit mask 7th bit 1
String encString = null;
if (enc == 0)
  encString = "UTF-8";
else
  encString = "UTF-16";

Бит с 5 по 0 представляет длину языка:

1
int ianaLength = status & 0x3F; // Bit mask bit 5..0

Теперь мы готовы прочитать «настоящий» контент:

1
2
3
4
5
6
7
try {
  String content = new String(payload, ianaLength + 1, payload.length - 1 - ianaLength, encString);
  record.payload = content;
}
catch(Throwable t) {
    t.printStackTrace();
}

Давайте предположим, что мы создаем простой текстовый / обычный контент с помощью Surviving с Android. Теперь, если мы прочитаем содержание, которое мы имеем:

1
02 65 6e 53 75 72 76 69 76 69 6e 67 20 77 69 74 68 20 61 6e 64 72 6f 69 64

Это полезная нагрузка, и если мы проанализируем ее, мы получим:

nfc_ndef_text_record

NFC Forum внешний тип

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

1
2
3
4
StringBuffer pCont = new StringBuffer();
for (int rn=0; rn < payload.length;rn++) {
   pCont.append(( char) payload[rn]);
}

Вся полезная нагрузка — содержание.

NDEF Умный постер

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

Первое, что нам нужно сделать, это прочитать заголовок:

1
2
3
4
int[] result = getHeader(payload); // 0 = MB, 1 = ME, 2 = SR
int numLenByte = 1;
if (result[2] == 0)
   numLenByte = 4; // This is not a Short Record. Length = 4 byte

Теперь мы знаем, сколько байтов равно длине полезной нагрузки, а затем мы должны получить длину:

1
2
3
String byteLen = "";
for (int p = 2; p <= 2 + numLenByte - 1; p++)
   byteLen = byteLen + payload[p]; // We simply append the bytes

Затем мы читаем тип записи, чтобы узнать, как с ним обращаться:

1
2
int pos = 2 + numLenByte;
int type = payload[pos];

Мы можем проанализировать полезную нагрузку в соответствии с типом записи:

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
if (type == 'U') {
  RDTUrl url = new RDTUrl();
  result = getHeader(payload);
  url.MB = result[0];
  url.ME = result[1];
  url.SR = result[2];
  url.prefix = payload[pos];
  Log.d("NFC", "Action:" + "0x" + Integer.toHexString(url.prefix));
  url.url = new String(payload, pos + 1, Integer.parseInt(byteLen) - 1);
  Log.d("NFC", "Content:" + url.url);
  record.records.add(url);
}
else if (type == 'T') {
   RDTTextRecord text = new RDTTextRecord();
   result = getHeader(payload);
   text.MB = result[0];
   text.ME = result[1];
   text.SR = result[2];
   int len = payload[pos];
   Log.d("Nfc", "Lan len ["+len+"]");
   text.language = "";
   for (int i = pos + 1; i <= pos + len; i++)
     text.language = text.language + (char) payload[i];
     Log.d("Nfc", "Lang ["+text.language+"]");
     text.payload = new String(payload, pos + len + 1, Integer.parseInt(byteLen) - len - 1);
     Log.d("NFC", "Content:" + text.payload);
     record.records.add(text);
   }
}

И, наконец, мы переходим к следующей части сообщения:

1
payload = Arrays.copyOfRange(payload, pos + Integer.parseInt(byteLen), payload.length);

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

Давайте предположим, что у нас есть тег NDEF, имеющий ссылку на этот веб-сайт и текстовую часть, например, выживающую.

Полезная нагрузка:

1
ffffff91 1 19 55 1 73 75 72 76 69 76 69 6e 67 77 69 74 68 61 6e 64 72 6f 69 64 2e 63 6f 6d 51 1 c 54 2 65 6e 73 75 72 76 69 76 69 6e 67

Теперь, если мы запустим наше приложение, мы получим:

nfc_ndef_text_url

Мы можем предположить, что теперь у нас есть тег, содержащий номер телефона с меткой:

nfc_ndef_text_tel

Ссылка: NFC Android: ознакомьтесь с тегом NDEF от нашего партнера JCG Франческо Аццолы в блоге Surviving w / Android .