[转载]NFC源码分析之R/W工作模式

时间:2024-03-18 20:46:00

https://blog.csdn.net/zy00000000001/article/details/71183262

文章整理总结java层,NFC读取和写入Tag的流程。
整体的时序图:
[转载]NFC源码分析之R/W工作模式
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
@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
        //当有别人调用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