[NFC]Tag設備響應流程

        接上部分的分析,當前系統已經進入到applyRouting()階段,後續應該是需要一直去監聽當前是否有NFC設備進入通訊範圍。如果有適合的NFC設備,則底層會先進行溝通,並將消息通知給上層。


進入NFC設備發現流程

        下面從applyRouting()函數開始分析,可以參考系統註釋:

void applyRouting(boolean force) {
	synchronized (this) {
		//@paul: 如果NFC沒有打開或者已經關閉,則直接發揮
		if (!isNfcEnabledOrShuttingDown()) {
			return;
		}
		...
		if (mInProvisionMode) {
			mInProvisionMode = Settings.Secure.getInt(mContentResolver,
					Settings.Global.DEVICE_PROVISIONED, 0) == 0;
			if (!mInProvisionMode) {
				//@paul: 原生的Android裏面Provision只做了一件事,就是寫入一個DEVICE_PROVISIONED標記。
				//@paul: 不過這個標記作用很大,這個標記只會在系統全新升級(雙清)的時候寫入一次,代表了Android系統升級準備完成,可以正常工作。
				mNfcDispatcher.disableProvisioningMode();
				mHandoverManager.setEnabled(true);
			}
		}
		
		//@paul: 如果有tag正在通訊時,delay一段時間再更新參數
		// Special case: if we're transitioning to unlocked state while
		// still talking to a tag, postpone re-configuration.
		if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && isTagPresent()) {
			Log.d(TAG, "Not updating discovery parameters, tag connected.");
			mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESUME_POLLING),
					APPLY_ROUTING_RETRY_TIMEOUT_MS);
			return;
		}

		try {
			watchDog.start();
			//@paul: 依據前面初始化的參數來更新NfcDiscoveryParameters
			// Compute new polling parameters
			NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
			if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
				//@paul: 判斷條件爲:mTechMask != 0 || mEnableHostRouting
				//@paul:mTechMask一般不爲0,mEnableHostRouting一般爲false
				if (newParams.shouldEnableDiscovery()) {
					boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
					//@paul:系統一般會進入enableDiscovery()
					mDeviceHost.enableDiscovery(newParams, shouldRestart);
				} else {
					mDeviceHost.disableDiscovery();
				}
				mCurrentDiscoveryParameters = newParams;
			} else {
				Log.d(TAG, "Discovery configuration equal, not updating.");
			}
		} finally {
			watchDog.cancel();
		}
	}
}

        由於系統調用到enableDiscovery()函數,此函數不論是nxp還是nci,都會調用到native函數doEnableDiscovery(),繼續追蹤此函數(後續都已NXP爲例),最終調用到com_android_nfc_NativeNfcManager.cpp中的:

   {"doEnableDiscovery", "(IZZZ)V",
      (void *)com_android_nfc_NfcManager_enableDiscovery},

        繼續追蹤com_android_nfc_NfcManager_enableDiscovery():

static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o, jint modes,
        jboolean, jboolean reader_mode, jboolean restart)
{
	...

   /* Register callback for remote device notifications.
    * Must re-register every time we turn on discovery, since other operations
    * (such as opening the Secure Element) can change the remote device
    * notification callback*/
	//@paul: 註冊偵聽到NFC設備時的回調函數,後續的流程從此開始
   REENTRANCE_LOCK();
   ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info, nfc_jni_Discovery_notification_callback, (void *)nat);
   REENTRANCE_UNLOCK();
   
	...
	//@paul: 啓動discovery流程
    nfc_jni_start_discovery_locked(nat, restart);
clean_and_return:
    CONCURRENCY_UNLOCK();
}

        此處的兩個函數nfc_jni_start_discovery_locked()和nfc_jni_Discovery_notification_callback()都需要在深入一點,一個一個的看:

