[NFC]P2P设备响应流程

 前文[NFC]Tag设备响应流程中有提到P2P设备的发现的函数始于:onLlcpLinkActivated().


        本文将基于onLlcpLinkActivated()开始后文的分析,进而引出P2P流程中的SNEP,NDEFPUSH,HANDOVER以及ECHOSERVER的响应过程.


        程序进入 onLlcpLinkActivated() 后,有点类似notify,开始同时上层,有P2P设备靠近了,解析一下,是什么格式,看看要做什么操作.

  1. @Override
  2. public void onLlcpLinkActivated(NfcDepEndpoint device) {
  3. sendMessage(NfcService.MSG_LLCP_LINK_ACTIVATION, device);
  4. }


        消息MSG_LLCP_LINK_ACTIVATION被NfcService.java自身注册的NfcServiceHandler进行处理

  1. case MSG_LLCP_LINK_ACTIVATION:
  2. if (mIsDebugBuild) {
  3. //@paul: 发送LLCP_UP的广播
  4. Intent actIntent = new Intent(ACTION_LLCP_UP);
  5. mContext.sendBroadcast(actIntent);
  6. }
  7. //@paul: 解析底层传递上来的NfcDepEndpoint信息
  8. llcpActivated((NfcDepEndpoint) msg.obj);
  9. break;

        在进入llcpActivated()之后,系统会区分Target和Initiator,这两种角色的流程基本上相同,差异点是在Target端会先执行connect(),此connect()是进行数据链路层的连接,保证底层已经是发现对方并且是可以连接的。

