NFC源码分析之RW工作模式

xiaoxiao2021-02-27  234

文章整理总结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 @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 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    //create a new Java NativeNfcTag object 35    jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V"); 36    ScopedLocalRef<jobject> tag(e, e->NewObject(tag_cls.get(), ctor)); 37 38    //fill NativeNfcTag's mProtocols, mTechList, mTechHandles, mTechLibNfcTypes 39    fillNativeNfcTagMembers1(e, tag_cls.get(), tag.get()); 40 41    //fill NativeNfcTag's members: mHandle, mConnectedTechnology 42    fillNativeNfcTagMembers2(e, tag_cls.get(), tag.get(), activationData); 43 44    //fill NativeNfcTag's members: mTechPollBytes 45    fillNativeNfcTagMembers3(e, tag_cls.get(), tag.get(), activationData); 46 47    //fill NativeNfcTag's members: mTechActBytes 48    fillNativeNfcTagMembers4(e, tag_cls.get(), tag.get(), activationData); 49 50    //fill NativeNfcTag's members: mUid 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        //notify NFC service about this new tag 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 // mConnectedHandle stores the *real* libnfc handle 2 // that we're connected to. 3 private int mConnectedHandle;      每次都是0,因为是默认值嘛!而且在native层也没有对它进行初始化。至于NativeNfcTag这个类的销 毁,好像出了这个方法就销毁了(native层)?Java层在相应的方法中使用完毕以后也就释放了      下面以findAndReadNdef为切入点,结合注释看一下connect和read Tag的流程. 1 @Override 2 public NdefMessage findAndReadNdef(){ 3    //去获取当前Tag支持的所有的tech 4    //如下technologies、handles都是在native层就实例化好的. 5    //TagTechnology有很多种, NFC_A、NFC_B、ISO_DEP、NFC_F、NFC_V. 6    //关于technologies和handles也需要多说一下,暂时对这块认知不高,通过Log观察到如下: 7    //使用了四张平常的卡进行测试抓取, 每次technologies和数量和handles的数量都是一致的, 8    //technologies是卡片支持的协议,对应于TagTechnology.java中的成员变量. 9    //handles 在这次试验的手机上值都是1,无论size是对少,每个位置都是1,而handles的添加在java层 10    //都是对应这个变量 private int mConnectedHandle;前面有注释是说代表了libnfc的handle,那么 11    //感觉一般的平台肯定是用的一个libnfc库呀,难道有些会用两个libnfc?然后去进行选择? 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            //高效处理,我们目前handle都是一样的情况 22            if (handles[i] == handles[techIndex]) { 23                continue;  // don't check duplicate handles 24           } 25       } 26        //网上说明:判断connectedHandle与当前Index对应的handle的关系,并更新状态,。 27        //自己理解不深,源码见后面. 28        status = connectWithStatus(technologies[techIndex]); 29        //连接失败直接跳过本次循环,连接下一个Tech!! 30        if (status != 0) { 31            Log.d(TAG, "Connect Failed - status = "+ status); 32            if (status == STATUS_CODE_TARGET_LOST) { 33                break; 34           } 35            continue;  // try next handle 36       } 37        if (!foundFormattable) { 38            //如果是标准的Ndef格式,进行如下操作,自己测试的四个卡都是. 39            if (isNdefFormatable()) { 40                foundFormattable = true; 41                formattableHandle = getConnectedHandle(); 42                formattableLibNfcType = getConnectedLibNfcType(); 43           } 44            //再次connect 45            reconnect(); 46       } 47 48        int[] ndefinfo = new int[2]; 49        //检查当前tag中ndef数据的合法性,传入ndefinfo会走到native层,进行数据的赋值. 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;  // try next handle 57       } 58        // found our NDEF handle 59        boolean generateEmptyNdef = false; 60        //check完以后ndefinfo这个里面的数据,正常情况下就是完整的了。 61        int supportedNdefLength = ndefinfo[0]; 62        int cardState = ndefinfo[1]; 63        //此处才是真正的读取,读取tag中的数据,读出来的是字节数组。 64        //注意此处经常丢失Log 65        byte[] buff = readNdef(); 66        if (buff != null && buff.length > 0) { 67            try { 68                //正常的应该走到这里,通过获取的数据实例化NdefMessage. 69                //在NdefMessage实例化的时候就按照NDEF的协议去解析这个byte数组 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              // Create an intent anyway, without NDEF messages 83              generateEmptyNdef = true; 84           } 85       } else if(buff != null){ 86            // Empty buffer, unformatted tags fall into this case 87            generateEmptyNdef = true; 88       } 89        //当产生异常的时候generateEmptyNdef为true的一些处理 90        if (generateEmptyNdef) { 91            ndefMsg = null; 92            //存一个空NdefMessage 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        //循环遍历所有的tech,找到需要connect的tech 7        if (mTechList[i] == technology) { 8            //当前的tag被检测到,第一次走到这里的时候mConnectedHandle是默认值, 9            //因为没有在native层进行实例化,所以是mConnectedHandle = 0; 10            // Get the handle and connect, if not already connected 11            if (mConnectedHandle != mTechHandles[i]) { 12                // We're not yet connected to this handle, there are 13                // a few scenario's here: 14                // 1) We are not connected to anything yet - allow 15                // 2) We are connected to a technology which has 16                //   a different handle (multi-protocol tag); we support 17                //   switching to that. 18                if (mConnectedHandle == -1) { 19                    //这个情况,Log一直观察不到,无论是重启/刷机的第一次,还是平常都不行 20                    //感觉也正常,因为可能是libnfc只要连接上,就不会初始化成-1. 21                    // Not connected yet 22                    //status = doConnect(mTechHandles[i]); 23                    Log.d(TAG," readerParams doConnect"); 24                    status = doConnect(i); 25               } else { 26                    //调用reconnectWithStatus传入handle.(暂时发现每个tag的handle都是一样的) 27                    // Connect to a tech with a different handle 28                    Log.d(TAG," readerParams Connect to a tech with a different handle"); 29                    status = reconnectWithStatus(i); 30               } 31                //如果成功链接,mConnectedHandle都是1(在我这个测试手机上),按照前面的变量分析的话, 32                //也可以理解 33                if (status == 0) { 34                    mConnectedHandle = mTechHandles[i]; 35                    mConnectedTechIndex = i;(把tech所在的正确的index传入) 36               } 37           } else { 38                //按照目前的手机和Log和Tag的分析都没有走到这里,暂时不太理解 39                // 1) We are connected to a technology which has the same 40                //   handle; we do not support connecting at a different 41                //   level (libnfc auto-activates to the max level on 42                //   any handle). 43                // 2) We are connecting to the ndef technology - always 44                //   allowed. 45                if ((technology == TagTechnology.NDEF) || 46                       (technology == TagTechnology.NDEF_FORMATABLE)) { 47                    // special case for NDEF, this will cause switch to ISO_DEP frame intf 48                    i = 0; 49                  // status = 0; 50               } 51                status = reconnectWithStatus(i); 52               ...... 53 54                if (status == 0) { 55                    mConnectedTechIndex = i; 56                    // Handle was already identical 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    //最后就是调用到native的方法去做进一步的连接操作. 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        //getTechExtras内部会更具Tech,创建出一个Bundle,最终NfcService可以获取这个然后取出 13        //来其中的数据,以及重要的NdefMessage. 14        if (mTechExtras == null) { 15            // This will build the tech extra's for the first time, 16            // including a NULL ref for the NDEF tech we generated above. 17            Bundle[] builtTechExtras = getTechExtras(); 18            builtTechExtras[builtTechExtras.length - 1] = extras; 19       } 20        else { 21            // Tech extras were built before, patch the NDEF one in 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    //Tag用来封装一个已经被发现的Tag.Tag类是framework提供的api 3    //tagEndpoint.getTechExtras():就包含我们最后addNdefTechnology的时候存入的NdefMessage. 4    Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(), 5            tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService); 6    //把tagEndpoint以 key = tagEndpoint.gethandle;value = tagEndpoint放入到集合中 7    //final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>(); 8    //由此手机看来,handle一样,那么一次检测基本就存一个,第一个连接成功Tech. 9    registerTagObject(tagEndpoint); 10    //当有第三方app 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                //调用回调onTagDiscovered. 18                readerParams.callback.onTagDiscovered(tag); 19                return; 20           } else { 21                // Follow normal dispatch below 22           } 23       } ... 24   } 25    //正真开始分发的地方. 26    //mNfcDispatcher是NfcDispatcher实例,初始化也是在NfcService启动的时候在构造当中 27    //mNfcDispatcher = new NfcDispatcher(mContext, mHandoverDataParser, mInProvisionMode); 28    //传入的mHandoverDataParser = new HandoverDataParser();同样是在构造当中 29    int dispatchResult = mNfcDispatcher.dispatchTag(tag); 30    //分发完毕后的处理: 31    //当失败的时候 32    if (dispatchResult == NfcDispatcher.DISPATCH_FAIL) { 33        unregisterObject(tagEndpoint.getHandle()); 34        //屏幕处于亮屏且解锁状态的时候: 35        //弹出:No supported apps for this tag installed on this device 36        //表示没有任何的activity处理当前的分发! 37        if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) { 38            String toastString = mContext.getString( 39                    R.string.nfc_strings_toast_prompt_read_error_txt); 40            //弹出Toast进行提示. 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        //overrideFilters的赋值是外部的app,运行在前台,想要处理Nfc的Tag使用的 9        //外部通过调用NfcService的setForegroundDispatch来实现。 10        overrideFilters = mOverrideFilters; 11        overrideIntent = mOverrideIntent; 12        overrideTechLists = mOverrideTechLists; 13        provisioningOnly = mProvisioningOnly; 14        provisioningMimes = mProvisioningMimes; 15   } 16    //在屏幕是SCREEN_STATE_ON_LOCKED的状态下尝试调用handleNfcUnlock去解锁屏幕。 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    //将Tag解析成Ndef的格式.此处是所读取的卡片的所有Tech中只要有Ndef的Tech即可. 28    //返回null的时候证明这个Tag不是NDEF formatted的,或者说这个Tag虽然是NDEF formatted的 29    //但是不是Android所支持的. 30    Ndef ndef = Ndef.get(tag); 31    if (ndef != null) { 32        //再通过这样获取到NdefMessage,此处的疑问,前面不是已经获取到NdefMessage 33        //为什么还要再用NdefMessage生成个Tag,然后在通过Tag获取,看起来好像是为了方便framewok 34        //统一接口,供外部调用. 35        message = ndef.getCachedNdefMessage(); 36   } else { 37        //当是null的时候,先看看是不是NfcBarcode Tech 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    //DispatchInfo是在Tag分发的过程中的帮助类,源码分析见后面 45    DispatchInfo dispatch = new DispatchInfo(mContext, tag, message); 46    //Tells the ActivityManager to resume allowing app switches. 47    resumeAppSwitches(); 48     49    //如果正在运行的APP有定义了前台分发机制,则会走到这里,就是某个app注册了一些接口 50    //并且运行在前台,它想去读取这些信息 51    if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters, 52            overrideTechLists)) { 53        return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 54   } 55    //尝试使用Handover来分发消息.就是带Nfc的蓝牙鼠标、键盘等.可以看到在普通的系统分发之前. 56    //通过BT传输图片不属于这个,那个是P2P的流程中的.忽然想起来这应该就是所谓的Hadover和Beam区分吧. 57    if (tryPeripheralHandover(message)) { 58        if (DBG) Log.i(TAG, "matched BT HANDOVER"); 59        return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 60   } 61    //WIFI相关的暂无了解 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    //下面开始根据优先级先将Tag消息发送给对action为ACTION_NDEF_DISCOVERED感兴趣的APP处理 . 68    if (tryNdef(dispatch, message)) { 69        return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 70   } 71    //将Tag消息发送给对ACTION_TECH_DISCOVERED感兴趣的APP处理   72    if (tryTech(dispatch, tag)) { 73        return DISPATCH_SUCCESS; 74   } 75    //如上两个action相关的activity都未处理的时候 76    //将intent的action设置为ACTION_TAG_DISCOVERED 77    dispatch.setTagIntent(); 78    if (dispatch.tryStartActivity()) { 79        if (DBG) Log.i(TAG, "matched TAG"); 80        return DISPATCH_SUCCESS; 81   } 82    //都无法处理就返回false. 83    if (DBG) Log.i(TAG, "no match"); 84    return DISPATCH_FAIL; 85 }       DispatchInfo类的源码分析: 1 /** 2 * Helper for re-used objects and methods during a single tag dispatch. 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        //构造会直接实例化一个intent,然后把tag先放入. 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            //当传入的是null的时候,就是Ndef.get(tag)不能获取正确的值. 25            ndefUri = null; 26            ndefMimeType = null; 27       } 28        //这个NfcRootActivity会直接去启动,Intent携带的EXTRA_LAUNCH_INTENT对应的activity 29        //对应到下面也就是启动,最一开始的intent = new Intent(); 30        //此时把你要启动的activity对应的intent添加到成员变量intent中即可. 31        //这样好像是为了提供NFC root task. 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    //设置注册action:ACTION_NDEF_DISCOVERED的intent. 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    //设置注册action:ACTION_TECH_DISCOVERED的intent. 53    public Intent setTechIntent() { 54        intent.setData(null); 55        intent.setType(null); 56        intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED); 57        return intent; 58   } 59    //设置注册action:ACTION_TAG_DISCOVERED的intent. 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    //以成员变量intent,启动所有的注册的activity 68    boolean tryStartActivity() { 69        //遍历所有符合成员变量intent要求的acitivity 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    //以特定的intentToStart,启动所有注册的activity 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    // 首先是当NdefMessage存在的时候,先去判断NDEF格式的消息 5    if (message != null) { 6        intent = dispatch.setNdefIntent(); 7        //当当前的NdefMessage中是对应的NDEF消息时,使用传入的PendingIntent去启动对应的activity 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    // TECH 21    intent = dispatch.setTechIntent(); 22    //当传入的Tag的Technolg也是支持的时候,把action设置成ACTION_NDEF_DISCOVERED去启动 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    // TAG 和上面同理。 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    //假如是进行Handover的时候,会按照Nfc Form中的Handover相关的协议从NdefMessage中解析出正确的数据 6    HandoverDataParser.BluetoothHandoverData handover = 7        mHandoverDataParser.parseBluetooth(m); 8   ...... 9    //得到handover的各个参数以后,设置到intent内,并启动PeripheralHandoverService这个service. 10    //通过下面可以看到,主要有BT的 device、name、uuid、等 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    //国内过CTA检测的时候的弹框 31    if (CtaUtils.showCtaBtDialogIfNeeded(mContext, null, intent, null)) { 32        return true; 33   } 34    //最后启动PeripheralHandoverService. 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        //进一步的回去打开BT,前面也注册了广播,在收到蓝牙开启广播以后开始做真正的Handover 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        //为dispatch设置ACTION_NDEF_DISCOVERED相关的intent. 4        Intent intent = dispatch.setNdefIntent(); 5        if (intent == null) return false; 6        //如果发送的NdefMessage包含打开这个消息的包的信息(AAR) 7        //那么就用指定的包处理这个Tag消息 8        List<String> aarPackages = extractAarPackages(message); 9        for (String pkg : aarPackages) { 10            dispatch.intent.setPackage(pkg); 11            //直接使用指定的apk去打开 12            if (dispatch.tryStartActivity()) { 13                if (DBG) Log.i(TAG, "matched AAR to NDEF"); 14                return true; 15           } 16       } 17        //到下面应该aar包中没有对ACTION_NDEF_DISCOVERED这个action感兴趣的activity. 18        //然后就区启动aar中的第一个包含的apk 19        // Try to perform regular launch of the first AAR 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            // Find the package in Market: (看这架势让我们去下载呀,这么流氓哈) 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        //当消息中没有包名的时候,就用前面设置的intent:setNdefIntent 43        //来启动activity,所以此时所有注册的activity都会受到这个action,然后你可以选择用那个启动 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        //tagServices 在 packages/app/Nfc中实现的 5        INfcTag tagService = mTag.getTagService(); 6       ...... 7        int serviceHandle = mTag.getServiceHandle(); 8        if (tagService.isNdef(serviceHandle)) { 9            //调用tagService的ndefWrite实现真正的写入 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                    // Should not happen 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    //可以看到去native层进行进一步的读写! 8    boolean result = doWrite(buf);//go to the native 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
转载请注明原文地址: https://www.6miu.com/read-8518.html

最新回复(0)