技术标签: NFC
最近一个项目需要将Android的nfc接口底层通过串口对接到外围一个单片机上,而nfc设备是接在单片机上的,这就需要对Android的nfc框架进行梳理,确定与单片机对接的接口通讯;上网查找发现资料不少,本次分析主要参考了以下大神的博客,基本上已经是非常清楚了!按照自己的理解对相关部分进行整理!
nfc的相关背景知识请参照此博客查阅即可:http://blog.csdn.NET/xuwen0306/article/category/2926997
以下以开机,并且一个读卡流程,一一介绍整个顺序:
一、开机启动Nfc服务流程:
Android5.0\frameworks\base\services\core\Java\com\android\server\am\ActivityManagerService.Java
public void systemReady(final RunnablegoingCallback) { .... if (mFactoryTest !=FactoryTest.FACTORY_TEST_LOW_LEVEL) { try { List apps =AppGlobals.getPackageManager(). getPersistentApplications(STOCK_PM_FLAGS); if (apps != null) { int N = apps.size(); int i; for (i=0; i<N; i++){ ApplicationInfoinfo =(ApplicationInfo)apps.get(i); if (info != null && !info.packageName.equals("android")) { addAppLocked(info, false, null /* ABI override */); } } } } catch (RemoteException ex) { // pm is in same process,this will never happen. } } ... }
继续分析addAppLocked()
final ProcessRecordaddAppLocked(ApplicationInfo info, boolean isolated, String abiOverride) { ... if (app.thread == null &&mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); startProcessLocked(app, "addedapplication", app.processName, abiOverride, null /* entryPoint */, null /*entryPointArgs */); } ... }
最终会call到NfcApplication.java中的NfcApplication()构造函数,其中的onCreate()会被调用到。
@Override public void onCreate() { super.onCreate(); boolean isMainProcess = false; // We start a service in a separateprocess to do // handover transfer. We don't want toinstantiate an NfcService // object in those cases, hence checkthe name of the process // to determine whether we're the mainNFC service, or the // handover process ActivityManager am =(ActivityManager)this.getSystemService(ACTIVITY_SERVICE); List processes =am.getRunningAppProcesses(); Iterator i = processes.iterator(); while (i.hasNext()) { //检查当前运行的App名字是否为"com.android.nfc",如果名字相同,则表示当前启动的程序是主程序 RunningAppProcessInfo appInfo =(RunningAppProcessInfo)(i.next()); if (appInfo.pid == Process.myPid()){ isMainProcess = (NFC_PROCESS.equals(appInfo.processName)); break; } } if (UserHandle.myUserId() == 0&& isMainProcess) {</span> //启动NfcService mNfcService = new NfcService(this); HardwareRenderer.enableForegroundTrimming(); } }
当调用了new NfcService()之后,后面会依次创建各个Service。
在进行后续分析前,简单的列举一下NFC的类图,方便后续的理解:
上层APP主要透过调用android.nfc.tech及android.nfc的接口来实现期望的功能;而android.nfc.tech和android.nfc透过AIDL的方式调用到NfcService中的接口。Framework中NfcService透过JNI与底层NFC Driver进行沟通,实现发送命令和接收event功能。
在进入NFCService.java前,需要对整个系统的架构有个初步的了解。
DeviceHost.java定义了目前几乎NFC需要的全部interface和API。不同的厂家依据DeviceHost.java提供的interface,实现对应的内容,就可以和上层app进行沟通了。
二、开机启动流程2:
从流程1中就会new一个NfcService对象,具体的流程就略过了!请查看对应的博客即可;
三、接下来开始描述一整个读卡流程:
1、Android app打开nfc为开始
首先是app上,4.4以前通过以下调用打开:
var1 = NfcAdapter.getDefaultAdapter(this.t)
PendingIntent var3 = PendingIntent.getActivity(this.t, 0, (new Intent(this.t, this.t.getClass())).addFlags(536870912), 0);
IntentFilter[] var4 = new IntentFilter[0];
var1.enableForegroundDispatch((Activity)this.t, var3, var4, (String[][])null);
本文以Android 5.1版本描述读卡流程,5.1版本通过以下调用打开:
a、重写Callback
nfcCallBack = new NfcAdapter.ReaderCallback() { @Override public void onTagDiscovered(Tag tag) { // TODO Auto-generated method stub if(isNFC) { Message message = new Message(); message.what = NFC_START; message.obj = tag; mHandler.sendMessage(message); } } };然后通过打开nfc
public boolean EnableSystemNFCMessage(ReaderCallback var1) { try { NfcAdapter var2; if((var2 = NfcAdapter.getDefaultAdapter(this.t)) != null && var2.isEnabled()) { Bundle var3; (var3 = new Bundle()).putInt("presence", 3000); var2.enableReaderMode((Activity)this.t, var1, d, var3); return true; } else { return false; } } catch (Exception var4) { var4.printStackTrace(); return false; } }
2、此时进入framework中,下面一个个记录调用顺序,本阶段为开启nfc扫描:
frameworks/base/core/java/android/nfc/NfcAdapter.java:1276
public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,Bundle extras)
frameworks/base/core/java/android/nfc/NfcActivityManager.java:206
public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,Bundle extras)
NfcAdapter.sService.setReaderMode(token, this, flags, extras);
PS:此处NfcAdapter.sService是通过NfcAdapter.java中的ServiceManager.getService("nfc")获取的,实际就是实例化packages/apps/Nfc/src/com/android/nfc/NfcService.java服务时,在构造函数中通过ServiceManager.addService(SERVICE_NAME, mNfcAdapter); 加入的,实际为NfcAdapterService实例
接上调用到:
packages/apps/Nfc/src/com/android/nfc/NfcService.java
935行:public void setReaderMode(IBinder binder, IAppCallback callback, int flags, Bundle extras)
接着进入1441行:void applyRouting(boolean force) 开始读卡
1472行:mDeviceHost.enableDiscovery(newParams, shouldRestart);
packages/apps/Nfc/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java(以nxp框架为例)
167行:调用doEnableDiscovery函数,进入jni
packages/apps/Nfc/nxp/jni/com_android_nfc_NativeNfcManager.cpp
static void com_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o, jint modes, jboolean, jboolean reader_mode, jboolean enable_p2p, jboolean restart)
注册扫描nfc卡回调 ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info, nfc_jni_Discovery_notification_callback, (void *)nat)
3、至此,设备开始进行nfc扫描;
接下来分析扫描到nfc卡之后,从调用回调函数开始:
packages/apps/Nfc/nxp/jni/com_android_nfc_NativeNfcManager.cpp
905行: static void nfc_jni_Discovery_notification_callback(void *pContext, phLibNfc_RemoteDevList_t *psRemoteDevList, uint8_t uNofRemoteDev, NFCSTATUS status)
其中判断nfc类型,依照类型实例化cached_NfcTag(非接卡)或者cached_P2pDevice(Nfc设备),以下以非接卡继续说明:
1、实例化
ScopedLocalRef<jclass> tag_cls(e, e->GetObjectClass(nat->cached_NfcTag));
jmethodID ctor = e->GetMethodID(tag_cls.get(), "<init>", "()V");
tag.reset(e->NewObject(tag_cls.get(), ctor)); //以上三行实际就是获得cached_NfcTag的构造函数,然后将tag实例化为cached_NfcTag
PS:通过1629行:if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeNfcTag",&(nat->cached_NfcTag)) == -1) 可知,cached_NfcTag实际就是packages/apps/Nfc/nxp/src/com/android/nfc/dhimpl/NativeNfcTag.java类 (implements TagEndpoint)
接下来将NativeNfcTag实例的各个数值初始化,包括支持的协议等,最后
e->CallVoidMethod(nat->manager, cached_NfcManager_notifyNdefMessageListeners, tag.get());通知上层进行处理
实际就是:
packages/apps/Nfc/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
360行:private void notifyNdefMessageListeners(NativeNfcTag tag)
packages/apps/Nfc/src/com/android/nfc/NfcService.java
236行:public void onRemoteEndpointDiscovered(TagEndpoint tag)
sendMessage(NfcService.MSG_NDEF_TAG, tag);
本文件中的final class NfcServiceHandler extends Handler中对此消息进行处理;
播放声音等,然后调用
tag.startPresenceChecking(presenceCheckDelay, callback);
dispatchTagEndpoint(tag, readerParams);等发送消息到上层
最终在dispatchTagEndpoint中,调用回调将TagEndpoint,根据相应的信息,转换为tag类传到应用层;PS:最重要此处将TagService封装进了Tag类中
if (readerParams.callback != null) {
readerParams.callback.onTagDiscovered(tag);
4、接下来又回到应用层,接收到nfc检测Tag:
应用层获取到Tag类之后,判断Tag类型后,可以通过具体的封装,获取对应的协议类进行处理,以下以NfcB举例:
this.A = NfcB.get(var1); try { this.A.connect(); if(this.A.isConnected()) { this.B = new m(this.A); return true; } } catch (IOException var7) { }
5、接下来应用可以自行调用NfcB等协议进行nfc卡的读写操作,以NfcB协议举例,西面是相关方法在framework中的位置:
frameworks/base/core/java/android/nfc/tech/NfcB.java
继承自frameworks/base/core/java/android/nfc/tech/BasicTagTechnology.java
内部依据具体的协议内容,最终通过传入的Tag类,获取TagService服务,通过TagService服务最终调用到NativeNfcTag中相应的connect,transceive等方法,与非接卡进行通讯;
自此,以流水账的形式将一个nfc的读卡流程描写完成!整体描述比较晦涩,后续再看看怎么样能够更好的表达记录nfc读卡流程!
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf