Android平台中,NFC应用的类型和NFC三种运行模式有关,我们先来看一个使用NFCR/W模式读取NFC Tag的示例。
1、NFC R/W模式示例
根据前文对NFC基础知识的介绍可知,和R/W模式相关的应用场景就是使用者利用NFC手机(充当NFC Reader的角色)来读取目标NFC Tag中的信息。Android平台为NFC R/W模式设计了“Tag分发系统”(Tag Dispatch System)的机制,描述了NFC系统模块如何向应用进程分发与目标NFC Tag相关的Intent(该Intent中包含了Tag中的数据或是一个代表目标NFC Tag的Tag对象)。
①、NFC Tag分发系统
Tag分发系统的工作机制如图8-27所示。
:-:
图8-27 Tag分发系统的工作机制
Tag分发系统的工作机制如下。
- 当本机扫描到一个NFC Tag后,NFC系统模块将首先尝试直接读取该Tag中的数据。
- 如果这些数据封装在NDEF消息中并且能映射成Android系统直接支持的数据类型(目前仅支持MIME和URI这两大类数据类型,详情见表8-11),则NFC系统模块将发送一个ACTION_NDEF_DISCOVERED的Intent给那些注册了对ACTION_NDEF_DISCOVERED通知感兴趣的Activity。如果找到目标Activity,则将此Intent(携带Tag中的NDEF消息和一个代表该NFC Tag的Tag对象)派发给它。如果NFC系统模块没有找到目标Activity,则将尝试发送一个ACTION_TECH_DISCOVERED的Intent(包含一个代表目标NFC Tag的Tag对象)。
- 如果NFC Tag中的数据不能转换成系统直接支持的类型,或者NFC Tag中的数据没有使用NDEF消息格式或者没有目标Activity对ACTION_NDEF_DISCOVERED通知感兴趣,则NFC系统模块将发送一个ACTION_TECH_DISCOVERED的Intent(包含一个Tag对象)。如果找到对该Intent感兴趣的Activity,则此Intent将派发给它。
- 如果没有Activity对ACTION_TECH_DISCOVERED感兴趣,则NFC系统模块将最后尝试发送一个ACTION_TAG_DICOVERED的Intent(包含一个Tag对象)。如果有对ACTION_TAG_DISCOVERED感兴趣的Activity,则此Intent将派发给它。
上述的Tag分发系统看起来很复杂,实际上其核心内容可概况成三个步骤。
- 步骤1如果目标NFC Tag包含了系统支持的NDEF消息,则NFC系统模块将直接把这个NDEF消息分发给感兴趣的Activity。如果有目标Activity,则直接分发给它,否则转步骤2。分支转换的判断标准是NFC Tag是否包含了系统支持的NDEF消息以及同时是否有目标Activity注册了ACTION_NDEF_DISCOVERED通知。
- 步骤2如果目标NFC Tag包含了系统不支持的NDEF消息或者步骤1中没有目标Activity,则NFC系统模块将尝试分发一个ACTION_TECH_DISCOVERED通知。NFC系统模块在分发此通知时,将首先分析目标NFC Tag所支持的Tag Technology(它代表目标NFC Tag所使用的技术,详情见下文分析),然后寻找注册了支持这些Tag Technology的目标Activity并将Intent分发给它。如果没有合适的目标Activity,则转入步骤3。
- 步骤3NFC系统模块将分发ACTION_TAG_DISCOVERED通知给注册了对该通知感兴趣的目标Activity。
除了Tag分发系统外,Android系统还有一个“前台分发系统”(Foreground Dispatch System)。其规则和Tag分发系统类似,二者区别主要集中在选择目标Activity上。
- Tag分发系统中,Activity在其AndroidManifest.xml中设置Intent分发条件,即设置对应的IntentFilter。在这种分发系统中,不考虑目标Activity是否在前台还是后台。只要找到目标Activity,NFC系统就会启动它。
- 前台分发系统中,当前活跃(即所谓的前台)的Activity在其启动过程中设置Intent分发条件。如果NFC Tag满足前台Activity设置的分发条件,NFC系统模块首先会把Intent分发给前台这个Activity。当该Activity退到后台时,它需要取消前台分发功能,即它不再是目标Activity。
简而言之,前台分发系统只检查当前显示的Activity是否满足分发条件,而Tag分发系统则会搜索系统内所有满足条件的Activity。
②、Tag分发通知
下面我们分别来看看这三个不同作用的Tag分发通知。
- 1)ACTION_NDEF_DISCOVERED:由上文可知,NFC系统模块首先尝试将NFC Tag中的数据映射成系统直接支持的数据格式。表8-11列举了Android系统直接支持的NFC Forum数据格式。
:-:
由表8-11可知,如果NFC Tag中数据格式能映射成功,则NFC系统模块将发送一个ACTION_NDEF_DISCOVERED Intent给目前Activity,而该Intent将包含此NFC Tag中的NDEF消息。
除了NFC Forum定义的数据类型外,Android还新增了一个名为AAR(Android Application Record)的数据类型,它其实是在一个NDEF的消息中封装了某个应用的package名。对AAR来说,分发系统的工作流程如下。
- 分发系统首先尝试使用IntentFilter来寻找目标Activity。如果和IntentFilter匹配的Activity同时和AAR匹配(即二者的package名一样),就启动该Activity。
- 如果Activity跟AAR不匹配,或者是有多个Activity能够处理该Intent,或者是没有能够处理该Intent的Activity,NFC系统模块将启动由AAR指定的应用程序。
- 如果系统中没有安装该AAR对应的应用程序,NFC系统模块将从Google Play下载该应用程序。
AAR的好处是能让某个公司部署的NFC标签只能由该公司开发的客户端(通过在NDEF中设置AAR)来处理。后文代码分析时候读者还将看到上述AAR的工作流程。
- 2)ACTION_TECH_DISCOVERED:如果系统不能映射NFC Tag中的数据,我们该如何处理呢?
提示 ACTION_TECH_DISCOVERED触发的另一个原因是没有Activity对ACTION_NDEF_DISCOVERED感兴趣。
该问题的直观答案就是应用程序自己去读取并解析Tag中的数据。不过,由于NFC Tag的类型有四种之多,甚至同一个厂商还生产了基于不同底层协议的NFC Tag,导致Android系统无法提供一种通用的接口来操作所有种类的NFC Tag。为了解决此问题,Android提供了一个名为"android.nfc.tech"的Java包来帮助应用程序操作对应的NFC Tag。表8-12为android.nfc.tech包
中的几个重要成员类。
:-:
NFC系统模块将在ACTION_TECH_DISCOVERED Intent中携带一个Tag对象,应用程序可调用该Tag对象的getTechList来获取该Tag所使用的Technology。注意,一个Tag可能同时支持表8-12中多种Technology。例如图8-28所示为笔者测试北京市公交卡时所得到的Tag Technology信息。
图8-28 北京市公交卡Tag Technology示例
由图8-28可知,北京市公交卡同时支持MifareClassic、NfcA和NdefFormatable这三种类型的Tag Technology。应用程序接着可根据目标NFC Tag所支持的Tag Technology来创建表8-12中的对象来和NFC Tag交互。
特别注意 严格来说,表8-12中所列的类不仅仅是用来读写对应类型的NFC Tag,它还支持一些控制操作以至于能在NFC Tag上实现一些特定的协议。以北京市公交卡为例,其内部肯定有一个相关的协议使得应用程序可通过这些协议来完成公交卡充值,付费等操作。“小木公交”软件即可读取多个城市公交卡的信息,读者不妨下载试试。
- 3)ACTION_TAG_DISCOVERED:如果目标NFC Tag不属于表8-12中的一种,则NFC系统模块将发送ACTION_TAG_DISCOVERED Intent并携带一个Tag对象传递给感兴趣的Activity。Activity将根据Tag的ID(调用Tag的getId函数)或该Tag使用的技术(调用Tag的getTechList)来创建合适的处理对象。
下面通过一个示例来了解上述三种通知的用法。
③、示例分析
本节将通过一个前台分发示例来看看应用程序如何处理上述三种Intent。
ForegroundDispatch.java::onCreate
在上述onCreate函数中,同时监听了三种Tag Intent通知,最终效果如下。
- 如果目标Tag中包含MIME类型的NDEF消息,则Tag分发系统将给我们传递一个ACTION_NDEF_DISCOVERED Intent。
- 如果目标Tag使用的Tag Technolog为NfcF或MifareClass,则Tag分发系统将给我们传递一个ACTION_TECH_DISCOVERED Intent。
- 最后,Tag分发系统将不满足上述条件的其他所有Tag通过ACTION_TAG_DISCOVEREDIntent传递给我们。
接下来,由于本例使用了NFC的前台分发系统,故需要将onCreate中设置的配置信息传递给NFC系统模块,相关代码如下所示。
ForegroundDispatch.java::onResume
当NFC系统模块扫描到一个NFC Tag时,前台分发系统通过将触发ForegroundDispatch这个Activity的onNewIntent函数,该函数的代码如下所示。
ForegroundDispatch.java::onNewIntent
当ForegroundActivity退出时,需要在onPause函数中停止使用前台分发系统,相关代码如下所示。
通过上述介绍可知,在R/W模式中:
- 如果应用程序仅用于读取NFC Tag中所包含的数据,则应尽量通过注册ACTION_NDEF_DISCOVERED通知来获取自己感兴趣的数据。
- 如果应用程序希望能和NFC Tag交互以实现自己的一套协议或者希望能直接读写NFCTag,则可通过注册ACTION_TECH_DISCOVERED通知来获得代表NFC Tag的Tag对象。应用程序接着要根据该Tag使用的Technology来构造对应的TagTechnology对象来操作此NFC Tag。
- 如果应用程序处理的NFC Tag不满足表8-12中的一种,则需要监听ACTION_TAG_DISCOVERED通知,然后再构造自己的TagTechnology对象来操作此NFC Tag。
提示 关于Android中NFC的分发系统,请读者阅读Android SDK关于NFC的介绍,相关资料位于http://developer.android.com/guide/topics/connectivity/nfc/nfc.html。
2、NFC P2P模式示例
Android平台中的NFC P2P模式使用了前文介绍的SNEP协议。在SNEP协议基础上,Android设计了"Android Beam"技术架构,该架构使得NFC客户端程序能非常容易得在两个NFC设备间传递NDEF消息。
提示 除了SNEP外,Android还定义了一个与之类似的NPP(Ndef Push Protocol),该协议对应的服务端SAP为0x10,服务名为"com.android.npp"。Android Beam中,系统首先使用SNEP进行传输。如果一些老旧的设备不支持SNEP,则系统将使用NPP。
和R/W模式一样,Android Beam的使用也需要绑定到一个Activity中,下面我们直接通过一例子来看看如何使用Android Beam。
①、发送端处理
Beam.java::onCreate
如图8-29所示,左图为数据发送通知框。在Android平台中,两个互相靠近的NFC设备都会弹出类似左图这样的数据发送通知框。至于最终是谁来发送数据则需要用户点击触摸屏来决定。当对端设备收到示例Beam所发送的NDEF消息后,对端设备的NFC系统模块将解析该NDEF消息然后通过Tag分发系统或前台分发系统找到目标Activity。图8-29右图即为对端设备收到本例中Beam发送的数据后的处理结果。
:-:
图8-29 Beam示例截图
下面来看createNdefMessage函数,它实现了CreateNdefMessageCallback接口类。当用户在图8-29左图中点击触摸屏后,NFC系统模块将通过该函数获取应用程序需要发送的NDEF消息,其代码如下所示。
Beam.java::createNdefMessage
当本机NFC系统模块成功发送了NDEF消息后,onNdefPushComplete将被调用以通知数据发送的情况。在Beam示例中,该函数的代码如下所示。
Beam.java::onNdefPushComplete
②、接收端处理
当对端NFC设备接收到此NDEF消息时,将通过Tag分发系统来处理它。Beam示例在其AndroidManifest.xml设置了如图8-30所示的IntentFilter。
:-:
图8-30 Beam设置的IntentFilter
根据前文对Tag分发系统的介绍,对端设备的Beam将被启动。启动过程中几个重要函数的代码如下所示。
Beam.java::onNewIntent/onResume/processIntent
至此,通过一个示例展示了NFC客户端程序如何使用Android Beam技术。Android Beam的本质是利用NFC P2P模式的SNEP协议在两个NFC设备间传递NDEF消息。除了NfcAdapter的setOnNdefPushCompleteCallback函数外,NfcAdapter还有其他方式能发送NDEF消息。关于这部分内容,请读者务必阅读SDK中的介绍
(http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#p2p)。
另外,Android Beam除了能发送NDEF消息外,它还支持发送URI Scheme为"file"或"content"类型的数据,也就是文件或数据库中的内容。这些数据的量可能比较大,所以Android Beam将使用Handover并选择蓝牙来传输它们。
3、NFC CE模式示例
在Android平台中,NFC CE的使用比较特殊,主要体现在两点。
- Android SDK没有直接提供Card Emulation相关的API,但Android系统内部提供了一个名为"com.android.nfc_extras.jar"的Java动态库。在这个动态库中,Android封装了和CE相关的API。应用程序需要主动加载这个nfc_extras库才能使用CE模式。
- 由于CE通常用于支付等方面的工作,所以Android系统在nfc_extras动态库的使用上有着非常严格的权限管理。
下面介绍相关知识。
①、nfc_extras和nfcee_access.xml
根据上文所述,应用程序如何才能使用CE模式呢?我们先来看看如何在应用程序中使用nfc_extras动态库。图8-31所示的AndroidManifest.xml指明了必要的做法。
:-:
图8-31 AndroidManifest.xml设置
使用NFC CE的应用必须通过标签申明"android.permission.NFC"权限。同时还需通过申明使用动态库"com.android.nfc_extras"。这样,当应用程序运行时,系统会为它加载com.android.nfc_extras.jar包。该包对应的文件位于/system/framework目录下。
接着,客户端在需要使用nfc_extras API的Java类文件中通过import语句导入相关的类,如图8-32所示。
:-:
图8-32 nfc_extras动态库相关类
nfc_extras主要包含三个类,其用法将留待下节的示例代码中再来介绍。
客户端导入相关类后,下一步要解决的问题就是编译。由于Android SDK没有提供这些类,故需要手动解决编译问题。目前有两种方法解决。
一种方法是为应用程序编写Android.mk,然后添加以下内容。
该方法要求在Android源码下编译此应用程序。
另外一种方式是在Eclipse中为应用程序手动添加一个编译路径,如图8-33所示。
:-:
图8-33 Eclipse设置编译路径
图8-33中,笔者在测试示例中添加了nfc_extras动态库(即classes-full-debug.jar,它是Android系统编译nfc_extras时生成的中间JAR文件包)。
注意 无论哪种方法,都需要有Android系统的源码。
通过上述步骤,应用程序可编译成功,但它此时依然没有权限操作NFC CE。这是因为在Android系统中,除了"android.permission.NFC"权限外,NFC系统模块针对NFC CE这种重要运行模式还需要检查另外一个权限,即客户端程序的签名信息。只有拥有系统指定签名的应用程序才能使用NFC CE模式。
Android系统所指定的签名信息都保存在/etc/nfcee_access.xml文件中,图8-34所示为Galaxy Note 2中该文件的内容。
:-:
图8-34 nfcee_access.xml示例
图8-34中所示nfcee_access.xml包含三个签名(由signer标签指定,图中由黑框标示)。
- 第一个签名为Google Wallet相关应用拥有。
- 第二个签名为Samsung Wallet相关应用拥有。
- 第三个签名为笔者测试时用的签名信息。
签名信息检查的工作流程如下。
- 客户端程序开展CE相关操作前,必须先获得一个NfcAdapterExtras对象。
- 在获取该对象时,NFC系统模块先检查应用程序是否拥有"android.permission.NFC"权限,接着检查该应用程序的签名信息。只有调用程序的签名信息在nfcee_access.xml有记录,该应用才能得到一个NfcAdapterExtras对象。
提示 显然,nfcee_access.xml要么由手机厂商在出厂前设置,要么在root的手机上修改。了解上述知识后,通过一个示例来介绍nfc_extras相关的API及使用方法。
②、示例分析
本节使用的示例通过修改AndroidBeamDemo而来。
示例
复制
从上述示例代码来看,nfc_extras的API似乎比较简单。但对一个实际应用程序而言,其最大难度却在于处理相关命令上。由于不同NFC芯片以及所使用的SE不同,其定义的命令也不尽相同。关于SE命令的格式,读者可参考ISO 7816-4规范。
至此,我们对NFC CE进行了一些简单介绍,并围绕nfc_extras动态库的使用进行了相关讨论。根据笔者的研究,CE模式的内容远比R/W及P2P模式复杂。为此,强烈建议读者继续阅读参考资料[23]、[24]和[25]。