Android系统编程入门系列之硬件交互——通信硬件NFC

上篇文章介绍了接入式USB硬件的简单使用,接下来将介绍不依赖物理连接的硬件通信了。本文的重点是近距离通信的硬件NFC。

NFC硬件

应用程序中可以通过NFC硬件读取或发送指定协议的技术实现,在Android10.0之前甚至可以封装大段的NDEF数据。另外,Android系统基于NFC的特性,结合移动设备的安全元件,构建了一层HCE架构,从而应用于安全级别较高的公交卡刷卡或付款交易等操作。

权限声明

要想使用NFC硬件的相关功能,必须要声明权限,在应用程序的清单文件中声明<uses-permission />标签,其中的android:name属性值为android.permission.NFC
当然,为了强调应用程序需要运行在支持NFC硬件的设备上,也可以在应用程序的清单文件中声明<uses-feature />标签,其中的android:name属性值为android.hardware.nfc

使用流程

由于NFC硬件是在近距离接触后即可触发,所以首先要在应用程序中确定触发NFC连接后要响应的界面Activity。在清单文件注册的<activity></activity>标签内部,增加意图过滤<intent-filter></intent-filter>标签,并在该标签内部添加<action />标签,并指定android:name属性值分别为android.nfc.action.NDEF_DISCOVERED作为NDEF数据传输行为、android.nfc.action.TECH_DISCOVERED作为技术标签行为或android.nfc.action.TAG_DISCOVERED作为普通行为。

上边指定的意图行为有三种方式,但是在连接NFC硬件后,系统将按照上述顺序优先启动对应的界面Activity。另外针对上述三种不同的意图行为,还要在清单文件下分别追加相应的配置。

在通过上述三种方式收到Intent意图启动的界面Activity中,可以通过getIntent()获取启动传入的Intent意图对象。在该对象中,可以接收其他NFC硬件输入的标签及相关内容。

默认功能

针对action值为android.nfc.action.TAG_DISCOVERED的配置,由于是优先级最低的意图行为,因此在上面两种意图行为及配置未调起时,就会调用该意图行为绑定的界面Activity。因此该意图行为下不需要其他的配置参数。

在启动的界面Activity中,调用Intent对象的getParcelableExtra(String name)方法,传入参数 name 值为NfcAdapter.EXTRA_TAG="android.nfc.extra.TAG",得到android.nfc.TagNFC标签类型的对象,该对象中记录了标签的基本信息。

NDEF数据传输功能

针对action值为android.nfc.action.NDEF_DISCOVERED的配置,还要声明要传输的数据类型。同样在意图过滤的<intent-filter></intent-filter>标签内部,添加<data />标签。可支持传输的数据类型可以使用的表示方式,包括 MIME TYPE 类型的结构,和 URI 结构,二选一皆可。在<data />标签中使用android:mimeType属性,可以设置 MIME TYPE 结构的数据类型,通常如属性值为text/plain的文本类型。另外,在<data />标签中使用android:schemeandroid:hostandroid:pathPrefix三个属性同时标注URI内容。

在启动的界面Activity中,调用Intent对象的getParcelableArrayExtra(String name)方法,传入参数 name 值为NfcAdapter.EXTRA_NDEF_MESSAGES="android.nfc.extra.NDEF_MESSAGES",得到Parcelable[]数组可以分别强转为android.nfc.NdefMessage用以保存消息内容的类型对象数组。当使用android.nfc.action.NDEF_DISCOVERED意图行为过滤的NFC硬件启动后,可通过该方法获取NDEF消息内容。

在应用程序内可以自定义NDEF消息体内容并发送。借助android.nfc.NdefRecordNFC记录类。在该类中有多个静态方法,包括createExternal(String domain, String type, byte[] data)创建有携带数据的NFC记录对象;createMime(String mimeType, byte[] mimeData)创建MIME类型的NFC记录对象;createTextRecord(String languageCode, String text)创建短文本内容的NFC记录对象;createUri (Uri uri)创建Uri类型的NFC记录对象等。