static void nfc_jni_start_discovery_locked(struct nfc_jni_native_data *nat, bool resume)
{
	...
configure:
   /* Start Polling loop */
   TRACE("******  Start NFC Discovery ******");
   REENTRANCE_LOCK();
   //@paul: nfc_jni_discover_callback()是discover後消息notify的開始
   ret = phLibNfc_Mgt_ConfigureDiscovery(resume ? NFC_DISCOVERY_RESUME : NFC_DISCOVERY_CONFIG,
      nat->discovery_cfg, nfc_jni_discover_callback, (void *)&cb_data);
   REENTRANCE_UNLOCK();

   ...
   
clean_and_return:
   nfc_cb_data_deinit(&cb_data);
}

        一旦上面進入了nfc_jni_discover_callback(),後續就會進入nfc_jni_Discovery_notification_callback(),此函數就會把底層看到的信息開始一層一層的notify:

static void nfc_jni_Discovery_notification_callback(void *pContext,
   phLibNfc_RemoteDevList_t *psRemoteDevList,
   uint8_t uNofRemoteDev, NFCSTATUS status)
{
	...

   if(status == NFCSTATUS_DESELECTED)
   {
      LOG_CALLBACK("nfc_jni_Discovery_notification_callback: Target deselected", status);

      /* Notify manager that a target was deselected */
	  //@paul: 執行cached_NfcManager_notifyTargetDeselected對應的java函數
      e->CallVoidMethod(nat->manager, cached_NfcManager_notifyTargetDeselected);
      if(e->ExceptionCheck())
      {
         ALOGE("Exception occurred");
         kill_client(nat);
      }
   }
   else
   {
	...
	  if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
          || (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
      {
		...
         /* Set P2P Target mode */
         jfieldID f = e->GetFieldID(tag_cls.get(), "mMode", "I");

         if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
         {
            ALOGD("Discovered P2P Initiator");
            e->SetIntField(tag.get(), f, (jint)MODE_P2P_INITIATOR);
         }
         else
         {
            ALOGD("Discovered P2P Target");
            e->SetIntField(tag.get(), f, (jint)MODE_P2P_TARGET);
         }
		 ...
      }
      else
      {
        ...
		
		/* New tag instance */
		...
		
        /* Set tag UID */
		...

        /* Generate technology list */
		...
      }

	  ...

      /* Notify the service */
      if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
          || (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
      {
         /* Store the handle of the P2P device */
         hLlcpHandle = remDevHandle;

         /* Notify manager that new a P2P device was found */
		 //@paul: 偵測到P2P設備進入範圍,調用JNI層對應的API,最終call到JAVA層API
         e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());
         if(e->ExceptionCheck())
         {
            ALOGE("Exception occurred");
            kill_client(nat);
         }
      }
      else
      {
         /* Notify manager that new a tag was found */
		 //@paul:偵測到Tag設備進入範圍,
         e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());
         if(e->ExceptionCheck())
         {
            ALOGE("Exception occurred");
            kill_client(nat);
         }
      }
   }
}

        所以Tag的真正開始時在執行下列函數後:

e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());

        最終調用到JNI層的notifyNdefMessageListeners(),對應的定義在:

    private void notifyNdefMessageListeners(NativeNfcTag tag) {
        mListener.onRemoteEndpointDiscovered(tag);
    }

      

        爲後文作準備,P2P設備的Framework開始時在執行下列函數:

e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());

        最終調用到JNI層的notifyLlcpLinkActivation(),對應的定義在:

    private void notifyLlcpLinkActivation(NativeP2pDevice device) {
        mListener.onLlcpLinkActivated(device);
    }


Tag設備發現framework流程

        下面開始分析Tag的Framework的流程:我們的分析會從mListener.onRemoteEndpointDiscovered(tag)開始:

    @Override
    public void onRemoteEndpointDiscovered(TagEndpoint tag) {
        sendMessage(NfcService.MSG_NDEF_TAG, tag);
    }

        其中發送的消息MSG_NDEF_TAG會進入到NfcService.java的handleMessage(),其中處理MSG_NDEF_TAG的流程如下:

case MSG_NDEF_TAG:
	if (DBG) Log.d(TAG, "Tag detected, notifying applications");
	...
	
	//@paul: 如果是read mode
	if (readerParams != null) {
		presenceCheckDelay = readerParams.presenceCheckDelay;
		//@paul: 如果設置不檢查能否轉成NDEF的標記
		if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {
			if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");
			//@paul: 判斷tag是否還在範圍內
			tag.startPresenceChecking(presenceCheckDelay, callback);
			//@paul: 將偵測的tag進行分發
			dispatchTagEndpoint(tag, readerParams);
			break;
		}
	}

	boolean playSound = readerParams == null ||
		(readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0;
	if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && playSound) {
		//paul: 播放開始聲音
		playSound(SOUND_START);
	}
	if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {
		// When these tags start containing NDEF, they will require
		// the stack to deal with them in a different way, since
		// they are activated only really shortly.
		// For now, don't consider NDEF on these.
		if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");
		//@paul: 如果是Barcode,直接進行tag的分發
		tag.startPresenceChecking(presenceCheckDelay, callback);
		dispatchTagEndpoint(tag, readerParams);
		break;
	}
	
	//@paul: 依據底層格式,進行NDEF的轉換
	NdefMessage ndefMsg = tag.findAndReadNdef();

	if (ndefMsg != null) {
		//@paul: NDEF轉換成功後,進行Tag的分發
		tag.startPresenceChecking(presenceCheckDelay, callback);
		dispatchTagEndpoint(tag, readerParams);
	} else {
		//@paul: 無法轉換時,先進行底層的連接後(物理層先重連),將消息分發
		if (tag.reconnect()) {
			tag.startPresenceChecking(presenceCheckDelay, callback);
			dispatchTagEndpoint(tag, readerParams);
		} else {
			tag.disconnect();
			playSound(SOUND_ERROR);
		}
	}
	break;

        其中比較關鍵的函數有兩個:

                findAndReadNdef()

                dispatchTagEndpoint()


        findAndReadNdef()是和芯片強相關的,其目的是依據芯片的支持能力,將讀到的Tag中的內容轉換成NDEF格式的數據. 不同芯片其支持的能力存在差異,此部分的code也是存在差異,針對NXP芯片簡單分析如下:

@Override
public NdefMessage findAndReadNdef() {
	...
	for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
		...
		
		//@paul: 判斷connectedHandle與當前Index對應的handle的關係,並更新狀態,
		status = connectWithStatus(technologies[techIndex]);
		...
		
		// Check if this type is NDEF formatable
		if (!foundFormattable) {
			//@paul: 依據芯片特性判斷哪些tag是可以轉成NDEF格式
			if (isNdefFormatable()) {
				//@paul: 更新對應的handle,handle用於後續的操作
				foundFormattable = true;
				formattableHandle = getConnectedHandle();
				formattableLibNfcType = getConnectedLibNfcType();
				// We'll only add formattable tech if no ndef is
				// found - this is because libNFC refuses to format
				// an already NDEF formatted tag.
			}
			reconnect();
		}
		...
		
		status = checkNdefWithStatus(ndefinfo);
		...
		
		//@paul: 讀取tag上的數據
		byte[] buff = readNdef();
		if (buff != null) {
			try {
				//@paul: 將數據轉換成NDEF格式
				ndefMsg = new NdefMessage(buff);
				//@paul: 更新對應Tag的信息
				addNdefTechnology(ndefMsg,
						getConnectedHandle(),
						getConnectedLibNfcType(),
						getConnectedTechnology(),
						supportedNdefLength, cardState);
				reconnect();
			} catch (FormatException e) {
			   // Create an intent anyway, without NDEF messages
			   generateEmptyNdef = true;
			}
		} else {
			generateEmptyNdef = true;
		}
		...		
	}

	if (ndefMsg == null && foundFormattable) {
		// Tag is not NDEF yet, and found a formattable target,
		// so add formattable tech to tech list.
		addNdefFormatableTechnology(
				formattableHandle,
				formattableLibNfcType);
	}

	return ndefMsg;
}


