NFC源码分析之R/W工作模式
void applyRouting(boolean force) {
synchronized (this) {
......
//mInProvisionMode:用来标记当前系统是否处于开机向导界面.
//对应于android开机时候的引导provision apk,是否出在引导模式
if (mInProvisionMode) {
mInProvisionMode = Settings.Secure.getInt(mContentResolver,
Settings.Global.DEVICE_PROVISIONED, 0) == 0;
//当运行到这里的时候就代表,这是刷完系统,开机引导完,第一次走到这里,
//并且现在开机引导已经完毕,此时下面可能已经是false
if (!mInProvisionMode) {
//就是设置NfcDispatcher中的mProvisioningOnly = false;
mNfcDispatcher.disableProvisioningMode();
//调用native方法doSetProvisionMode,去通知native层,当前不是处于ProvisionMode
mDeviceHost.doSetProvisionMode(mInProvisionMode);
}
}
// 当此时正在和一个Tag在通信的时候,延迟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 {
//此处更新于NFC RF的Discovery参数.
NfcDiscoveryParameters newParams = computeDiscoveryParameters(mScreenState);
//传入参数为true的时候或者参数有所变化.
if (force || !newParams.equals(mCurrentDiscoveryParameters)) {
//只要routingtable变过,或者有新的tech的改变。
if (newParams.shouldEnableDiscovery()) {
boolean shouldRestart =
mCurrentDiscoveryParameters.shouldEnableDiscovery();
//最重要的就是调用到了这里,mDeviceHost的实现类是NativeNfcManager
//最终调用到内部的如下:
// private native void doEnableDiscovery(int techMask,
// boolean enableLowPowerPolling,
// boolean enableReaderMode,
// boolean enableP2p,
// boolean restart);
//去native层,初始化RF的相关参数。
mDeviceHost.enableDiscovery(newParams, shouldRestart);
} else {
mDeviceHost.disableDiscovery();
}
mCurrentDiscoveryParameters = newParams;
} else {
Log.d(TAG, "Discovery configuration equal, not updating.");
}
} finally {
......
}
}
}
//基于屏幕的状态重新计算NfcDiscoveryParameters.
private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
//当屏幕状态>= NFC_POLLING_MODE,而如下:
//static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
//其实就是在亮屏并且处于解锁状态的时候,或mIsTaskBoot为true,mIsTaskBoot这个变量只有在处于初始化
//阶段,NfcService从构造的结尾处进行Nfc的启动.
// Polling;mIsInterruptedByCamera :是否被camera打断.
if (((screenState >= NFC_POLLING_MODE)||mIsTaskBoot) && !mIsInterruptedByCamera) {
//mReaderModeParams是在setReaderMode()这个接口中被设置的,应该是第三方app,在它们
//的应用界面想要把手机作为reader的时候会打印。
//当前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 {
//系统内部的会设置为NFC_POLL_DEFAULT这个参数 对应:
//static final int NFC_POLL_DEFAULT = -1; 但是感觉传入到native层的时候,可能根据这个
//有个默认的设置.
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);
paramsBuilder.setEnableP2p(true);
//当有人调用addNfcUnlockHandler的时候,isLockscreenPollingEnabled才可能被设置为true,
//发现在sony的设计中LockScreenHeadsetHandover的构造处有添加.
//注意此时的屏幕状态是SCREEN_STATE_ON_LOCKED.就是亮屏,但是锁着那!
} 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);
}
//mIsHceCapable代表是否具有卡模拟的功能。不插卡的时候也是true,可能是针对HCE的?
if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
// Host routing is always enabled at lock screen or later
paramsBuilder.setEnableHostRouting(true);
}
//只要commit过routingtable,或者设置过屏幕状态,或者换过卡,等等mIsRoutingTableDirty会为true.
if(mIsRoutingTableDirty) {
mIsRoutingTableDirty = false;
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(); //返回对应字符串,代表相关的参数
}
gCachedNfcManagerNotifyNdefMessageListeners = e->GetMethodID(cls.get(),
"notifyNdefMessageListeners", "(Lcom/android/nfc/dhimpl/NativeNfcTag;)V");
private void notifyNdefMessageListeners(NativeNfcTag tag) {
mListener.onRemoteEndpointDiscovered(tag);
}
@Override
public void onRemoteEndpointDiscovered(TagEndpoint tag) {
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);
}
case MSG_NDEF_TAG:
if (DBG) Log.d(TAG, "Tag detected, notifying applications");
......
//当有别人调用setReaderMode()接口去设置mReaderModeParams的时候.
//如第三方app模拟reader.(一般都是在第三方app的reader/write界面,由它们调用)
synchronized (NfcService.this) {
readerParams = mReaderModeParams;
}
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条形码的时候
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.(此时就是读取NDEF格式的信息了)
NdefMessage ndefMsg = tag.findAndReadNdef();
//解析的消息无法被实例化成NDEF的时候。
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) {
//"Read error. Try again"
String toastString = mContext.getString(
R.string.nfc_strings_toast_prompt_touch_again_txt);
if (!ToastMaster.isSameToastShown(toastString)) {
ToastMaster.showToast(mContext, toastString);
}
}
break;
}
}
//这个如名字是抖动的tag,就是由于距离没控制好,一直检测到同一个tag的时候。
//应该就是这个Tag检测完,没有离开范围,就不会再次检测
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;
//case MSG_NDEF_TAG over.
/*******************************************************************************
**
** Function: createNativeNfcTag
**
** Description: Create a brand new Java NativeNfcTag object;
** fill the objects's member variables with data;
** notify NFC service;
** activationData: data from activation.
**
** Returns: None
**
*******************************************************************************/
void NfcTag::createNativeNfcTag (tNFA_ACTIVATED& activationData)
{
static const char fn [] = "NfcTag::createNativeNfcTag";
ALOGV("%s: enter", fn);
JNIEnv* e = NULL;
ScopedAttach attach(mNativeData->vm, &e);
if (e == NULL)
{
ALOGE("%s: jni env is null", fn);
return;
}
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(mNativeData->cached_NfcTag));
if (e->ExceptionCheck())
{
e->ExceptionClear();
ALOGE("%s: failed to get class", fn);
return;
}
//create a new Java NativeNfcTag object
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
ScopedLocalRef<jobject> tag(e, e->NewObject(tag_cls.get(), ctor));
//fill NativeNfcTag's mProtocols, mTechList, mTechHandles, mTechLibNfcTypes
fillNativeNfcTagMembers1(e, tag_cls.get(), tag.get());
//fill NativeNfcTag's members: mHandle, mConnectedTechnology
fillNativeNfcTagMembers2(e, tag_cls.get(), tag.get(), activationData);
//fill NativeNfcTag's members: mTechPollBytes
fillNativeNfcTagMembers3(e, tag_cls.get(), tag.get(), activationData);
//fill NativeNfcTag's members: mTechActBytes
fillNativeNfcTagMembers4(e, tag_cls.get(), tag.get(), activationData);
//fill NativeNfcTag's members: mUid
fillNativeNfcTagMembers5(e, tag_cls.get(), tag.get(), activationData);
if (mNativeData->tag != NULL)
{
e->DeleteGlobalRef(mNativeData->tag);
}
mNativeData->tag = e->NewGlobalRef(tag.get());
ALOGV("%s; mNumDiscNtf=%x", fn,mNumDiscNtf);
if(!mNumDiscNtf || NfcTag::getInstance().checkNextValidProtocol() == -1)
{
//notify NFC service about this new tag
mNumDiscNtf = 0;
ALOGV("%s: try notify nfc service", fn);
storeActivationParams();
e->CallVoidMethod(mNativeData->manager,
android::gCachedNfcManagerNotifyNdefMessageListeners, tag.get());
if (e->ExceptionCheck())
{
e->ExceptionClear();
ALOGE("%s: fail notify nfc service", fn);
}
deleteglobaldata(e);
}
else
{
ALOGV("%s: Selecting next tag", fn);
}
ALOGV("%s: exit", fn);
}
// mConnectedHandle stores the *real* libnfc handle
// that we're connected to.
private int mConnectedHandle;
@Override
public NdefMessage findAndReadNdef(){
//去获取当前Tag支持的所有的tech
//如下technologies、handles都是在native层就实例化好的.
//TagTechnology有很多种, NFC_A、NFC_B、ISO_DEP、NFC_F、NFC_V.
//关于technologies和handles也需要多说一下,暂时对这块认知不高,通过Log观察到如下:
//使用了四张平常的卡进行测试抓取, 每次technologies和数量和handles的数量都是一致的,
//technologies是卡片支持的协议,对应于TagTechnology.java中的成员变量.
//handles 在这次试验的手机上值都是1,无论size是对少,每个位置都是1,而handles的添加在java层
//都是对应这个变量 private int mConnectedHandle;前面有注释是说代表了libnfc的handle,那么
//感觉一般的平台肯定是用的一个libnfc库呀,难道有些会用两个libnfc?然后去进行选择?
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++) {
for (int i = 0; i < techIndex; i++) {
//高效处理,我们目前handle都是一样的情况
if (handles[i] == handles[techIndex]) {
continue; // don't check duplicate handles
}
}
//网上说明:判断connectedHandle与当前Index对应的handle的关系,并更新状态,。
//自己理解不深,源码见后面.
status = connectWithStatus(technologies[techIndex]);
//连接失败直接跳过本次循环,连接下一个Tech!!
if (status != 0) {
Log.d(TAG, "Connect Failed - status = "+ status);
if (status == STATUS_CODE_TARGET_LOST) {
break;
}
continue; // try next handle
}
if (!foundFormattable) {
//如果是标准的Ndef格式,进行如下操作,自己测试的四个卡都是.
if (isNdefFormatable()) {
foundFormattable = true;
formattableHandle = getConnectedHandle();
formattableLibNfcType = getConnectedLibNfcType();
}
//再次connect
reconnect();
}
int[] ndefinfo = new int[2];
//检查当前tag中ndef数据的合法性,传入ndefinfo会走到native层,进行数据的赋值.
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;
//check完以后ndefinfo这个里面的数据,正常情况下就是完整的了。
int supportedNdefLength = ndefinfo[0];
int cardState = ndefinfo[1];
//此处才是真正的读取,读取tag中的数据,读出来的是字节数组。
//注意此处经常丢失Log
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;
//存一个空NdefMessage
addNdefTechnology(null,
getConnectedHandle(),
getConnectedLibNfcType(),
getConnectedTechnology(),
supportedNdefLength, cardState);
foundFormattable = false;
reconnect();
}
break;
}
......
return ndefMsg;
}
private native int doConnect(int handle);
public synchronized int connectWithStatus(int technology) {
......
int status = -1;
for (int i = 0; i < mTechList.length; i++) {
//循环遍历所有的tech,找到需要connect的tech
if (mTechList[i] == technology) {
//当前的tag被检测到,第一次走到这里的时候mConnectedHandle是默认值,
//因为没有在native层进行实例化,所以是mConnectedHandle = 0;
// Get the handle and connect, if not already connected
if (mConnectedHandle != mTechHandles[i]) {
// We're not yet connected to this handle, there are
// a few scenario's here:
// 1) We are not connected to anything yet - allow
// 2) We are connected to a technology which has
// a different handle (multi-protocol tag); we support
// switching to that.
if (mConnectedHandle == -1) {
//这个情况,Log一直观察不到,无论是重启/刷机的第一次,还是平常都不行
//感觉也正常,因为可能是libnfc只要连接上,就不会初始化成-1.
// Not connected yet
//status = doConnect(mTechHandles[i]);
Log.d(TAG," readerParams doConnect");
status = doConnect(i);
} else {
//调用reconnectWithStatus传入handle.(暂时发现每个tag的handle都是一样的)
// Connect to a tech with a different handle
Log.d(TAG," readerParams Connect to a tech with a different handle");
status = reconnectWithStatus(i);
}
//如果成功链接,mConnectedHandle都是1(在我这个测试手机上),按照前面的变量分析的话,
//也可以理解
if (status == 0) {
mConnectedHandle = mTechHandles[i];
mConnectedTechIndex = i;(把tech所在的正确的index传入)
}
} else {
//按照目前的手机和Log和Tag的分析都没有走到这里,暂时不太理解
// 1) We are connected to a technology which has the same
// handle; we do not support connecting at a different
// level (libnfc auto-activates to the max level on
// any handle).
// 2) We are connecting to the ndef technology - always
// allowed.
if ((technology == TagTechnology.NDEF) ||
(technology == TagTechnology.NDEF_FORMATABLE)) {
// special case for NDEF, this will cause switch to ISO_DEP frame intf
i = 0;
// status = 0;
}
status = reconnectWithStatus(i);
......
if (status == 0) {
mConnectedTechIndex = i;
// Handle was already identical
}
}
break;
}
}
......
return status;
}
native int doHandleReconnect(int handle);
public synchronized int reconnectWithStatus(int handle) {
......
//最后就是调用到native的方法去做进一步的连接操作.
int status = doHandleReconnect(handle);
......
return status;
}
@Override
public synchronized boolean reconnect() {
return reconnectWithStatus() == 0;
}
native int doReconnect();
public synchronized int reconnectWithStatus() {
......
int status = doReconnect();
......
return status;
}
private native int doCheckNdef(int[] ndefinfo);
private synchronized int checkNdefWithStatus(int[] ndefinfo) {
......
int status = doCheckNdef(ndefinfo);
......
return status;
}
public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType,
int javaType, int maxLength, int cardState) {
synchronized (this) {
addTechnology(TagTechnology.NDEF, handle, libnfcType);
Bundle extras = new Bundle();
extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg);
extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength);
extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState);
extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType));
Log.d(TAG,"addNdefTechnology readerParams mTechExtras = "+mTechExtras);
//getTechExtras内部会更具Tech,创建出一个Bundle,最终NfcService可以获取这个然后取出
//来其中的数据,以及重要的NdefMessage.
if (mTechExtras == null) {
// This will build the tech extra's for the first time,
// including a NULL ref for the NDEF tech we generated above.
Bundle[] builtTechExtras = getTechExtras();
builtTechExtras[builtTechExtras.length - 1] = extras;
}
else {
// Tech extras were built before, patch the NDEF one in
Bundle[] oldTechExtras = getTechExtras();
Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1];
System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length);
newTechExtras[oldTechExtras.length] = extras;
mTechExtras = newTechExtras;
}
}
}
private native byte[] doRead();
@Override
public synchronized byte[] readNdef() {
......
byte[] result = doRead();
......
return result;
}
private void dispatchTagEndpoint(TagEndpoint tagEndpoint, ReaderModeParams readerParams){
//Tag用来封装一个已经被发现的Tag.Tag类是framework提供的api
//tagEndpoint.getTechExtras():就包含我们最后addNdefTechnology的时候存入的NdefMessage.
Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(),
tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService);
//把tagEndpoint以 key = tagEndpoint.gethandle;value = tagEndpoint放入到集合中
//final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
//由此手机看来,handle一样,那么一次检测基本就存一个,第一个连接成功Tech.
registerTagObject(tagEndpoint);
//当有第三方app
if (readerParams != null) {
try {
if ((readerParams.flags & NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS) == 0) {
playSound(SOUND_END);
}
if (readerParams.callback != null) {
//调用回调onTagDiscovered.
readerParams.callback.onTagDiscovered(tag);
return;
} else {
// Follow normal dispatch below
}
} ...
}
//正真开始分发的地方.
//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());
//屏幕处于亮屏且解锁状态的时候:
//弹出:No supported apps for this tag installed on this device
//表示没有任何的activity处理当前的分发!
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) {
//成功的时候,播放震动和声音.
if (mAudioManager.getRingerMode() != mAudioManager.RINGER_MODE_SILENT) {
mVibrator.vibrate(200);
}
playSound(SOUND_END);
}
}
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的格式.此处是所读取的卡片的所有Tech中只要有Ndef的Tech即可.
//返回null的时候证明这个Tag不是NDEF formatted的,或者说这个Tag虽然是NDEF formatted的
//但是不是Android所支持的.
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
//再通过这样获取到NdefMessage,此处的疑问,前面不是已经获取到NdefMessage
//为什么还要再用NdefMessage生成个Tag,然后在通过Tag获取,看起来好像是为了方便framewok
//统一接口,供外部调用.
message = ndef.getCachedNdefMessage();
} else {
//当是null的时候,先看看是不是NfcBarcode Tech
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有定义了前台分发机制,则会走到这里,就是某个app注册了一些接口
//并且运行在前台,它想去读取这些信息
if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
overrideTechLists)) {
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
//尝试使用Handover来分发消息.就是带Nfc的蓝牙鼠标、键盘等.可以看到在普通的系统分发之前.
//通过BT传输图片不属于这个,那个是P2P的流程中的.忽然想起来这应该就是所谓的Hadover和Beam区分吧.
if (tryPeripheralHandover(message)) {
if (DBG) Log.i(TAG, "matched BT HANDOVER");
return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}
//WIFI相关的暂无了解
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设置为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;
}
/**
* Helper for re-used objects and methods during a single tag dispatch.
*/
static class DispatchInfo {
public final Intent intent;
final Intent rootIntent;
final Uri ndefUri;
final String ndefMimeType;
final PackageManager packageManager;
final Context context;
final Tag tag;
public DispatchInfo(Context context, Tag tag, NdefMessage message) {
//构造会直接实例化一个intent,然后把tag先放入.
intent = new Intent();
intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
if (message != null) {
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message});
ndefUri = message.getRecords()[0].toUri();
ndefMimeType = message.getRecords()[0].toMimeType();
} else {
//当传入的是null的时候,就是Ndef.get(tag)不能获取正确的值.
ndefUri = null;
ndefMimeType = null;
}
//这个NfcRootActivity会直接去启动,Intent携带的EXTRA_LAUNCH_INTENT对应的activity
//对应到下面也就是启动,最一开始的intent = new Intent();
//此时把你要启动的activity对应的intent添加到成员变量intent中即可.
//这样好像是为了提供NFC root task.
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.tag = tag;
this.context = context;
packageManager = context.getPackageManager();
}
//设置注册action:ACTION_NDEF_DISCOVERED的intent.
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;
}
//设置注册action:ACTION_TECH_DISCOVERED的intent.
public Intent setTechIntent() {
intent.setData(null);
intent.setType(null);
intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED);
return intent;
}
//设置注册action:ACTION_TAG_DISCOVERED的intent.
public Intent setTagIntent() {
intent.setData(null);
intent.setType(null);
intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
return intent;
}
//以成员变量intent,启动所有的注册的activity
boolean tryStartActivity() {
//遍历所有符合成员变量intent要求的acitivity
List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0,
ActivityManager.getCurrentUser());
if (activities.size() > 0) {
//直接启动
context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
return true;
}
return false;
}
//以特定的intentToStart,启动所有注册的activity
boolean tryStartActivity(Intent intentToStart) {
List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
intentToStart, 0, ActivityManager.getCurrentUser());
if (activities.size() > 0) {
rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart);
context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
NfcIddEvent.NfcTag.dispatchedToApp(tag, activities);
return true;
}
return false;
}
}
public synchronized void setForegroundDispatch(PendingIntent intent,
IntentFilter[] filters, String[][] techLists) {
if (DBG) Log.d(TAG, "Set Foreground Dispatch");
mOverrideIntent = intent;
mOverrideFilters = filters;
mOverrideTechLists = techLists;
}
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;
}
public boolean tryPeripheralHandover(NdefMessage m) {
//消息为空,或者说设备不支持蓝牙,那么就直接返回
if (m == null || !mDeviceSupportsBluetooth) return false;
if (DBG) Log.d(TAG, "tryHandover(): " + m.toString());
//假如是进行Handover的时候,会按照Nfc Form中的Handover相关的协议从NdefMessage中解析出正确的数据
HandoverDataParser.BluetoothHandoverData handover =
mHandoverDataParser.parseBluetooth(m);
......
//得到handover的各个参数以后,设置到intent内,并启动PeripheralHandoverService这个service.
//通过下面可以看到,主要有BT的 device、name、uuid、等
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());
//国内过CTA检测的时候的弹框
if (CtaUtils.showCtaBtDialogIfNeeded(mContext, null, intent, null)) {
return true;
}
//最后启动PeripheralHandoverService.
mContext.startServiceAsUser(intent, UserHandle.CURRENT);
return true;
}
public class PeripheralHandoverService extends Service
implements BluetoothPeripheralHandover.Callback {
......
public PeripheralHandoverService() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mHandler = new MessageHandler();
mMessenger = new Messenger(mHandler);
mBluetoothHeadsetConnected = false;
mBluetoothEnabledByNfc = false;
mStartId = 0;
}
@Override
public void onCreate() {
super.onCreate();
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
//注册一个广播监听蓝牙状态,是否打开等.
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothStatusReceiver, filter);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
//进一步的回去打开BT,前面也注册了广播,在收到蓝牙开启广播以后开始做真正的Handover
//去连接键盘了,等等.
if (doPeripheralHandover(intent.getExtras())) {
return START_STICKY;
}
......
}
}
boolean tryNdef(DispatchInfo dispatch, NdefMessage message) {
......
//为dispatch设置ACTION_NDEF_DISCOVERED相关的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);
//直接使用指定的apk去打开
if (dispatch.tryStartActivity()) {
if (DBG) Log.i(TAG, "matched AAR to NDEF");
return true;
}
}
//到下面应该aar包中没有对ACTION_NDEF_DISCOVERED这个action感兴趣的activity.
//然后就区启动aar中的第一个包含的apk
// Try to perform regular launch of the first AAR
if (aarPackages.size() > 0) {
String firstPackage = aarPackages.get(0);
PackageManager pm;
try {
UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser());
pm = mContext.createPackageContextAsUser("android", 0,
currentUser).getPackageManager();
} ...
Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage);
if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) {
if (DBG) Log.i(TAG, "matched AAR to application launch");
return true;
}
// Find the package in Market: (看这架势让我们去下载呀,这么流氓哈)
Intent marketIntent = getAppSearchIntent(firstPackage);
if (marketIntent != null && dispatch.tryStartActivity(marketIntent)) {
if (DBG) Log.i(TAG, "matched AAR to market launch");
return true;
}
}
......
//当消息中没有包名的时候,就用前面设置的intent:setNdefIntent
//来启动activity,所以此时所有注册的activity都会受到这个action,然后你可以选择用那个启动
dispatch.intent.setPackage(null);
if (dispatch.tryStartActivity()) {
if (DBG) Log.i(TAG, "matched NDEF");
return true;
}
return false;
}
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);
}
}
@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;
}
}
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;
}
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
NFC源码分析之R/W工作模式相关推荐
- [转载]NFC源码分析之R/W工作模式
https://blog.csdn.net/zy00000000001/article/details/71183262 文章整理总结java层,NFC读取和写入Tag的流程. 整体的时序图: 1.读 ...
- Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...
- Android源码分析—属性动画的工作原理
转载请注明出处: http://blog.csdn.net/singwhatiwanna/article/details/17853275 前言 本文为Android动画系列的最后一篇文章,通过对源码 ...
- Nginx源码分析:master/worker工作流程概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...
- 【转】ABP源码分析九:后台工作任务
文主要说明ABP中后台工作者模块(BackgroundWorker)的实现方式,和后台工作模块(BackgroundJob).ABP通过BackgroundWorkerManager来管理Backgr ...
- Android属性动画赏析,Android源码分析—属性动画的工作原理
前言 本文为Android动画系列的最后一篇文章,通过对源码的分析,能够让大家更深刻地理解属性动画的工作原理,这有助于我们更好地使用属性动画.但是,由于动画的底层实现已经深入到jni层,并且涉及到显示 ...
- NFC源码分析之Handover
此处的代码为Android O的代码,本也有有一些patch的添加和修改,不过这些都不影响整个流程的理解.Handover相关的 大致可分为两种情况:直接读取带NFC的特殊设备如:带Nfc的蓝牙耳机. ...
- Java源码分析:深入探讨Iterator模式
http://tech.ccidnet.com/art/3539/20060712/618391_1.html java.util包中包含了一系列重要的集合类.本文将从分析源码入手,深入研究一个集合类 ...
- NFC源码分析-ZGS
Nfc的app代码位于: android/package/apps/Nfc/... 编译生成Nfc的apk和libnfc_nci_jni.so Nfc的协议栈和Hal相关的代码位于: system/n ...
最新文章
- 前端Vue学习之路(一)-初识Vue
- androidx和android的区别,【译】使用AndroidX代替Android支持库
- android webview 多文件上传,Android中的webview支持页面中的文件上传实例代码
- stm32 Bootloader设计(YModem协议)
- WCF绑定细节(2)——绑定,绑定元素
- jdk自带的Unicode转utf8工具
- Oracle入门(十三B)之高级查询(上)
- android webview远程调试
- http状态码 以及请求响应头相关
- 【超详细转】VMware 9 安装 Mac OS X 10.8 Mountain Lion 图文全程
- Matlab 四阶龙格库塔法求解二元常微分方程组
- Linux系统中解决Device eth0 does not seem to be present,delaying initialization问题
- 2015年8月4日工作日志--------赵鑫
- 打乱魔方软件_怎样打乱魔方
- 华为关于LINUX的认证,华为认证:华为TA128在LINUX下的简单设置
- 鸿蒙系统和安卓的区别在哪里,有什么不同
- CSS Border (边框)
- 杭州市民卡面试题【杭州多测师】【杭州多测师_王sir】
- xcode中添加新字体【ttf】
- 数字化时代10:从我国经济建设目标看社会产品形态的变化
热门文章
- macino404 || cinema 4D 基础解释 || 第一篇
- 手机html在哪儿,怎么定位手机位置在哪?其实很简单就这样设置就可以了
- HTML、JS的压缩
- 平均薪资28K,前端开发的涨薪史,看哭了...
- 修改注册表恢复IE设置(转)
- fftw的Windows版安装及遇到的问题
- 【SWAT水文模型】SWAT水文模型建立及应用第二期:土地利用数据的准备
- Python自动移动电驴下载完成的文件(未完)
- Java判断手机号归属地
- 嵌入式系统开发笔记48:计算圆的直径、周长和面积(Python语言)