[NFC]Tag设备响应流程

来源:互联网 发布:算法韦恩图 编辑:程序博客网 时间:2024/06/03 23:39

接上部分的分析,当前系统已经进入到applyRouting()阶段,后续应该是需要一直去监听当前是否有NFC设备进入通讯范围。如果有适合的NFC设备,则底层会先进行沟通,并将消息通知给上层。


进入NFC设备发现流程

        下面从applyRouting()函数开始分析,可以参考系统注释:

[java] view plain copy
 print?
  1. void applyRouting(boolean force) {  
  2.     synchronized (this) {  
  3.         //@paul: 如果NFC没有打开或者已经关闭,则直接发挥  
  4.         if (!isNfcEnabledOrShuttingDown()) {  
  5.             return;  
  6.         }  
  7.         ...  
  8.         if (mInProvisionMode) {  
  9.             mInProvisionMode = Settings.Secure.getInt(mContentResolver,  
  10.                     Settings.Global.DEVICE_PROVISIONED, 0) == 0;  
  11.             if (!mInProvisionMode) {  
  12.                 //@paul: 原生的Android里面Provision只做了一件事,就是写入一个DEVICE_PROVISIONED标记。  
  13.                 //@paul: 不过这个标记作用很大,这个标记只会在系统全新升级(双清)的时候写入一次,代表了Android系统升级准备完成,可以正常工作。  
  14.                 mNfcDispatcher.disableProvisioningMode();  
  15.                 mHandoverManager.setEnabled(true);  
  16.             }  
  17.         }  
  18.           
  19.         //@paul: 如果有tag正在通讯时,delay一段时间再更新参数  
  20.         // Special case: if we're transitioning to unlocked state while  
  21.         // still talking to a tag, postpone re-configuration.  
  22.         if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && isTagPresent()) {  
  23.             Log.d(TAG, "Not updating discovery parameters, tag connected.");  
  24.             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESUME_POLLING),  
  25.                     APPLY_ROUTING_RETRY_TIMEOUT_MS);  
  26.             return;  
  27.         }  
  28.   
  29.         try {  
  30.             watchDog.start();  
  31.             //@paul: 依据前面初始化的参数来更新NfcDiscoveryParameters  
  32.             // Compute new polling parameters  
  33.             NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);  
  34.             if (force || !newParams.equals(mCurrentDiscoveryParameters)) {  
  35.                 //@paul: 判断条件为:mTechMask != 0 || mEnableHostRouting  
  36.                 //@paul:mTechMask一般不为0,mEnableHostRouting一般为false  
  37.                 if (newParams.shouldEnableDiscovery()) {  
  38.                     boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();  
  39.                     //@paul:系统一般会进入enableDiscovery()  
  40.                     mDeviceHost.enableDiscovery(newParams, shouldRestart);  
  41.                 } else {  
  42.                     mDeviceHost.disableDiscovery();  
  43.                 }  
  44.                 mCurrentDiscoveryParameters = newParams;  
  45.             } else {  
  46.                 Log.d(TAG, "Discovery configuration equal, not updating.");  
  47.             }  
  48.         } finally {  
  49.             watchDog.cancel();  
  50.         }  
  51.     }  
  52. }  

        由于系统调用到enableDiscovery()函数,此函数不论是nxp还是nci,都会调用到native函数doEnableDiscovery(),继续追踪此函数(后续都已NXP为例),最终调用到com_android_nfc_NativeNfcManager.cpp中的:

[cpp] view plain copy
 print?
  1. {"doEnableDiscovery""(IZZZ)V",  
  2.    (void *)com_android_nfc_NfcManager_enableDiscovery},  

        继续追踪com_android_nfc_NfcManager_enableDiscovery():

[cpp] view plain copy
 print?
  1. static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o, jint modes,  
  2.         jboolean, jboolean reader_mode, jboolean restart)  
  3. {  
  4.     ...  
  5.   
  6.    /* Register callback for remote device notifications. 
  7.     * Must re-register every time we turn on discovery, since other operations 
  8.     * (such as opening the Secure Element) can change the remote device 
  9.     * notification callback*/  
  10.     //@paul: 注册侦听到NFC设备时的回调函数,后续的流程从此开始  
  11.    REENTRANCE_LOCK();  
  12.    ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info, nfc_jni_Discovery_notification_callback, (void *)nat);  
  13.    REENTRANCE_UNLOCK();  
  14.      
  15.     ...  
  16.     //@paul: 启动discovery流程  
  17.     nfc_jni_start_discovery_locked(nat, restart);  
  18. clean_and_return:  
  19.     CONCURRENCY_UNLOCK();  
  20. }  

        此处的两个函数nfc_jni_start_discovery_locked()和nfc_jni_Discovery_notification_callback()都需要在深入一点,一个一个的看:

