NFC--Tag读写模式java层源码分析

来源:互联网 发布:淘宝卖家交流论坛 编辑:程序博客网 时间:2024/05/16 18:15
          文章整理总结NFC读取和写入Tag的java层的流程,至于一些native层的接入点,暂记住。后续持续更新。
1、读取Tag的流程
           NfcService启动完成后,会进入到NfcService中的applyRouting方法持续的监听是否有Tag靠近,它会使Nfc的RF工作在
polling模式,polling模式下会检测那个Tag进入到了自己的射频厂,进而产生反应。与其相对的是listen模式,Tag就相当于是listen
模式,监听谁往外polling,产生响应.
  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. if (!mInProvisionMode) {
  10. //就是设置NfcDispatcher中的mProvisioningOnly = false;
  11. mNfcDispatcher.disableProvisioningMode();
  12. //调用native方法doSetProvisionMode,去通知native层,当前不是处于ProvisionMode
  13. mDeviceHost.doSetProvisionMode(mInProvisionMode);
  14. }
  15. }
  16. ......
  17. try {
  18. //此处更新于polling状态的NFC的参数.computeDiscoveryParameters方法就是位于NfcService内部
  19. //所以就直接用执行到applyRouting之前实例化的一些全局参数进行了初始化,详见下一个函数.
  20. NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
  21. //传入参数为true的时候或者参数有所变化.
  22. if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
  23. // shouldEnableDiscovery内及时一个判断语句为:mTechMask != 0 || mEnableHostRouting
  24. if (newParams.shouldEnableDiscovery()) {
  25. boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
  26. //最重要的就是调用到了这里,mDeviceHost的实现类是NativeNfcManager最终调用到内部的如下:
  27. // private native void doEnableDiscovery(int techMask,
  28. // boolean enableLowPowerPolling,
  29. // boolean enableReaderMode,
  30. // boolean enableP2p,
  31. // boolean restart);
  32. //去native层,初始化发现Tag的流程。
  33. mDeviceHost.enableDiscovery(newParams, shouldRestart);
  34. } else {
  35. mDeviceHost.disableDiscovery();
  36. }
  37. mCurrentDiscoveryParameters = newParams;
  38. } else {
  39. Log.d(TAG, "Discovery configuration equal, not updating.");
  40. }
  41. } finally {
  42. ......
  43. }
  44. }
  45. }
         我们看一下computeDiscoveryParameters这个函数,它返回NfcDiscoveryParameters,这个类描述了处于polling或者listen
