背景
当前Android NFC手机基本都能支持基于Host的卡模拟(HCE),其实现原理上需要读卡端和模拟端互相通过校验特定AID来实现通信,这个特性就让手机模拟的普通HCE卡无法被其他手机上通用的NFC三方APP直接读取到数据,比如NFC手机通过HCE模拟出一张银行卡,只有对该HCE银行卡AID 支持的POS机或特殊定制APP才可以与该HCE 银行卡通信,完成交易。其他三方APP由于不知道该卡AID,都是无法直接与之通信的。
鉴于上面的特殊,这样限制了HCE被普通用户使用。比如我们期望自己手机通过HCE分享出BT,WIFI秘钥,或分享的联系人等信息 能够被其他手机的NFC三方APP直接读取数据。为实现这样的目的,我们就需要基于HCE实现一个Ndef格式的标签卡。因为Ndef格式是NFC联盟定义的一种NFC Data Exchange Format,NFC三方APP基本可以直接读取。
实现HCE模拟Ndef标签卡
1. 首次Android studio创建一个新的HceNdefTag APP,并保证可以正常编译出APK。
2. 在APP内创建NdefHceService.java并继承HostApduService,之后覆写相关抽象方法。
package com.example.hcendeftag;
import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
public class NdefHceService extends HostApduService {
@Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
return new byte[0];
}
@Override
public void onDeactivated(int reason) {
}
}
3.实现Ndef主卡模拟服务实现,并在其定义简单的Ndef消息。
package com.example.hcendeftag;
import android.content.ComponentName;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
import android.util.Log;
import java.util.Arrays;
public class NdefHceService extends HostApduService {
// source: https://github.com/TechBooster/C85-Android-4.4-Sample/blob/master/chapter08/NdefCard/src/com/example/ndefcard/NdefHostApduService.java
public static final ComponentName COMPONENT = new ComponentName("com.example.hcendeftag", NdefHceService.class.getName());
private final static String TAG = "NfcTest_NdefHostApduService";
private static final byte[] SELECT_APPLICATION = {
(byte) 0x00, // CLA - Class - Class of instruction
(byte) 0xA4, // INS - Instruction - Instruction code
(byte) 0x04, // P1 - Parameter 1 - Instruction parameter 1
(byte) 0x00, // P2 - Parameter 2 - Instruction parameter 2
(byte) 0x07, // Lc field - Number of bytes present in the data field of the command
(byte) 0xD2, (byte) 0x76, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01,
(byte) 0x01, // NDEF Tag Application name D2 76 00 00 85 01 01
(byte) 0x00 // Le field - Maximum number of bytes expected in the data field of
// the response to the command
};
private static final byte[] SELECT_CAPABILITY_CONTAINER = {
(byte) 0x00, // CLA - Class - Class of instruction
(byte) 0xa4, // INS - Instruction - Instruction code
(byte) 0x00, // P1 - Parameter 1 - Instruction parameter 1
(byte) 0x0c, // P2 - Parameter 2 - Instruction parameter 2
(byte) 0x02, // Lc field - Number of bytes present in the data field of the command
(byte) 0xe1, (byte) 0x03 // file identifier of the CC file
};
private static