[NFC]Tag设备响应流程

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


进入NFC设备发现流程

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

  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. //@paul: 如果有tag正在通讯时,delay一段时间再更新参数
  19. // Special case: if we're transitioning to unlocked state while
  20. // still talking to a tag, postpone re-configuration.
  21. if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && isTagPresent()) {
  22. Log.d(TAG, "Not updating discovery parameters, tag connected.");
  23. mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESUME_POLLING),
  24. APPLY_ROUTING_RETRY_TIMEOUT_MS);
  25. return;
  26. }
  27. try {
  28. watchDog.start();
  29. //@paul: 依据前面初始化的参数来更新NfcDiscoveryParameters
  30. // Compute new polling parameters
  31. NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
  32. if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
  33. //@paul: 判断条件为:mTechMask != 0 || mEnableHostRouting
  34. //@paul:mTechMask一般不为0,mEnableHostRouting一般为false
  35. if (newParams.shouldEnableDiscovery()) {
  36. boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
  37. //@paul:系统一般会进入enableDiscovery()
  38. mDeviceHost.enableDiscovery(newParams, shouldRestart);
  39. } else {
  40. mDeviceHost.disableDiscovery();
  41. }
  42. mCurrentDiscoveryParameters = newParams;
  43. } else {
  44. Log.d(TAG, "Discovery configuration equal, not updating.");
  45. }
  46. } finally {
  47. watchDog.cancel();
  48. }
  49. }
  50. }

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

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

        继续追踪com_android_nfc_NfcManager_enableDiscovery():

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

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

  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. clean_and_return:
  14. nfc_cb_data_deinit(&cb_data);
  15. }

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

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

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

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

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

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

      

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

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

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

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


Tag设备发现framework流程

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

  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的流程如下:

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

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

                findAndReadNdef()

                dispatchTagEndpoint()


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

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


Tag消息分发流程

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

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

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


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

  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, 0, 0);
  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的信息发送给上述完整的代码分析如下:

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


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

  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. // TODO: figure out a way to disable polling without deactivating current target
  15. if (transport == BluetoothDevice.TRANSPORT_LE) {
  16. mHandler.sendMessageDelayed(
  17. mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);
  18. }
  19. //@paul: 如果Bluetooth有启动,则执行对应的start函数
  20. if (mBluetoothAdapter.isEnabled()) {
  21. if (!mBluetoothPeripheralHandover.start()) {
  22. //@paul: Handover成功后,NFC继续polling
  23. mNfcAdapter.resumePolling();
  24. }
  25. } else {
  26. if (!enableBluetooth()) {
  27. Log.e(TAG, "Error enabling Bluetooth.");
  28. mBluetoothPeripheralHandover = null;
  29. }
  30. }
  31. }
  32. //@paul:接上述start函数
  33. public boolean start() {
  34. ...
  35. //paul: 目前只关注此函数
  36. nextStep();
  37. return true;
  38. }
  39. //@paul: 进入对应的状态机,启动BT连接
  40. void nextStep() {
  41. if (mAction == ACTION_INIT) {
  42. nextStepInit();
  43. } else if (mAction == ACTION_CONNECT) {
  44. nextStepConnect();
  45. } else {
  46. nextStepDisconnect();
  47. }
  48. }


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

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


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

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

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

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


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


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

猜你喜欢

转载自blog.csdn.net/weixin_38503885/article/details/80927668
NFC