Tag消息分發流程

        上述執行完成後,理論上會進入Tag的Dispatch流程,中間的流程省略,我們直接進入mNfcDispatcher.dispatchTag(tag)函數的分析,這纔是tag分發的終極形式,直接看代碼吧:

/** Returns:
 * <ul>
 *  <li /> DISPATCH_SUCCESS if dispatched to an activity,
 *  <li /> DISPATCH_FAIL if no activities were found to dispatch to,
 *  <li /> DISPATCH_UNLOCK if the tag was used to unlock the device
 * </ul>
 */
public int dispatchTag(Tag tag) {
	PendingIntent overrideIntent;
	IntentFilter[] overrideFilters;
	String[][] overrideTechLists;
	boolean provisioningOnly;

	//@paul: 如果上層APP定義了下列值,就會在這裏進行更新
	synchronized (this) {
		overrideFilters = mOverrideFilters;
		overrideIntent = mOverrideIntent;
		overrideTechLists = mOverrideTechLists;
		provisioningOnly = mProvisioningOnly;
	}

	//@paul: Tag在screen on 且lock的情況下,需要嘗試unlock,否則如法處理
	boolean screenUnlocked = false;
	if (!provisioningOnly &&
			mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
		screenUnlocked = handleNfcUnlock(tag);
		if (!screenUnlocked) {
			return DISPATCH_FAIL;
		}
	}

	NdefMessage message = null;
	//@paul: 將Tag解析成NDEF格式數據,並讀取內容
	Ndef ndef = Ndef.get(tag);
	if (ndef != null) {
		message = ndef.getCachedNdefMessage();
	}

	if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);

	DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);

	//@paul: 和APP相關,暫時沒有研究
	resumeAppSwitches();

	//@paul: 如果上層APP有定義前臺分發機制,則會調用到PendingIntent.send()功能,實現前臺分發機制
	if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
			overrideTechLists)) {
		return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
	}

	//@paul: 判斷NDEF消息是否是handover格式
	if (mHandoverManager.tryHandover(message)) {
		if (DBG) Log.i(TAG, "matched BT HANDOVER");
		return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
	}

	//@paul: 判斷NDEF消息是否是WifiConfiguration格式
	if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
		if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
		return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
	}

	//@paul: 將消息發送給對ACTION_NDEF_DISCOVERED感興趣的APP處理
	if (tryNdef(dispatch, message, provisioningOnly)) {
		return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
	}
	
	if (screenUnlocked) {
		// We only allow NDEF-based mimeType matching in case of an unlock
		return DISPATCH_UNLOCK;
	}

	if (provisioningOnly) {
		// We only allow NDEF-based mimeType matching
		return DISPATCH_FAIL;
	}

	// Only allow NDEF-based mimeType matching for unlock tags
	//@paul: <span style="font-family: Arial, Helvetica, sans-serif;">將消息發送給對</span>ACTION_TECH_DISCOVERED感興趣的APP處理
	if (tryTech(dispatch, tag)) {
		return DISPATCH_SUCCESS;
	}

	//@paul: 更新Intent爲ACTION_TAG_DISCOVERED
	dispatch.setTagIntent();
	//@paul: 將消息發送給對ACTION_TAG_DISCOVERED感興趣的APP處理
	if (dispatch.tryStartActivity()) {
		if (DBG) Log.i(TAG, "matched TAG");
		return DISPATCH_SUCCESS;
	}

	if (DBG) Log.i(TAG, "no match");
	return DISPATCH_FAIL;
}

        下面分別介紹tryHandover(),tryNfcWifiSetup(),tryNdef(),tryTech().


         首先看看tryHandover(),此函數主要做BlueTooth的handover,當然如果要做wifi的handover,從技術上看是完全沒有問題的.

