Источник: Разработка NFC приложений для Android

NFC (near field communication) – стандартизированная технология обмена данными на короткие расстояния, позволяющая осуществлять взаимодействия между двумя электронными устройствами простым и интуитивно понятным способом. Например, с помощью оснащенного NFC смартфона вы можете делать покупки, раздавать визитные карты, скачивать купоны на скидки и так далее. Множество новых применений для NFC будет найдено в ближайшее время.
Эта статья описывает технологии, использующие NFC и способы их применения на сегодняшний день. Также показано, как использовать NFC в Android приложениях и, наконец, приведены два примера NFC приложений с исходными кодами.

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

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

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

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

Режим эмуляции 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 устройства могут легко взаимодействовать друг с другом напрямую, обмениваясь небольшими файлами. Для установления Bluetooth/WiFi соединения необходимо обменяться XML файлом специального формата. В этом режиме ЭБ не используется.

Режим записи/чтения

В данном режиме NFC устройство может читать и записывать NFC метки. Хорошим примером применения является чтение информации с оснащенных NFC «умных» постеров.

Введение в разработку 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 обнаруживает метку, система доставки автоматически создает пакет целевой информации. Если в телефоне имеется несколько приложений, способных работать с этой целевой информаций, пользователю будет показано окно с предложением выбрать одно из списка. Система доставки меток определяет три типа целевой информации, в порядке убывания приоритета: NDEF_DISCOVERED, TECH_DISCOVERED, TAG_DISCOVERED.
Здесь мы используем целевой фильтр для работы со всеми типами информации начиная с TECH_DISCOVERED до ACTION_TECH_DISCOVERED. Файл nfc_tech_filter.xml используется для всех типов, определенных в метке. Подробности можно найти в документации Android. Рисунок ниже показывает схему действий при обнаружении метки.

Пример 1. Разработка NFC приложения для чтения/записи меток.

Следующий пример показывает функции чтения/записи NFC метки. Для того, чтобы получить доступ к аппаратной части NFC и корректно обрабатывать NFC информацию, объявите эти позиции в файле AndroidManifest.xml.

<uses-permission android:name="android.permission.NFC" />

Минимальную версию SDK, которую должно поддерживать ваше приложение — 10, объявите об этом в файле AndroidManifest.xml

<uses-sdk android:minSdkVersion="10"/>
In the onCreate function,you can apply the NfcAdapter:
public void onCreate(Bundle savedInstanceState) {
……
adapter = NfcAdapter.getDefaultAdapter(this);
……
}  

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

@Override
        protected void onNewIntent(Intent intent){
            if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
            mytag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  // get the detected tag
            Parcelable[] msgs =
    intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
                NdefRecord firstRecord = ((NdefMessage)msgs[0]).getRecords()[0];
                byte[] payload = firstRecord.getPayload();
                int payloadLength = payload.length;
                int langLength = payload[0];
                int textLength = payloadLength - langLength - 1;
                byte[] text = new byte[textLength];
                System.arraycopy(payload, 1+langLength, text, 0, textLength);
                Toast.makeText(this, this.getString(R.string.ok_detection)+new String(text), Toast.LENGTH_LONG).show();
                        }
        }

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

    If (mytag==Null){
        ……
    }
    else{
    ……
    write(message.getText().toString(),mytag);
    ……
    }
        private void write(String text, Tag tag) throws IOException, FormatException {
            NdefRecord[] records = { createRecord(text) };
            NdefMessage  message = new NdefMessage(records);
    // Get an instance of Ndef for the tag.
            Ndef ndef = Ndef.get(tag); // Enable I/O
            ndef.connect(); // Write the message
            ndef.writeNdefMessage(message); // Close the connection
            ndef.close();
        }

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

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

