[NFC]P2P设备响应流程

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xuwen0306/article/details/44724859

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


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


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

    @Override
    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()是进行数据链路层的连接,保证底层已经是发现对方并且是可以连接的。


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

<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;
		...
		}
	}
}
        上述函数的重点就是:prepareMessageToSend() 以及mEventListener.onP2pSendConfirmationRequested(); 分析完需要确认的流程,那不需要确认的流程也就打通了。

         由于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()。

@Override
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;
}


@Override
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();
	}
}




@Override
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后自动执行,具体的代码如下:

@Override
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);



                                    
展开阅读全文

android NFC P2P问题

10-15

找到了个 SnepPusher的代码,但只能把数据push到PC,PC回传给手机的没数据么?是什么问题呢?rnrnpackage it.ismb.nfc.sneppusher;rnrnimport java.nio.charset.Charset;rnrnimport android.net.Uri;rnimport android.nfc.NdefMessage;rnimport android.nfc.NdefRecord;rnimport android.nfc.NfcAdapter;rnimport android.nfc.NfcAdapter.CreateBeamUrisCallback;rnimport android.nfc.NfcAdapter.CreateNdefMessageCallback;rnimport android.nfc.NfcAdapter.OnNdefPushCompleteCallback;rnimport android.nfc.NfcEvent;rnimport android.os.Bundle;rnimport android.os.Handler;rnimport android.os.Message;rnimport android.os.Parcelable;rnimport android.app.Activity;rnimport android.content.Intent;rnimport android.view.Menu;rnimport android.widget.TextView;rnimport android.widget.Toast;rnimport android.text.format.Time;rnrnpublic class SnepPusher extends Activity rn implements CreateNdefMessageCallback, rn OnNdefPushCompleteCallback,rn CreateBeamUrisCallbackrnrnrnrn rn NfcAdapter mNfcAdapter;rn TextView mInfoText;rn rn private final Handler mHandler = new Handler() rn @Overridern public void handleMessage(Message msg) rn switch (msg.what) rn case 1:rn Toast.makeText(getApplicationContext(), "Message sent!", Toast.LENGTH_LONG).show();rn break;rn rn rn ;rnrn rn @Overridern public void onCreate(Bundle savedInstanceState) rn super.onCreate(savedInstanceState);rn setContentView(R.layout.activity_snep_pusher);rn rn // Check for available NFC Adapterrn mNfcAdapter = NfcAdapter.getDefaultAdapter(this);rn if (mNfcAdapter == null) rn mInfoText = (TextView) findViewById(R.id.textView3);rn mInfoText.setText("NFC is not available on this device.");rn rn rn // Register callback to set NDEF messagern mNfcAdapter.setNdefPushMessageCallback(this, this);rn // Register callback to listen for message-sent successrn mNfcAdapter.setOnNdefPushCompleteCallback(this, this);rn mNfcAdapter.setBeamPushUrisCallback(this, this);rn rnrn @Overridern public void onResume() rn super.onResume();rn // Check to see that the Activity started due to an Android Beamrn if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) rn processIntent(getIntent());rn rn rn rn @Overridern public boolean onCreateOptionsMenu(Menu menu) rn getMenuInflater().inflate(R.menu.activity_snep_pusher, menu);rn return true;rn rnrn @Overridern public void onNdefPushComplete(NfcEvent arg0) rn // A handler is needed to send messages to the activity when thisrn // callback occurs, because it happens from a binder threadrn mHandler.obtainMessage(1).sendToTarget();rn rn // 55 AA 05 12 FF 02 01 00 FF rn // 55 AA 05 12 FF 02 00 00 FF rn // 55 AA 22 12 FF 05 61 6D 20 6D 65 20 75 70 21 0A 0A 42 65 61 6D 20 54 69 6D 65 3A 20 31 30 3A 33 32 3A 31 32 00 FF rn // 55 AA 05 12 FF 02 01 00 FF rn rn @Overridern public NdefMessage createNdefMessage(NfcEvent event) rn Time time = new Time();rn time.setToNow();rn String text = ("Beam me up!\n\n" + "Beam Time: " + time.format("%H:%M:%S"));rn NdefMessage msg = new NdefMessage(rn new NdefRecord[] createMimeRecord(rn "application/it.ismb.nfc.sneppusher", text.getBytes()));rn return msg; rn rnrn public NdefRecord createMimeRecord(String mimeType, byte[] payload) rn byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));rn NdefRecord mimeRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);rn return mimeRecord;rn rn rn void processIntent(Intent intent) rn Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);rn // only one message sentrn NdefMessage msg = (NdefMessage) rawMsgs[0];rn // record 0 contains the MIME type, record 1 is the AAR, if presentrn mInfoText.setText(new String(msg.getRecords()[0].getPayload()));rn rn @Overridern public Uri[] createBeamUris(NfcEvent arg0) rn // TODO Auto-generated method stubrn return null;rn rnrn rnrnrnrn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rn rnrn 论坛

没有更多推荐了,返回首页