NFC--Tag读写模式java层源码分析
来源:互联网 发布:淘宝卖家交流论坛 编辑:程序博客网 时间:2024/05/16 18:15
文章整理总结NFC读取和写入Tag的java层的流程,至于一些native层的接入点,暂记住。后续持续更新。
1、读取Tag的流程
NfcService启动完成后,会进入到NfcService中的applyRouting方法持续的监听是否有Tag靠近,它会使Nfc的RF工作在
polling模式,polling模式下会检测那个Tag进入到了自己的射频厂,进而产生反应。与其相对的是listen模式,Tag就相当于是listen
模式,监听谁往外polling,产生响应.
void applyRouting(boolean force) {
synchronized (this) {
......
//mInProvisionMode:用来标记当前系统是否处于开机向导界面.
//对应于android开机时候的引导provision apk,是否出在引导模式
if (mInProvisionMode) {
mInProvisionMode = Settings.Secure.getInt(mContentResolver,
Settings.Global.DEVICE_PROVISIONED, 0) == 0;
if (!mInProvisionMode) {
//就是设置NfcDispatcher中的mProvisioningOnly = false;
mNfcDispatcher.disableProvisioningMode();
//调用native方法doSetProvisionMode,去通知native层,当前不是处于ProvisionMode
mDeviceHost.doSetProvisionMode(mInProvisionMode);
}
}
......
try {
//此处更新于polling状态的NFC的参数.computeDiscoveryParameters方法就是位于NfcService内部
//所以就直接用执行到applyRouting之前实例化的一些全局参数进行了初始化,详见下一个函数.
NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
//传入参数为true的时候或者参数有所变化.
if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
// shouldEnableDiscovery内及时一个判断语句为:mTechMask != 0 || mEnableHostRouting
if (newParams.shouldEnableDiscovery()) {
boolean shouldRestart = mCurrentDiscoveryParameters.shouldEnableDiscovery();
//最重要的就是调用到了这里,mDeviceHost的实现类是NativeNfcManager最终调用到内部的如下:
// private native void doEnableDiscovery(int techMask,
// boolean enableLowPowerPolling,
// boolean enableReaderMode,
// boolean enableP2p,
// boolean restart);
//去native层,初始化发现Tag的流程。
mDeviceHost.enableDiscovery(newParams, shouldRestart);
} else {
mDeviceHost.disableDiscovery();
}
mCurrentDiscoveryParameters = newParams;
} else {
Log.d(TAG, "Discovery configuration equal, not updating.");
}
} finally {
......
}
}
}
我们看一下computeDiscoveryParameters这个函数,它返回NfcDiscoveryParameters,这个类描述了处于polling或者listen
以及host card emulation.状态的NFC的参数.
private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
//基于屏幕的状态重新计算NfcDiscoveryParameters.
NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
// Polling
if ((screenState >= NFC_POLLING_MODE)||mIsTaskBoot) {
// Check if reader-mode is enabled
//当当前NFC读模式开启后,按照协议,看看当前的Tag属于那种类型的Tag,然后设置techMask.
//此处支持如下五中.
if (mReaderModeParams != null) {
int techMask = 0;
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_A) != 0)
techMask |= NFC_POLL_A;
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_B) != 0)
techMask |= NFC_POLL_B;
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_F) != 0)
techMask |= NFC_POLL_F;
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_V) != 0)
techMask |= NFC_POLL_ISO15693;
if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0)
techMask |= NFC_POLL_KOVIO;
paramsBuilder.setTechMask(techMask);
paramsBuilder.setEnableReaderMode(true);
} else {
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
paramsBuilder.setEnableP2p(true);
}
//锁屏并且在ProvisionMode
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED && mInProvisionMode) {
paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
// enable P2P for MFM/EDU/Corp provisioning
paramsBuilder.setEnableP2p(true);
} else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED &&
mNfcUnlockManager.isLockscreenPollingEnabled()) {
// For lock-screen tags, no low-power polling
paramsBuilder.setTechMask(mNfcUnlockManager.getLockscreenPollMask());
paramsBuilder.setEnableLowPowerDiscovery(false);
paramsBuilder.setEnableP2p(false);
}
//处在卡模拟状态是一直是使能的,即使屏幕关闭了。
if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
// Host routing is always enabled at lock screen or later
paramsBuilder.setEnableHostRouting(true);
}
//To make routing table update.
if(mIsRoutingTableDirty) {
mIsRoutingTableDirty = false;
//TODO: Take this logic from L_OSP_EXT [PN547C2]
int protoRoute = mNxpPrefs.getInt("PREF_MIFARE_DESFIRE_PROTO_ROUTE_ID",
GetDefaultMifareDesfireRouteEntry());
int defaultRoute=mNxpPrefs.getInt("PREF_SET_DEFAULT_ROUTE_ID", GetDefaultRouteEntry());
int techRoute=mNxpPrefs.getInt("PREF_MIFARE_CLT_ROUTE_ID", GetDefaultMifateCLTRouteEntry());
defaultRoute = NfcCertDebugModeUtil.calculateDefaultRoute(defaultRoute,
mDeviceHost.getDefaultAidPowerState(), ROUTE_LOC_MASK);
if (DBG) Log.d(TAG, "Set default Route Entry");
setDefaultRoute(defaultRoute, protoRoute, techRoute);
}
...
return paramsBuilder.build();
}
通过JNI技术native层会调用doEnableDiscovery相对应的函数,进行一些列处理后,最终会回调到NativeNfcManager中的
private void notifyNdefMessageListeners(NativeNfcTag tag) {
mListener.onRemoteEndpointDiscovered(tag);
}
mListener的实现就是NfcService,又重新回到Nfcservice中
@Override
public void onRemoteEndpointDiscovered(TagEndpoint tag) {
android.util.Log.d("zy","nfeservice onRemoteEndpointDiscovered ");
sendMessage(NfcService.MSG_NDEF_TAG, tag);
}
void sendMessage(int what, Object obj) {
Message msg = mHandler.obtainMessage();
msg.what = what;
msg.obj = obj;
mHandler.sendMessage(msg);
}
到此为止,native层对发现的tag进行了一系列的初始化和封装操作,如按照ndef协议把Tag的消息封装到TagEndpoint当中
TagEndpoint就是代表了一个远端的Nfc tag.
然后执行到mHandler中的MSG_NDEF_TAG
case MSG_NDEF_TAG:
......
//如果是reader模式
if (readerParams != null) {
presenceCheckDelay = readerParams.presenceCheckDelay;
//如果FLAG_READER_SKIP_NDEF_CHECK标记位不为0,那么直接开始调用dispatchTagEndpoint分发
if ((readerParams.flags & NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK) != 0) {
if (DBG) Log.d(TAG, "Skipping NDEF detection in reader mode");
tag.startPresenceChecking(presenceCheckDelay, callback);
dispatchTagEndpoint(tag, readerParams);
break;
}
}
//当是NFC Barcode的时候也是直接分发,(???)NFC Barcode是NFC tag中数据的一种模式吗?
if (tag.getConnectedTechnology() == TagTechnology.NFC_BARCODE) {
......
if (DBG) Log.d(TAG, "Skipping NDEF detection for NFC Barcode");
tag.startPresenceChecking(presenceCheckDelay, callback);
dispatchTagEndpoint(tag, readerParams);
break;
}
//去调用NativeNfcTag的findAndReadNdef,进行相关初始化后把native层的数据按照NDEF
//协议封装成java中的类NdefMessage.
NdefMessage ndefMsg = tag.findAndReadNdef();
//解析的消息有问题的时候,比如解析一个身份证或者公交卡,在你底层没有专门处理的时候是会失败的。
if (ndefMsg == null) {
// First try to see if this was a bad tag read
if (!tag.reconnect()) {
tag.disconnect();
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
String toastString = mContext.getString(
R.string.nfc_strings_toast_prompt_touch_again_txt);
if (!ToastMaster.isSameToastShown(toastString)) {
ToastMaster.showToast(mContext, toastString);
}
}
break;
}
}
if(tag.getConnectedTechnology() == TagTechnology.NFC_F)
{
presenceCheckDelay = NFC_F_TRANSCEIVE_PRESENCE_CHECK_DELAY;
}
//这个如名字是抖动的tag,就是由于距离没控制好,一直检测到同一个tag的uid
if (debounceTagUid != null) {
// If we're debouncing and the UID or the NDEF message of the tag match,
// don't dispatch but drop it.
......
}
mLastReadNdefMessage = ndefMsg;
tag.startPresenceChecking(presenceCheckDelay, callback);
//解析出问题的ndef消息也要分发出去.
dispatchTagEndpoint(tag, readerParams);
break;
TagTechnology有很多种,按照对应的Tag技术协议进行的定定义
public static final int NFC_A = 1;
public static final int NFC_B = 2;
public static final int ISO_DEP = 3;
public static final int NFC_F = 4;
public static final int NFC_V = 5;
......
先了解NativeNfcTag几个常用的方法,方法内具体的native实现暂未分析。
判断tag是否能转换成ndef的java层的代码如下,从注释也可以看出是让native去实现的,可能和芯片有关.
native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act);
@Override
public synchronized boolean isNdefFormatable() {
// Let native code decide whether the currently activated tag
// is formatable. Although the name of the JNI function refers
// to ISO-DEP, the JNI function checks all tag types.
return doIsIsoDepNdefFormatable(mTechPollBytes[0],
mTechActBytes[0]);
}
检查ndef的数据格式是否合法,方法过后感觉底层会往这个数组存放,当先Tag的一些信息.当前正在和那个Tag通信就是读
取的谁。
private native int doCheckNdef(int[] ndefinfo);
private synchronized int checkNdefWithStatus(int[] ndefinfo) {
if (mWatchdog != null) {
mWatchdog.pause();
}
int status = doCheckNdef(ndefinfo);
if (mWatchdog != null) {
mWatchdog.doResume();
}
return status;
}
读取tag上的ndef数据,当前正在和那个Tag通信就是读取的谁。
private native byte[] doRead();
@Override
public synchronized byte[] readNdef() {
if (mWatchdog != null) {
mWatchdog.pause();
}
byte[] result = doRead();
if (mWatchdog != null) {
mWatchdog.doResume();
}
return result;
}
然后就是重头戏findAndReadNdef
@Override
public NdefMessage findAndReadNdef(){
// Try to find NDEF on any of the technologies.
int[] technologies = getTechList();
int[] handles = mTechHandles;
NdefMessage ndefMsg = null;
boolean foundFormattable = false;
int formattableHandle = 0;
int formattableLibNfcType = 0;
int status;
for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
......
//???下面这个方法可能也和协议的实现相关,读了几遍代码暂很难理解
//网上:判断connectedHandle与当前Index对应的handle的关系,并更新状态, 。
status = connectWithStatus(technologies[techIndex]);
//状态返回不为0的时候直接跳过本次循环。
if (status != 0) {
Log.d(TAG, "Connect Failed - status = "+ status);
if (status == STATUS_CODE_TARGET_LOST) {
break;
}
continue; // try next handle
}
// 判断当前的tag是否可以转换成ndef格式.
if (!foundFormattable) {
//如果可以转换
if (isNdefFormatable()) {
foundFormattable = true;
formattableHandle = getConnectedHandle();
formattableLibNfcType = getConnectedLibNfcType();
}
reconnect();
}
//检查当前tag中ndef数据的合法性
int[] ndefinfo = new int[2];
status = checkNdefWithStatus(ndefinfo);
if (status != 0) {
Log.d(TAG, "Check NDEF Failed - status = " + status);
if (status == STATUS_CODE_TARGET_LOST) {
break;
}
continue; // try next handle
}
// found our NDEF handle
boolean generateEmptyNdef = false;
int supportedNdefLength = ndefinfo[0];
int cardState = ndefinfo[1];
//读取tag中的数据,读出来的是字节数组。
byte[] buff = readNdef();
if (buff != null && buff.length > 0) {
try {
//正常的应该走到这里,通过获取的数据实例化NdefMessage.
//在NdefMessage实例化的时候就按照NDEF的协议去解析这个byte数组
//中的数据,然后封装成各个类和相应的字段供我们到时候直接调用
ndefMsg = new NdefMessage(buff);
addNdefTechnology(ndefMsg,
getConnectedHandle(),
getConnectedLibNfcType(),
getConnectedTechnology(),
supportedNdefLength, cardState);
foundFormattable = false;
reconnect();
} catch (FormatException e) {
// Create an intent anyway, without NDEF messages
generateEmptyNdef = true;
}
} else if(buff != null){
// Empty buffer, unformatted tags fall into this case
generateEmptyNdef = true;
}
//当产生异常的时候generateEmptyNdef为true的一些处理
if (generateEmptyNdef) {
ndefMsg = null;
addNdefTechnology(null,
getConnectedHandle(),
getConnectedLibNfcType(),
getConnectedTechnology(),
supportedNdefLength, cardState);
foundFormattable = false;
reconnect();
}
break;
}
......
return ndefMsg;
}
然后就是分发Tag消息的最终实现了dispatchTagEndpoint
private void dispatchTagEndpoint(TagEndpoint tagEndpoint, ReaderModeParams readerParams){
//Tag用来封装一个已经被发现的Tag.Tag类是framework提供的api
Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(),
tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService);
//把tagEndpoint以 key = tagEndpoint.gethandle;value = tagEndpoint放入到集合中
registerTagObject(tagEndpoint);
if (readerParams != null) {
try {
if ((readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0) {
playSound(SOUND_END);
}
if (readerParams.callback != null) {
/调用回调.
readerParams.callback.onTagDiscovered(tag);
return;
} else {
// Follow normal dispatch below
}
} catch (RemoteException e) {
......
} catch (Exception e) {
......
}
}
//正真开始分发的地方.
//mNfcDispatcher是NfcDispatcher实例,初始化也是在NfcService启动的时候在构造当中
//mNfcDispatcher = new NfcDispatcher(mContext, mHandoverDataParser, mInProvisionMode);
//传入的mHandoverDataParser = new HandoverDataParser();同样是在构造当中
int dispatchResult = mNfcDispatcher.dispatchTag(tag);
if (dispatchResult == NfcDispatcher.DISPATCH_FAIL) {
unregisterObject(tagEndpoint.getHandle());
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
String toastString = mContext.getString(
R.string.nfc_strings_toast_prompt_read_error_txt);
//弹出Toast进行提示.
if (!ToastMaster.isSameToastShown(toastString)) {
ToastMaster.showToast(mContext, toastString);
playSound(SOUND_ERROR);
}
}
} else if (dispatchResult == NfcDispatcher.DISPATCH_SUCCESS) {
...
//播放声音
playSound(SOUND_END);
}
}
上面的函数可以看到最最重要的就是NfcDispatcher中的dispatchTag(Tag)了,先前一直是在NfcService中进入到
NfcDispatcher类,此类从名字也能看出主要功能,它就是分发NFC的events然后去开启指定的activity。
阅读下面的流程代码之前,最好对Android NFC的TAG的分发机制有个整体了解,这样和代码对应上就更为轻松了。
public int dispatchTag(Tag tag) {
PendingIntent overrideIntent;
IntentFilter[] overrideFilters;
String[][] overrideTechLists;
String[] provisioningMimes;
boolean provisioningOnly;
synchronized (this) {
//overrideFilters的赋值是外部的app,运行在前台,想要处理Nfc的Tag使用的
//外部通过调用NfcService的setForegroundDispatch来实现。
overrideFilters = mOverrideFilters;
overrideIntent = mOverrideIntent;
overrideTechLists = mOverrideTechLists;
provisioningOnly = mProvisioningOnly;
provisioningMimes = mProvisioningMimes;
}
//在屏幕是SCREEN_STATE_ON_LOCKED的状态下尝试调用handleNfcUnlock去解锁屏幕。
boolean screenUnlocked = false;
if (!provisioningOnly &&
mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
screenUnlocked = handleNfcUnlock(tag);
if (!screenUnlocked) {
return DISPATCH_FAIL;
}
}
NdefMessage message = null;
//将Tag解析成Ndef的格式.
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
//再通过这样获取到NdefMessage,(???)此处的疑问,前面不是已经获取到NdefMessage
//为什么还要再用NdefMessage生成个Tag,是然后在通过Tag获取,看起来好像是威力方便framewok
//统一接口,供外部调用.
message = ndef.getCachedNdefMessage();
} else {
NfcBarcode nfcBarcode = NfcBarcode.get(tag);
if (nfcBarcode != null && nfcBarcode.getType() == NfcBarcode.TYPE_KOVIO) {
message = decodeNfcBarcodeUri(nfcBarcode);
}
}
if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);
//DispatchInfo一是在一个Tag分发的过程中的帮助类。
DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);
//Tells the ActivityManager to resume allowing app switches.
resumeAppSwitches();
如果正在运行的APP有定义了前台分发机制,则会走到这里
if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
overrideTechLists)) {
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
//判断NDEF消息是否是handover格式
if (tryPeripheralHandover(message)) {
if (DBG) Log.i(TAG, "matched BT HANDOVER");
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
//判断NDEF消息是否是WifiConfiguration格式
if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
......
//将TAg消息发送给对action为ACTION_NDEF_DISCOVERED感兴趣的APP处理
if (tryNdef(dispatch, message)) {
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
//将TAg消息发送给对ACTION_TECH_DISCOVERED感兴趣的APP处理
if (tryTech(dispatch, tag)) {
return DISPATCH_SUCCESS;
}
//如上两个action相关的activity都未处理的时候
//将intent设置为ACTION_TAG_DISCOVERED
dispatch.setTagIntent();
if (dispatch.tryStartActivity()) {
if (DBG) Log.i(TAG, "matched TAG");
return DISPATCH_SUCCESS;
}
//都无法处理就返回false.
if (DBG) Log.i(TAG, "no match");
return DISPATCH_FAIL;
}
下面依次介绍这个方法中用到的几个具体的子函数:
NfcService的setForegroundDispatch最终会调用到NfcDispatcher中的.
public synchronized void setForegroundDispatch(PendingIntent intent,
IntentFilter[] filters, String[][] techLists) {
if (DBG) Log.d(TAG, "Set Foreground Dispatch");
mOverrideIntent = intent;
mOverrideFilters = filters;
mOverrideTechLists = techLists;
}
帮助类DispatchInfo中的几个方法
构造:
public DispatchInfo(Context context, Tag tag, NdefMessage message) {
intent = new Intent();
//先把这个Tag对象放入intent,app获取到当前intent的时候可以取出来
intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
if (message != null) {
//有ndef消息的时候也把消息设置进去
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message});
ndefUri = message.getRecords()[0].toUri();
ndefMimeType = message.getRecords()[0].toMimeType();
} else {
ndefUri = null;
ndefMimeType = null;
}
//启动rootIntent的时候会启动下面三个设置的intent,就是putExtra处的参数.
rootIntent = new Intent(context, NfcRootActivity.class);
rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent);
rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
this.context = context;
packageManager = context.getPackageManager();
}
把当前的intent设置action为NfcAdapter.ACTION_TAG_DISCOVERED
public Intent setTagIntent() {
android.util.Log.d("zy","NfcDispatcher setTagIntent ");
intent.setData(null);
intent.setType(null);
intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
return intent;
}
把当前的intent设置action为NfcAdapter.ACTION_NDEF_DISCOVERED
public Intent setNdefIntent() {
intent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
if (ndefUri != null) {
intent.setData(ndefUri);
return intent;
} else if (ndefMimeType != null) {
intent.setType(ndefMimeType);
return intent;
}
return null;
}
把当前的intent设置action为NfcAdapter.ACTION_TECH_DISCOVERED
public Intent setTechIntent() {
android.util.Log.d("zy","NfcDispatcher setTechIntent ");
intent.setData(null);
intent.setType(null);
intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED);
return intent;
}
第三方app使用前台分发系统时的调用
boolean tryOverrides(DispatchInfo dispatch, Tag tag, NdefMessage message, PendingIntent overrideIntent,
IntentFilter[] overrideFilters, String[][] overrideTechLists) {
......
// 首先是当NdefMessage存在的时候,先去判断NDEF格式的消息
if (message != null) {
intent = dispatch.setNdefIntent();
//当当前的NdefMessage中是对应的NDEF消息时,使用传入的PendingIntent去启动对应的activity
if (intent != null &&
isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
try {
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
if (DBG) Log.i(TAG, "matched NDEF override");
return true;
} catch (CanceledException e) {
return false;
}
}
}
// TECH
intent = dispatch.setTechIntent();
//当传入的Tag的Technolg也是支持的时候,把action设置成ACTION_NDEF_DISCOVERED去启动
if (isTechMatch(tag, overrideTechLists)) {
try {
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
if (DBG) Log.i(TAG, "matched TECH override");
return true;
} catch (CanceledException e) {
return false;
}
}
// TAG 和上面同理。
intent = dispatch.setTagIntent();
if (isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
try {
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
if (DBG) Log.i(TAG, "matched TAG override");
return true;
} catch (CanceledException e) {
return false;
}
}
return false;
}
接下来是通过tryPeripheralHandover的,就是交由Bt进行发送的数据的处理
public boolean tryPeripheralHandover(NdefMessage m) {
//消息为空,或者说设备不支持蓝牙,那么就直接返回
if (m == null || !mDeviceSupportsBluetooth) return false;
if (DBG) Log.d(TAG, "tryHandover(): " + m.toString());
HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m);
......
//得到使用的handover的各个参数以后,设置到intent内,并启动PeripheralHandoverService这个service.
Intent intent = new Intent(mContext, PeripheralHandoverService.class);
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
if (handover.oobData != null) {
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
}
if (handover.uuids != null) {
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
}
if (handover.bluetoothClass != null) {
intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS, handover.bluetoothClass);
}
intent.putExtra(PeripheralHandoverService.EXTRA_CLIENT, mMessenger);
intent.putExtra(PeripheralHandoverService.EXTRA_BT_ENABLED, mBluetoothEnabledByNfc.get());
if (CtaUtils.showCtaBtDialogIfNeeded(mContext, null, intent, null)) {
return true;
}
//最后启动这个Intent的service.
mContext.startServiceAsUser(intent, UserHandle.CURRENT);
return true;
}
public BluetoothHandoverData parseBluetooth(NdefMessage m){
//取得Ndef消息中的第一个NdefRecord记录,这个记录中肯定包含了完整的Header
NdefRecord r = m.getRecords()[0];
//获取Header中的tnf字段。
short tnf = r.getTnf();
//获取Header的Type字段。
byte[] type = r.getType();
//上面的获取不清楚的话去看看NDEF协议,较为简单.
// Check for BT OOB record 感觉现在都不用这个了,打印Log看看.
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);
}
......
return null;
}
如上可以看到最终都是调用到parseBtOob然后根据getPayload的数据的(???看了源码不太懂)
接下来我们去看看PeripheralHandoverService这个service的启动流程
生命周期的回调:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
//最主要的就是调用如下方法去打开蓝牙.
if (doPeripheralHandover(intent.getExtras())) {
return START_STICKY;
} else {
stopSelf(startId);
return START_NOT_STICKY;
}
}
@Override
public void onCreate() {
super.onCreate();
mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
mSuccessSound = mSoundPool.load(this, R.raw.end, 1);
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothStatusReceiver, filter);
}
boolean doPeripheralHandover(Bundle msgData) {
......
- mDevice = msgData.getParcelable(EXTRA_PERIPHERAL_DEVICE);
String name = msgData.getString(EXTRA_PERIPHERAL_NAME);
int transport = msgData.getInt(EXTRA_PERIPHERAL_TRANSPORT);
OobData oobData = msgData.getParcelable(EXTRA_PERIPHERAL_OOB_DATA);
Parcelable[] parcelables = msgData.getParcelableArray(EXTRA_PERIPHERAL_UUIDS);
BluetoothClass bluetoothClass = msgData.getParcelable(EXTRA_PERIPHERAL_CLASS);
ParcelUuid[] uuids = null;
if (parcelables != null) {
uuids = new ParcelUuid[parcelables.length];
for (int i = 0; i < parcelables.length; i++) {
uuids[i] = (ParcelUuid)parcelables[i];
}
}
mClient = msgData.getParcelable(EXTRA_CLIENT);
mBluetoothEnabledByNfc = msgData.getBoolean(EXTRA_BT_ENABLED);
mBluetoothPeripheralHandover = new BluetoothPeripheralHandover(
this, mDevice, name, transport, oobData, uuids, bluetoothClass, this);
if (transport == BluetoothDevice.TRANSPORT_LE) {
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_PAUSE_POLLING), PAUSE_DELAY_MILLIS);
}
if (mBluetoothAdapter.isEnabled()) {
if (!mBluetoothPeripheralHandover.start()) {
mHandler.removeMessages(MSG_PAUSE_POLLING);
mNfcAdapter.resumePolling();
}
} else {
//调用enableBluetooth去打开蓝牙
if (!enableBluetooth()) {
Log.e(TAG, "Error enabling Bluetooth.");
mBluetoothPeripheralHandover = null;
return false;
}
}
return true;
}
boolean enableBluetooth() {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothEnabledByNfc = true;
//再往下就是Bluetooth中的启动链接流程了!
return mBluetoothAdapter.enableNoAutoConnect();
}
return true;
}
然后就是优先级最高的处理方式,注册了action为NfcAdapter.ACTION_NDEF_DISCOVERED的
boolean tryNdef(DispatchInfo dispatch, NdefMessage message) {
......
//获取对应的intent
Intent intent = dispatch.setNdefIntent();
if (intent == null) return false;
//如果发送的NdefMessage包含打开这个消息的包的信息(AAR)
//那么就用指定的包处理这个Tag消息
List<String> aarPackages = extractAarPackages(message);
for (String pkg : aarPackages) {
dispatch.intent.setPackage(pkg);
if (dispatch.tryStartActivity()) {
if (DBG) Log.i(TAG, "matched AAR to NDEF");
return true;
}
}
// Try to perform regular launch of the first AAR
if (aarPackages.size() > 0) {
......
}
......
//当消息中没有包名的时候,就用前面设置的intent:setNdefIntent
//来启动activity,所以此时所有注册的activity都会受到这个action,然后你可以选择用那个启动
dispatch.intent.setPackage(null);
if (dispatch.tryStartActivity()) {
if (DBG) Log.i(TAG, "matched NDEF");
return true;
}
return false;
}
而tryTech()的思想是一样的,也是经过一系列判断后,通过dispatch.tryStartActivity()来启动,不过它启动的是action为
NfcAdapter.ACTION_TECH_DISCOVERED的,不再赘述。
最终当都不能处理的时候就调用dispatch.setTagIntent()把intent设置为NfcAdapter.ACTION_TAG_DISCOVERED
用它去启动符合要求的activity.
至此完成整体Tag的读取分发系统,回到NfcService中的mHandler中MSG_MOCK_NDEF,继续向下执行发送相关的成功
或失败的音效
2、写入Tag的流程
写开发时app调用framework层的对应的不同协议的Tag代表的类的接口有如下:
位于:android/frameworks/base/core/java/android/nfc/tech
有: NfcA.java、NfcB.java、NfcF.java;
Ndef.java、IsoDep.java ... 等等。这些类都代表了指定的不同协议的Tag的实现.
此处我们以android中的Ndef为例,你想要写入数据的时候,调用Ndef类的如下接口
public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
checkConnected();
try {
//tagServices 在 packages/app/Nfc中实现的
INfcTag tagService = mTag.getTagService();
......
int serviceHandle = mTag.getServiceHandle();
if (tagService.isNdef(serviceHandle)) {
//调用tagService的ndefWrite实现真正的写入
int errorCode = tagService.ndefWrite(serviceHandle, msg);
switch (errorCode) {
case ErrorCodes.SUCCESS:
break;
case ErrorCodes.ERROR_IO:
throw new IOException();
case ErrorCodes.ERROR_INVALID_PARAM:
throw new FormatException();
default:
// Should not happen
throw new IOException();
}
}
......
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
}
}
那么我们就需要去packages/app/Nfc当中找INfcTag的具体实现了,是位于NfcService当中的内部类
final class TagService extends INfcTag.Stub调用内部方法ndefWrite
@Override
public int ndefWrite(int nativeHandle, NdefMessage msg) throws RemoteException {
NfcPermissions.enforceUserPermissions(mContext);
TagEndpoint tag;
......
//下面是真正的写入的地方
if (tag.writeNdef(msg.toByteArray())) {
return ErrorCodes.SUCCESS;
} else {
return ErrorCodes.ERROR_IO;
}
}
前面我们已经说过TagEndpoint是有NativeNfcTag实现的,对应的方法如下
private native boolean doWrite(byte[] buf);
@Override
public synchronized boolean writeNdef(byte[] buf) {
if (mWatchdog != null) {
mWatchdog.pause();
}
//可以看到去native层进行进一步的读写!
boolean result = doWrite(buf);//go to the native
if (mWatchdog != null) {
mWatchdog.doResume();
}
return result;
}
至此java层的写入流程就分析完毕了。
下面是调试的时候一次写入的Log记录一下,会先调用链接,再调用transceive,最后再写入。
04-21 00:37:02.032 D/zy ( 2652): Nfcervice connect
04-21 00:37:02.042 D/zy ( 2652): NativeNfc transceive
04-21 00:37:02.070 D/zy ( 2652): Nfcervice connect
04-21 00:37:02.094 D/zy ( 2652): Nfcervice ndefWrite
04-21 00:37:02.094 D/zy ( 2652): NativeNfc writeNdef
文章参考:
http://blog.csdn.net/xuwen0306/article/details/44724845
0 0
- NFC--Tag读写模式java层源码分析
- Nfc--Handover相关java层源码分析
- NFC--P2P模式 java层源码码流程解析。
- NFC三种模式NDEF,TECH,TAG
- Handler源码分析 - Java层
- Android NFC 开发教程(3): Mifare Tag 读写示例
- Android NFC 开发教程(3): Mifare Tag 读写示例
- Android NFC Mifare Tag 读写示例
- Android NFC 开发教程: Mifare Tag 读写示例
- NFC应用(二)读写器模式
- NFC三种模式NDEF,TECH,TAG 解析顺序
- Binder源码分析之Java层
- Binder源码分析之Java层
- NFC Tag Types
- BB10 读取NFC tag
- NFC源码分析之初始化流程
- NFC的读写卡模式——前台调度系统
- RIL层源码分析
- L2-019. 悄悄关注
- BZOJ 1461: 字符串的匹配 kmp套树状数组
- Present CodeForces
- 链表面试题--两种方法逆序打印单链表(栈和递归)
- LeetCode 1. Two Sum
- NFC--Tag读写模式java层源码分析
- python的wheel以及安装包查找
- LTE,LTE是什么意思
- jQuery选择器总结
- 三维空间中刚体的变换旋转和平移
- GO语言基础及特性
- 一个不错的shell脚本入门教程
- 516. Longest Palindromic Subsequence
- 多重背包