[NFC]P2P设备响应流程

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


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


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

[java]  view plain  copy
 print ?
  1. @Override  
  2. public void onLlcpLinkActivated(NfcDepEndpoint device) {  
  3.     sendMessage(NfcService.MSG_LLCP_LINK_ACTIVATION, device);  
  4. }  


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

[java]  view plain  copy
 print ?
  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()是进行数据链路层的连接,保证底层已经是发现对方并且是可以连接的。


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

[java]  view plain  copy
 print ?
  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()函数:

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

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


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

[java]  view plain  copy
 print ?
  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.           
  16.         //@Paul: 由上层定义的Callback信息  
  17.         if (mCallbackNdef != null) {  
  18.             if (foregroundUids.contains(mNdefCallbackUid)) {  
  19.                 try {  
  20.                     //@Paul: 如果有定义,则调用上层的createBeamShareData()函数  
  21.                     BeamShareData shareData = mCallbackNdef.createBeamShareData();  
  22.                     mMessageToSend = shareData.ndefMessage;  
  23.                     mUrisToSend = shareData.uris;  
  24.                     mSendFlags = shareData.flags;  
  25.                     return;  
  26.                 } catch (Exception e) {  
  27.                     Log.e(TAG, "Failed NDEF callback: " + e.getMessage());  
  28.                 }  
  29.             } else {  
  30.                 ...  
  31.             }  
  32.         }  
  33.   
  34.         // fall back to default NDEF for the foreground activity, unless the  
  35.         // application disabled this explicitly in their manifest.  
  36.         //@Paul: 如果前面没有进入,则使用默认值,将当前Pkg在Google Play的信息打包发送到上层  
  37.         String[] pkgs = mPackageManager.getPackagesForUid(foregroundUids.get(0));  
  38.         if (pkgs != null && pkgs.length >= 1) {  
  39.             if (!generatePlayLink || beamDefaultDisabled(pkgs[0])  
  40.                     || isManagedOrBeamDisabled(foregroundUids.get(0))) {  
  41.                 if (DBG) Log.d(TAG, "Disabling default Beam behavior");  
  42.                 mMessageToSend = null;  
  43.                 mUrisToSend = null;  
  44.             } else {  
  45.                 mMessageToSend = createDefaultNdef(pkgs[0]);  
  46.                 mUrisToSend = null;  
  47.             }  
  48.         }  
  49.   
  50.         if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);  
  51.         if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);  
  52.     }  
  53. }  


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


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



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

[java]  view plain  copy
 print ?
  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()。

[java]  view plain  copy
 print ?
  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.   
  10.     // Cancel any ongoing animations  
  11.     mFrameCounterAnimator.cancel();  
  12.     mPreAnimator.cancel();  
  13.   
  14.     //@Paul: 启动onSendConfirmed()  
  15.     mCallback.onSendConfirmed();  
  16.     return true;  
  17. }  
  18.   
  19.   
  20. @Override  
  21. public void onSendConfirmed() {  
  22.     //@Paul: 如果没有发送动作,则调用showStartSend()  
  23.     if (!mSending) {  
  24.         if (mSendUi != null) {  
  25.             mSendUi.showStartSend();  
  26.         }  
  27.         //@Paul: 又调用了onP2pSendConfirmed()  
  28.         mCallback.onP2pSendConfirmed();  
  29.     }  
  30.     mSending = true;  
  31. }  

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

[java]  view plain  copy
 print ?
  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服务就可能一项一项的启动.用代码说话吧:

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


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

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

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

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

        下面分别介绍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规定发送出去:

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


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

[java]  view plain  copy
 print ?
  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.   
  28.     //@Paul: 解析第一包数据收到的头中的字段  
  29.     DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));  
  30.     requestVersion = dataIn.readByte();  
  31.     byte requestField = dataIn.readByte();  
  32.     requestSize = dataIn.readInt();  
  33.   
  34.     if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);  
  35.   
  36.     //@Paul: 如果SNEP的Version不匹配,则认为接收完成  
  37.     if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {  
  38.         // Invalid protocol version; treat message as complete.  
  39.         return new SnepMessage(requestVersion, requestField, 00null);  
  40.     }  
  41.   
  42.     //@Paul: 如果接收的数据小于Header中规定的数据,则请求对方继续发送,回复Continue  
  43.     if (requestSize > readSize) {  
  44.         if (DBG) Log.d(TAG, "requesting continuation");  
  45.         mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());  
  46.     } else {  
  47.         doneReading = true;  
  48.     }  
  49.   
  50.     //@Paul: 发送完Continue后,执行loop中的receive(),等待后续的package  
  51.     // Remaining fragments  
  52.     while (!doneReading) {  
  53.         try {  
  54.             size = mSocket.receive(partial);  
  55.             if (DBG) Log.d(TAG, "read " + size + " bytes");  
  56.             if (size < 0) {  
  57.                 try {  
  58.                     //@Paul:接收的数据段错误,直接回复Reject  
  59.                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());  
  60.                 } catch (IOException e) {  
  61.                     // Ignore  
  62.                 }  
  63.                 throw new IOException();  
  64.             } else {  
  65.                 //@Paul:不断更新收到的数据  
  66.                 readSize += size;  
  67.                 buffer.write(partial, 0, size);  
  68.                 if (readSize == requestSize) {  
  69.                     doneReading = true;  
  70.                 }  
  71.             }  
  72.         } catch (IOException e) {  
  73.             try {  
  74.                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());  
  75.             } catch (IOException e2) {  
  76.                 // Ignore  
  77.             }  
  78.             throw e;  
  79.         }  
  80.     }  
  81.   
  82.     // Build NDEF message set from the stream  
  83.     try {  
  84.         //@Paul:将接收的数据转换成SNEP格式  
  85.         return SnepMessage.fromByteArray(buffer.toByteArray());  
  86.     } catch (FormatException e) {  
  87.         Log.e(TAG, "Badly formatted NDEF message, ignoring", e);  
  88.         throw new SnepException(e);  
  89.     }  
  90. }  


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

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

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

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

[java]  view plain  copy
 print ?
  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,函数为:

[java]  view plain  copy
 print ?
  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():

[java]  view plain  copy
 print ?
  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
 print ?
  1.   
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值