[cpp] view plain copy
 print?
  1. static void nfc_jni_start_discovery_locked(struct nfc_jni_native_data *nat, bool resume)  
  2. {  
  3.     ...  
  4. configure:  
  5.    /* Start Polling loop */  
  6.    TRACE("******  Start NFC Discovery ******");  
  7.    REENTRANCE_LOCK();  
  8.    //@paul: nfc_jni_discover_callback()是discover后消息notify的开始  
  9.    ret = phLibNfc_Mgt_ConfigureDiscovery(resume ? NFC_DISCOVERY_RESUME : NFC_DISCOVERY_CONFIG,  
  10.       nat->discovery_cfg, nfc_jni_discover_callback, (void *)&cb_data);  
  11.    REENTRANCE_UNLOCK();  
  12.   
  13.    ...  
  14.      
  15. clean_and_return:  
  16.    nfc_cb_data_deinit(&cb_data);  
  17. }  

        一旦上面进入了nfc_jni_discover_callback(),后续就会进入nfc_jni_Discovery_notification_callback(),此函数就会把底层看到的信息开始一层一层的notify:

[cpp] view plain copy
 print?
  1. static void nfc_jni_Discovery_notification_callback(void *pContext,  
  2.    phLibNfc_RemoteDevList_t *psRemoteDevList,  
  3.    uint8_t uNofRemoteDev, NFCSTATUS status)  
  4. {  
  5.     ...  
  6.   
  7.    if(status == NFCSTATUS_DESELECTED)  
  8.    {  
  9.       LOG_CALLBACK("nfc_jni_Discovery_notification_callback: Target deselected", status);  
  10.   
  11.       /* Notify manager that a target was deselected */  
  12.       //@paul: 执行cached_NfcManager_notifyTargetDeselected对应的java函数  
  13.       e->CallVoidMethod(nat->manager, cached_NfcManager_notifyTargetDeselected);  
  14.       if(e->ExceptionCheck())  
  15.       {  
  16.          ALOGE("Exception occurred");  
  17.          kill_client(nat);  
  18.       }  
  19.    }  
  20.    else  
  21.    {  
  22.     ...  
  23.       if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)  
  24.           || (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))  
  25.       {  
  26.         ...  
  27.          /* Set P2P Target mode */  
  28.          jfieldID f = e->GetFieldID(tag_cls.get(), "mMode""I");  
  29.   
  30.          if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)  
  31.          {  
  32.             ALOGD("Discovered P2P Initiator");  
  33.             e->SetIntField(tag.get(), f, (jint)MODE_P2P_INITIATOR);  
  34.          }  
  35.          else  
  36.          {  
  37.             ALOGD("Discovered P2P Target");  
  38.             e->SetIntField(tag.get(), f, (jint)MODE_P2P_TARGET);  
  39.          }  
  40.          ...  
  41.       }  
  42.       else  
  43.       {  
  44.         ...  
  45.           
  46.         /* New tag instance */  
  47.         ...  
  48.           
  49.         /* Set tag UID */  
  50.         ...  
  51.   
  52.         /* Generate technology list */  
  53.         ...  
  54.       }  
  55.   
  56.       ...  
  57.   
  58.       /* Notify the service */  
  59.       if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)  
  60.           || (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))  
  61.       {  
  62.          /* Store the handle of the P2P device */  
  63.          hLlcpHandle = remDevHandle;  
  64.   
  65.          /* Notify manager that new a P2P device was found */  
  66.          //@paul: 侦测到P2P设备进入范围,调用JNI层对应的API,最终call到JAVA层API  
  67.          e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());  
  68.          if(e->ExceptionCheck())  
  69.          {  
  70.             ALOGE("Exception occurred");  
  71.             kill_client(nat);  
  72.          }  
  73.       }  
  74.       else  
  75.       {  
  76.          /* Notify manager that new a tag was found */  
  77.          //@paul:侦测到Tag设备进入范围,  
  78.          e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());  
  79.          if(e->ExceptionCheck())  
  80.          {  
  81.             ALOGE("Exception occurred");  
  82.             kill_client(nat);  
  83.          }  
  84.       }  
  85.    }  
  86. }  

        所以Tag的真正开始时在执行下列函数后:

[cpp] view plain copy
 print?
  1. e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());  

        最终调用到JNI层的notifyNdefMessageListeners(),对应的定义在:

[java] view plain copy
 print?
  1. private void notifyNdefMessageListeners(NativeNfcTag tag) {  
  2.     mListener.onRemoteEndpointDiscovered(tag);  
  3. }  

      

        为后文作准备,P2P设备的Framework开始时在执行下列函数:

[cpp] view plain copy
 print?
  1. e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());  

        最终调用到JNI层的notifyLlcpLinkActivation(),对应的定义在:

[java] view plain copy
 print?
  1. private void notifyLlcpLinkActivation(NativeP2pDevice device) {  
  2.     mListener.onLlcpLinkActivated(device);  
  3. }  


Tag设备发现framework流程

        下面开始分析Tag的Framework的流程:我们的分析会从mListener.onRemoteEndpointDiscovered(tag)开始:

[java] view plain copy
 print?
  1. @Override  
  2. public void onRemoteEndpointDiscovered(TagEndpoint tag) {  
  3.     sendMessage(NfcService.MSG_NDEF_TAG, tag);  
  4. }  

        其中发送的消息MSG_NDEF_TAG会进入到NfcService.java的handleMessage(),其中处理MSG_NDEF_TAG的流程如下:

[java] view plain copy
 print?
  1. case MSG_NDEF_TAG:  
  2.     if (DBG) Log.d(TAG, "Tag detected, notifying applications");  
  3.     ...  
  4.       
  5.     //@paul: 如果是read mode  
  6.     if (readerParams != null) {  
  7.         presenceCheckDelay = readerParams.presenceCheckDelay;  
  8.         //@paul: 如果设置不检查能否转成NDEF的标记  
  9.         if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {  
  10.             if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");  
  11.             //@paul: 判断tag是否还在范围内  
  12.             tag.startPresenceChecking(presenceCheckDelay, callback);  
  13.             //@paul: 将侦测的tag进行分发  
  14.             dispatchTagEndpoint(tag, readerParams);  
  15.             break;  
  16.         }  
  17.     }  
  18.   
  19.     boolean playSound = readerParams == null ||  
  20.         (readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0;  
  21.     if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && playSound) {  
  22.         //paul: 播放开始声音  
  23.         playSound(SOUND_START);  
  24.     }  
  25.     if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {  
  26.         // When these tags start containing NDEF, they will require  
  27.         // the stack to deal with them in a different way, since  
  28.         // they are activated only really shortly.  
  29.         // For now, don't consider NDEF on these.  
  30.         if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");  
  31.         //@paul: 如果是Barcode,直接进行tag的分发  
  32.         tag.startPresenceChecking(presenceCheckDelay, callback);  
  33.         dispatchTagEndpoint(tag, readerParams);  
  34.         break;  
  35.     }  
  36.       
  37.     //@paul: 依据底层格式,进行NDEF的转换  
  38.     NdefMessage ndefMsg = tag.findAndReadNdef();  
  39.   
  40.     if (ndefMsg != null) {  
  41.         //@paul: NDEF转换成功后,进行Tag的分发  
  42.         tag.startPresenceChecking(presenceCheckDelay, callback);  
  43.         dispatchTagEndpoint(tag, readerParams);  
  44.     } else {  
  45.         //@paul: 无法转换时,先进行底层的连接后(物理层先重连),将消息分发  
  46.         if (tag.reconnect()) {  
  47.             tag.startPresenceChecking(presenceCheckDelay, callback);  
  48.             dispatchTagEndpoint(tag, readerParams);  
  49.         } else {  
  50.             tag.disconnect();  
  51.             playSound(SOUND_ERROR);  
  52.         }  
  53.     }  
  54.     break;  

        其中比较关键的函数有两个:

                findAndReadNdef()

                dispatchTagEndpoint()


        findAndReadNdef()是和芯片强相关的,其目的是依据芯片的支持能力,将读到的Tag中的内容转换成NDEF格式的数据. 不同芯片其支持的能力存在差异,此部分的code也是存在差异,针对NXP芯片简单分析如下:

[java] view plain copy
 print?
  1. @Override  
  2. public NdefMessage findAndReadNdef() {  
  3.     ...  
  4.     for (int techIndex = 0; techIndex < technologies.length; techIndex++) {  
  5.         ...  
  6.           
  7.         //@paul: 判断connectedHandle与当前Index对应的handle的关系,并更新状态,  
  8.         status = connectWithStatus(technologies[techIndex]);  
  9.         ...  
  10.           
  11.         // Check if this type is NDEF formatable  
  12.         if (!foundFormattable) {  
  13.             //@paul: 依据芯片特性判断哪些tag是可以转成NDEF格式  
  14.             if (isNdefFormatable()) {  
  15.                 //@paul: 更新对应的handle,handle用于后续的操作  
  16.                 foundFormattable = true;  
  17.                 formattableHandle = getConnectedHandle();  
  18.                 formattableLibNfcType = getConnectedLibNfcType();  
  19.                 // We'll only add formattable tech if no ndef is  
  20.                 // found - this is because libNFC refuses to format  
  21.                 // an already NDEF formatted tag.  
  22.             }  
  23.             reconnect();  
  24.         }  
  25.         ...  
  26.           
  27.         status = checkNdefWithStatus(ndefinfo);  
  28.         ...  
  29.           
  30.         //@paul: 读取tag上的数据  
  31.         byte[] buff = readNdef();  
  32.         if (buff != null) {  
  33.             try {  
  34.                 //@paul: 将数据转换成NDEF格式  
  35.                 ndefMsg = new NdefMessage(buff);  
  36.                 //@paul: 更新对应Tag的信息  
  37.                 addNdefTechnology(ndefMsg,  
  38.                         getConnectedHandle(),  
  39.                         getConnectedLibNfcType(),  
  40.                         getConnectedTechnology(),  
  41.                         supportedNdefLength, cardState);  
  42.                 reconnect();  
  43.             } catch (FormatException e) {  
  44.                // Create an intent anyway, without NDEF messages  
  45.                generateEmptyNdef = true;  
  46.             }  
  47.         } else {  
  48.             generateEmptyNdef = true;  
  49.         }  
  50.         ...       
  51.     }  
  52.   
  53.     if (ndefMsg == null && foundFormattable) {  
  54.         // Tag is not NDEF yet, and found a formattable target,  
  55.         // so add formattable tech to tech list.  
  56.         addNdefFormatableTechnology(  
  57.                 formattableHandle,  
  58.                 formattableLibNfcType);  
  59.     }  
  60.   
  61.     return ndefMsg;  
  62. }  


Tag消息分发流程

        上述执行完成后,理论上会进入Tag的Dispatch流程,中间的流程省略,我们直接进入mNfcDispatcher.dispatchTag(tag)函数的分析,这才是tag分发的终极形式,直接看代码吧:

[java] view plain copy
 print?
  1. /** Returns: 
  2.  * <ul> 
  3.  *  <li /> DISPATCH_SUCCESS if dispatched to an activity, 
  4.  *  <li /> DISPATCH_FAIL if no activities were found to dispatch to, 
  5.  *  <li /> DISPATCH_UNLOCK if the tag was used to unlock the device 
  6.  * </ul> 
  7.  */  
  8. public int dispatchTag(Tag tag) {  
  9.     PendingIntent overrideIntent;  
  10.     IntentFilter[] overrideFilters;  
  11.     String[][] overrideTechLists;  
  12.     boolean provisioningOnly;  
  13.   
  14.     //@paul: 如果上层APP定义了下列值,就会在这里进行更新  
  15.     synchronized (this) {  
  16.         overrideFilters = mOverrideFilters;  
  17.         overrideIntent = mOverrideIntent;  
  18.         overrideTechLists = mOverrideTechLists;  
  19.         provisioningOnly = mProvisioningOnly;  
  20.     }  
  21.   
  22.     //@paul: Tag在screen on 且lock的情况下,需要尝试unlock,否则如法处理  
  23.     boolean screenUnlocked = false;  
  24.     if (!provisioningOnly &&  
  25.             mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {  
  26.         screenUnlocked = handleNfcUnlock(tag);  
  27.         if (!screenUnlocked) {  
  28.             return DISPATCH_FAIL;  
  29.         }  
  30.     }  
  31.   
  32.     NdefMessage message = null;  
  33.     //@paul: 将Tag解析成NDEF格式数据,并读取内容  
  34.     Ndef ndef = Ndef.get(tag);  
  35.     if (ndef != null) {  
  36.         message = ndef.getCachedNdefMessage();  
  37.     }  
  38.   
  39.     if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);  
  40.   
  41.     DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);  
  42.   
  43.     //@paul: 和APP相关,暂时没有研究  
  44.     resumeAppSwitches();  
  45.   
  46.     //@paul: 如果上层APP有定义前台分发机制,则会调用到PendingIntent.send()功能,实现前台分发机制  
  47.     if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,  
  48.             overrideTechLists)) {  
  49.         return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;  
  50.     }  
  51.   
  52.     //@paul: 判断NDEF消息是否是handover格式  
  53.     if (mHandoverManager.tryHandover(message)) {  
  54.         if (DBG) Log.i(TAG, "matched BT HANDOVER");  
  55.         return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;  
  56.     }  
  57.   
  58.     //@paul: 判断NDEF消息是否是WifiConfiguration格式  
  59.     if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {  
  60.         if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");  
  61.         return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;  
  62.     }  
  63.   
  64.     //@paul: 将消息发送给对ACTION_NDEF_DISCOVERED感兴趣的APP处理  
  65.     if (tryNdef(dispatch, message, provisioningOnly)) {  
  66.         return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;  
  67.     }  
  68.       
  69.     if (screenUnlocked) {  
  70.         // We only allow NDEF-based mimeType matching in case of an unlock  
  71.         return DISPATCH_UNLOCK;  
  72.     }  
  73.   
  74.     if (provisioningOnly) {  
  75.         // We only allow NDEF-based mimeType matching  
  76.         return DISPATCH_FAIL;  
  77.     }  
  78.   
  79.     // Only allow NDEF-based mimeType matching for unlock tags  
  80.     //@paul: <span style="font-family: Arial, Helvetica, sans-serif;">将消息发送给对</span>ACTION_TECH_DISCOVERED感兴趣的APP处理  
  81.     if (tryTech(dispatch, tag)) {  
  82.         return DISPATCH_SUCCESS;  
  83.     }  
  84.   
  85.     //@paul: 更新Intent为ACTION_TAG_DISCOVERED  
  86.     dispatch.setTagIntent();  
  87.     //@paul: 将消息发送给对ACTION_TAG_DISCOVERED感兴趣的APP处理  
  88.     if (dispatch.tryStartActivity()) {  
  89.         if (DBG) Log.i(TAG, "matched TAG");  
  90.         return DISPATCH_SUCCESS;  
  91.     }  
  92.   
  93.     if (DBG) Log.i(TAG, "no match");  
  94.     return DISPATCH_FAIL;  
  95. }  

        下面分别介绍tryHandover(),tryNfcWifiSetup(),tryNdef(),tryTech().


         首先看看tryHandover(),此函数主要做BlueTooth的handover,当然如果要做wifi的handover,从技术上看是完全没有问题的.

[java] view plain copy
 print?
  1. public boolean tryHandover(NdefMessage m) {  
  2.     ...  
  3.     BluetoothHandoverData handover = parseBluetooth(m);  
  4.     ...  
  5.     synchronized (mLock) {  
  6.         ...  
  7. <span style="white-space:pre">        </span>//@paul: 发送MSG_PERIPHERAL_HANDOVER消息,在HandleMessage中进行处理  
  8.         Message msg = Message.obtain(null, HandoverService.MSG_PERIPHERAL_HANDOVER, 00);  
  9.         Bundle headsetData = new Bundle();  
  10.         headsetData.putParcelable(HandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);  
  11.         headsetData.putString(HandoverService.EXTRA_PERIPHERAL_NAME, handover.name);  
  12.         headsetData.putInt(HandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);  
  13.         msg.setData(headsetData);  
  14.         return sendOrQueueMessageLocked(msg);  
  15.     }  
  16. }  

        比较重要的就是parseBluetooth()和最后的sendOrQueueMessageLocked()函数。其中parseBluetooth逻辑比较简单,就是按照Spec的要求,把NDEF数据一个字节一个字节的解析出来,存放在对应的结构体中。sendOrQueueMessageLocked则是将BT的信息发送给上述完整的代码分析如下:

[java] view plain copy
 print?
  1. BluetoothHandoverData parseBluetooth(NdefMessage m) {  
  2.     ...  
  3.       
  4.     // Check for BT OOB record  
  5.     if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BT_OOB)) {  
  6.         return parseBtOob(ByteBuffer.wrap(r.getPayload()));  
  7.     }  
  8.   
  9.     // Check for BLE OOB record  
  10.     if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BLE_OOB)) {  
  11.         return parseBleOob(ByteBuffer.wrap(r.getPayload()));  
  12.     }  
  13.   
  14.     // Check for Handover Select, followed by a BT OOB record  
  15.     if (tnf == NdefRecord.TNF_WELL_KNOWN &&  
  16.             Arrays.equals(type, NdefRecord.RTD_HANDOVER_SELECT)) {  
  17.         return parseBluetoothHandoverSelect(m);  
  18.     }  
  19.   
  20.     // Check for Nokia BT record, found on some Nokia BH-505 Headsets  
  21.     if (tnf == NdefRecord.TNF_EXTERNAL_TYPE && Arrays.equals(type, TYPE_NOKIA)) {  
  22.         return parseNokia(ByteBuffer.wrap(r.getPayload()));  
  23.     }  
  24.   
  25.     return null;  
  26. }  
  27.   
  28.   
  29.   
  30. public boolean sendOrQueueMessageLocked(Message msg) {  
  31.     if (!mBound || mService == null) {  
  32.         // Need to start service, let us know if we can queue the message  
  33.         //@paul: 首先bind Service,成功后会调用onServiceConnected()  
  34.         if (!bindServiceIfNeededLocked()) {  
  35.             Log.e(TAG, "Could not start service");  
  36.             return false;  
  37.         }  
  38.         // Queue the message to send when the service is bound  
  39.         //@paul: 先将消息放在缓存队列中  
  40.         mPendingServiceMessages.add(msg);  
  41.     } else {  
  42.         try {  
  43.             //@paul: 已经绑定后,则直接诶发送消息  
  44.             mService.send(msg);  
  45.         } catch (RemoteException e) {  
  46.             Log.e(TAG, "Could not connect to handover service");  
  47.             return false;  
  48.         }  
  49.     }  
  50.     return true;  
  51. }  
  52.   
  53.   
  54.   
  55. public void onServiceConnected(ComponentName name, IBinder service) {  
  56.     synchronized (mLock) {  
  57.         ...  
  58.         try {  
  59.             //@paul: 直接发送消息  
  60.             mService.send(msg);  
  61.         } catch (RemoteException e) {  
  62.             Log.e(TAG, "Failed to register client");  
  63.         }  
  64.         // Send all queued messages  
  65.         while (!mPendingServiceMessages.isEmpty()) {  
  66.             //@paul: 如果mPendingServiceMessages有数据是,会一直尝试把pending的数据发送完成  
  67.             msg = mPendingServiceMessages.remove(0);  
  68.             try {  
  69.                 mService.send(msg);  
  70.             } catch (RemoteException e) {  
  71.                 Log.e(TAG, "Failed to send queued message to service");  
  72.             }  
  73.         }  
  74.     }  
  75. }  


        前文提到发送了MSG_PERIPHERAL_HANDOVER,最终进入doPeripheralHandover()函数,目前只是分析到BT流程启动,后续建立连接部分暂不分析了。