以及host card emulation.状态的NFC的参数.
  1. private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
  2. //基于屏幕的状态重新计算NfcDiscoveryParameters.
  3. NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
  4. // Polling
  5. if ((screenState >= NFC_POLLING_MODE)||mIsTaskBoot) {
  6. // Check if reader-mode is enabled
  7. //当当前NFC读模式开启后,按照协议,看看当前的Tag属于那种类型的Tag,然后设置techMask.
  8. //此处支持如下五中.
  9. if (mReaderModeParams != null) {
  10. int techMask = 0;
  11. if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_A) != 0)
  12. techMask |= NFC_POLL_A;
  13. if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_B) != 0)
  14. techMask |= NFC_POLL_B;
  15. if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_F) != 0)
  16. techMask |= NFC_POLL_F;
  17. if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_V) != 0)
  18. techMask |= NFC_POLL_ISO15693;
  19. if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0)
  20. techMask |= NFC_POLL_KOVIO;
  21. paramsBuilder.setTechMask(techMask);
  22. paramsBuilder.setEnableReaderMode(true);
  23. } else {
  24. paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
  25. paramsBuilder.setEnableP2p(true);
  26. }
  27. //锁屏并且在ProvisionMode
  28. } else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED && mInProvisionMode) {
  29. paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
  30. // enable P2P for MFM/EDU/Corp provisioning
  31. paramsBuilder.setEnableP2p(true);
  32. } else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED &&
  33. mNfcUnlockManager.isLockscreenPollingEnabled()) {
  34. // For lock-screen tags, no low-power polling
  35. paramsBuilder.setTechMask(mNfcUnlockManager.getLockscreenPollMask());
  36. paramsBuilder.setEnableLowPowerDiscovery(false);
  37. paramsBuilder.setEnableP2p(false);
  38. }
  39. //处在卡模拟状态是一直是使能的,即使屏幕关闭了。
  40. if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
  41. // Host routing is always enabled at lock screen or later
  42. paramsBuilder.setEnableHostRouting(true);
  43. }
  44. //To make routing table update.
  45. if(mIsRoutingTableDirty) {
  46. mIsRoutingTableDirty = false;
  47. //TODO: Take this logic from L_OSP_EXT [PN547C2]
  48. int protoRoute = mNxpPrefs.getInt("PREF_MIFARE_DESFIRE_PROTO_ROUTE_ID", 
  49. GetDefaultMifareDesfireRouteEntry());
  50. int defaultRoute=mNxpPrefs.getInt("PREF_SET_DEFAULT_ROUTE_ID", GetDefaultRouteEntry());
  51. int techRoute=mNxpPrefs.getInt("PREF_MIFARE_CLT_ROUTE_ID", GetDefaultMifateCLTRouteEntry());
  52. defaultRoute = NfcCertDebugModeUtil.calculateDefaultRoute(defaultRoute,
  53. mDeviceHost.getDefaultAidPowerState(), ROUTE_LOC_MASK);
  54. if (DBG) Log.d(TAG, "Set default Route Entry");
  55. setDefaultRoute(defaultRoute, protoRoute, techRoute);
  56. }
  57. ...
  58. return paramsBuilder.build();
  59. }
          通过JNI技术native层会调用doEnableDiscovery相对应的函数,进行一些列处理后,最终会回调到NativeNfcManager中的
  1. private void notifyNdefMessageListeners(NativeNfcTag tag) {
  2. mListener.onRemoteEndpointDiscovered(tag);
  3. }
          mListener的实现就是NfcService,又重新回到Nfcservice中
  1. @Override
  2. public void onRemoteEndpointDiscovered(TagEndpoint tag) {
  3. android.util.Log.d("zy","nfeservice onRemoteEndpointDiscovered ");
  4. sendMessage(NfcService.MSG_NDEF_TAG, tag);
  5. }
  6. void sendMessage(int what, Object obj) {
  7. Message msg = mHandler.obtainMessage();
  8. msg.what = what;
  9. msg.obj = obj;
  10. mHandler.sendMessage(msg);
  11. }
         到此为止,native层对发现的tag进行了一系列的初始化和封装操作,如按照ndef协议把Tag的消息封装到TagEndpoint当中
TagEndpoint就是代表了一个远端的Nfc tag.
         然后执行到mHandler中的MSG_NDEF_TAG
  1. case MSG_NDEF_TAG:
  2. ......
  3. //如果是reader模式
  4. if (readerParams != null) {
  5. presenceCheckDelay = readerParams.presenceCheckDelay;
  6. //如果FLAG_READER_SKIP_NDEF_CHECK标记位不为0,那么直接开始调用dispatchTagEndpoint分发
  7. if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {
  8. if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");
  9. tag.startPresenceChecking(presenceCheckDelay, callback);
  10. dispatchTagEndpoint(tag, readerParams);
  11. break;
  12. }
  13. }
  14. //当是NFC Barcode的时候也是直接分发,(???)NFC Barcode是NFC tag中数据的一种模式吗?
  15. if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {
  16. ......
  17. if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");
  18. tag.startPresenceChecking(presenceCheckDelay, callback);
  19. dispatchTagEndpoint(tag, readerParams);
  20. break;
  21. }
  22. //去调用NativeNfcTag的findAndReadNdef,进行相关初始化后把native层的数据按照NDEF
  23. //协议封装成java中的类NdefMessage.
  24. NdefMessage ndefMsg = tag.findAndReadNdef();
  25. //解析的消息有问题的时候,比如解析一个身份证或者公交卡,在你底层没有专门处理的时候是会失败的。
  26. if (ndefMsg == null) {
  27. // First try to see if this was a bad tag read
  28. if (!tag.reconnect()) {
  29. tag.disconnect();
  30. if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
  31. String toastString = mContext.getString(
  32. R.string.nfc_strings_toast_prompt_touch_again_txt);
  33. if (!ToastMaster.isSameToastShown(toastString)) {
  34. ToastMaster.showToast(mContext, toastString);
  35. }
  36. }
  37. break;
  38. }
  39. }
  40. if(tag.getConnectedTechnology() == TagTechnology.NFC_F)
  41. {
  42. presenceCheckDelay = NFC_F_TRANSCEIVE_PRESENCE_CHECK_DELAY;
  43. }
  44. //这个如名字是抖动的tag,就是由于距离没控制好,一直检测到同一个tag的uid
  45. if (debounceTagUid != null) {
  46. // If we're debouncing and the UID or the NDEF message of the tag match,
  47. // don't dispatch but drop it.
  48. ......
  49. }
  50. mLastReadNdefMessage = ndefMsg;
  51. tag.startPresenceChecking(presenceCheckDelay, callback);
  52. //解析出问题的ndef消息也要分发出去.
  53. dispatchTagEndpoint(tag, readerParams);
  54. break;
        TagTechnology有很多种,按照对应的Tag技术协议进行的定定义
  1. public static final int NFC_A = 1;
  2. public static final int NFC_B = 2;
  3. public static final int ISO_DEP = 3;
  4. public static final int NFC_F = 4;
  5. public static final int NFC_V = 5;
  6. ......
          先了解NativeNfcTag几个常用的方法,方法内具体的native实现暂未分析。
         判断tag是否能转换成ndef的java层的代码如下,从注释也可以看出是让native去实现的,可能和芯片有关.
  1. native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act);
  2. @Override
  3. public synchronized boolean isNdefFormatable() {
  4. // Let native code decide whether the currently activated tag
  5. // is formatable. Although the name of the JNI function refers
  6. // to ISO-DEP, the JNI function checks all tag types.
  7. return doIsIsoDepNdefFormatable(mTechPollBytes[0],
  8. mTechActBytes[0]);
  9. }
          检查ndef的数据格式是否合法,方法过后感觉底层会往这个数组存放,当先Tag的一些信息.当前正在和那个Tag通信就是读