在创建NdefRecord对象之后,可以作为参数传入NdefMessageNFC消息类的构造方法中,从而创建NdefMessage对象,将该对象作为参数,传入NdefAdapter对象的setNdefPushMessage(NdefMessage message, Activity activity, Activity... activities)方法中,这样在当前参数 activity 中将会一直发送参数 message 中的内容。

从Android10.0即API级别29开始,NFC功能的NDEF消息传输功能就被废弃了,因此其相关功能方法在以后的版本中也将不再支持。

标签技术功能

针对action值为android.nfc.action.TECH_DISCOVERED的配置,还要通过资源文件声明要依赖的技术集。资源文件的声明是在注册的<activity></activity>标签内部,添加<meta-data />标签,同样设置其属性android:name值为android.nfc.action.TECH_DISCOVERED,更别忘了设置属性android:resource,其值为保存在 res/xml 目录下的资源文件。而资源文件的定义,是在 res/xml 路径下,定义 xml 格式的资源文件,在该文件中使用<tech-list></tech-list>标签作为最外层的集合,在该标签中包含了一堆使用<tech>标签定义的可支持技术。这些技术的定义均实现了android.nfc.tech.TagTechnology标签技术接口,在各种实现类中定义了相应技术的常量值。

在启动的界面Activity中,同样可以调用Intent对象的getParcelableExtra(String name)方法,传入参数 name 值为[NfcAdapter.EXTRA_TAG]得到TAGNFC标签类的对象。

另外可以通过其getTechList()方法,获取通过意图行为android.nfc.action.TECH_DISCOVERED启动的NFC硬件中所使用技术列表。其中系统支持的技术类型均实现了android.nfc.tech.TagTechnology标签技术接口。

在得到的TagNFC标签类的对象后,可以遵循相关技术类型对标签进行读写操作。对实现了TagTechnology接口的具体技术类中,可以调用静态方法get(Tag tag)得到具体的技术类对象,其参数即得到的TagNFC标签类对象。在实现TagTechnology接口的对象中,需要首先调用connect()建立连接;之后可以根据不同的技术实现,调用相关的读写操作方法;最终在操作结束后,调用close()断开连接即可。

HCE服务功能

该功能是由Android系统实现的一套在后台使用NFC进行交易等服务的架构。该架构主要依赖android.nfc.cardemulation.HostApduService抽象服务类。在自定义的服务中必须继承自HostApduService服务类,并实现其两个抽象方法,接收并响应数据的byte[] processCommandApdu(byte[] apdu, Bundle extras)方法,和NFC切换或关闭当前APDU连接时的void onDeactivated(int reason)方法。

在定义了继承自HostApduService的服务之后,需要在清单文件中注册该服务组件,在<service></service>标签中要设置android:exported属性值为true。同时在标签内嵌入<intent-filter></intent-filter>意图过滤标签中,设置行为值为HostApduService.SERVICE_INTERFACE="android.nfc.cardemulation.action.HOST_APDU_SERVICE"。另外要在标签内嵌入<meta-data />额外数据标签,设置其android:name属性值为HostApduService.SERVICE_META_DATA="android.nfc.cardemulation.host_apdu_service",设置其android:resource属性为包含 AID 群组的资源文件。

定义的 AID 群组,是由NFC卡提供的应用唯一标识,如用于支付的银联储蓄卡、公交卡等,每张卡都有唯一的16字节组成的AID。而在资源文件中可以定义该应用程序自定义的HostApduService服务中允许交易的 AID 群组,该群组的所有 AID 只要有一个 AID 连接当前设备的 NFC 硬件,都会唤起 HCE 服务。

在应用程序的资源文件 res/xml 目录下,可以创建自定义的资源文件以定义上述 AID 群组,在资源文件中根标签为<host-apdu-service></host-apdu-service>,在其中可以嵌入多组标签<aid-group></aid-group>用来标记 AID 群组,其中设置android:description属性值为字符串类型的群组介绍,还需要设置android:category属性值为android.nfc.cardemulation.CardEmulation.CATEGORY_PAYMENT="payment"作为支付使用、或者属性值为android.nfc.cardemulation.CardEmulation.CATEGORY_OTHER="other"。在该标签内部,就可以嵌入多组<aid-filter / >标签并设置其android:name属性值,用以标记当前群组下的 AID 值。