接上部分的分析,当前系统已经进入到applyRouting()阶段,后续应该是需要一直去监听当前是否有NFC设备进入通讯范围。如果有适合的NFC设备,则底层会先进行沟通,并将消息通知给上层。
进入NFC设备发现流程
下面从applyRouting()函数开始分析,可以参考系统注释:
-
void applyRouting(boolean force) {
-
synchronized ( this) {
-
//@paul: 如果NFC没有打开或者已经关闭,则直接发挥
-
if (!isNfcEnabledOrShuttingDown()) {
-
return;
-
}
-
...
-
if (mInProvisionMode) {
-
mInProvisionMode = Settings.Secure.getInt(mContentResolver,
-
Settings.Global.DEVICE_PROVISIONED, 0) == 0;
-
if (!mInProvisionMode) {
-
//@paul: 原生的Android里面Provision只做了一件事,就是写入一个DEVICE_PROVISIONED标记。
-
//@paul: 不过这个标记作用很大,这个标记只会在系统全新升级(双清)的时候写入一次,代表了Android系统升级准备完成,可以正常工作。
-
mNfcDispatcher.disableProvisioningMode();
-
mHandoverManager.setEnabled( true);
-
}
-
}
-
-
//@paul: 如果有tag正在通讯时,delay一段时间再更新参数
-
// Special case: if we're transitioning to unlocked state while
-
// still talking to a tag, postpone re-configuration.
-
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && isTagPresent()) {
-
Log.d(TAG, "Not updating discovery parameters, tag connected.");
-
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESUME_POLLING),
-
APPLY_ROUTING_RETRY_TIMEOUT_MS);
-
return;
-
}
-
-
try {
-
watchDog.start();
-
//@paul: 依据前面初始化的参数来更新NfcDiscoveryParameters
-
// Compute new polling parameters
-
NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
-
if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
-
//@paul: 判断条件为:mTechMask != 0 || mEnableHostRouting
-
//@paul:mTechMask一般不为0,mEnableHostRouting一般为false
-
if (newParams.shouldEnableDiscovery()) {
-
boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
-
//@paul:系统一般会进入enableDiscovery()
-
mDeviceHost.enableDiscovery(newParams, shouldRestart);
-
} else {
-
mDeviceHost.disableDiscovery();
-
}
-
mCurrentDiscoveryParameters = newParams;
-
} else {
-
Log.d(TAG, "Discovery configuration equal, not updating.");
-
}
-
} finally {
-
watchDog.cancel();
-
}
-
}
-
}
由于系统调用到enableDiscovery()函数,此函数不论是nxp还是nci,都会调用到native函数doEnableDiscovery(),继续追踪此函数(后续都已NXP为例),最终调用到com_android_nfc_NativeNfcManager.cpp中的:
-
{ "doEnableDiscovery", "(IZZZ)V",
-
( void *)com_android_nfc_NfcManager_enableDiscovery},
继续追踪com_android_nfc_NfcManager_enableDiscovery():
-
static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o, jint modes,
-
jboolean, jboolean reader_mode, jboolean restart)
-
{
-
...
-
-
/* Register callback for remote device notifications.
-
* Must re-register every time we turn on discovery, since other operations
-
* (such as opening the Secure Element) can change the remote device
-
* notification callback*/
-
//@paul: 注册侦听到NFC设备时的回调函数,后续的流程从此开始
-
REENTRANCE_LOCK();
-
ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info, nfc_jni_Discovery_notification_callback, ( void *)nat);
-
REENTRANCE_UNLOCK();
-
-
...
-
//@paul: 启动discovery流程
-
nfc_jni_start_discovery_locked(nat, restart);
-
clean_and_return:
-
CONCURRENCY_UNLOCK();
-
}
此处的两个函数nfc_jni_start_discovery_locked()和nfc_jni_Discovery_notification_callback()都需要在深入一点,一个一个的看:
-
static void nfc_jni_start_discovery_locked(struct nfc_jni_native_data *nat, bool resume)
-
{
-
...
-
configure:
-
/* Start Polling loop */
-
TRACE( "****** Start NFC Discovery ******");
-
REENTRANCE_LOCK();
-
//@paul: nfc_jni_discover_callback()是discover后消息notify的开始
-
ret = phLibNfc_Mgt_ConfigureDiscovery(resume ? NFC_DISCOVERY_RESUME : NFC_DISCOVERY_CONFIG,
-
nat->discovery_cfg, nfc_jni_discover_callback, ( void *)&cb_data);
-
REENTRANCE_UNLOCK();
-
-
...
-
-
clean_and_return:
-
nfc_cb_data_deinit(&cb_data);
-
}
一旦上面进入了nfc_jni_discover_callback(),后续就会进入nfc_jni_Discovery_notification_callback(),此函数就会把底层看到的信息开始一层一层的notify:
-
static void nfc_jni_Discovery_notification_callback(void *pContext,
-
phLibNfc_RemoteDevList_t *psRemoteDevList,
-
uint8_t uNofRemoteDev, NFCSTATUS status)
-
{
-
...
-
-
if(status == NFCSTATUS_DESELECTED)
-
{
-
LOG_CALLBACK( "nfc_jni_Discovery_notification_callback: Target deselected", status);
-
-
/* Notify manager that a target was deselected */
-
//@paul: 执行cached_NfcManager_notifyTargetDeselected对应的java函数
-
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyTargetDeselected);
-
if(e->ExceptionCheck())
-
{
-
ALOGE( "Exception occurred");
-
kill_client(nat);
-
}
-
}
-
else
-
{
-
...
-
if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
-
|| (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
-
{
-
...
-
/* Set P2P Target mode */
-
jfieldID f = e->GetFieldID(tag_cls.get(), "mMode", "I");
-
-
if(remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
-
{
-
ALOGD( "Discovered P2P Initiator");
-
e->SetIntField(tag.get(), f, (jint)MODE_P2P_INITIATOR);
-
}
-
else
-
{
-
ALOGD( "Discovered P2P Target");
-
e->SetIntField(tag.get(), f, (jint)MODE_P2P_TARGET);
-
}
-
...
-
}
-
else
-
{
-
...
-
-
/* New tag instance */
-
...
-
-
/* Set tag UID */
-
...
-
-
/* Generate technology list */
-
...
-
}
-
-
...
-
-
/* Notify the service */
-
if((remDevInfo->RemDevType == phNfc_eNfcIP1_Initiator)
-
|| (remDevInfo->RemDevType == phNfc_eNfcIP1_Target))
-
{
-
/* Store the handle of the P2P device */
-
hLlcpHandle = remDevHandle;
-
-
/* Notify manager that new a P2P device was found */
-
//@paul: 侦测到P2P设备进入范围,调用JNI层对应的API,最终call到JAVA层API
-
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());
-
if(e->ExceptionCheck())
-
{
-
ALOGE( "Exception occurred");
-
kill_client(nat);
-
}
-
}
-
else
-
{
-
/* Notify manager that new a tag was found */
-
//@paul:侦测到Tag设备进入范围,
-
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());
-
if(e->ExceptionCheck())
-
{
-
ALOGE( "Exception occurred");
-
kill_client(nat);
-
}
-
}
-
}
-
}
所以Tag的真正开始时在执行下列函数后:
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());
最终调用到JNI层的notifyNdefMessageListeners(),对应的定义在:
-
private void notifyNdefMessageListeners(NativeNfcTag tag) {
-
mListener.onRemoteEndpointDiscovered(tag);
-
}
为后文作准备,P2P设备的Framework开始时在执行下列函数:
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyLlcpLinkActivation, tag.get());
最终调用到JNI层的notifyLlcpLinkActivation(),对应的定义在:
-
private void notifyLlcpLinkActivation(NativeP2pDevice device) {
-
mListener.onLlcpLinkActivated(device);
-
}
Tag设备发现framework流程
下面开始分析Tag的Framework的流程:我们的分析会从mListener.onRemoteEndpointDiscovered(tag)开始:
-
-
public void onRemoteEndpointDiscovered(TagEndpoint tag) {
-
sendMessage(NfcService.MSG_NDEF_TAG, tag);
-
}
其中发送的消息MSG_NDEF_TAG会进入到NfcService.java的handleMessage(),其中处理MSG_NDEF_TAG的流程如下:
-
case MSG_NDEF_TAG:
-
if (DBG) Log.d(TAG, "Tag detected, notifying applications");
-
...
-
-
//@paul: 如果是read mode
-
if (readerParams != null) {
-
presenceCheckDelay = readerParams.presenceCheckDelay;
-
//@paul: 如果设置不检查能否转成NDEF的标记
-
if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {
-
if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");
-
//@paul: 判断tag是否还在范围内
-
tag.startPresenceChecking(presenceCheckDelay, callback);
-
//@paul: 将侦测的tag进行分发
-
dispatchTagEndpoint(tag, readerParams);
-
break;
-
}
-
}
-
-
boolean playSound = readerParams == null ||
-
(readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0;
-
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED && playSound) {
-
//paul: 播放开始声音
-
playSound(SOUND_START);
-
}
-
if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {
-
// When these tags start containing NDEF, they will require
-
// the stack to deal with them in a different way, since
-
// they are activated only really shortly.
-
// For now, don't consider NDEF on these.
-
if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");
-
//@paul: 如果是Barcode,直接进行tag的分发
-
tag.startPresenceChecking(presenceCheckDelay, callback);
-
dispatchTagEndpoint(tag, readerParams);
-
break;
-
}
-
-
//@paul: 依据底层格式,进行NDEF的转换
-
NdefMessage ndefMsg = tag.findAndReadNdef();
-
-
if (ndefMsg != null) {
-
//@paul: NDEF转换成功后,进行Tag的分发
-
tag.startPresenceChecking(presenceCheckDelay, callback);
-
dispatchTagEndpoint(tag, readerParams);
-
} else {
-
//@paul: 无法转换时,先进行底层的连接后(物理层先重连),将消息分发
-
if (tag.reconnect()) {
-
tag.startPresenceChecking(presenceCheckDelay, callback);
-
dispatchTagEndpoint(tag, readerParams);
-
} else {
-
tag.disconnect();
-
playSound(SOUND_ERROR);
-
}
-
}
-
break;
其中比较关键的函数有两个:
findAndReadNdef()
dispatchTagEndpoint()
findAndReadNdef()是和芯片强相关的,其目的是依据芯片的支持能力,将读到的Tag中的内容转换成NDEF格式的数据. 不同芯片其支持的能力存在差异,此部分的code也是存在差异,针对NXP芯片简单分析如下:
-
-
public NdefMessage findAndReadNdef() {
-
...
-
for ( int techIndex = 0; techIndex < technologies.length; techIndex++) {
-
...
-
-
//@paul: 判断connectedHandle与当前Index对应的handle的关系,并更新状态,
-
status = connectWithStatus(technologies[techIndex]);
-
...
-
-
// Check if this type is NDEF formatable
-
if (!foundFormattable) {
-
//@paul: 依据芯片特性判断哪些tag是可以转成NDEF格式
-
if (isNdefFormatable()) {
-
//@paul: 更新对应的handle,handle用于后续的操作
-
foundFormattable = true;
-
formattableHandle = getConnectedHandle();
-
formattableLibNfcType = getConnectedLibNfcType();
-
// We'll only add formattable tech if no ndef is
-
// found - this is because libNFC refuses to format
-
// an already NDEF formatted tag.
-
}
-
reconnect();
-
}
-
...
-
-
status = checkNdefWithStatus(ndefinfo);
-
...
-
-
//@paul: 读取tag上的数据
-
byte[] buff = readNdef();
-
if (buff != null) {
-
try {
-
//@paul: 将数据转换成NDEF格式
-
ndefMsg = new NdefMessage(buff);
-
//@paul: 更新对应Tag的信息
-
addNdefTechnology(ndefMsg,
-
getConnectedHandle(),
-
getConnectedLibNfcType(),
-
getConnectedTechnology(),
-
supportedNdefLength, cardState);
-
reconnect();
-
} catch (FormatException e) {
-
// Create an intent anyway, without NDEF messages
-
generateEmptyNdef = true;
-
}
-
} else {
-
generateEmptyNdef = true;
-
}
-
...
-
}
-
-
if (ndefMsg == null && foundFormattable) {
-
// Tag is not NDEF yet, and found a formattable target,
-
// so add formattable tech to tech list.
-
addNdefFormatableTechnology(
-
formattableHandle,
-
formattableLibNfcType);
-
}
-
-
return ndefMsg;
-
}
Tag消息分发流程
上述执行完成后,理论上会进入Tag的Dispatch流程,中间的流程省略,我们直接进入mNfcDispatcher.dispatchTag(tag)函数的分析,这才是tag分发的终极形式,直接看代码吧:
-
/** Returns:
-
* <ul>
-
* <li /> DISPATCH_SUCCESS if dispatched to an activity,
-
* <li /> DISPATCH_FAIL if no activities were found to dispatch to,
-
* <li /> DISPATCH_UNLOCK if the tag was used to unlock the device
-
* </ul>
-
*/
-
public int dispatchTag(Tag tag) {
-
PendingIntent overrideIntent;
-
IntentFilter[] overrideFilters;
-
String[][] overrideTechLists;
-
boolean provisioningOnly;
-
-
//@paul: 如果上层APP定义了下列值,就会在这里进行更新
-
synchronized ( this) {
-
overrideFilters = mOverrideFilters;
-
overrideIntent = mOverrideIntent;
-
overrideTechLists = mOverrideTechLists;
-
provisioningOnly = mProvisioningOnly;
-
}
-
-
//@paul: Tag在screen on 且lock的情况下,需要尝试unlock,否则如法处理
-
boolean screenUnlocked = false;
-
if (!provisioningOnly &&
-
mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
-
screenUnlocked = handleNfcUnlock(tag);
-
if (!screenUnlocked) {
-
return DISPATCH_FAIL;
-
}
-
}
-
-
NdefMessage message = null;
-
//@paul: 将Tag解析成NDEF格式数据,并读取内容
-
Ndef ndef = Ndef.get(tag);
-
if (ndef != null) {
-
message = ndef.getCachedNdefMessage();
-
}
-
-
if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);
-
-
DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);
-
-
//@paul: 和APP相关,暂时没有研究
-
resumeAppSwitches();
-
-
//@paul: 如果上层APP有定义前台分发机制,则会调用到PendingIntent.send()功能,实现前台分发机制
-
if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
-
overrideTechLists)) {
-
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
-
}
-
-
//@paul: 判断NDEF消息是否是handover格式
-
if (mHandoverManager.tryHandover(message)) {
-
if (DBG) Log.i(TAG, "matched BT HANDOVER");
-
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
-
}
-
-
//@paul: 判断NDEF消息是否是WifiConfiguration格式
-
if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
-
if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
-
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
-
}
-
-
//@paul: 将消息发送给对ACTION_NDEF_DISCOVERED感兴趣的APP处理
-
if (tryNdef(dispatch, message, provisioningOnly)) {
-
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
-
}
-
-
if (screenUnlocked) {
-
// We only allow NDEF-based mimeType matching in case of an unlock
-
return DISPATCH_UNLOCK;
-
}
-
-
if (provisioningOnly) {
-
// We only allow NDEF-based mimeType matching
-
return DISPATCH_FAIL;
-
}
-
-
// Only allow NDEF-based mimeType matching for unlock tags
-
//@paul: <span style="font-family: Arial, Helvetica, sans-serif;">将消息发送给对</span>ACTION_TECH_DISCOVERED感兴趣的APP处理
-
if (tryTech(dispatch, tag)) {
-
return DISPATCH_SUCCESS;
-
}
-
-
//@paul: 更新Intent为ACTION_TAG_DISCOVERED
-
dispatch.setTagIntent();
-
//@paul: 将消息发送给对ACTION_TAG_DISCOVERED感兴趣的APP处理
-
if (dispatch.tryStartActivity()) {
-
if (DBG) Log.i(TAG, "matched TAG");
-
return DISPATCH_SUCCESS;
-
}
-
-
if (DBG) Log.i(TAG, "no match");
-
return DISPATCH_FAIL;
-
}
下面分别介绍tryHandover(),tryNfcWifiSetup(),tryNdef(),tryTech().
首先看看tryHandover(),此函数主要做BlueTooth的handover,当然如果要做wifi的handover,从技术上看是完全没有问题的.
-
public boolean tryHandover(NdefMessage m) {
-
...
-
BluetoothHandoverData handover = parseBluetooth(m);
-
...
-
synchronized (mLock) {
-
...
-
<span style= "white-space:pre"> </span> //@paul: 发送MSG_PERIPHERAL_HANDOVER消息,在HandleMessage中进行处理
-
Message msg = Message.obtain( null, HandoverService.MSG_PERIPHERAL_HANDOVER, 0, 0);
-
Bundle headsetData = new Bundle();
-
headsetData.putParcelable(HandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
-
headsetData.putString(HandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
-
headsetData.putInt(HandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
-
msg.setData(headsetData);
-
return sendOrQueueMessageLocked(msg);
-
}
-
}
比较重要的就是parseBluetooth()和最后的sendOrQueueMessageLocked()函数。其中parseBluetooth逻辑比较简单,就是按照Spec的要求,把NDEF数据一个字节一个字节的解析出来,存放在对应的结构体中。sendOrQueueMessageLocked则是将BT的信息发送给上述完整的代码分析如下:
-
BluetoothHandoverData parseBluetooth(NdefMessage m) {
-
...
-
-
// Check for BT OOB record
-
if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BT_OOB)) {
-
return parseBtOob(ByteBuffer.wrap(r.getPayload()));
-
}
-
-
// Check for BLE OOB record
-
if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA && Arrays.equals(r.getType(), TYPE_BLE_OOB)) {
-
return parseBleOob(ByteBuffer.wrap(r.getPayload()));
-
}
-
-
// Check for Handover Select, followed by a BT OOB record
-
if (tnf == NdefRecord.TNF_WELL_KNOWN &&
-
Arrays.equals(type, NdefRecord.RTD_HANDOVER_SELECT)) {
-
return parseBluetoothHandoverSelect(m);
-
}
-
-
// Check for Nokia BT record, found on some Nokia BH-505 Headsets
-
if (tnf == NdefRecord.TNF_EXTERNAL_TYPE && Arrays.equals(type, TYPE_NOKIA)) {
-
return parseNokia(ByteBuffer.wrap(r.getPayload()));
-
}
-
-
return null;
-
}
-
-
-
-
public boolean sendOrQueueMessageLocked(Message msg) {
-
if (!mBound || mService == null) {
-
// Need to start service, let us know if we can queue the message
-
//@paul: 首先bind Service,成功后会调用onServiceConnected()
-
if (!bindServiceIfNeededLocked()) {
-
Log.e(TAG, "Could not start service");
-
return false;
-
}
-
// Queue the message to send when the service is bound
-
//@paul: 先将消息放在缓存队列中
-
mPendingServiceMessages.add(msg);
-
} else {
-
try {
-
//@paul: 已经绑定后,则直接诶发送消息
-
mService.send(msg);
-
} catch (RemoteException e) {
-
Log.e(TAG, "Could not connect to handover service");
-
return false;
-
}
-
}
-
return true;
-
}
-
-
-
-
public void onServiceConnected(ComponentName name, IBinder service) {
-
synchronized (mLock) {
-
...
-
try {
-
//@paul: 直接发送消息
-
mService.send(msg);
-
} catch (RemoteException e) {
-
Log.e(TAG, "Failed to register client");
-
}
-
// Send all queued messages
-
while (!mPendingServiceMessages.isEmpty()) {
-
//@paul: 如果mPendingServiceMessages有数据是,会一直尝试把pending的数据发送完成
-
msg = mPendingServiceMessages.remove( 0);
-
try {
-
mService.send(msg);
-
} catch (RemoteException e) {
-
Log.e(TAG, "Failed to send queued message to service");
-
}
-
}
-
}
-
}
前文提到发送了MSG_PERIPHERAL_HANDOVER,最终进入doPeripheralHandover()函数,目前只是分析到BT流程启动,后续建立连接部分暂不分析了。
-
void doPeripheralHandover(Message msg) {
-
Bundle msgData = msg.getData();
-
BluetoothDevice device = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);
-
String name = msgData.getString(EXTRA_PERIPHERAL_NAME);
-
int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);
-
//@paul: 如果存在mBluetoothPeripheralHandover,表明有handover正在进行
-
if (mBluetoothPeripheralHandover != null) {
-
Log.d(TAG, "Ignoring pairing request, existing handover in progress.");
-
return;
-
}
-
//@paul: mBluetoothPeripheralHandover
-
mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(HandoverService. this,
-
device, name, transport, HandoverService. this);
-
-
// TODO: figure out a way to disable polling without deactivating current target
-
if (transport == BluetoothDevice.TRANSPORT_LE) {
-
mHandler.sendMessageDelayed(
-
mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);
-
}
-
-
//@paul: 如果Bluetooth有启动,则执行对应的start函数
-
if (mBluetoothAdapter.isEnabled()) {
-
if (!mBluetoothPeripheralHandover.start()) {
-
//@paul: Handover成功后,NFC继续polling
-
mNfcAdapter.resumePolling();
-
}
-
} else {
-
if (!enableBluetooth()) {
-
Log.e(TAG, "Error enabling Bluetooth.");
-
mBluetoothPeripheralHandover = null;
-
}
-
}
-
}
-
-
//@paul:接上述start函数
-
public boolean start() {
-
...
-
-
//paul: 目前只关注此函数
-
nextStep();
-
-
return true;
-
}
-
-
-
//@paul: 进入对应的状态机,启动BT连接
-
void nextStep() {
-
if (mAction == ACTION_INIT) {
-
nextStepInit();
-
} else if (mAction == ACTION_CONNECT) {
-
nextStepConnect();
-
} else {
-
nextStepDisconnect();
-
}
-
}
分析完Handover,可能已经觉得有点累了,那我们来个简单的,看看tryNfcWifiSetup(),其实这部分和BT的原理基本类似,就是透过NFC拿到WIFI相关的credential,然后利用credential简历WIFI连接,实现Handover的目的。
-
public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {
-
...
-
-
//@paul: 解析得到Wifi相关的credential数据
-
final WifiConfiguration wifiConfiguration = parse(cachedNdefMessage);
-
-
if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(
-
UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
-
Intent configureNetworkIntent = new Intent()
-
.putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)
-
.setClass(context, ConfirmConnectToWifiNetworkActivity.class)
-
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-
//@paul: 将wifi数据放在intent中,然后以设置的参数启动wifi相关的连接
-
context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);
-
return true;
-
}
-
-
return false;
-
}
接下来就是tryNdef()和tryTech()的分析,其实此部分的流程是前面提到的ACTION_TAG_DISCOVERED的流程是类似,主要是分两步:
1. 设置对应的intent为:ACTION_NDEF_DISCOVERED/ACTION_TECH_DISCOVERED
2. 以上述intent启动相关的activity. 只不过会加入AAR消息的检查,关于AAR的说明可以自行查找。简单的说明如下:
-
在 Android4 .0( API Level 14)中引入的 Android应用程序记录( AAR),提供了较强的在扫描到 NFC标签时,启动应用程序的确定性。 AAR有嵌入到 NDEF记录内部的应用程序的包名。你能够把一个 AAR添加到你的 NDEF消息的任何记录中,因为 Android会针对 AAR来搜索整个 NDEF消息。如果它找到一个 AAR,它就会基于 AAR内部的包名来启动应用程序。如果该应用程序不在当前的设备上,会启动 Google Play来下载对应的应用程序。
-
-
如果你想要防止其他的应用对相同的 Intent的过滤并潜在的处理你部署的特定的 NFC标签,那么 AAR是有用的。 AAR仅在应用程序级被支持,因为包名的约束,并不能在 Activity级别来过滤 Intent。如果你想要在 Activity级处理 Intent,请使用 Intent过滤器。
以上基本就介绍完Tag的整体处理流程。代码流程稍微有点多,建议代码分几次看,以免遗忘。
后续会在介绍一下NFC P2P设备相互发现的流程。由于P2P的应用比较多,介绍的篇幅也会相对较多