public boolean tryHandover(NdefMessage m) {
	...
	BluetoothHandoverData handover = parseBluetooth(m);
	...
	synchronized (mLock) {
		...
<span style="white-space:pre">		</span>//@paul: 發送MSG_PERIPHERAL_HANDOVER消息,在HandleMessage中進行處理
		Message msg = Message.obtain(null, HandoverService.MSG_PERIPHERAL_HANDOVER, 0, 0);
		Bundle headsetData = new Bundle();
		headsetData.putParcelable(HandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
		headsetData.putString(HandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
		headsetData.putInt(HandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
		msg.setData(headsetData);
		return sendOrQueueMessageLocked(msg);
	}
}

        比較重要的就是parseBluetooth()和最後的sendOrQueueMessageLocked()函數。其中parseBluetooth邏輯比較簡單,就是按照Spec的要求,把NDEF數據一個字節一個字節的解析出來,存放在對應的結構體中。sendOrQueueMessageLocked則是將BT的信息發送給上述完整的代碼分析如下:

BluetoothHandoverData parseBluetooth(NdefMessage m) {
	...
	
	// Check for BT OOB record
	if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BT_OOB)) {
		return parseBtOob(ByteBuffer.wrap(r.getPayload()));
	}

	// Check for BLE OOB record
	if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BLE_OOB)) {
		return parseBleOob(ByteBuffer.wrap(r.getPayload()));
	}

	// Check for Handover Select, followed by a BT OOB record
	if (tnf == NdefRecord.TNF_WELL_KNOWN &&
			Arrays.equals(type, NdefRecord.RTD_HANDOVER_SELECT)) {
		return parseBluetoothHandoverSelect(m);
	}

	// Check for Nokia BT record, found on some Nokia BH-505 Headsets
	if (tnf == NdefRecord.TNF_EXTERNAL_TYPE && Arrays.equals(type, TYPE_NOKIA)) {
		return parseNokia(ByteBuffer.wrap(r.getPayload()));
	}

	return null;
}



public boolean sendOrQueueMessageLocked(Message msg) {
	if (!mBound || mService == null) {
		// Need to start service, let us know if we can queue the message
		//@paul: 首先bind Service,成功後會調用onServiceConnected()
		if (!bindServiceIfNeededLocked()) {
			Log.e(TAG, "Could not start service");
			return false;
		}
		// Queue the message to send when the service is bound
		//@paul: 先將消息放在緩存隊列中
		mPendingServiceMessages.add(msg);
	} else {
		try {
			//@paul: 已經綁定後,則直接誒發送消息
			mService.send(msg);
		} catch (RemoteException e) {
			Log.e(TAG, "Could not connect to handover service");
			return false;
		}
	}
	return true;
}



public void onServiceConnected(ComponentName name, IBinder service) {
	synchronized (mLock) {
		...
		try {
			//@paul: 直接發送消息
			mService.send(msg);
		} catch (RemoteException e) {
			Log.e(TAG, "Failed to register client");
		}
		// Send all queued messages
		while (!mPendingServiceMessages.isEmpty()) {
			//@paul: 如果mPendingServiceMessages有數據是,會一直嘗試把pending的數據發送完成
			msg = mPendingServiceMessages.remove(0);
			try {
				mService.send(msg);
			} catch (RemoteException e) {
				Log.e(TAG, "Failed to send queued message to service");
			}
		}
	}
}


        前文提到發送了MSG_PERIPHERAL_HANDOVER,最終進入doPeripheralHandover()函數,目前只是分析到BT流程啓動,後續建立連接部分暫不分析了。

