https://blog.csdn.net/zy00000000001/article/details/71183262
文章整理总结java层,NFC读取和写入Tag的流程。
整体的时序图:
1、读取Tag的流程
NfcService启动完成后,会通过NfcService中的applyRouting方法设置对应的Discovery,
也就是NCI的
2103的命令(此处不了解不影响后面),根据设置的命令参数来决定设备是处于listen模式,
还是polling模式。
当处于polling模式下会检测那个Tag进入到了自己的射频厂,进而产生反应。与其
相对的是listen模式,Tag
就相当于是listen模式,监听谁往外polling,产生响应.
下面先详细分析applyRouting。
参数force为true的时候,基本上就会执行一次enableDiscovery.
1
void applyRouting(boolean force) {
2
synchronized (this) {
3
......
4
//mInProvisionMode:用来标记当前系统是否处于开机向导界面.
5
//对应于android开机时候的引导provision apk,是否出在引导模式
6
if (mInProvisionMode) {
7
mInProvisionMode = Settings.Secure.getInt(mContentResolver,
8
Settings.Global.DEVICE_PROVISIONED, 0) == 0;
9
//当运行到这里的时候就代表,这是刷完系统,开机引导完,第一次走到这里,
10
//并且现在开机引导已经完毕,此时下面可能已经是false
11
if (!mInProvisionMode) {
12
//就是设置NfcDispatcher中的mProvisioningOnly = false;
13
mNfcDispatcher.disableProvisioningMode();
14
//调用native方法doSetProvisionMode,去通知native层,当前不是处于ProvisionMode
15
mDeviceHost.doSetProvisionMode(mInProvisionMode);
16
}
17
}
18
// 当此时正在和一个Tag在通信的时候,延迟re-configuration.
19
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && isTagPresent()) {
20
Log.d(TAG, "Not updating discovery parameters, tag connected.");
21
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESUME_POLLING),
22
APPLY_ROUTING_RETRY_TIMEOUT_MS);
23
return;
24
}
25
try {
26
//此处更新于NFC RF的Discovery参数.
27
NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
28
//传入参数为true的时候或者参数有所变化.
29
if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
30
//只要routingtable变过,或者有新的tech的改变。
31
if (newParams.shouldEnableDiscovery()) {
32
boolean shouldRestart =
33
mCurrentDiscoveryParameters.shouldEnableDiscovery();
34
//最重要的就是调用到了这里,mDeviceHost的实现类是NativeNfcManager
35
//最终调用到内部的如下:
36
// private native void doEnableDiscovery(int techMask,
37
// boolean enableLowPowerPolling,
38
// boolean enableReaderMode,
39
// boolean enableP2p,
40
// boolean restart);
41
//去native层,初始化RF的相关参数。
42
mDeviceHost.enableDiscovery(newParams, shouldRestart);
43
} else {
44
mDeviceHost.disableDiscovery();
45
}
46
mCurrentDiscoveryParameters = newParams;
47
} else {
48
Log.d(TAG, "Discovery configuration equal, not updating.");
49
}
50
} finally {
51
......
52
}
53
}
54
}
我们看一下computeDiscoveryParameters这个函数,它返回NfcDiscoveryParameters,
这个类描述了
用于enable NFC的tag discovery、polling、host card emulation的各个参数,
配置的不同最终初始化的NFC
功能不同.
1
//基于屏幕的状态重新计算NfcDiscoveryParameters.
2
private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
3
NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
4
//当屏幕状态>= NFC_POLLING_MODE,而如下:
5
//static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
6
//其实就是在亮屏并且处于解锁状态的时候,或mIsTaskBoot为true,mIsTaskBoot这个变量只有在处于初始化
7
//阶段,NfcService从构造的结尾处进行Nfc的启动.
8
// Polling;mIsInterruptedByCamera :是否被camera打断.
9
if (((screenState >= NFC_POLLING_MODE)||mIsTaskBoot) && !mIsInterruptedByCamera) {
10
//mReaderModeParams是在setReaderMode()这个接口中被设置的,应该是第三方app,在它们
11
//的应用界面想要把手机作为reader的时候会打印。
12
//当前NFC读模式开启后,按照协议,看看当前的Tag属于那种类型的Tag,然后设置techMask.
13
if (mReaderModeParams != null) {
14
int techMask = 0;
15
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_A) != 0)
16
techMask |= NFC_POLL_A;
17
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_B) != 0)
18
techMask |= NFC_POLL_B;
19
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_F) != 0)
20
techMask |= NFC_POLL_F;
21
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_V) != 0)
22
techMask |= NFC_POLL_ISO15693;
23
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0)
24
techMask |= NFC_POLL_KOVIO;
25
paramsBuilder.setTechMask(techMask);
26
paramsBuilder.setEnableReaderMode(true);
27
} else {
28
//系统内部的会设置为NFC_POLL_DEFAULT这个参数 对应:
29
//static final int NFC_POLL_DEFAULT = -1; 但是感觉传入到native层的时候,可能根据这个
30
//有个默认的设置.
31
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
32
paramsBuilder.setEnableP2p(true);
33
}
34
//锁屏并且在ProvisionMode
35
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED && mInProvisionMode) {
36
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
37
paramsBuilder.setEnableP2p(true);
38
//当有人调用addNfcUnlockHandler的时候,isLockscreenPollingEnabled才可能被设置为true,
39
//发现在sony的设计中LockScreenHeadsetHandover的构造处有添加.
40
//注意此时的屏幕状态是SCREEN_STATE_ON_LOCKED.就是亮屏,但是锁着那!
41
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED &&
42
mNfcUnlockManager.isLockscreenPollingEnabled()) {
43
// For lock-screen tags, no low-power polling
44
paramsBuilder.setTechMask(mNfcUnlockManager.getLockscreenPollMask());
45
paramsBuilder.setEnableLowPowerDiscovery(false);
46
paramsBuilder.setEnableP2p(false);
47
}
48
//mIsHceCapable代表是否具有卡模拟的功能。不插卡的时候也是true,可能是针对HCE的?
49
if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
50
// Host routing is always enabled at lock screen or later
51
paramsBuilder.setEnableHostRouting(true);
52
}
53
//只要commit过routingtable,或者设置过屏幕状态,或者换过卡,等等mIsRoutingTableDirty会为true.
54
if(mIsRoutingTableDirty) {
55
mIsRoutingTableDirty = false;
56
int protoRoute = mNxpPrefs.getInt("PREF_MIFARE_DESFIRE_PROTO_ROUTE_ID",
57
GetDefaultMifareDesfireRouteEntry());
58
int defaultRoute=mNxpPrefs.getInt("PREF_SET_DEFAULT_ROUTE_ID",
59
GetDefaultRouteEntry());
60
int techRoute=mNxpPrefs.getInt("PREF_MIFARE_CLT_ROUTE_ID",
61
GetDefaultMifateCLTRouteEntry());
62
defaultRoute = NfcCertDebugModeUtil.calculateDefaultRoute(defaultRoute,
63
mDeviceHost.getDefaultAidPowerState(), ROUTE_LOC_MASK);
64
if (DBG) Log.d(TAG, "Set default Route Entry");
65
setDefaultRoute(defaultRoute, protoRoute, techRoute);
66
}
67
...
68
return paramsBuilder.build(); //返回对应字符串,代表相关的参数
69
}
以上是关于NFC工作的RF状态的描述分析,下面开始分析,当NFC检测到remote tag的时候的回调。
最终native层通过NativeNfcManager.cpp中的
1
gCachedNfcManagerNotifyNdefMessageListeners = e->GetMethodID(cls.get(),
2
"notifyNdefMessageListeners", "(Lcom/android/nfc/dhimpl/NativeNfcTag;)V");
回调于NfcTag.cpp中的createNativeNfcTag(..)方法,然后通过JNI调用到NativeNfcManager.java中的
1
private void notifyNdefMessageListeners(NativeNfcTag tag) {
2
mListener.onRemoteEndpointDiscovered(tag);
3
}
mListener的实现就是NfcService,又重新回到Nfcservice中
1
2
public void onRemoteEndpointDiscovered(TagEndpoint tag) {
3
sendMessage(NfcService.MSG_NDEF_TAG, tag);
4
}
5
void sendMessage(int what, Object obj) {
6
Message msg = mHandler.obtainMessage();
7
msg.what = what;
8
msg.obj = obj;
9
mHandler.sendMessage(msg);
10
}
到此为止,native层对发现的tag进行了一系列的初始化和封装操作,就是按照ndef协议把Tag的消息封
装
到TagEndpoint当中,TagEndpoint就是代表了一个远端的Nfc tag.
然后执行到mHandler中的MSG_NDEF_TAG
1
case MSG_NDEF_TAG:
2
if (DBG) Log.d(TAG, "Tag detected, notifying applications");
3
......
4
//当有别人调用setReaderMode()接口去设置mReaderModeParams的时候.
5
//如第三方app模拟reader.(一般都是在第三方app的reader/write界面,由它们调用)
6
synchronized (NfcService.this) {
7
readerParams = mReaderModeParams;
8
}
9
if (readerParams != null) {
10
presenceCheckDelay = readerParams.presenceCheckDelay;
11
//如果FLAG_READER_SKIP_NDEF_CHECK标记位不为0,那么直接开始调用dispatchTagEndpoint分发
12
if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {
13
if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");
14
tag.startPresenceChecking(presenceCheckDelay, callback);
15
dispatchTagEndpoint(tag, readerParams);
16
break;
17
}
18
}
19
//当是NFC Barcode的时候也是直接分发,看其实意思是解析NFC条形码的时候
20
if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {
21
......
22
if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");
23
tag.startPresenceChecking(presenceCheckDelay, callback);
24
dispatchTagEndpoint(tag, readerParams);
25
break;
26
}
27
//去调用NativeNfcTag的findAndReadNdef,进行相关初始化后把native层的数据按照NDEF
28
//协议封装成java中的类NdefMessage.(此时就是读取NDEF格式的信息了)
29
NdefMessage ndefMsg = tag.findAndReadNdef();
30
//解析的消息无法被实例化成NDEF的时候。
31
if (ndefMsg == null) {
32
// First try to see if this was a bad tag read
33
if (!tag.reconnect()) {
34
tag.disconnect();
35
//当是亮屏的时候弹出提示信息.
36
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
37
//"Read error. Try again"
38
String toastString = mContext.getString(
39
R.string.nfc_strings_toast_prompt_touch_again_txt);
40
if (!ToastMaster.isSameToastShown(toastString)) {
41
ToastMaster.showToast(mContext, toastString);
42
}
43
}
44
break;
45
}
46
}
47
//这个如名字是抖动的tag,就是由于距离没控制好,一直检测到同一个tag的时候。
48
//应该就是这个Tag检测完,没有离开范围,就不会再次检测
49
if (debounceTagUid != null) {
50
// If we're debouncing and the UID or the NDEF message of the tag match,
51
// don't dispatch but drop it.
52
......
53
}
54
mLastReadNdefMessage = ndefMsg;
55
tag.startPresenceChecking(presenceCheckDelay, callback);
56
//解析出问题的ndef消息也要分发出去.
57
dispatchTagEndpoint(tag, readerParams);
58
break;
59
//case MSG_NDEF_TAG over.
至此开始准备进行读取Tag。
findAndReadNdef的解析,位于NativeNfcTag.这个方法中的注释为我们的分析带来很多遍历,此处需要
特别注意:
NativeNfcTag是Nfc Tag在java层的一个代表,它在native层由NfcTag对象来表示,在NfcTag.cpp
中
有如下方法用于创建NativeNfcTag对象,并且是实例化相应的参数,注释写的很详细,源码如下:
1
/*******************************************************************************
2
**
3
** Function: createNativeNfcTag
4
**
5
** Description: Create a brand new Java NativeNfcTag object;
6
** fill the objects's member variables with data;
7
** notify NFC service;
8
** activationData: data from activation.
9
**
10
** Returns: None
11
**
12
*******************************************************************************/
13
void NfcTag::createNativeNfcTag (tNFA_ACTIVATED& activationData)
14
{
15
static const char fn [] = "NfcTag::createNativeNfcTag";
16
ALOGV("%s: enter", fn);
17
18
JNIEnv* e = NULL;
19
ScopedAttach attach(mNativeData->vm, &e);
20
if (e == NULL)
21
{
22
ALOGE("%s: jni env is null", fn);
23
return;
24
}
25