取的谁。
  1. private native int doCheckNdef(int[] ndefinfo);
  2. private synchronized int checkNdefWithStatus(int[] ndefinfo) {
  3. if (mWatchdog != null) {
  4. mWatchdog.pause();
  5. }
  6. int status = doCheckNdef(ndefinfo);
  7. if (mWatchdog != null) {
  8. mWatchdog.doResume();
  9. }
  10. return status;
  11. }
          读取tag上的ndef数据,当前正在和那个Tag通信就是读取的谁。
  1. private native byte[] doRead();
  2. @Override
  3. public synchronized byte[] readNdef() {
  4. if (mWatchdog != null) {
  5. mWatchdog.pause();
  6. }
  7. byte[] result = doRead();
  8. if (mWatchdog != null) {
  9. mWatchdog.doResume();
  10. }
  11. return result;
  12. }
          然后就是重头戏findAndReadNdef
  1. @Override
  2. public NdefMessage findAndReadNdef(){
  3. // Try to find NDEF on any of the technologies.
  4. int[] technologies = getTechList();
  5. int[] handles = mTechHandles;
  6. NdefMessage ndefMsg = null;
  7. boolean foundFormattable = false;
  8. int formattableHandle = 0;
  9. int formattableLibNfcType = 0;
  10. int status;
  11. for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
  12. ......
  13. //???下面这个方法可能也和协议的实现相关,读了几遍代码暂很难理解
  14. //网上:判断connectedHandle与当前Index对应的handle的关系,并更新状态, 。
  15. status = connectWithStatus(technologies[techIndex]);
  16. //状态返回不为0的时候直接跳过本次循环。
  17. if (status != 0) {
  18. Log.d(TAG, "Connect Failed - status = "+ status);
  19. if (status == STATUS_CODE_TARGET_LOST) {
  20. break;
  21. }
  22. continue; // try next handle
  23. }
  24. // 判断当前的tag是否可以转换成ndef格式.
  25. if (!foundFormattable) {
  26. //如果可以转换
  27. if (isNdefFormatable()) {
  28. foundFormattable = true;
  29. formattableHandle = getConnectedHandle();
  30. formattableLibNfcType = getConnectedLibNfcType();
  31. }
  32. reconnect();
  33. }
  34. //检查当前tag中ndef数据的合法性
  35. int[] ndefinfo = new int[2];
  36. status = checkNdefWithStatus(ndefinfo);
  37. if (status != 0) {
  38. Log.d(TAG, "Check NDEF Failed - status = " + status);
  39. if (status == STATUS_CODE_TARGET_LOST) {
  40. break;
  41. }
  42. continue; // try next handle
  43. }
  44. // found our NDEF handle
  45. boolean generateEmptyNdef = false;
  46. int supportedNdefLength = ndefinfo[0];
  47. int cardState = ndefinfo[1];
  48. //读取tag中的数据,读出来的是字节数组。
  49. byte[] buff = readNdef();
  50. if (buff != null && buff.length > 0) {
  51. try {
  52. //正常的应该走到这里,通过获取的数据实例化NdefMessage.
  53. //在NdefMessage实例化的时候就按照NDEF的协议去解析这个byte数组
  54. //中的数据,然后封装成各个类和相应的字段供我们到时候直接调用
  55. ndefMsg = new NdefMessage(buff);
  56. addNdefTechnology(ndefMsg,
  57. getConnectedHandle(),
  58. getConnectedLibNfcType(),
  59. getConnectedTechnology(),
  60. supportedNdefLength, cardState);
  61. foundFormattable = false;
  62. reconnect();
  63. } catch (FormatException e) {
  64. // Create an intent anyway, without NDEF messages
  65. generateEmptyNdef = true;
  66. }
  67. } else if(buff != null){
  68. // Empty buffer, unformatted tags fall into this case
  69. generateEmptyNdef = true;
  70. }
  71. //当产生异常的时候generateEmptyNdef为true的一些处理
  72. if (generateEmptyNdef) {
  73. ndefMsg = null;
  74. addNdefTechnology(null,
  75. getConnectedHandle(),
  76. getConnectedLibNfcType(),
  77. getConnectedTechnology(),
  78. supportedNdefLength, cardState);
  79. foundFormattable = false;
  80. reconnect();
  81. }
  82. break;
  83. }
  84. ......
  85. return ndefMsg;
  86. }
          然后就是分发Tag消息的最终实现了dispatchTagEndpoint
  1. private void dispatchTagEndpoint(TagEndpoint tagEndpoint, ReaderModeParams readerParams){
  2. //Tag用来封装一个已经被发现的Tag.Tag类是framework提供的api
  3. Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(),
  4. tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService);
  5. //把tagEndpoint以 key = tagEndpoint.gethandle;value = tagEndpoint放入到集合中
  6. registerTagObject(tagEndpoint);
  7. if (readerParams != null) {
  8. try {
  9. if ((readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0) {
  10. playSound(SOUND_END);
  11. }
  12. if (readerParams.callback != null) {
  13. /调用回调.
  14. readerParams.callback.onTagDiscovered(tag);
  15. return;
  16. } else {
  17. // Follow normal dispatch below
  18. }
  19. } catch (RemoteException e) {
  20. ......
  21. } catch (Exception e) {
  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. if (dispatchResult == NfcDispatcher.DISPATCH_FAIL) {
  31. unregisterObject(tagEndpoint.getHandle());
  32. if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
  33. String toastString = mContext.getString(
  34. R.string.nfc_strings_toast_prompt_read_error_txt);
  35. //弹出Toast进行提示.
  36. if (!ToastMaster.isSameToastShown(toastString)) {
  37. ToastMaster.showToast(mContext, toastString);
  38. playSound(SOUND_ERROR);
  39. }
  40. }
  41. } else if (dispatchResult == NfcDispatcher.DISPATCH_SUCCESS) {
  42. ...
  43. //播放声音
  44. playSound(SOUND_END);
  45. }
  46. }
         上面的函数可以看到最最重要的就是NfcDispatcher中的dispatchTag(Tag)了,先前一直是在NfcService中进入到
NfcDispatcher类,此类从名字也能看出主要功能,它就是分发NFC的events然后去开启指定的activity。
         阅读下面的流程代码之前,最好对Android NFC的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() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
  20. screenUnlocked = handleNfcUnlock(tag);
  21. if (!screenUnlocked) {
  22. return DISPATCH_FAIL;
  23. }
  24. }
  25. NdefMessage message = null;
  26. //将Tag解析成Ndef的格式.
  27. Ndef ndef = Ndef.get(tag);
  28. if (ndef != null) {
  29. //再通过这样获取到NdefMessage,(???)此处的疑问,前面不是已经获取到NdefMessage
  30. //为什么还要再用NdefMessage生成个Tag,是然后在通过Tag获取,看起来好像是威力方便framewok
  31. //统一接口,供外部调用.
  32. message = ndef.getCachedNdefMessage();
  33. } else {
  34. NfcBarcode nfcBarcode = NfcBarcode.get(tag);
  35. if (nfcBarcode != null && nfcBarcode.getType() == NfcBarcode.TYPE_KOVIO) {
  36. message = decodeNfcBarcodeUri(nfcBarcode);
  37. }
  38. }
  39. if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);
  40. //DispatchInfo一是在一个Tag分发的过程中的帮助类。
  41. DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);
  42. //Tells the ActivityManager to resume allowing app switches.
  43. resumeAppSwitches();
  44. 如果正在运行的APP有定义了前台分发机制,则会走到这里
  45. if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
  46. overrideTechLists)) {
  47. return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
  48. }
  49. //判断NDEF消息是否是handover格式
  50. if (tryPeripheralHandover(message)) {
  51. if (DBG) Log.i(TAG, "matched BT HANDOVER");
  52. return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
  53. }
  54. //判断NDEF消息是否是WifiConfiguration格式
  55. if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
  56. if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
  57. return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
  58. }
  59. ......
  60. //将TAg消息发送给对action为ACTION_NDEF_DISCOVERED感兴趣的APP处理
  61. if (tryNdef(dispatch, message)) {
  62. return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
  63. }
  64. //将TAg消息发送给对ACTION_TECH_DISCOVERED感兴趣的APP处理
  65. if (tryTech(dispatch, tag)) {
  66. return DISPATCH_SUCCESS;
  67. }
  68. //如上两个action相关的activity都未处理的时候
  69. //将intent设置为ACTION_TAG_DISCOVERED
  70. dispatch.setTagIntent();
  71. if (dispatch.tryStartActivity()) {
  72. if (DBG) Log.i(TAG, "matched TAG");
  73. return DISPATCH_SUCCESS;
  74. }
  75. //都无法处理就返回false.
  76. if (DBG) Log.i(TAG, "no match");
  77. return DISPATCH_FAIL;
  78. }
         下面依次介绍这个方法中用到的几个具体的子函数:
          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. }
         帮助类DispatchInfo中的几个方法
        构造:
  1. public DispatchInfo(Context context, Tag tag, NdefMessage message) {
  2. intent = new Intent();
  3. //先把这个Tag对象放入intent,app获取到当前intent的时候可以取出来
  4. intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
  5. intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
  6. if (message != null) {
  7. //有ndef消息的时候也把消息设置进去
  8. intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message});
  9. ndefUri = message.getRecords()[0].toUri();
  10. ndefMimeType = message.getRecords()[0].toMimeType();
  11. } else {
  12. ndefUri = null;
  13. ndefMimeType = null;
  14. }
  15. //启动rootIntent的时候会启动下面三个设置的intent,就是putExtra处的参数.
  16. rootIntent = new Intent(context, NfcRootActivity.class);
  17. rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent);
  18. rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
  19. this.context = context;
  20. packageManager = context.getPackageManager();
  21. }
          把当前的intent设置action为NfcAdapter.ACTION_TAG_DISCOVERED
  1. public Intent setTagIntent() {
  2. android.util.Log.d("zy","NfcDispatcher setTagIntent ");
  3. intent.setData(null);
  4. intent.setType(null);
  5. intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
  6. return intent;
  7. }
         把当前的intent设置action为NfcAdapter.ACTION_NDEF_DISCOVERED
  1. public Intent setNdefIntent() {
  2. intent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
  3. if (ndefUri != null) {
  4. intent.setData(ndefUri);
  5. return intent;
  6. } else if (ndefMimeType != null) {
  7. intent.setType(ndefMimeType);
  8. return intent;
  9. }
  10. return null;
  11. }
          把当前的intent设置action为NfcAdapter.ACTION_TECH_DISCOVERED
  1. public Intent setTechIntent() {
  2. android.util.Log.d("zy","NfcDispatcher setTechIntent ");
  3. intent.setData(null);
  4. intent.setType(null);
  5. intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED);
  6. return intent;
  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. overrideIntent.send(mContext, Activity.RESULT_OK, intent);
  12. if (DBG) Log.i(TAG, "matched NDEF override");
  13. return true;
  14. } catch (CanceledException e) {
  15. return false;
  16. }
  17. }
  18. }
  19. // TECH
  20. intent = dispatch.setTechIntent();
  21. //当传入的Tag的Technolg也是支持的时候,把action设置成ACTION_NDEF_DISCOVERED去启动
  22. if (isTechMatch(tag, overrideTechLists)) {
  23. try {
  24. overrideIntent.send(mContext, Activity.RESULT_OK, intent);
  25. if (DBG) Log.i(TAG, "matched TECH override");
  26. return true;
  27. } catch (CanceledException e) {
  28. return false;
  29. }
  30. }
  31. // TAG 和上面同理。
  32. intent = dispatch.setTagIntent();
  33. if (isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
  34. try {
  35. overrideIntent.send(mContext, Activity.RESULT_OK, intent);
  36. if (DBG) Log.i(TAG, "matched TAG override");
  37. return true;
  38. } catch (CanceledException e) {
  39. return false;
  40. }
  41. }
  42. return false;
  43. }
          接下来是通过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. HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m);
  6. ......
  7. //得到使用的handover的各个参数以后,设置到intent内,并启动PeripheralHandoverService这个service.
  8. Intent intent = new Intent(mContext, PeripheralHandoverService.class);
  9. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
  10. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
  11. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
  12. if (handover.oobData != null) {
  13. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
  14. }
  15. if (handover.uuids != null) {
  16. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
  17. }
  18. if (handover.bluetoothClass != null) {
  19. intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS, handover.bluetoothClass);
  20. }
  21. intent.putExtra(PeripheralHandoverService.EXTRA_CLIENT, mMessenger);
  22. intent.putExtra(PeripheralHandoverService.EXTRA_BT_ENABLED, mBluetoothEnabledByNfc.get());
  23. if (CtaUtils.showCtaBtDialogIfNeeded(mContext, null, intent, null)) {
  24. return true;
  25. }
  26. //最后启动这个Intent的service.
  27. mContext.startServiceAsUser(intent, UserHandle.CURRENT);
  28. return true;
  29. }
  1. public BluetoothHandoverData parseBluetooth(NdefMessage m){
  2. //取得Ndef消息中的第一个NdefRecord记录,这个记录中肯定包含了完整的Header
  3. NdefRecord r = m.getRecords()[0];
  4. //获取Header中的tnf字段。
  5. short tnf = r.getTnf();
  6. //获取Header的Type字段。
  7. byte[] type = r.getType();
  8. //上面的获取不清楚的话去看看NDEF协议,较为简单.
  9. // Check for BT OOB record 感觉现在都不用这个了,打印Log看看.
  10. if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BT_OOB)) {
  11. return parseBtOob(ByteBuffer.wrap(r.getPayload()));
  12. }
  13. // Check for BLE OOB record
  14. if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BLE_OOB)) {
  15. return parseBleOob(ByteBuffer.wrap(r.getPayload()));
  16. }
  17. // Check for Handover Select, followed by a BT OOB record
  18. if (tnf == NdefRecord.TNF_WELL_KNOWN &&
  19. Arrays.equals(type, NdefRecord.RTD_HANDOVER_SELECT)) {
  20. return parseBluetoothHandoverSelect(m);
  21. }
  22. ......
  23. return null;
  24. }
         如上可以看到最终都是调用到parseBtOob然后根据getPayload的数据的(???看了源码不太懂)
         接下来我们去看看PeripheralHandoverService这个service的启动流程
         生命周期的回调:
  1. @Override
  2. public int onStartCommand(Intent intent, int flags, int startId) {
  3. ......
  4. //最主要的就是调用如下方法去打开蓝牙.
  5. if (doPeripheralHandover(intent.getExtras())) {
  6. return START_STICKY;
  7. } else {
  8. stopSelf(startId);
  9. return START_NOT_STICKY;
  10. }
  11. }
  12. @Override
  13. public void onCreate() {
  14. super.onCreate();
  15. mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
  16. mSuccessSound = mSoundPool.load(this, R.raw.end, 1);
  17. mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
  18. IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
  19. registerReceiver(mBluetoothStatusReceiver, filter);
  20. }
  1. boolean doPeripheralHandover(Bundle msgData) {
  2. ......
  3. mDevice = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);
  4. String name = msgData.getString(EXTRA_PERIPHERAL_NAME);
  5. int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);
  6. OobData oobData = msgData.getParcelable(EXTRA_PERIPHERAL_OOB_DATA);
  7. Parcelable[] parcelables = msgData.getParcelableArray(EXTRA_PERIPHERAL_UUIDS);
  8. BluetoothClass bluetoothClass = msgData.getParcelable(EXTRA_PERIPHERAL_CLASS);
  9. ParcelUuid[] uuids = null;
  10. if (parcelables != null) {
  11. uuids = new ParcelUuid[parcelables.length];
  12. for (int i = 0; i < parcelables.length; i++) {
  13. uuids[i] = (ParcelUuid)parcelables[i];
  14. }
  15. }
  16. mClient = msgData.getParcelable(EXTRA_CLIENT);
  17. mBluetoothEnabledByNfc = msgData.getBoolean(EXTRA_BT_ENABLED);
  18. mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(
  19. this, mDevice, name, transport, oobData, uuids, bluetoothClass, this);
  20. if (transport == BluetoothDevice.TRANSPORT_LE) {
  21. mHandler.sendMessageDelayed(
  22. mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);
  23. }
  24. if (mBluetoothAdapter.isEnabled()) {
  25. if (!mBluetoothPeripheralHandover.start()) {
  26. mHandler.removeMessages(MSG_PAUSE_POLLING);
  27. mNfcAdapter.resumePolling();
  28. }
  29. } else {
  30. //调用enableBluetooth去打开蓝牙
  31. if (!enableBluetooth()) {
  32. Log.e(TAG, "Error enabling Bluetooth.");
  33. mBluetoothPeripheralHandover = null;
  34. return false;
  35. }
  36. }
  37. return true;
  38. }
  39. boolean enableBluetooth() {
  40. if (!mBluetoothAdapter.isEnabled()) {
  41. mBluetoothEnabledByNfc = true;
  42. //再往下就是Bluetooth中的启动链接流程了!
  43. return mBluetoothAdapter.enableNoAutoConnect();
  44. }
  45. return true;
  46. }
          然后就是优先级最高的处理方式,注册了action为NfcAdapter.ACTION_NDEF_DISCOVERED的
  1. boolean tryNdef(DispatchInfo dispatch, NdefMessage message) {
  2. ......
  3. //获取对应的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. if (dispatch.tryStartActivity()) {
  12. if (DBG) Log.i(TAG, "matched AAR to NDEF");
  13. return true;
  14. }
  15. }
  16. // Try to perform regular launch of the first AAR
  17. if (aarPackages.size() > 0) {
  18. ......
  19. }
  20. ......
  21. //当消息中没有包名的时候,就用前面设置的intent:setNdefIntent
  22. //来启动activity,所以此时所有注册的activity都会受到这个action,然后你可以选择用那个启动
  23. dispatch.intent.setPackage(null);
  24. if (dispatch.tryStartActivity()) {
  25. if (DBG) Log.i(TAG, "matched NDEF");
  26. return true;
  27. }
  28. return false;
  29. }
          而tryTech()的思想是一样的,也是经过一系列判断后,通过dispatch.tryStartActivity()来启动,不过它启动的是action为
NfcAdapter.ACTION_TECH_DISCOVERED的,不再赘述。
          最终当都不能处理的时候就调用dispatch.setTagIntent()把intent设置为NfcAdapter.ACTION_TAG_DISCOVERED
用它去启动符合要求的activity.
          至此完成整体Tag的读取分发系统,回到NfcService中的mHandler中MSG_MOCK_NDEF,继续向下执行发送相关的成功
或失败的音效

2、写入Tag的流程
      写开发时app调用framework层的对应的不同协议的Tag代表的类的接口有如下:
             位于:android/frameworks/base/core/java/android/nfc/tech
             有:  NfcA.java、NfcB.java、NfcF.java;
                       Ndef.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当中的内部类
  1. final class TagService extends INfcTag.Stub调用内部方法ndefWrite
  2. @Override
  3. public int ndefWrite(int nativeHandle, NdefMessage msg) throws RemoteException {
  4. NfcPermissions.enforceUserPermissions(mContext);
  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

文章参考:
        http://blog.csdn.net/xuwen0306/article/details/44724845
 

0 0
原创粉丝点击