В этом примере для чтения мы будем использовать карты MifareClassic и соответствующий им тип метки. Карты MifareClassic широко используются для различных нужд, таких как идентификация человека, автобусный билет и т.д. В традиционной карте MifareClassic область хранения разбита на 16 зон, в каждой зоне 4 блока, и каждый блок может хранить 16 байт данных.
Последний блок в зоне называется трейлером и используется обычно для хранения локального ключа чтения/записи. Он содержит два ключа, А и В, 6 байт длиной каждый, по умолчанию забитые 00 или FF, в зависимости от значения MifareClassic.KEY_DEFAULT.
Для записи на карту Mifare вы, прежде всего, должны иметь корректное значение ключа (что играет защитную роль), а также успешно пройти аутентификацию.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
        package="org.reno"  
        android:versionCode="1"  
        android:versionName="1.0" >   
        <uses-permission android:name="android.permission.NFC" />   
        <uses-sdk android:minSdkVersion="14" />   
        <uses-feature android:name="android.hardware.nfc" android:required="true" />   
        <application  
            android:icon="@drawable/ic_launcher"  
            android:label="@string/app_name" >   
            <activity  
                android:name="org.reno.Beam"  
                android:label="@string/app_name"  
                android:launchMode="singleTop" >   
                <intent-filter>   
                    <action android:name="android.intent.action.MAIN" />   

                    <category android:name="android.intent.category.LAUNCHER" />   
                </intent-filter>   
                <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>  
        </application>   
    </manifest>

res/xml/nfc_tech_filter.xml:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 
        <tech-list> 
           <tech>android.nfc.tech.MifareClassic</tech> 
        </tech-list> 
    </resources>

Пример того, как читать карту MifareClassic:

    private void processIntent(Intent intent) {    
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);   
        for (String tech : tagFromIntent.getTechList()) {   
            System.out.println(tech);   
        }   
        boolean auth = false;   
        MifareClassic mfc = MifareClassic.get(tagFromIntent);   
        try {   
            String metaInfo = "";   
            //Enable I/O operations to the tag from this TagTechnology object.   
            mfc.connect();   
            int type = mfc.getType(); 
            int sectorCount = mfc.getSectorCount();   
            String typeS = "";   
            switch (type) {   
            case MifareClassic.TYPE_CLASSIC:   
                typeS = "TYPE_CLASSIC";   
                break;   
            case MifareClassic.TYPE_PLUS:   
                typeS = "TYPE_PLUS";   
                break;   
            case MifareClassic.TYPE_PRO:   
                typeS = "TYPE_PRO";   
                break;   
            case MifareClassic.TYPE_UNKNOWN:   
                typeS = "TYPE_UNKNOWN";   
                break;   
            }   
            metaInfo += "Card type:" + typeS + "n with" + sectorCount + " Sectorsn, "  
                    + mfc.getBlockCount() + " BlocksnStorage Space: " + mfc.getSize() + "Bn";   
            for (int j = 0; j < sectorCount; j++) {   
                //Authenticate a sector with key A.   
                auth = mfc.authenticateSectorWithKeyA(j,   
                        MifareClassic.KEY_DEFAULT);   
                int bCount;   
                int bIndex;   
                if (auth) {   
                    metaInfo += "Sector " + j + ": Verified successfullyn";   
                    bCount = mfc.getBlockCountInSector(j);   
                    bIndex = mfc.sectorToBlock(j);   
                    for (int i = 0; i < bCount; i++) {   
                        byte[] data = mfc.readBlock(bIndex);   
                        metaInfo += "Block " + bIndex + " : "  
                                + bytesToHexString(data) + "n";   
                        bIndex++;   
                    }   
                } else {   
                    metaInfo += "Sector " + j + ": Verified failuren";   
                }   
            }   
            promt.setText(metaInfo);   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
    }

Об авторах

Songyue Wang и Liang Zhang — инженеры в Intel Software and Service Group, разрабатывающие мобильные приложения, в том числе и для Android, и оптимизирующие их под платформу х86.

Комментарии запрещены.