title: Android上的NFC開發(fā)
date: 2017-09-02 16:34:33
tags: NFC
摘要:
最近了解了一下NFC相關(guān)開發(fā),做一下記錄哦
正文:
NFC開發(fā)
NFC是Near Field Communication縮寫,即近距離無線通訊技術(shù)。NFC提供了一種簡單、觸控式的解決方案,可以讓消費(fèi)者簡單直觀地交換信息、訪問內(nèi)容與服務(wù)。NFC技術(shù)允許電子設(shè)備之間進(jìn)行非接觸式點(diǎn)對(duì)點(diǎn)數(shù)據(jù)傳輸,在十厘米(3.9英吋)內(nèi),交換數(shù)據(jù),其傳輸速度有106Kbit/秒、212Kbit/秒或者424Kbit/秒三種。
工作模式
卡模式
例如QuickPay。
數(shù)據(jù)在支持NFC的手機(jī)或其它電子設(shè)備中,可以簡單理解成“刷手機(jī)”。本質(zhì)上就是將支持NFC的手機(jī)或其它電子設(shè)備當(dāng)成借記卡、公交卡、門禁卡等IC卡使用。基本原理是將相應(yīng)IC卡中的信息憑證封裝成數(shù)據(jù)包存儲(chǔ)在支持NFC的外設(shè)中 。在使用時(shí)還需要一個(gè)NFC射頻器(相當(dāng)于刷卡器)。將手機(jī)靠近NFC射頻器,手機(jī)就會(huì)接收到NFC射頻器發(fā)過來的信號(hào),在通過一系列復(fù)雜的驗(yàn)證后,將IC卡的相應(yīng)信息傳入NFC射頻器,最后這些IC卡數(shù)據(jù)會(huì)傳入NFC射頻器連接的電腦,并進(jìn)行相應(yīng)的處理(如電子轉(zhuǎn)帳、開門等操作)。
虛擬卡模式(Virtual Card Mode)
這些芯片的內(nèi)部實(shí)際上運(yùn)行一個(gè)微型JAVA虛擬機(jī),,一個(gè)卡(手機(jī))可以裝多個(gè)applet,它有自己的證書,上層協(xié)議是卡片與芯片內(nèi)置的Applet進(jìn)行加密交互。通過OAT(空中發(fā)卡)業(yè)務(wù)可以實(shí)現(xiàn)把服務(wù)器中的Applet二進(jìn)制文件下載到手機(jī)芯片中(是不是有點(diǎn)像HotFix技術(shù)?),俗稱“卡包”。
主機(jī)卡模式(Host Card Mode)
Google提供HCE的方法,在Android開發(fā)中集成HostAptuService 服務(wù),就可以實(shí)現(xiàn)卡模擬。
CardReader < -- >NFCAdapter <----> HostApduService <----> BackendCloudServer
讀卡器模式
數(shù)據(jù)在NFC芯片中,可以簡單理解成“刷標(biāo)簽”。本質(zhì)上就是通過支持NFC的手機(jī)或其它電子設(shè)備從帶有NFC芯片的標(biāo)簽、貼紙、名片等媒介中讀寫信息。通常NFC標(biāo)簽是不需要外部供電的。當(dāng)支持NFC的外設(shè)向NFC讀寫數(shù)據(jù)時(shí),它會(huì)發(fā)送某種磁場,而這個(gè)磁場會(huì)自動(dòng)的向NFC標(biāo)簽供電。
sample:公交卡充值查詢
點(diǎn)對(duì)點(diǎn)模式
該模式與藍(lán)牙、紅外差不多,用于不同NFC設(shè)備之間進(jìn)行數(shù)據(jù)交換,不過這個(gè)模式已經(jīng)沒有有“刷”的感覺了。其有效距離一般不能超過4厘米,但傳輸建立速度要比紅外和藍(lán)牙技術(shù)快很多,傳輸速度比紅外塊得多,如過雙方都使用Android4.2,NFC會(huì)直接利用藍(lán)牙傳輸。這種技術(shù)被稱為AndroidBeam。所以使用androidBeam傳輸數(shù)據(jù)的兩部設(shè)備不再限于4厘米之內(nèi)。
<img src="http://upload-images.jianshu.io/upload_images/5443336-a02d3bde35fa7d5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/>
NFC的傳輸層協(xié)議棧
JAVA中 byte 是有符號(hào)一字節(jié)的,而char是編碼過的兩個(gè)字節(jié)的;C中byte(也就是#define byte (unsigned char))是無符號(hào)一個(gè)字節(jié)的,而char是有符號(hào)一字節(jié)的; 為了方便,我們?nèi)渴褂胋yte與16進(jìn)制進(jìn)行表示
APDU的數(shù)據(jù)結(jié)構(gòu),本質(zhì)是一種編碼。
發(fā)送的數(shù)據(jù)報(bào)
NFC讀卡器的Android開發(fā)
配置權(quán)限與feature
<!--Use NFC feature and Permissions-->
<uses-permission android:name="android.permission.NFC"/>
<uses-feature
android:name="android.hardware.nfc"
android:required="true"/>
注意配置Activity的模式為singleTop,配置Activity不可轉(zhuǎn)變屏幕(防止Intent丟失,支付寶也是這樣做的),配置NFCTech過濾器,配置Intent接收器。
<activity
android:name=".NfcReaderActivity"
android:launchMode="singleTop"
android:alwaysRetainTaskState="true"
android:label="@string/title_activity_nfcscanner">
\<!--nfc filter-->
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
配置NFC卡片技術(shù)過濾器,每個(gè)卡對(duì)應(yīng)一個(gè) <tech-list>
/<!--file nfc_tech_filter.xml -->
<resources>
/<!--重慶一卡通-->
<tech-list>
<!--ISO 14443-4-->
<tech>android.nfc.tech.IsoDep</tech>
<!--ISO 14443-3A-->
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
</resources>
獲取Intent
在Activity 中針對(duì)收到的Intent進(jìn)行處理,Oncreate進(jìn)入的情況。
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfcscanner);
handleIntent(getIntent());
}
@Override protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
//獲取到Intent的Action,注意多打Log
Log.d(TAG, "handleIntent: " + intent.getAction());
if (!intent.getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
Log.d(TAG, "handleIntent: no valid action");
return;
}
//獲取Tag對(duì)象
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//獲取卡ID,這個(gè)ID一般沒什么用,有可能是卡自動(dòng)生成的
Log.d(TAG, "Id:" + Util.byteArraytoHexString(tag.getId()));
//NFC卡片所支持的技術(shù)標(biāo)準(zhǔn)
Log.d(TAG, "TechList:" + Arrays.toString(tag.getTechList()));
}
I/O處理
我們通過Tag獲取到I/O對(duì)象,通過字節(jié)流進(jìn)行處理,這里的例子按照如下的標(biāo)準(zhǔn)進(jìn)行解析
ISO-DEP (ISO 14443-4) 注意它與ISO-7816也是兼容的
目前最完善的代碼如下,基于RxAndroid進(jìn)行異步處理,可以處理所有的異常,注意這里的subscriber是一個(gè)RxAndroid的回掉接口,它處理成功,下一個(gè),異常這三個(gè)回掉
public final Observable<ResponseAPDU> getResponseAPDUObservable(final Tag tag,
final byte... bytes) {
return Observable.create(new Observable.OnSubscribe<ResponseAPDU>() {
@WorkerThread @Override public void call(Subscriber<? super ResponseAPDU> subscriber) {
if (tag == null) {
subscriber.onError(new NullPointerException(
"Tag is null,try again to turn on NFC and keep your card close to your phone!"));
return;
}
if (bytes == null || bytes.length == 0) {
subscriber.onError(
new NullPointerException("apdu is null or empty, cheak your command!"));
return;
}
IsoDep iso = IsoDep.get(tag);
byte[] result_all;
//NfcA iso = NfcA.get(tag);
if (iso == null) {
subscriber.onError(new NullPointerException("Tech was not enumerated in NfcTechList"));
return;
}
iso.setTimeout(5000);//ms
try {
if (!iso.isConnected()) {
iso.connect();
} else {
iso.close();
iso.connect();
}
result_all = iso.transceive(bytes);
subscriber.onNext(ResponseAPDU.createFromPdu(result_all));
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
} finally {
if (iso != null) {
try {
iso.close();
} catch (IOException ignored) {
subscriber.onError(ignored);
}
}
}
}
});
}
在UI線程下,進(jìn)行如下的調(diào)用
//RxAndroid的回調(diào)
Subscriber subscriber = new Subscriber<ResponseAPDU>() {
@Override public void onCompleted() {
Log.d(TAG, "onCompleted:");
}
@Override public void onError(Throwable e) {
mTextView_response.setText(e.getMessage());
}
@Override public void onNext(ResponseAPDU responseAPDU) {
Log.d(TAG, "onNext:" + responseAPDU.toString());
mTextView_response.setText(responseAPDU.toString());
}
};
getResponseAPDUObservable(tag, data)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
這里還有很大的優(yōu)化空間,比如加入 keep-alive 機(jī)制,避免每讀寫一次都重新打開關(guān)閉流,當(dāng)然這個(gè)需要與場景進(jìn)行適配
封裝成高級(jí)對(duì)象(DTO)
通過以上步驟,我們能夠得到一個(gè)簡單的基于字節(jié)流的NFC讀取器,但是更加進(jìn)一步的開發(fā)(比如查詢命令,SDK等)需要卡廠商提供內(nèi)部公開的SDK,均有私鑰的,就不能具體寫了。有興趣的可以去網(wǎng)上搜索PBOC2.0,網(wǎng)上有泄露的代碼,貌似是GPL協(xié)議,各位慎重學(xué)一下。這個(gè)開發(fā)一點(diǎn)都不難,封裝好I/O后,寫改刪查即可,主要難點(diǎn)在能夠與公交公司談攏(人家說不定看不上你,當(dāng)然這個(gè)活不是程序員干的),以及談攏后與對(duì)方開發(fā)進(jìn)行聯(lián)調(diào)。
NFC卡模擬
從12年開始,隨著O2O的發(fā)展,國外的品牌手機(jī)都開始加入了對(duì)SE芯片的支持,中華酷聯(lián)也開始加入了對(duì)SE卡的支持。