[java] view plain copy
 print?
  1. void doPeripheralHandover(Message msg) {  
  2.     Bundle msgData = msg.getData();  
  3.     BluetoothDevice device = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);  
  4.     String name = msgData.getString(EXTRA_PERIPHERAL_NAME);  
  5.     int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);  
  6.     //@paul: 如果存在mBluetoothPeripheralHandover,表明有handover正在进行  
  7.     if (mBluetoothPeripheralHandover != null) {  
  8.        Log.d(TAG, "Ignoring pairing request, existing handover in progress.");  
  9.        return;  
  10.     }  
  11.     //@paul: mBluetoothPeripheralHandover  
  12.     mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(HandoverService.this,  
  13.             device, name, transport, HandoverService.this);  
  14.               
  15.     // TODO: figure out a way to disable polling without deactivating current target  
  16.     if (transport == BluetoothDevice.TRANSPORT_LE) {  
  17.         mHandler.sendMessageDelayed(  
  18.                 mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);  
  19.     }  
  20.       
  21.     //@paul: 如果Bluetooth有启动,则执行对应的start函数  
  22.     if (mBluetoothAdapter.isEnabled()) {  
  23.         if (!mBluetoothPeripheralHandover.start()) {  
  24.             //@paul: Handover成功后,NFC继续polling  
  25.             mNfcAdapter.resumePolling();  
  26.         }  
  27.     } else {  
  28.         if (!enableBluetooth()) {  
  29.             Log.e(TAG, "Error enabling Bluetooth.");  
  30.             mBluetoothPeripheralHandover = null;  
  31.         }  
  32.     }  
  33. }  
  34.   
  35. //@paul:接上述start函数  
  36. public boolean start() {  
  37.     ...  
  38.   
  39.     //paul: 目前只关注此函数  
  40.     nextStep();  
  41.   
  42.     return true;  
  43. }  
  44.   
  45.   
  46. //@paul: 进入对应的状态机,启动BT连接  
  47. void nextStep() {  
  48.     if (mAction == ACTION_INIT) {  
  49.         nextStepInit();  
  50.     } else if (mAction == ACTION_CONNECT) {  
  51.         nextStepConnect();  
  52.     } else {  
  53.         nextStepDisconnect();  
  54.     }  
  55. }  


        分析完Handover,可能已经觉得有点累了,那我们来个简单的,看看tryNfcWifiSetup(),其实这部分和BT的原理基本类似,就是透过NFC拿到WIFI相关的credential,然后利用credential简历WIFI连接,实现Handover的目的。

[java] view plain copy
 print?
  1. public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {  
  2.     ...  
  3.   
  4.     //@paul: 解析得到Wifi相关的credential数据  
  5.     final WifiConfiguration wifiConfiguration = parse(cachedNdefMessage);  
  6.   
  7.     if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(  
  8.             UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {  
  9.         Intent configureNetworkIntent = new Intent()  
  10.                 .putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)  
  11.                 .setClass(context, ConfirmConnectToWifiNetworkActivity.class)  
  12.                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  13.   
  14.         //@paul: 将wifi数据放在intent中,然后以设置的参数启动wifi相关的连接  
  15.         context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);  
  16.         return true;  
  17.     }  
  18.   
  19.     return false;  
  20. }  


       接下来就是tryNdef()和tryTech()的分析,其实此部分的流程是前面提到的ACTION_TAG_DISCOVERED的流程是类似,主要是分两步:

        1. 设置对应的intent为:ACTION_NDEF_DISCOVERED/ACTION_TECH_DISCOVERED

        2. 以上述intent启动相关的activity. 只不过会加入AAR消息的检查,关于AAR的说明可以自行查找。简单的说明如下:

[plain] view plain copy
 print?
  1. 在Android4.0(API Level 14)中引入的Android应用程序记录(AAR),提供了较强的在扫描到NFC标签时,启动应用程序的确定性。AAR有嵌入到NDEF记录内部的应用程序的包名。你能够把一个AAR添加到你的NDEF消息的任何记录中,因为Android会针对AAR来搜索整个NDEF消息。如果它找到一个AAR,它就会基于AAR内部的包名来启动应用程序。如果该应用程序不在当前的设备上,会启动Google Play来下载对应的应用程序。  
  2.   
  3. 如果你想要防止其他的应用对相同的Intent的过滤并潜在的处理你部署的特定的NFC标签,那么AAR是有用的。AAR仅在应用程序级被支持,因为包名的约束,并不能在Activity级别来过滤Intent。如果你想要在Activity级处理Intent,请使用Intent过滤器。  


        以上基本就介绍完Tag的整体处理流程。代码流程稍微有点多,建议代码分几次看,以免遗忘。


         后续会在介绍一下NFC P2P设备相互发现的流程。由于P2P的应用比较多,介绍的篇幅也会相对较多

原创粉丝点击