void doPeripheralHandover(Message msg) {
	Bundle msgData = msg.getData();
	BluetoothDevice device = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);
	String name = msgData.getString(EXTRA_PERIPHERAL_NAME);
	int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);
	//@paul: 如果存在mBluetoothPeripheralHandover,表明有handover正在進行
	if (mBluetoothPeripheralHandover != null) {
	   Log.d(TAG, "Ignoring pairing request, existing handover in progress.");
	   return;
	}
	//@paul: mBluetoothPeripheralHandover
	mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(HandoverService.this,
			device, name, transport, HandoverService.this);
			
	// TODO: figure out a way to disable polling without deactivating current target
	if (transport == BluetoothDevice.TRANSPORT_LE) {
		mHandler.sendMessageDelayed(
				mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);
	}
	
	//@paul: 如果Bluetooth有啓動,則執行對應的start函數
	if (mBluetoothAdapter.isEnabled()) {
		if (!mBluetoothPeripheralHandover.start()) {
			//@paul: Handover成功後,NFC繼續polling
			mNfcAdapter.resumePolling();
		}
	} else {
		if (!enableBluetooth()) {
			Log.e(TAG, "Error enabling Bluetooth.");
			mBluetoothPeripheralHandover = null;
		}
	}
}

//@paul:接上述start函數
public boolean start() {
	...

	//paul: 目前只關注此函數
	nextStep();

	return true;
}


//@paul: 進入對應的狀態機,啓動BT連接
void nextStep() {
	if (mAction == ACTION_INIT) {
		nextStepInit();
	} else if (mAction == ACTION_CONNECT) {
		nextStepConnect();
	} else {
		nextStepDisconnect();
	}
}


        分析完Handover,可能已經覺得有點累了,那我們來個簡單的,看看tryNfcWifiSetup(),其實這部分和BT的原理基本類似,就是透過NFC拿到WIFI相關的credential,然後利用credential簡歷WIFI連接,實現Handover的目的。

public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {
	...

	//@paul: 解析得到Wifi相關的credential數據
	final WifiConfiguration wifiConfiguration = parse(cachedNdefMessage);

	if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(
			UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
		Intent configureNetworkIntent = new Intent()
				.putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)
				.setClass(context, ConfirmConnectToWifiNetworkActivity.class)
				.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

		//@paul: 將wifi數據放在intent中,然後以設置的參數啓動wifi相關的連接
		context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);
		return true;
	}

	return false;
}


       接下來就是tryNdef()和tryTech()的分析,其實此部分的流程是前面提到的ACTION_TAG_DISCOVERED的流程是類似,主要是分兩步:

        1. 設置對應的intent爲:ACTION_NDEF_DISCOVERED/ACTION_TECH_DISCOVERED

        2. 以上述intent啓動相關的activity. 只不過會加入AAR消息的檢查,關於AAR的說明可以自行查找。簡單的說明如下:

在Android4.0(API Level 14)中引入的Android應用程序記錄(AAR),提供了較強的在掃描到NFC標籤時,啓動應用程序的確定性。AAR有嵌入到NDEF記錄內部的應用程序的包名。你能夠把一個AAR添加到你的NDEF消息的任何記錄中,因爲Android會針對AAR來搜索整個NDEF消息。如果它找到一個AAR,它就會基於AAR內部的包名來啓動應用程序。如果該應用程序不在當前的設備上,會啓動Google Play來下載對應的應用程序。

如果你想要防止其他的應用對相同的Intent的過濾並潛在的處理你部署的特定的NFC標籤,那麼AAR是有用的。AAR僅在應用程序級被支持,因爲包名的約束,並不能在Activity級別來過濾Intent。如果你想要在Activity級處理Intent,請使用Intent過濾器。


        以上基本就介紹完Tag的整體處理流程。代碼流程稍微有點多,建議代碼分幾次看,以免遺忘。


         後續會在介紹一下NFC P2P設備相互發現的流程。由於P2P的應用比較多,介紹的篇幅也會相對較多



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章