Android* 上的 NFC 应用开发与案例研究

介绍

NFC(近场通信)是一种基于标准的短距离无线连接技术,支持在电子设备间进行简单、直观的双向互动。 触摸 NFC 设备,并在两台 NFC 设备之间通信非常方便。 例如,借助智能手机集成的 NFC 技术,您可以轻松触控手机来购买物品、分享名片、下载打折券等。 您将会看到许多基于 NFC 的新用法在未来开发。

本文介绍了当前市场基于 NFC 的技术和使用模式。 接着介绍了如何在 android 应用中使用 NFC。 最后介绍了两个案例研究,以帮助了解如何开发基于 NFC 的读/写应用。

NFC 技术架构

NFC 基于 RFID 技术,频率为 13.56 MHz,并采用 10 cm 的典型操作距离。数据交换速率为 424 比特/秒。与其他的通信技术相比,NFC 的最大优势是速度快且易于使用。 下图对 NFC 与其他通信技术进行了比较。

图 1: 短距离通信技术比较

 

NFC 技术有三种模式: NFC 卡仿真模式,对等模式和读/写模式,如下图所示。

图 2: NFC 协议家族

 

在卡仿真模式中,NFC 可模拟 RFID 集成电路(IC)卡并带有一个安全模块 — 支持用户安全购买。 在对等模式中,您可以通过 NFC 连接在不同的 NFC 设备之间共享信息,如名片。 此外,您也可以通过 NFC 连接快速设置 WiFi* 或蓝牙* 连接,并通过 WiFi 或蓝牙连接传输大型文件。 在读/写模式中,您可以使用启用了 NFC 的设备来读取 NFC 标签和启动智能任务。

以下将对每种模式分别进行详细介绍。

NFC 卡仿真模式

NFC 模式通常包括两部分: NFC 控制器和安全组件(SE)。 NFC 控制器负责通信。 SE 负责加密和解密敏感数据。

图 3: NFC 硬件组件

 

SE 通过 SWP(单线路协议)或 DCLB(数字非接触式桥)总线与 NFC 控制器进行连接。 NFC 标准为主机和控制器之间的逻辑接口下定义,以便允许它们通过射频场(RF-field)通信。 内置应用或小型操作系统执行 SE,后者负责加密和解密敏感数据。

执行 SE 的三种解决方案:

  • 嵌入 SIM 卡
  • 嵌入 SD 卡
  • 嵌入 NFC 芯片

图 4: 三种 NFC SE 解决方案

通常电信运行商,如 CMCC(中国移动)、Vodafone 和 AT&T,更倾向于使用基于 SIM 卡的解决方案,它们鼓励其用户用全新的启用了 NFC 的 SIM 卡免费替换旧 SIM 卡。

NFC 对等模式

使用 NFC 的两台设备可直接通信并轻松分享小型文件,如名片。 此外,启用 NFC 的两台设备也可以彼此共享 configured .xml 文件,并建立蓝牙/WiFi 连接来共享大型文件。 在该模式中,无需 SE 模块。

NFC 读/写模式

在该模式中,NFC 主机可以读/写 NFC 标签。 从智能海报读取有用信息

就是一个好例子。 用户可以访问链接来查看广告和下载打折券。

图 5: NFC 读/写模式

Android NFC 开发介绍

Android 可为 NFC 提供两个文件包: android.nfc 和 android.nfc.tech。

android.nfc 包中的主类是:

NfcManager: 用户可以使用 Android 设备来管理所有显示的 NFC 适配器,但是因为大部分 Android 设备仅支持一个 NFC 适配器,一般直接使用 getDefaultAdapter 调用 NfcManager 来获取特定的手机适配器。

NfcAdapter: 它起到 NFC 代理的作用,类似于电脑中安装的网络适配器;手机可以通过它使用 NFC 硬件以初始化 NFC 通信。

NDEF: NFC 标准可定义名为“NFC 数据交换格式(NFC Data Exchange Format,NDEF)”的通用数据格式,它可存储和传输各种项目,从 MIME 类型的对象到极短的 RTD 文档,如 URL。 NdefMessage 和 NdefRecord 是面向 NFC 论坛定义的数据格式的两种 NDEF,将用于样本代码。

标签: Android 定义其代表被动对象,如标签、卡等。 当设备检查出一个标签时, Android 将创建一个标签对象,然后将其放至 Intent 对象,最后将其发送至适当的 Activity。

android.nfc.tech 程序包还包括许多重要的子类。 该子类可提供对标签技术特性的访问,包括读写操作。 根据使用的技术类型,这些子类可划分为不同的类别,如: NfcA、NfcB、NfcF、MifareClassic 等。

当手机开启 NFC 时,检测到 TAG 后,TAG 分配系统将自动创建意向 NFC TAG 信息软件包。 如果手机有一个以上的应用可以处理该意向,弹出文本框将会要求用户选择要执行的 TAG 活动。 TAG 分配系统可定义三种类型的意向。 它按照降序排列:

NDEF_DISCOVERED, TECH_DISCOVERED, TAG_DISCOVERED

此处,我们使用了操作意向过滤器类型处理从 TECH_DISCOVERED 至 ACTION_TECH_DISCOVERED 的所有类型。 文件 nfc_tech_filter.xml 用于文件 TAG 中定义的类型。 如欲了解更多详细信息,请参见 Android 文档。 下图展示了手机检测到 TAG 时的匹配流程 Activity。

图 6: 检测到 NFC 标签的流程

案例研究: 开发基于 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 的值之前,您必须知道是否检测到标签,然后才能将信息写入 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();

	    }

根据从标签中读取到的信息,您可以执行更多操作,如启动智能任务、访问网站等。

案例研究: 开发使用 MifareClassic 卡的基于 NFC 的应用

在该演示中,我们在数据读取测试中使用了 Mifare 卡并使用了卡的 TAG 类型 — MifareClassic。 MifareClassic 卡可在许多场景中通用,如 ID 卡、总线卡等。 传统 MifareClassic 卡的存储空间可分为 16 个区域(Sector),每个区域有四个数据块(Block),每个数据块可以存储 16 字节的数据。

每个区域的最后一个数据块称 Trailer,主要用于为数据的读和写存储本地数据块密钥。 它有两种密钥: A 和 B,每种密钥的长度为 6 字节,其默认值通常是由 MifareClassic.KEY_DEFAULT 定义的 full-key FF 或 0。

因此,当编写 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();   

	        }   

	    }   

	

总结

配备了 NFC 的智能手机支持个人随时随地获取所需的内容,而无需被大量的票根或停车证包围。 该技术甚至支持与朋友取得联系以分享信息、玩游戏和传输数据。 NFC 致力于在工作和娱乐两个方面都取得进步,这是取得成功和推动我们未来生活的关键因素。

关于作者

Songyue Wang 和 Liang Zhang 是英特尔软件和服务事业部的应用工程师,专注于移动应用支持领域,主要负责为 x86 设备进行 Android 应用开发和优化以及 Web HTML5 应用开发。

有关编译器优化的更完整信息,请参阅优化通知