扫描二维码关注公众号,回复: 1904691 查看本文章


        对于我们来说,需要关注的是llcp链路建立成功后的操作,插播一下P2pLinkManager.java中的一些状态变化函数:

  1. <span style= "font-size:12px;"> public void onLlcpActivated()
  2. public void onLlcpFirstPacketReceived ()
  3. public void onLlcpDeactivated ()
  4. void onSendComplete (NdefMessage msg, long elapsedRealtime)</span>

       这些函数必须在UI main thread 调用,用于接收到底层连接的各种状态的更新。各个函数的意义依照名字基本上就能理解了。

       直接分析onLlcpActivated()函数:

  1. /**
  2. * Must be called on UI Thread.
  3. */
  4. public void onLlcpActivated() {
  5. Log.i(TAG, "LLCP activated");
  6. synchronized (P2pLinkManager. this) {
  7. ...
  8. mLlcpConnectDelayed = false;
  9. //@paul: 初始状态mLinkState为LINK_STATE_DOWN
  10. switch (mLinkState) {
  11. case LINK_STATE_DOWN:
  12. if (DBG) Log.d(TAG, "onP2pInRange()");
  13. mLinkState = LINK_STATE_WAITING_PDU;
  14. //@paul: 通知UI,发现P2P设备
  15. mEventListener.onP2pInRange();
  16. //@paul: 初始状态mSendState为SEND_STATE_NOTHING_TO_SEND
  17. if (mSendState == SEND_STATE_PENDING) {
  18. if (DBG) Log.d(TAG, "Sending pending data.");
  19. mHandler.removeMessages(MSG_WAIT_FOR_LINK_TIMEOUT);
  20. mSendState = SEND_STATE_SENDING;
  21. onP2pSendConfirmed( false);
  22. } else {
  23. mSendState = SEND_STATE_NOTHING_TO_SEND;
  24. //@paul: 依据APP设置的信息,准备发送的信息
  25. prepareMessageToSend( true);
  26. if (mMessageToSend != null ||
  27. (mUrisToSend != null && mHandoverManager.isHandoverSupported())) {
  28. if ((mSendFlags & NfcAdapter.FLAG_NDEF_PUSH_NO_CONFIRM) != 0) {
  29. mSendState = SEND_STATE_SENDING;
  30. //@paul: 不需要UI确认
  31. onP2pSendConfirmed( false);
  32. } else {
  33. mSendState = SEND_STATE_NEED_CONFIRMATION;
  34. if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
  35. //@paul: 需要UI上确认过才能发送
  36. mEventListener.onP2pSendConfirmationRequested();
  37. }
  38. }
  39. }
  40. break;
  41. ...
  42. }
  43. }
  44. }
         上述函数的重点就是:prepareMessageToSend() 以及mEventListener.onP2pSendConfirmationRequested(); 分析完需要确认的流程,那不需要确认的流程也就打通了。

         由于NFC APP(例如Gallery,Calendar,联系簿等)在使用NFC发送数据时,都需要先设置要发送的数据的格式。设置的主要内容存放在变量:mMessageToSend ,mUrisToSend 中。此处涉及到APP部分,后续在单独开一节说明这部分。


        先说明一下prepareMessageToSend(),此函数流程在注释中说明:

  1. void prepareMessageToSend(boolean generatePlayLink) {
  2. synchronized (P2pLinkManager. this) {
  3. //@Paul:准备要发送的消息,分别存储在mMessageToSend和mUrisToSend中
  4. mMessageToSend = null;
  5. mUrisToSend = null;
  6. //@Paul:如果没有启动send,则直接返回,该变量有上层APP设定
  7. if (!mIsSendEnabled) {
  8. return;
  9. }
  10. //@Paul:判断前台程序是否启动
  11. List<Integer> foregroundUids = mForegroundUtils.getForegroundUids();
  12. if (foregroundUids.isEmpty()) {
  13. ...
  14. }
  15. //@Paul: 由上层定义的Callback信息
  16. if (mCallbackNdef != null) {
  17. if (foregroundUids.contains(mNdefCallbackUid)) {
  18. try {
  19. //@Paul: 如果有定义,则调用上层的createBeamShareData()函数
  20. BeamShareData shareData = mCallbackNdef.createBeamShareData();
  21. mMessageToSend = shareData.ndefMessage;
  22. mUrisToSend = shareData.uris;
  23. mSendFlags = shareData.flags;
  24. return;
  25. } catch (Exception e) {
  26. Log.e(TAG, "Failed NDEF callback: " + e.getMessage());
  27. }
  28. } else {
  29. ...
  30. }
  31. }
  32. // fall back to default NDEF for the foreground activity, unless the
  33. // application disabled this explicitly in their manifest.
  34. //@Paul: 如果前面没有进入,则使用默认值,将当前Pkg在Google Play的信息打包发送到上层
  35. String[] pkgs = mPackageManager.getPackagesForUid(foregroundUids.get( 0));
  36. if (pkgs != null && pkgs.length >= 1) {
  37. if (!generatePlayLink || beamDefaultDisabled(pkgs[ 0])
  38. || isManagedOrBeamDisabled(foregroundUids.get( 0))) {
  39. if (DBG) Log.d(TAG, "Disabling default Beam behavior");
  40. mMessageToSend = null;
  41. mUrisToSend = null;
  42. } else {
  43. mMessageToSend = createDefaultNdef(pkgs[ 0]);
  44. mUrisToSend = null;
  45. }
  46. }
  47. if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
  48. if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
  49. }
  50. }


        当要发送的信息准备好时,等待上层APP的确认动作(如果需要确认,否则直接把前面准备好的信息发送出去),确认的动作是上层APP来进行的。


        其实前文也有稍微提到,关于P2P和上层SendUI之间会有一些交互流程,此部分的流程用图形的方式说明如下:



        接下来就是onP2pSendConfirmationRequested()的介绍,此部分主要的目的是通知到SendUI层,请求用户确认或者拒绝。

  1. public void onP2pSendConfirmationRequested() {
  2. //@Paul: 一般来讲,都会有UI界面,所以默认会进入showPreSend()
  3. if (mSendUi != null) {
  4. //@Paul: showPreSend从感官上看,就是将当前的荧幕进行缩小,提示用户进行点击确认
  5. mSendUi.showPreSend( false);
  6. } else {
  7. //@Paul: 如果没有用户界面,默认就会进行到确认的流程
  8. mCallback.onP2pSendConfirmed();
  9. }
  10. }

        上述onP2pSendConfirmed(),在后面你又会看到,多留心。接上面介绍,一旦进入上述showPreSend()后,屏幕会缩小,然后提示用户点击确认,一旦用户执行了点击确认的动作,就会进入到SendUI.onTouch(),提前剧透一下,一旦点击了onTouch()后,就能看到上面的onP2pSendConfirmed()。

  1. @Override
  2. public boolean onTouch(View v, MotionEvent event) {
  3. if (mState != STATE_W4_TOUCH) {
  4. return false;
  5. }
  6. mState = STATE_SENDING;
  7. // Ignore future touches
  8. mScreenshotView.setOnTouchListener( null);
  9. // Cancel any ongoing animations
  10. mFrameCounterAnimator.cancel();
  11. mPreAnimator.cancel();
  12. //@Paul: 启动onSendConfirmed()
  13. mCallback.onSendConfirmed();
  14. return true;
  15. }
  16. @Override
  17. public void onSendConfirmed() {
  18. //@Paul: 如果没有发送动作,则调用showStartSend()
  19. if (!mSending) {
  20. if (mSendUi != null) {
  21. mSendUi.showStartSend();
  22. }
  23. //@Paul: 又调用了onP2pSendConfirmed()
  24. mCallback.onP2pSendConfirmed();
  25. }
  26. mSending = true;
  27. }

        这个地方补充说明一下,关于角色的确定,如果谁主动点击了屏幕,执行了确定的动作,那么就是发送端,发送端默认是Client端,因为接收端默认已经启动了Server端,等待对方发起连接. 所以后续在分析onP2pSendConfirmed()时,你会看到代码会启动clinet端.

  1. private void onP2pSendConfirmed(boolean requireConfirmation) {
  2. if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
  3. synchronized ( this) {
  4. //@Paul:状态检查
  5. if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
  6. && mSendState != SEND_STATE_NEED_CONFIRMATION)) {
  7. return;
  8. }
  9. mSendState = SEND_STATE_SENDING;
  10. if (mLinkState == LINK_STATE_WAITING_PDU) {
  11. //@Paul: 如果当前状态时WAITING PDU,就执行llcp连接
  12. mLinkState = LINK_STATE_UP;
  13. connectLlcpServices();
  14. } else if (mLinkState == LINK_STATE_UP && mLlcpServicesConnected) {
  15. //@Paul: 如果llcp已经连接上了,则进行Ndef消息的发送
  16. sendNdefMessage();
  17. } else if (mLinkState == LINK_STATE_UP && mLlcpConnectDelayed) {
  18. // Connect was delayed to interop with pre-MR2 stacks; send connect now.
  19. connectLlcpServices();
  20. } else if (mLinkState == LINK_STATE_DEBOUNCE) {
  21. // Restart debounce timeout and tell user to tap again
  22. scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CONFIRMED_DEBOUNCE_MS);
  23. mEventListener.onP2pSendDebounce();
  24. }
  25. }
  26. }

        进入ConnectLlcpServices()后,你就看到LLCP的client服务就可能一项一项的启动.用代码说话吧:

  1. void connectLlcpServices() {
  2. synchronized (P2pLinkManager. this) {
  3. //@Paul: 如果有connectTask正在运行,则返回
  4. if (mConnectTask != null) {
  5. Log.e(TAG, "Still had a reference to mConnectTask!");
  6. }
  7. //@Paul: 创建新的connectTask,并执行此Task
  8. mConnectTask = new ConnectTask();
  9. mConnectTask.execute();
  10. }
  11. }
  12. @Override
  13. protected Boolean doInBackground(Void... params) {
  14. boolean needsHandover = false;
  15. boolean needsNdef = false;
  16. boolean success = false;
  17. HandoverClient handoverClient = null;
  18. SnepClient snepClient = null;
  19. NdefPushClient nppClient = null;
  20. synchronized(P2pLinkManager. this) {
  21. if (mUrisToSend != null) {
  22. //@Paul:如果URI存在,则可能进行进行Handover
  23. needsHandover = true;
  24. }
  25. if (mMessageToSend != null) {
  26. //@Paul: 如果要发送的消息不为空,则可能是Ndef消息
  27. needsNdef = true;
  28. }
  29. }
  30. // We know either is requested - otherwise this task
  31. // wouldn't have been started.
  32. if (needsHandover) {
  33. //@Paul: 创建HandoverClient
  34. handoverClient = new HandoverClient();
  35. try {
  36. //@Paul: 进行连接操作,主要分两步
  37. // service.createLlcpSocket(0, MIU, 1, 1024);
  38. // sock.connectToService(HandoverServer.HANDOVER_SERVICE_NAME);
  39. // 上述连接是依据服务名来的
  40. // 主要函数:sendHandoverRequest()
  41. handoverClient.connect();
  42. success = true; // Regardless of NDEF result
  43. } catch (IOException e) {
  44. handoverClient = null;
  45. }
  46. }
  47. if (needsNdef || (needsHandover && handoverClient == null)) {
  48. //@Paul: 创建SnepClient
  49. snepClient = new SnepClient();
  50. try {
  51. //@Paul: 进行连接操作,主要分三步
  52. // NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024)
  53. // socket.connectToService(mServiceName)/socket.connectToSap(mPort);
  54. // 依据服务名或者Port连接
  55. // new SnepMessenger(),用于接收或发送SNEP消息
  56. // 主要函数:put()/get()
  57. snepClient.connect();
  58. success = true;
  59. } catch (IOException e) {
  60. snepClient = null;
  61. }
  62. if (!success) {
  63. //@Paul:如果上述SNEP Client创建失败,则创建NPP Client
  64. nppClient = new NdefPushClient();
  65. try {
  66. //@Paul:进行连接操作,主要分两步
  67. // service.createLlcpSocket(0, MIU, 1, 1024);
  68. // sock.connectToService(NdefPushServer.SERVICE_NAME);
  69. // 主要函数:push()/close()
  70. nppClient.connect();
  71. success = true;
  72. } catch (IOException e) {
  73. nppClient = null;
  74. }
  75. }
  76. }
  77. synchronized (P2pLinkManager. this) {
  78. //如果有取消,则将前面的client端全部关闭
  79. if (isCancelled()) {
  80. // Cancelled by onLlcpDeactivated on UI thread
  81. if (handoverClient != null) {
  82. handoverClient.close();
  83. }
  84. if (snepClient != null) {
  85. snepClient.close();
  86. }
  87. if (nppClient != null) {
  88. nppClient.close();
  89. }
  90. return false;
  91. } else {
  92. // Once assigned, these are the responsibility of
  93. // the code on the UI thread to release - typically
  94. // through onLlcpDeactivated().
  95. mHandoverClient = handoverClient;
  96. mSnepClient = snepClient;
  97. mNdefPushClient = nppClient;
  98. return success;
  99. }
  100. }
  101. }


        一旦连接上之后, 就会顺序进入onP2pSendConfirmed() 中的第2个else中的sendNdefMessage()。此函数看名字就能知道意义,就是将前面的Ndef消息发送出去:

  1. void sendNdefMessage() {
  2. synchronized ( this) {
  3. cancelSendNdefMessage();
  4. //@Paul:启动新的进程,处理要发送的数据
  5. mSendTask = new SendTask();
  6. mSendTask.execute();
  7. }
  8. }

        启动SendTask,该类是继承于AsyncTask,调用execute后自动执行,具体的代码如下:

  1. @Override
  2. public Void doInBackground(Void... args) {
  3. NdefMessage m;
  4. Uri[] uris;
  5. boolean result = false;
  6. //@Paul: 前面connect时候创建的mSnepClient等
  7. synchronized (P2pLinkManager. this) {
  8. if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
  9. return null;
  10. }
  11. m = mMessageToSend;
  12. uris = mUrisToSend;
  13. snepClient = mSnepClient;
  14. handoverClient = mHandoverClient;
  15. nppClient = mNdefPushClient;
  16. }
  17. long time = SystemClock.elapsedRealtime();
  18. //@Paul: 前面uri有赋值的话,就直接进入Handover的处理
  19. if (uris != null) {
  20. if (DBG) Log.d(TAG, "Trying handover request");
  21. try {
  22. int handoverResult = doHandover(uris);
  23. switch (handoverResult) {
  24. case HANDOVER_SUCCESS:
  25. result = true;
  26. break;
  27. case HANDOVER_FAILURE:
  28. result = false;
  29. break;
  30. case HANDOVER_UNSUPPORTED:
  31. result = false;
  32. onHandoverUnsupported();
  33. break;
  34. }
  35. } catch (IOException e) {
  36. result = false;
  37. }
  38. }
  39. //@Paul: 前面message有赋值的话,进入NDEF消息的处理
  40. if (!result && m != null && snepClient != null) {
  41. if (DBG) Log.d(TAG, "Sending ndef via SNEP");
  42. try {
  43. int snepResult = doSnepProtocol(m);
  44. switch (snepResult) {
  45. case SNEP_SUCCESS:
  46. result = true;
  47. break;
  48. case SNEP_FAILURE:
  49. result = false;
  50. break;
  51. default:
  52. result = false;
  53. }
  54. } catch (IOException e) {
  55. result = false;
  56. }
  57. }
  58. //@Paul: 若不支持Snep格式,则用NPP方式放松
  59. if (!result && m != null && nppClient != null) {
  60. result = nppClient.push(m);
  61. }
  62. time = SystemClock.elapsedRealtime() - time;
  63. if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);
  64. if (result) {
  65. //@Paul: 若发送成功后,调用onSendComplete,发送MSG_SEND_COMPLETE
  66. onSendComplete(m, time);
  67. }
  68. return null;
  69. }

        下面分别介绍doHandover(uris),doSnepProtocol(m),nppClient.push(m);  onSendComplete(m, time)。


         doHandover():在AOSP中主要是生成BT 的NDEF Message,主要流程是先产生NDEF Record,然后将Record组成MSG,然后调用socket的send(),等send() 调用完成,在while循环中等待Handover的Response。如果对方不支持Handover时,就会尝试调用SNEP的处理函数,处理完成之后,开始解析Response数据,然后将解析的数据存放在Intent中,然后发送广播消息,启动对事件关心的BT activity。




        doSnepProtocol():主要是依据SNEP协议规定,进行Snep协议的处理。简单的流程分析如下:




        为了进一步说清楚SNEP,可以先参考一下,前面介绍的SNEP的协议,首先分析sendMessage(),此函数的目的是将request消息依据spec规定发送出去:

  1. public void sendMessage(SnepMessage msg) throws IOException {
  2. ...
  3. if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
  4. // Send first fragment
  5. //@Paul: 去当前buffer的长度和定义的Fragment长度中较小值
  6. int length = Math.min(buffer.length, mFragmentLength);
  7. byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
  8. if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
  9. //@Paul: 将消息透过socket发送出去
  10. mSocket.send(tmpBuffer);
  11. //@Paul: 若数据包不用切割(一个数据包能发完),则直接返回
  12. if (length == buffer.length) {
  13. return;
  14. }
  15. //@Paul: 若切片后发送,则需要等待对方的回复后再决定下一步行动
  16. // Look for Continue or Reject from peer.
  17. int offset = length;
  18. byte[] responseBytes = new byte[HEADER_LENGTH];
  19. mSocket.receive(responseBytes);
  20. //@Paul: 接收对方的Response消息
  21. SnepMessage snepResponse;
  22. try {
  23. snepResponse = SnepMessage.fromByteArray(responseBytes);
  24. } catch (FormatException e) {
  25. throw new IOException( "Invalid SNEP message", e);
  26. }
  27. //@Paul: 如果对方的回复不是continue,在返回错误
  28. if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
  29. if (snepResponse.getField() != remoteContinue) {
  30. throw new IOException( "Invalid response from server (" +
  31. snepResponse.getField() + ")");
  32. }
  33. // Send remaining fragments.
  34. //@Paul: 对方要求继续发送时,将剩余的数据发送完成
  35. while (offset < buffer.length) {
  36. length = Math.min(buffer.length - offset, mFragmentLength);
  37. tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
  38. if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
  39. mSocket.send(tmpBuffer);
  40. offset += length;
  41. }
  42. }


        在上述消息发送完成后,需要等待对方的response消息,就进入了getMessage()阶段,代码分析如下:

  1. public SnepMessage getMessage() throws IOException, SnepException {
  2. ...
  3. //@Paul: 等待对方的回复消息
  4. size = mSocket.receive(partial);
  5. if (DBG) Log.d(TAG, "read " + size + " bytes");
  6. if (size < 0) {
  7. try {
  8. //@Paul:接收的数据大小小于0,直接回复Reject
  9. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  10. } catch (IOException e) {
  11. // Ignore
  12. }
  13. throw new IOException( "Error reading SNEP message.");
  14. } else if (size < HEADER_LENGTH) {
  15. try {
  16. //@Paul:接收的数据大小小于Header Size(因为Snep数据必须包含Header),直接回复Reject
  17. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  18. } catch (IOException e) {
  19. // Ignore
  20. }
  21. throw new IOException( "Invalid fragment from sender.");
  22. } else {
  23. //@Paul: 更新buffer值
  24. readSize = size - HEADER_LENGTH;
  25. buffer.write(partial, 0, size);
  26. }
  27. //@Paul: 解析第一包数据收到的头中的字段
  28. DataInputStream dataIn = new DataInputStream( new ByteArrayInputStream(partial));
  29. requestVersion = dataIn.readByte();
  30. byte requestField = dataIn.readByte();
  31. requestSize = dataIn.readInt();
  32. if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
  33. //@Paul: 如果SNEP的Version不匹配,则认为接收完成
  34. if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
  35. // Invalid protocol version; treat message as complete.
  36. return new SnepMessage(requestVersion, requestField, 0, 0, null);
  37. }
  38. //@Paul: 如果接收的数据小于Header中规定的数据,则请求对方继续发送,回复Continue
  39. if (requestSize > readSize) {
  40. if (DBG) Log.d(TAG, "requesting continuation");
  41. mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
  42. } else {
  43. doneReading = true;
  44. }
  45. //@Paul: 发送完Continue后,执行loop中的receive(),等待后续的package
  46. // Remaining fragments
  47. while (!doneReading) {
  48. try {
  49. size = mSocket.receive(partial);
  50. if (DBG) Log.d(TAG, "read " + size + " bytes");
  51. if (size < 0) {
  52. try {
  53. //@Paul:接收的数据段错误,直接回复Reject
  54. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  55. } catch (IOException e) {
  56. // Ignore
  57. }
  58. throw new IOException();
  59. } else {
  60. //@Paul:不断更新收到的数据
  61. readSize += size;
  62. buffer.write(partial, 0, size);
  63. if (readSize == requestSize) {
  64. doneReading = true;
  65. }
  66. }
  67. } catch (IOException e) {
  68. try {
  69. mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
  70. } catch (IOException e2) {
  71. // Ignore
  72. }
  73. throw e;
  74. }
  75. }
  76. // Build NDEF message set from the stream
  77. try {
  78. //@Paul:将接收的数据转换成SNEP格式
  79. return SnepMessage.fromByteArray(buffer.toByteArray());
  80. } catch (FormatException e) {
  81. Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
  82. throw new SnepException(e);
  83. }
  84. }


        为了兼容性的问题,可能当前的P2P并不支持SNEP协议,那么就会尝试用NPP协议进行数据的解析,这样就到了NdefPushClient的push流程,此部分的协议比较简单,流程也比较简单:

  1. public boolean push(NdefMessage msg) {
  2. ...
  3. // We only handle a single immediate action for now
  4. //@Paul: 按照制定格式创建NdefPushProtocol
  5. NdefPushProtocol proto = new NdefPushProtocol(msg, NdefPushProtocol.ACTION_IMMEDIATE);
  6. byte[] buffer = proto.toByteArray();
  7. int offset = 0;
  8. int remoteMiu;
  9. try {
  10. remoteMiu = sock.getRemoteMiu();
  11. if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
  12. //@Paul: 将当前信息完整的发送出去,一直到完成
  13. while (offset < buffer.length) {
  14. int length = Math.min(buffer.length - offset, remoteMiu);
  15. byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
  16. if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
  17. sock.send(tmpBuffer);
  18. offset += length;
  19. }
  20. return true;
  21. }
  22. ...
  23. return false;
  24. }

        当上述操作都完成后,表示send已经完成,自然就需要通知上层,你需要做的操作已经做完,如果后续有操作,请另行指示。

        通知上层的方式,就是用Message的方式执行, 系统会给当前的进程的handler发送MSG_SEND_COMPLETE消息,收到此消息后,进入处理函数,主要内容如下:

  1. {
  2. ...
  3. //@Paul: 又是Listener,最终又到SendUI
  4. mEventListener.onP2pSendComplete();
  5. //@Paul:如果有定义callback,则执行callback中定义的onNdefPushComplete
  6. if (mCallbackNdef != null) {
  7. try {
  8. mCallbackNdef.onNdefPushComplete();
  9. } catch (Exception e) {
  10. Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
  11. }
  12. }
  13. }

        Listerner的处理比较简单,就是用户的notify,函数为:

  1. public void onP2pSendComplete() {
  2. mNfcService.playSound(NfcService.SOUND_END);
  3. mVibrator.vibrate(VIBRATION_PATTERN, - 1);
  4. if (mSendUi != null) {
  5. mSendUi.finish(SendUi.FINISH_SEND_SUCCESS);
  6. }
  7. mSending = false;
  8. mNdefSent = true;
  9. }


        至此,P2P的流程基本已经分析完成。这样基本上可以对NFC中P2P的流程有了基本的了解。


        后续会有一个APP的实例说明,参考实例能更加深刻的理解NFC处理流程,加深对该功能的理解。




补充说明:

1. 上述P2P的各个Server是在哪里创建?

Answer:参考P2pLinkManager.java中的构造函数P2pLinkManager()

  1. mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback);
  2. mDefaultSnepServer = new SnepServer(mDefaultSnepCallback, defaultMiu, defaultRwSize);
  3. mHandoverServer = new HandoverServer(HANDOVER_SAP, handoverManager, mHandoverCallback);


[java]  view plain  copy

猜你喜欢

转载自blog.csdn.net/weixin_38503885/article/details/80927683
P2P
今日推荐