文章整理总结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
5
6
if (mInProvisionMode) {
7
mInProvisionMode = Settings.Secure.getInt(mContentResolver,
8
Settings.Global.DEVICE_PROVISIONED, 0) == 0;
9
10
11
if (!mInProvisionMode) {
12
13
mNfcDispatcher.disableProvisioningMode();
14
15
mDeviceHost.doSetProvisionMode(mInProvisionMode);
16
}
17
}
18
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
27
NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
28
29
if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
30
31
if (newParams.shouldEnableDiscovery()) {
32
boolean shouldRestart =
33
mCurrentDiscoveryParameters.shouldEnableDiscovery();
34
35
36
37
38
39
40
41
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
2
private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
3
NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
4
5
6
7
8
9
if (((screenState >= NFC_POLLING_MODE)||mIsTaskBoot) && !mIsInterruptedByCamera) {
10
11
12
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
29
30
31
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
32
paramsBuilder.setEnableP2p(true);
33
}
34
35
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED && mInProvisionMode) {
36
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
37
paramsBuilder.setEnableP2p(true);
38
39
40
41
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED &&
42
mNfcUnlockManager.isLockscreenPollingEnabled()) {
43
44
paramsBuilder.setTechMask(mNfcUnlockManager.getLockscreenPollMask());
45
paramsBuilder.setEnableLowPowerDiscovery(false);
46
paramsBuilder.setEnableP2p(false);
47
}
48
49
if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
50
51
paramsBuilder.setEnableHostRouting(true);
52
}
53
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
@Override
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
5
6
synchronized (NfcService.this) {
7
readerParams = mReaderModeParams;
8
}
9
if (readerParams != null) {
10
presenceCheckDelay = readerParams.presenceCheckDelay;
11
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
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
28
29
NdefMessage ndefMsg = tag.findAndReadNdef();
30
31
if (ndefMsg == null) {
32
33
if (!tag.reconnect()) {
34
tag.disconnect();
35
36
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
37
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
48
49
if (debounceTagUid != null) {
50
51
52
......
53
}
54
mLastReadNdefMessage = ndefMsg;
55
tag.startPresenceChecking(presenceCheckDelay, callback);
56
57
dispatchTagEndpoint(tag, readerParams);
58
break;
59
至此开始准备进行读取Tag。
findAndReadNdef的解析,位于NativeNfcTag.这个方法中的注释为我们的分析带来很多遍历,此处需要
特别注意:
NativeNfcTag是Nfc Tag在java层的一个代表,它在native层由NfcTag对象来表示,在NfcTag.cpp
中
有如下方法用于创建NativeNfcTag对象,并且是实例化相应的参数,注释写的很详细,源码如下:
1
2
3
4
5
6
7
8
9
10
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
26
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(mNativeData->cached_NfcTag));
27
if (e->ExceptionCheck())
28
{
29
e->ExceptionClear();
30
ALOGE("%s: failed to get class", fn);
31
return;
32
}
33
34
35
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
36
ScopedLocalRef<jobject> tag(e, e->NewObject(tag_cls.get(), ctor));
37
38
39
fillNativeNfcTagMembers1(e, tag_cls.get(), tag.get());
40
41
42
fillNativeNfcTagMembers2(e, tag_cls.get(), tag.get(), activationData);
43
44
45
fillNativeNfcTagMembers3(e, tag_cls.get(), tag.get(), activationData);
46
47
48
fillNativeNfcTagMembers4(e, tag_cls.get(), tag.get(), activationData);
49
50
51
fillNativeNfcTagMembers5(e, tag_cls.get(), tag.get(), activationData);
52
53
if (mNativeData->tag != NULL)
54
{
55
e->DeleteGlobalRef(mNativeData->tag);
56
}
57
mNativeData->tag = e->NewGlobalRef(tag.get());
58
59
ALOGV("%s; mNumDiscNtf=%x", fn,mNumDiscNtf);
60
if(!mNumDiscNtf || NfcTag::getInstance().checkNextValidProtocol() == -1)
61
{
62
63
mNumDiscNtf = 0;
64
ALOGV("%s: try notify nfc service", fn);
65
storeActivationParams();
66
e->CallVoidMethod(mNativeData->manager,
67
android::gCachedNfcManagerNotifyNdefMessageListeners, tag.get());
68
if (e->ExceptionCheck())
69
{
70
e->ExceptionClear();
71
ALOGE("%s: fail notify nfc service", fn);
72
}
73
deleteglobaldata(e);
74
}
75
else
76
{
77
ALOGV("%s: Selecting next tag", fn);
78
}
79
80
ALOGV("%s: exit", fn);
81
}
如上需要注意,每次检测到一个Tag都会调用createNativeNfcTag,去实例化一个native对应的
对象(刷一
次卡创建一个),然后实例化各个变量。也因此如下变量:
1
2
3
private int mConnectedHandle;
每次都是0,因为是默认值嘛!而且在native层也没有对它进行初始化。至于NativeNfcTag这个类的销
毁,好像出了这个方法就销毁了(native层)?Java层在相应的方法中使用完毕以后也就释放了
下面以findAndReadNdef为切入点,结合注释看一下connect和read Tag的流程.
1
@Override
2
public NdefMessage findAndReadNdef(){
3
4
5
6
7
8
9
10
11
12
int[] technologies = getTechList();
13
int[] handles = mTechHandles;
14
NdefMessage ndefMsg = null;
15
boolean foundFormattable = false;
16
int formattableHandle = 0;
17
int formattableLibNfcType = 0;
18
int status;
19
for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
20
for (int i = 0; i < techIndex; i++) {
21
22
if (handles[i] == handles[techIndex]) {
23
continue;
24
}
25
}
26
27
28
status = connectWithStatus(technologies[techIndex]);
29
30
if (status != 0) {
31
Log.d(TAG, "Connect Failed - status = "+ status);
32
if (status == STATUS_CODE_TARGET_LOST) {
33
break;
34
}
35
continue;
36
}
37
if (!foundFormattable) {
38
39
if (isNdefFormatable()) {
40
foundFormattable = true;
41
formattableHandle = getConnectedHandle();
42
formattableLibNfcType = getConnectedLibNfcType();
43
}
44
45
reconnect();
46
}
47
48
int[] ndefinfo = new int[2];
49
50
status = checkNdefWithStatus(ndefinfo);
51
if (status != 0) {
52
Log.d(TAG, "Check NDEF Failed - status = " + status);
53
if (status == STATUS_CODE_TARGET_LOST) {
54
break;
55
}
56
continue;
57
}
58
59
boolean generateEmptyNdef = false;
60
61
int supportedNdefLength = ndefinfo[0];
62
int cardState = ndefinfo[1];
63
64
65
byte[] buff = readNdef();
66
if (buff != null && buff.length > 0) {
67
try {
68
69
70
71
ndefMsg = new NdefMessage(buff);
72
73
addNdefTechnology(ndefMsg,
74
getConnectedHandle(),
75
getConnectedLibNfcType(),
76
getConnectedTechnology(),
77
supportedNdefLength, cardState);
78
foundFormattable = false;
79
80
reconnect();
81
} catch (FormatException e) {
82
83
generateEmptyNdef = true;
84
}
85
} else if(buff != null){
86
87
generateEmptyNdef = true;
88
}
89
90
if (generateEmptyNdef) {
91
ndefMsg = null;
92
93
addNdefTechnology(null,
94
getConnectedHandle(),
95
getConnectedLibNfcType(),
96
getConnectedTechnology(),
97
supportedNdefLength, cardState);
98
foundFormattable = false;
99
reconnect();
100
}
101
break;
102
}
103
......
104
return ndefMsg;
105
}
connectWithStatus源码:(注释挺多的,容易看懂但不容易理解)
1
private native int doConnect(int handle);
2
public synchronized int connectWithStatus(int technology) {
3
......
4
int status = -1;
5
for (int i = 0; i < mTechList.length; i++) {
6
7
if (mTechList[i] == technology) {
8
9
10
11
if (mConnectedHandle != mTechHandles[i]) {
12
13
14
15
16
17
18
if (mConnectedHandle == -1) {
19
20
21
22
23
Log.d(TAG," readerParams doConnect");
24
status = doConnect(i);
25
} else {
26
27
28
Log.d(TAG," readerParams Connect to a tech with a different handle");
29
status = reconnectWithStatus(i);
30
}
31
32
33
if (status == 0) {
34
mConnectedHandle = mTechHandles[i];
35
mConnectedTechIndex = i;(把tech所在的正确的index传入)
36
}
37
} else {
38
39
40
41
42
43
44
45
if ((technology == TagTechnology.NDEF) ||
46
(technology == TagTechnology.NDEF_FORMATABLE)) {
47
48
i = 0;
49
50
}
51
status = reconnectWithStatus(i);
52
......
53
54
if (status == 0) {
55
mConnectedTechIndex = i;
56
57
}
58
}
59
break;
60
}
61
}
62
......
63
return status;
64
}
reconnectWithStatus()的相关源码流程:
1
native int doHandleReconnect(int handle);
2
public synchronized int reconnectWithStatus(int handle) {
3
......
4
5
int status = doHandleReconnect(handle);
6
......
7
return status;
8
}
reconnect相关源码分析.
1
@Override
2
public synchronized boolean reconnect() {
3
return reconnectWithStatus() == 0;
4
}
5
native int doReconnect();
6
public synchronized int reconnectWithStatus() {
7
......
8
int status = doReconnect();
9
......
10
return status;
11
}
checkNdefWithStatus相关源码分析
1
private native int doCheckNdef(int[] ndefinfo);
2
private synchronized int checkNdefWithStatus(int[] ndefinfo) {
3
......
4
int status = doCheckNdef(ndefinfo);
5
......
6
return status;
7
}
addNdefTechnology相关源码
1
public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType,
2
int javaType, int maxLength, int cardState) {
3
synchronized (this) {
4
addTechnology(TagTechnology.NDEF, handle, libnfcType);
5
6
Bundle extras = new Bundle();
7
extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg);
8
extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength);
9
extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState);
10
extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType));
11
Log.d(TAG,"addNdefTechnology readerParams mTechExtras = "+mTechExtras);
12
13
14
if (mTechExtras == null) {
15
16
17
Bundle[] builtTechExtras = getTechExtras();
18
builtTechExtras[builtTechExtras.length - 1] = extras;
19
}
20
else {
21
22
Bundle[] oldTechExtras = getTechExtras();
23
Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1];
24
System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length);
25
newTechExtras[oldTechExtras.length] = extras;
26
mTechExtras = newTechExtras;
27
}
28
}
29
}
doRead()相关源码分析.
1
private native byte[] doRead();
2
@Override
3
public synchronized byte[] readNdef() {
4
......
5
byte[] result = doRead();
6
......
7
return result;
8
}
至此和findAndReadNdef()相关的都源码进行了简单的跟踪处理。再回到前面的NfcService中的
case MSG_NDEF_TAG: 再往下就是真正分发的地方dispatchTagEndpoint了。
1
private void dispatchTagEndpoint(TagEndpoint tagEndpoint, ReaderModeParams readerParams){
2
3
4
Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(),
5
tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService);
6
7
8
9
registerTagObject(tagEndpoint);
10
11
if (readerParams != null) {
12
try {
13
if ((readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0) {
14
playSound(SOUND_END);
15
}
16
if (readerParams.callback != null) {
17
18
readerParams.callback.onTagDiscovered(tag);
19
return;
20
} else {
21
22
}
23
} ...
24
}
25
26
27
28
29
int dispatchResult = mNfcDispatcher.dispatchTag(tag);
30
31
32
if (dispatchResult == NfcDispatcher.DISPATCH_FAIL) {
33
unregisterObject(tagEndpoint.getHandle());
34
35
36
37
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
38
String toastString = mContext.getString(
39
R.string.nfc_strings_toast_prompt_read_error_txt);
40
41
if (!ToastMaster.isSameToastShown(toastString)) {
42
ToastMaster.showToast(mContext, toastString);
43
playSound(SOUND_ERROR);
44
}
45
}
46
} else if (dispatchResult == NfcDispatcher.DISPATCH_SUCCESS) {
47
48
if (mAudioManager.getRingerMode() != mAudioManager.RINGER_MODE_SILENT) {
49
mVibrator.vibrate(200);
50
}
51
playSound(SOUND_END);
52
}
53
}
可以看到最最重要的就是NfcDispatcher中的dispatchTag(Tag)了,进入到NfcDispatcher类,
此类从名字也能看出主要功能,它就是分发NFC的actions然后去开启指定的activity。
阅读下面的流程代码之前,最好对Android NFC的TAG的分发机制有个整体了解,这样和代码对应上就更为轻松了。
整体的代码是按照Android Tag的分发优先级来决定的.
1
public int dispatchTag(Tag tag) {
2
PendingIntent overrideIntent;
3
IntentFilter[] overrideFilters;
4
String[][] overrideTechLists;
5
String[] provisioningMimes;
6
boolean provisioningOnly;
7
synchronized (this) {
8
9
10
overrideFilters = mOverrideFilters;
11
overrideIntent = mOverrideIntent;
12
overrideTechLists = mOverrideTechLists;
13
provisioningOnly = mProvisioningOnly;
14
provisioningMimes = mProvisioningMimes;
15
}
16
17
boolean screenUnlocked = false;
18
if (!provisioningOnly &&
19
mScreenStateHelper.checkScreenState() ==
20
ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
21
screenUnlocked = handleNfcUnlock(tag);
22
if (!screenUnlocked) {
23
return DISPATCH_FAIL;
24
}
25
}
26
NdefMessage message = null;
27
28
29
30
Ndef ndef = Ndef.get(tag);
31
if (ndef != null) {
32
33
34
35
message = ndef.getCachedNdefMessage();
36
} else {
37
38
NfcBarcode nfcBarcode = NfcBarcode.get(tag);
39
if (nfcBarcode != null && nfcBarcode.getType() == NfcBarcode.TYPE_KOVIO) {
40
message = decodeNfcBarcodeUri(nfcBarcode);
41
}
42
}
43
if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);
44
45
DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);
46
47
resumeAppSwitches();
48
49
50
51
if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
52
overrideTechLists)) {
53
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
54
}
55
56
57
if (tryPeripheralHandover(message)) {
58
if (DBG) Log.i(TAG, "matched BT HANDOVER");
59
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
60
}
61
62
if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
63
if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
64
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
65
}
66
......
67
68
if (tryNdef(dispatch, message)) {
69
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
70
}
71
72
if (tryTech(dispatch, tag)) {
73
return DISPATCH_SUCCESS;
74
}
75
76
77
dispatch.setTagIntent();
78
if (dispatch.tryStartActivity()) {
79
if (DBG) Log.i(TAG, "matched TAG");
80
return DISPATCH_SUCCESS;
81
}
82
83
if (DBG) Log.i(TAG, "no match");
84
return DISPATCH_FAIL;
85
}
DispatchInfo类的源码分析:
1
2
3
4
static class DispatchInfo {
5
public final Intent intent;
6
7
final Intent rootIntent;
8
final Uri ndefUri;
9
final String ndefMimeType;
10
final PackageManager packageManager;
11
final Context context;
12
final Tag tag;
13
14
public DispatchInfo(Context context, Tag tag, NdefMessage message) {
15
16
intent = new Intent();
17
intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
18
intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
19
if (message != null) {
20
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message});
21
ndefUri = message.getRecords()[0].toUri();
22
ndefMimeType = message.getRecords()[0].toMimeType();
23
} else {
24
25
ndefUri = null;
26
ndefMimeType = null;
27
}
28
29
30
31
32
rootIntent = new Intent(context, NfcRootActivity.class);
33
rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent);
34
rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
35
36
this.tag = tag;
37
this.context = context;
38
packageManager = context.getPackageManager();
39
}
40
41
public Intent setNdefIntent() {
42
intent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
43
if (ndefUri != null) {
44
intent.setData(ndefUri);
45
return intent;
46
} else if (ndefMimeType != null) {
47
intent.setType(ndefMimeType);
48
return intent;
49
}
50
return null;
51
}
52
53
public Intent setTechIntent() {
54
intent.setData(null);
55
intent.setType(null);
56
intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED);
57
return intent;
58
}
59
60
public Intent setTagIntent() {
61
intent.setData(null);
62
intent.setType(null);
63
intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
64
return intent;
65
}
66
67
68
boolean tryStartActivity() {
69
70
List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0,
71
ActivityManager.getCurrentUser());
72
if (activities.size() > 0) {
73
74
context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
75
return true;
76
}
77
return false;
78
}
79
80
boolean tryStartActivity(Intent intentToStart) {
81
List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
82
intentToStart, 0, ActivityManager.getCurrentUser());
83
if (activities.size() > 0) {
84
rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart);
85
context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
86
NfcIddEvent.NfcTag.dispatchedToApp(tag, activities);
87
return true;
88
}
89
return false;
90
}
91
}
下面先介绍一个接口,用于运行在前台的app注册,拦截分发时间
NfcService的setForegroundDispatch最
终会调用到NfcDispatcher中的.
1
public synchronized void setForegroundDispatch(PendingIntent intent,
2
IntentFilter[] filters, String[][] techLists) {
3
if (DBG) Log.d(TAG, "Set Foreground Dispatch");
4
mOverrideIntent = intent;
5
mOverrideFilters = filters;
6
mOverrideTechLists = techLists;
7
}
第三方app使用前台分发系统时的调用
1
boolean tryOverrides(DispatchInfo dispatch, Tag tag, NdefMessage message, PendingIntent overrideIntent,
2
IntentFilter[] overrideFilters, String[][] overrideTechLists) {
3
......
4
5
if (message != null) {
6
intent = dispatch.setNdefIntent();
7
8
if (intent != null &&
9
isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
10
try {
11
12
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
13
if (DBG) Log.i(TAG, "matched NDEF override");
14
return true;
15
} catch (CanceledException e) {
16
return false;
17
}
18
}
19
}
20
21
intent = dispatch.setTechIntent();
22
23
if (isTechMatch(tag, overrideTechLists)) {
24
try {
25
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
26
if (DBG) Log.i(TAG, "matched TECH override");
27
return true;
28
} catch (CanceledException e) {
29
return false;
30
}
31
}
32
33
intent = dispatch.setTagIntent();
34
if (isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
35
try {
36
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
37
if (DBG) Log.i(TAG, "matched TAG override");
38
return true;
39
} catch (CanceledException e) {
40
return false;
41
}
42
}
43
return false;
44
}
接下来是通过tryPeripheralHandover的,就是交由Bt进行发送的数据的处理
1
public boolean tryPeripheralHandover(NdefMessage m) {
2
3
if (m == null || !mDeviceSupportsBluetooth) return false;
4
if (DBG) Log.d(TAG, "tryHandover(): " + m.toString());
5
6
HandoverDataParser.BluetoothHandoverData handover =
7
mHandoverDataParser.parseBluetooth(m);
8
......
9
10
11
Intent intent = new Intent(mContext, PeripheralHandoverService.class);
12
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
13
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
14
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT,
15
handover.transport);
16
if (handover.oobData != null) {
17
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA,
18
handover.oobData);
19
}
20
if (handover.uuids != null) {
21
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
22
}
23
if (handover.bluetoothClass != null) {
24
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS,
25
handover.bluetoothClass);
26
}
27
intent.putExtra(PeripheralHandoverService.EXTRA_CLIENT, mMessenger);
28
intent.putExtra(PeripheralHandoverService.EXTRA_BT_ENABLED,
29
mBluetoothEnabledByNfc.get());
30
31
if (CtaUtils.showCtaBtDialogIfNeeded(mContext, null, intent, null)) {
32
return true;
33
}
34
35
mContext.startServiceAsUser(intent, UserHandle.CURRENT);
36
return true;
37
}
关于PeripheralHandoverService的介绍
这个service主要是用于负责Handover,也就是链接蓝牙键盘、Speaker等.
1
public class PeripheralHandoverService extends Service
2
implements BluetoothPeripheralHandover.Callback {
3
......
4
public PeripheralHandoverService() {
5
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
6
mHandler = new MessageHandler();
7
mMessenger = new Messenger(mHandler);
8
mBluetoothHeadsetConnected = false;
9
mBluetoothEnabledByNfc = false;
10
mStartId = 0;
11
}
12
@Override
13
public void onCreate() {
14
super.onCreate();
15
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
16
17
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
18
registerReceiver(mBluetoothStatusReceiver, filter);
19
}
20
21
@Override
22
public int onStartCommand(Intent intent, int flags, int startId) {
23
......
24
25
26
if (doPeripheralHandover(intent.getExtras())) {
27
return START_STICKY;
28
}
29
......
30
}
31
}
Handover到时候专门在分析源码相关的源码,此处我们回到前面的dispatchTag()方法中,然后就是
按照Android的分发系统,优先处理注册了action为NfcAdapter.ACTION_NDEF_DISCOVERED的
1
boolean tryNdef(DispatchInfo dispatch, NdefMessage message) {
2
......
3
4
Intent intent = dispatch.setNdefIntent();
5
if (intent == null) return false;
6
7
8
List<String> aarPackages = extractAarPackages(message);
9
for (String pkg : aarPackages) {
10
dispatch.intent.setPackage(pkg);
11
12
if (dispatch.tryStartActivity()) {
13
if (DBG) Log.i(TAG, "matched AAR to NDEF");
14
return true;
15
}
16
}
17
18
19
20
if (aarPackages.size() > 0) {
21
String firstPackage = aarPackages.get(0);
22
PackageManager pm;
23
try {
24
UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser());
25
pm = mContext.createPackageContextAsUser("android", 0,
26
currentUser).getPackageManager();
27
} ...
28
29
Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage);
30
if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) {
31
if (DBG) Log.i(TAG, "matched AAR to application launch");
32
return true;
33
}
34
35
Intent marketIntent = getAppSearchIntent(firstPackage);
36
if (marketIntent != null && dispatch.tryStartActivity(marketIntent)) {
37
if (DBG) Log.i(TAG, "matched AAR to market launch");
38
return true;
39
}
40
}
41
......
42
43
44
dispatch.intent.setPackage(null);
45
if (dispatch.tryStartActivity()) {
46
if (DBG) Log.i(TAG, "matched NDEF");
47
return true;
48
}
49
return false;
50
}
而tryTech()的思想是一样的,也是经过一系列判断后,通过dispatch.tryStartActivity()来启动,不过它启动
的
是action为NfcAdapter.ACTION_TECH_DISCOVERED的,不再赘述。
当都不能处理的时候就调用dispatch.setTagIntent()把intent设置NfcAdapter.ACTION_TAG_DISCOVERED
用它去启动符合要求的activity,在启动失败的时候就会返回分发失败的状态.
至此完成整体Tag的读取分发系统.
2、写入Tag的简单流程
写开发时app调用framework层的对应的不同协议的Tag代表的类的接口有如下:
位于:android/frameworks/base/core/java/android/nfc/tech
有: NfcA.java、NfcB.java、NfcF.java、def.java、IsoDep.java ... 等等。
这些类都代表了指定的不同协议的Tag的实现.此处我们以android中的Ndef为例,你想要写入数据的
时候,调用Ndef类的如下接口,第三方写入的时候的过程(???具体前奏还不太清楚)
1
public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
2
checkConnected();
3
try {
4
5
INfcTag tagService = mTag.getTagService();
6
......
7
int serviceHandle = mTag.getServiceHandle();
8
if (tagService.isNdef(serviceHandle)) {
9
10
int errorCode = tagService.ndefWrite(serviceHandle, msg);
11
switch (errorCode) {
12
case ErrorCodes.SUCCESS:
13
break;
14
case ErrorCodes.ERROR_IO:
15
throw new IOException();
16
case ErrorCodes.ERROR_INVALID_PARAM:
17
throw new FormatException();
18
default:
19
20
throw new IOException();
21
}
22
}
23
......
24
} catch (RemoteException e) {
25
Log.e(TAG, "NFC service dead", e);
26
}
27
}
那么我们就需要去packages/app/Nfc当中找INfcTag的具体实现了,是位于NfcService当中的内部类
final class TagService extends INfcTag.Stub调用内部方法ndefWrite
1
@Override
2
public int ndefWrite(int nativeHandle, NdefMessage msg) throws RemoteException {
3
NfcPermissions.enforceUserPermissions(mContext);
4
5
TagEndpoint tag;
6
......
7
8
if (tag.writeNdef(msg.toByteArray())) {
9
return ErrorCodes.SUCCESS;
10
} else {
11
return ErrorCodes.ERROR_IO;
12
}
13
}
前面我们已经说过TagEndpoint是有NativeNfcTag实现的,对应的方法如下
1
private native boolean doWrite(byte[] buf);
2
@Override
3
public synchronized boolean writeNdef(byte[] buf) {
4
if (mWatchdog != null) {
5
mWatchdog.pause();
6
}
7
8
boolean result = doWrite(buf);
9
if (mWatchdog != null) {
10
mWatchdog.doResume();
11
}
12
return result;
13
}
至此java层的写入流程就分析完毕了。
下面是调试的时候一次写入的Log记录一下,会先调用链接,再调用transceive,最后再写入。
1
04-21 00:37:02.032 D/zy ( 2652): Nfcervice connect
2
04-21 00:37:02.042 D/zy ( 2652): NativeNfc transceive
3
04-21 00:37:02.070 D/zy ( 2652): Nfcervice connect
4
04-21 00:37:02.094 D/zy ( 2652): Nfcervice ndefWrite
5
04-21 00:37:02.094 D/zy ( 2652): NativeNfc writeNdef