前文[NFC]Tag设备响应流程中有提到P2P设备的发现的函数始于:onLlcpLinkActivated().
本文将基于onLlcpLinkActivated()开始后文的分析,进而引出P2P流程中的SNEP,NDEFPUSH,HANDOVER以及ECHOSERVER的响应过程.
程序进入 onLlcpLinkActivated() 后,有点类似notify,开始同时上层,有P2P设备靠近了,解析一下,是什么格式,看看要做什么操作.
-
-
public void onLlcpLinkActivated(NfcDepEndpoint device) {
-
sendMessage(NfcService.MSG_LLCP_LINK_ACTIVATION, device);
-
}
消息MSG_LLCP_LINK_ACTIVATION被NfcService.java自身注册的NfcServiceHandler进行处理
-
case MSG_LLCP_LINK_ACTIVATION:
-
if (mIsDebugBuild) {
-
//@paul: 发送LLCP_UP的广播
-
Intent actIntent = new Intent(ACTION_LLCP_UP);
-
mContext.sendBroadcast(actIntent);
-
}
-
//@paul: 解析底层传递上来的NfcDepEndpoint信息
-
llcpActivated((NfcDepEndpoint) msg.obj);
-
break;
在进入llcpActivated()之后,系统会区分Target和Initiator,这两种角色的流程基本上相同,差异点是在Target端会先执行connect(),此connect()是进行数据链路层的连接,保证底层已经是发现对方并且是可以连接的。
-
<span style= "font-size:12px;"> public void onLlcpActivated()
-
public void onLlcpFirstPacketReceived ()
-
public void onLlcpDeactivated ()
-
void onSendComplete (NdefMessage msg, long elapsedRealtime)</span>
这些函数必须在UI main thread 调用,用于接收到底层连接的各种状态的更新。各个函数的意义依照名字基本上就能理解了。
直接分析onLlcpActivated()函数:
-
/**
-
* Must be called on UI Thread.
-
*/
-
public void onLlcpActivated() {
-
Log.i(TAG, "LLCP activated");
-
-
synchronized (P2pLinkManager. this) {
-
...
-
mLlcpConnectDelayed = false;
-
//@paul: 初始状态mLinkState为LINK_STATE_DOWN
-
switch (mLinkState) {
-
case LINK_STATE_DOWN:
-
if (DBG) Log.d(TAG, "onP2pInRange()");
-
mLinkState = LINK_STATE_WAITING_PDU;
-
//@paul: 通知UI,发现P2P设备
-
mEventListener.onP2pInRange();
-
//@paul: 初始状态mSendState为SEND_STATE_NOTHING_TO_SEND
-
if (mSendState == SEND_STATE_PENDING) {
-
if (DBG) Log.d(TAG, "Sending pending data.");
-
mHandler.removeMessages(MSG_WAIT_FOR_LINK_TIMEOUT);
-
mSendState = SEND_STATE_SENDING;
-
onP2pSendConfirmed( false);
-
} else {
-
mSendState = SEND_STATE_NOTHING_TO_SEND;
-
//@paul: 依据APP设置的信息,准备发送的信息
-
prepareMessageToSend( true);
-
if (mMessageToSend != null ||
-
(mUrisToSend != null && mHandoverManager.isHandoverSupported())) {
-
if ((mSendFlags & NfcAdapter.FLAG_NDEF_PUSH_NO_CONFIRM) != 0) {
-
mSendState = SEND_STATE_SENDING;
-
//@paul: 不需要UI确认
-
onP2pSendConfirmed( false);
-
} else {
-
mSendState = SEND_STATE_NEED_CONFIRMATION;
-
if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
-
//@paul: 需要UI上确认过才能发送
-
mEventListener.onP2pSendConfirmationRequested();
-
}
-
}
-
}
-
break;
-
...
-
}
-
}
-
}
由于NFC APP(例如Gallery,Calendar,联系簿等)在使用NFC发送数据时,都需要先设置要发送的数据的格式。设置的主要内容存放在变量:mMessageToSend ,mUrisToSend 中。此处涉及到APP部分,后续在单独开一节说明这部分。
先说明一下prepareMessageToSend(),此函数流程在注释中说明:
-
void prepareMessageToSend(boolean generatePlayLink) {
-
synchronized (P2pLinkManager. this) {
-
//@Paul:准备要发送的消息,分别存储在mMessageToSend和mUrisToSend中
-
mMessageToSend = null;
-
mUrisToSend = null;
-
//@Paul:如果没有启动send,则直接返回,该变量有上层APP设定
-
if (!mIsSendEnabled) {
-
return;
-
}
-
//@Paul:判断前台程序是否启动
-
List<Integer> foregroundUids = mForegroundUtils.getForegroundUids();
-
if (foregroundUids.isEmpty()) {
-
...
-
}
-
-
//@Paul: 由上层定义的Callback信息
-
if (mCallbackNdef != null) {
-
if (foregroundUids.contains(mNdefCallbackUid)) {
-
try {
-
//@Paul: 如果有定义,则调用上层的createBeamShareData()函数
-
BeamShareData shareData = mCallbackNdef.createBeamShareData();
-
mMessageToSend = shareData.ndefMessage;
-
mUrisToSend = shareData.uris;
-
mSendFlags = shareData.flags;
-
return;
-
} catch (Exception e) {
-
Log.e(TAG, "Failed NDEF callback: " + e.getMessage());
-
}
-
} else {
-
...
-
}
-
}
-
-
// fall back to default NDEF for the foreground activity, unless the
-
// application disabled this explicitly in their manifest.
-
//@Paul: 如果前面没有进入,则使用默认值,将当前Pkg在Google Play的信息打包发送到上层
-
String[] pkgs = mPackageManager.getPackagesForUid(foregroundUids.get( 0));
-
if (pkgs != null && pkgs.length >= 1) {
-
if (!generatePlayLink || beamDefaultDisabled(pkgs[ 0])
-
|| isManagedOrBeamDisabled(foregroundUids.get( 0))) {
-
if (DBG) Log.d(TAG, "Disabling default Beam behavior");
-
mMessageToSend = null;
-
mUrisToSend = null;
-
} else {
-
mMessageToSend = createDefaultNdef(pkgs[ 0]);
-
mUrisToSend = null;
-
}
-
}
-
-
if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
-
if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
-
}
-
}
当要发送的信息准备好时,等待上层APP的确认动作(如果需要确认,否则直接把前面准备好的信息发送出去),确认的动作是上层APP来进行的。
其实前文也有稍微提到,关于P2P和上层SendUI之间会有一些交互流程,此部分的流程用图形的方式说明如下:
接下来就是onP2pSendConfirmationRequested()的介绍,此部分主要的目的是通知到SendUI层,请求用户确认或者拒绝。
-
public void onP2pSendConfirmationRequested() {
-
//@Paul: 一般来讲,都会有UI界面,所以默认会进入showPreSend()
-
if (mSendUi != null) {
-
//@Paul: showPreSend从感官上看,就是将当前的荧幕进行缩小,提示用户进行点击确认
-
mSendUi.showPreSend( false);
-
} else {
-
//@Paul: 如果没有用户界面,默认就会进行到确认的流程
-
mCallback.onP2pSendConfirmed();
-
}
-
}
上述onP2pSendConfirmed(),在后面你又会看到,多留心。接上面介绍,一旦进入上述showPreSend()后,屏幕会缩小,然后提示用户点击确认,一旦用户执行了点击确认的动作,就会进入到SendUI.onTouch(),提前剧透一下,一旦点击了onTouch()后,就能看到上面的onP2pSendConfirmed()。
-
-
public boolean onTouch(View v, MotionEvent event) {
-
if (mState != STATE_W4_TOUCH) {
-
return false;
-
}
-
mState = STATE_SENDING;
-
// Ignore future touches
-
mScreenshotView.setOnTouchListener( null);
-
-
// Cancel any ongoing animations
-
mFrameCounterAnimator.cancel();
-
mPreAnimator.cancel();
-
-
//@Paul: 启动onSendConfirmed()
-
mCallback.onSendConfirmed();
-
return true;
-
}
-
-
-
-
public void onSendConfirmed() {
-
//@Paul: 如果没有发送动作,则调用showStartSend()
-
if (!mSending) {
-
if (mSendUi != null) {
-
mSendUi.showStartSend();
-
}
-
//@Paul: 又调用了onP2pSendConfirmed()
-
mCallback.onP2pSendConfirmed();
-
}
-
mSending = true;
-
}
这个地方补充说明一下,关于角色的确定,如果谁主动点击了屏幕,执行了确定的动作,那么就是发送端,发送端默认是Client端,因为接收端默认已经启动了Server端,等待对方发起连接. 所以后续在分析onP2pSendConfirmed()时,你会看到代码会启动clinet端.
-
private void onP2pSendConfirmed(boolean requireConfirmation) {
-
if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
-
synchronized ( this) {
-
//@Paul:状态检查
-
if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
-
&& mSendState != SEND_STATE_NEED_CONFIRMATION)) {
-
return;
-
}
-
mSendState = SEND_STATE_SENDING;
-
if (mLinkState == LINK_STATE_WAITING_PDU) {
-
//@Paul: 如果当前状态时WAITING PDU,就执行llcp连接
-
mLinkState = LINK_STATE_UP;
-
connectLlcpServices();
-
} else if (mLinkState == LINK_STATE_UP && mLlcpServicesConnected) {
-
//@Paul: 如果llcp已经连接上了,则进行Ndef消息的发送
-
sendNdefMessage();
-
} else if (mLinkState == LINK_STATE_UP && mLlcpConnectDelayed) {
-
// Connect was delayed to interop with pre-MR2 stacks; send connect now.
-
connectLlcpServices();
-
} else if (mLinkState == LINK_STATE_DEBOUNCE) {
-
// Restart debounce timeout and tell user to tap again
-
scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CONFIRMED_DEBOUNCE_MS);
-
mEventListener.onP2pSendDebounce();
-
}
-
}
-
}
进入ConnectLlcpServices()后,你就看到LLCP的client服务就可能一项一项的启动.用代码说话吧:
-
void connectLlcpServices() {
-
synchronized (P2pLinkManager. this) {
-
//@Paul: 如果有connectTask正在运行,则返回
-
if (mConnectTask != null) {
-
Log.e(TAG, "Still had a reference to mConnectTask!");
-
}
-
//@Paul: 创建新的connectTask,并执行此Task
-
mConnectTask = new ConnectTask();
-
mConnectTask.execute();
-
}
-
}
-
-
-
-
-
-
protected Boolean doInBackground(Void... params) {
-
boolean needsHandover = false;
-
boolean needsNdef = false;
-
boolean success = false;
-
HandoverClient handoverClient = null;
-
SnepClient snepClient = null;
-
NdefPushClient nppClient = null;
-
-
-
synchronized(P2pLinkManager. this) {
-
if (mUrisToSend != null) {
-
//@Paul:如果URI存在,则可能进行进行Handover
-
needsHandover = true;
-
}
-
-
-
if (mMessageToSend != null) {
-
//@Paul: 如果要发送的消息不为空,则可能是Ndef消息
-
needsNdef = true;
-
}
-
}
-
// We know either is requested - otherwise this task
-
// wouldn't have been started.
-
if (needsHandover) {
-
//@Paul: 创建HandoverClient
-
handoverClient = new HandoverClient();
-
try {
-
//@Paul: 进行连接操作,主要分两步
-
// service.createLlcpSocket(0, MIU, 1, 1024);
-
// sock.connectToService(HandoverServer.HANDOVER_SERVICE_NAME);
-
// 上述连接是依据服务名来的
-
// 主要函数:sendHandoverRequest()
-
handoverClient.connect();
-
success = true; // Regardless of NDEF result
-
} catch (IOException e) {
-
handoverClient = null;
-
}
-
}
-
-
-
if (needsNdef || (needsHandover && handoverClient == null)) {
-
//@Paul: 创建SnepClient
-
snepClient = new SnepClient();
-
try {
-
//@Paul: 进行连接操作,主要分三步
-
// NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024)
-
// socket.connectToService(mServiceName)/socket.connectToSap(mPort);
-
// 依据服务名或者Port连接
-
// new SnepMessenger(),用于接收或发送SNEP消息
-
// 主要函数:put()/get()
-
snepClient.connect();
-
success = true;
-
} catch (IOException e) {
-
snepClient = null;
-
}
-
-
-
if (!success) {
-
//@Paul:如果上述SNEP Client创建失败,则创建NPP Client
-
nppClient = new NdefPushClient();
-
try {
-
//@Paul:进行连接操作,主要分两步
-
// service.createLlcpSocket(0, MIU, 1, 1024);
-
// sock.connectToService(NdefPushServer.SERVICE_NAME);
-
// 主要函数:push()/close()
-
nppClient.connect();
-
success = true;
-
} catch (IOException e) {
-
nppClient = null;
-
}
-
}
-
}
-
-
-
synchronized (P2pLinkManager. this) {
-
//如果有取消,则将前面的client端全部关闭
-
if (isCancelled()) {
-
// Cancelled by onLlcpDeactivated on UI thread
-
if (handoverClient != null) {
-
handoverClient.close();
-
}
-
if (snepClient != null) {
-
snepClient.close();
-
}
-
if (nppClient != null) {
-
nppClient.close();
-
}
-
return false;
-
} else {
-
// Once assigned, these are the responsibility of
-
// the code on the UI thread to release - typically
-
// through onLlcpDeactivated().
-
mHandoverClient = handoverClient;
-
mSnepClient = snepClient;
-
mNdefPushClient = nppClient;
-
return success;
-
}
-
}
-
}
一旦连接上之后, 就会顺序进入onP2pSendConfirmed() 中的第2个else中的sendNdefMessage()。此函数看名字就能知道意义,就是将前面的Ndef消息发送出去:
-
void sendNdefMessage() {
-
synchronized ( this) {
-
cancelSendNdefMessage();
-
//@Paul:启动新的进程,处理要发送的数据
-
mSendTask = new SendTask();
-
mSendTask.execute();
-
}
-
}
启动SendTask,该类是继承于AsyncTask,调用execute后自动执行,具体的代码如下:
-
-
public Void doInBackground(Void... args) {
-
NdefMessage m;
-
Uri[] uris;
-
boolean result = false;
-
-
//@Paul: 前面connect时候创建的mSnepClient等
-
synchronized (P2pLinkManager. this) {
-
if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
-
return null;
-
}
-
m = mMessageToSend;
-
uris = mUrisToSend;
-
snepClient = mSnepClient;
-
handoverClient = mHandoverClient;
-
nppClient = mNdefPushClient;
-
}
-
-
long time = SystemClock.elapsedRealtime();
-
-
//@Paul: 前面uri有赋值的话,就直接进入Handover的处理
-
if (uris != null) {
-
if (DBG) Log.d(TAG, "Trying handover request");
-
try {
-
int handoverResult = doHandover(uris);
-
switch (handoverResult) {
-
case HANDOVER_SUCCESS:
-
result = true;
-
break;
-
case HANDOVER_FAILURE:
-
result = false;
-
break;
-
case HANDOVER_UNSUPPORTED:
-
result = false;
-
onHandoverUnsupported();
-
break;
-
}
-
} catch (IOException e) {
-
result = false;
-
}
-
}
-
-
//@Paul: 前面message有赋值的话,进入NDEF消息的处理
-
if (!result && m != null && snepClient != null) {
-
if (DBG) Log.d(TAG, "Sending ndef via SNEP");
-
try {
-
int snepResult = doSnepProtocol(m);
-
switch (snepResult) {
-
case SNEP_SUCCESS:
-
result = true;
-
break;
-
case SNEP_FAILURE:
-
result = false;
-
break;
-
default:
-
result = false;
-
}
-
} catch (IOException e) {
-
result = false;
-
}
-
}
-
-
//@Paul: 若不支持Snep格式,则用NPP方式放松
-
if (!result && m != null && nppClient != null) {
-
result = nppClient.push(m);
-
}
-
-
time = SystemClock.elapsedRealtime() - time;
-
if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);
-
if (result) {
-
//@Paul: 若发送成功后,调用onSendComplete,发送MSG_SEND_COMPLETE
-
onSendComplete(m, time);
-
}
-
-
return null;
-
}
下面分别介绍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规定发送出去:
-
public void sendMessage(SnepMessage msg) throws IOException {
-
...
-
if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
-
-
// Send first fragment
-
//@Paul: 去当前buffer的长度和定义的Fragment长度中较小值
-
int length = Math.min(buffer.length, mFragmentLength);
-
byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
-
if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
-
//@Paul: 将消息透过socket发送出去
-
mSocket.send(tmpBuffer);
-
-
//@Paul: 若数据包不用切割(一个数据包能发完),则直接返回
-
if (length == buffer.length) {
-
return;
-
}
-
-
//@Paul: 若切片后发送,则需要等待对方的回复后再决定下一步行动
-
// Look for Continue or Reject from peer.
-
int offset = length;
-
byte[] responseBytes = new byte[HEADER_LENGTH];
-
mSocket.receive(responseBytes);
-
//@Paul: 接收对方的Response消息
-
SnepMessage snepResponse;
-
try {
-
snepResponse = SnepMessage.fromByteArray(responseBytes);
-
} catch (FormatException e) {
-
throw new IOException( "Invalid SNEP message", e);
-
}
-
-
//@Paul: 如果对方的回复不是continue,在返回错误
-
if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
-
if (snepResponse.getField() != remoteContinue) {
-
throw new IOException( "Invalid response from server (" +
-
snepResponse.getField() + ")");
-
}
-
-
// Send remaining fragments.
-
//@Paul: 对方要求继续发送时,将剩余的数据发送完成
-
while (offset < buffer.length) {
-
length = Math.min(buffer.length - offset, mFragmentLength);
-
tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
-
if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
-
mSocket.send(tmpBuffer);
-
offset += length;
-
}
-
}
在上述消息发送完成后,需要等待对方的response消息,就进入了getMessage()阶段,代码分析如下:
-
public SnepMessage getMessage() throws IOException, SnepException {
-
...
-
//@Paul: 等待对方的回复消息
-
size = mSocket.receive(partial);
-
if (DBG) Log.d(TAG, "read " + size + " bytes");
-
if (size < 0) {
-
try {
-
//@Paul:接收的数据大小小于0,直接回复Reject
-
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
-
} catch (IOException e) {
-
// Ignore
-
}
-
throw new IOException( "Error reading SNEP message.");
-
} else if (size < HEADER_LENGTH) {
-
try {
-
//@Paul:接收的数据大小小于Header Size(因为Snep数据必须包含Header),直接回复Reject
-
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
-
} catch (IOException e) {
-
// Ignore
-
}
-
throw new IOException( "Invalid fragment from sender.");
-
} else {
-
//@Paul: 更新buffer值
-
readSize = size - HEADER_LENGTH;
-
buffer.write(partial, 0, size);
-
}
-
-
//@Paul: 解析第一包数据收到的头中的字段
-
DataInputStream dataIn = new DataInputStream( new ByteArrayInputStream(partial));
-
requestVersion = dataIn.readByte();
-
byte requestField = dataIn.readByte();
-
requestSize = dataIn.readInt();
-
-
if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
-
-
//@Paul: 如果SNEP的Version不匹配,则认为接收完成
-
if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
-
// Invalid protocol version; treat message as complete.
-
return new SnepMessage(requestVersion, requestField, 0, 0, null);
-
}
-
-
//@Paul: 如果接收的数据小于Header中规定的数据,则请求对方继续发送,回复Continue
-
if (requestSize > readSize) {
-
if (DBG) Log.d(TAG, "requesting continuation");
-
mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
-
} else {
-
doneReading = true;
-
}
-
-
//@Paul: 发送完Continue后,执行loop中的receive(),等待后续的package
-
// Remaining fragments
-
while (!doneReading) {
-
try {
-
size = mSocket.receive(partial);
-
if (DBG) Log.d(TAG, "read " + size + " bytes");
-
if (size < 0) {
-
try {
-
//@Paul:接收的数据段错误,直接回复Reject
-
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
-
} catch (IOException e) {
-
// Ignore
-
}
-
throw new IOException();
-
} else {
-
//@Paul:不断更新收到的数据
-
readSize += size;
-
buffer.write(partial, 0, size);
-
if (readSize == requestSize) {
-
doneReading = true;
-
}
-
}
-
} catch (IOException e) {
-
try {
-
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
-
} catch (IOException e2) {
-
// Ignore
-
}
-
throw e;
-
}
-
}
-
-
// Build NDEF message set from the stream
-
try {
-
//@Paul:将接收的数据转换成SNEP格式
-
return SnepMessage.fromByteArray(buffer.toByteArray());
-
} catch (FormatException e) {
-
Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
-
throw new SnepException(e);
-
}
-
}
为了兼容性的问题,可能当前的P2P并不支持SNEP协议,那么就会尝试用NPP协议进行数据的解析,这样就到了NdefPushClient的push流程,此部分的协议比较简单,流程也比较简单:
-
public boolean push(NdefMessage msg) {
-
...
-
-
// We only handle a single immediate action for now
-
//@Paul: 按照制定格式创建NdefPushProtocol
-
NdefPushProtocol proto = new NdefPushProtocol(msg, NdefPushProtocol.ACTION_IMMEDIATE);
-
byte[] buffer = proto.toByteArray();
-
int offset = 0;
-
int remoteMiu;
-
-
try {
-
remoteMiu = sock.getRemoteMiu();
-
if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
-
//@Paul: 将当前信息完整的发送出去,一直到完成
-
while (offset < buffer.length) {
-
int length = Math.min(buffer.length - offset, remoteMiu);
-
byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
-
if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
-
sock.send(tmpBuffer);
-
offset += length;
-
}
-
return true;
-
}
-
...
-
return false;
-
}
当上述操作都完成后,表示send已经完成,自然就需要通知上层,你需要做的操作已经做完,如果后续有操作,请另行指示。
通知上层的方式,就是用Message的方式执行, 系统会给当前的进程的handler发送MSG_SEND_COMPLETE消息,收到此消息后,进入处理函数,主要内容如下:
-
{
-
...
-
//@Paul: 又是Listener,最终又到SendUI
-
mEventListener.onP2pSendComplete();
-
//@Paul:如果有定义callback,则执行callback中定义的onNdefPushComplete
-
if (mCallbackNdef != null) {
-
try {
-
mCallbackNdef.onNdefPushComplete();
-
} catch (Exception e) {
-
Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
-
}
-
}
-
}
Listerner的处理比较简单,就是用户的notify,函数为:
-
public void onP2pSendComplete() {
-
mNfcService.playSound(NfcService.SOUND_END);
-
mVibrator.vibrate(VIBRATION_PATTERN, - 1);
-
if (mSendUi != null) {
-
mSendUi.finish(SendUi.FINISH_SEND_SUCCESS);
-
}
-
mSending = false;
-
mNdefSent = true;
-
}
至此,P2P的流程基本已经分析完成。这样基本上可以对NFC中P2P的流程有了基本的了解。
后续会有一个APP的实例说明,参考实例能更加深刻的理解NFC处理流程,加深对该功能的理解。
补充说明:
1. 上述P2P的各个Server是在哪里创建?
Answer:参考P2pLinkManager.java中的构造函数P2pLinkManager():
-
mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback);
-
mDefaultSnepServer = new SnepServer(mDefaultSnepCallback, defaultMiu, defaultRwSize);
-
mHandoverServer = new HandoverServer(HANDOVER_SAP, handoverManager, mHandoverCallback);