尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Android NFC模拟门禁卡全攻略:从原理到HCE实战

发布时间:2026/9/28 1:17:28

资讯中心
01
ARTICLE

Android NFC模拟门禁卡全攻略:从原理到HCE实战

Android NFC模拟门禁卡全攻略:从原理到HCE实战
大概每个在写字楼上班的人都有过这种经历下班走到闸机口翻遍口袋找不到工牌只好尴尬地等同事刷一下。折腾Android NFC模拟门禁卡这件事我前后花了一个多星期把网上的教程翻了遍踩了不少坑才真正跑通。这篇文章不会开头铺垫太多直接把我实测过的原理、代码、坑点全部倒出来你需要做的就是把代码复制进Android Studio跟着操作大概率能成。要提前说明一点这篇文章提到的方法和代码只适合模拟你自己有权使用的门禁卡比如自家的单元门、自己工位的门禁、自己实验室的门禁。复制别人的卡、破解加密扇区、绕过门禁管理规则这些事情既不合法也容易惹麻烦请务必在合规范围内操作。1. NFC模拟门禁卡先搞清楚原理再动手1.1 NFC三种工作模式门禁卡走的是哪种NFC全称是Near Field Communication近距离无线通信技术工作频率在13.56MHz。Android手机从很早的版本就内置了NFC芯片只不过很多人只用它来碰一碰传文件和移动支付。其实NFC设备有三种工作模式模拟门禁卡用的正是其中一种。第一种是读卡器模式手机相当于一个读卡器主动去读取周围NFC卡片的数据。第二种是点对点模式两个NFC设备之间互相传数据Android Beam就是典型应用。第三种是卡模拟模式手机把自己伪装成一张非接触式IC卡让外部的读卡器觉得它就是一张银行卡或门禁卡。门禁系统的读卡器是固定安装的它不会关心对面是一张塑料卡片还是一部手机只要通信协议一致、数据对得上就会放行。这就是手机模拟门禁卡能够成立的最底层逻辑。关键来了卡模拟模式在Android上分为两种实现路径。一种是靠硬件芯片内置的安全模块比如NFC-SIM卡或者eSE安全芯片手机厂商的公交卡功能大多走这条路。另一种是纯软件实现叫HCEHost Card Emulation主机卡模拟从Android 4.4API 19开始系统级支持。HCE的精髓是所有卡模拟的逻辑跑在App进程里不需要额外的安全芯片读卡器发过来的APDU指令由你的代码直接处理。这意味着普通开发者也能通过写一个App让手机瞬间变成一张自定义的卡。1.2 门禁卡为什么“难模拟”Mifare Classic和UID的门道搞清模式之后还得搞清卡片类型这是无数人卡住的根源。国内常见的门禁卡绝大多数是NXP公司生产的Mifare Classic系列尤其是Mifare Classic 1K也叫S50。这种卡有1024字节的存储空间分成16个扇区每个扇区4个块每个块16字节。第0扇区的第0块特别重要它存着卡片的UID唯一标识符、厂商数据等。现实中模拟门禁卡最麻烦的点就是UID。门禁系统在发卡的时候把UID和用户身份在后台数据库里做了绑定读卡器读到的第一件事就是验证UID是不是在白名单里。如果只是把数据和密钥复制到另一张卡上但UID对不上门禁照样不认识你。很多网上卖的“UID可写卡”就是为了解决这个问题它们允许你把任意UID值写入新卡的出厂区块。至于为什么手机直接模拟Mifare Classic经常失败原因是多方面的。Mifare Classic的认证协议没有公开授权而且使用了Crypto-1加密算法很多手机底层的NFC控制器芯片根本不对普通App开放Mifare Classic的卡模拟能力。就算你的手机硬件支持系统层面也不一定把相关接口开放给开发者。这时候就需要HCE出马配合能捕获APDU指令的读卡器自己实现一个软卡模拟。但更关键的是门禁系统用的是ISO 14443-4的Type A协议还是ISO 14443-3的Mifare Classic协议直接决定了你的手机能不能模拟它。很多老式门禁只认Mifare Classic手机却只能用ISO 14443-4的HCE两边讲话的方式不一样自然就“聊不到一块去”。1.3 搞清楚你要模拟的卡先做一次“体检”我强烈建议动手之前先把你手头那张门禁卡的信息完整读出来。因为不同卡片的协议类型、扇区加密情况都不一样后面选择的模拟方案完全不同。你可以在手机上装一个NFC Reader工具或者直接用下面第二部分的App代码把卡片靠近手机背面读取设备的ID、技术类型、支持的协议等一系列信息。拿到这些信息之后对照判断它属于Mifare ClassicIC卡还是ISO 14443-4的Type A卡还是支持NDEF格式的NFC标签。“体检”做得越细后面模拟方案就越明确。我自己踩过的坑是一开始以为所有门禁卡都是同一套规范结果手上的卡读出来是Mifare Classic 1K扇区0被加密携带了专有的数据格式。如果用HCE去模拟ISO 14443-4的虚拟卡门禁读卡器根本不会发起标准的APDU流程而是直接跑Mifare Classic认证协议这时HCE完全无能为力。所以别跳过这一步先把卡的类型确诊了再谈模拟方案。2. 开发环境准备与核心代码设计2.1 开发环境与权限配置无论你选哪种模拟方案开发环境都是固定的一台电脑、Android Studio、一台带NFC的真机。Android Studio版本用最新的稳定版就行不需要特别配置什么。真机建议选小米、华为、一加等市场主流机型不同厂商对NFC的支持略有差异但大体一致。在AndroidManifest.xml里先声明NFC权限和NFC特性uses-permission android:nameandroid.permission.NFC / uses-feature android:nameandroid.hardware.nfc android:requiredtrue /需要特别强调的是NFC属于正常权限不需要在运行时动态申请直接在Manifest里声明即可。但你必须保证手机上真的开启了NFC开关系统设置里的“NFC”选项没打开的话什么代码都白搭。为了让App在检测到NFC标签时自动被唤起还要在MainActivity里配置intent-filteractivity android:name.MainActivity android:launchModesingleTop intent-filter action android:nameandroid.nfc.action.TECH_DISCOVERED / /intent-filter meta-data android:nameandroid.nfc.action.TECH_DISCOVERED android:resourcexml/nfc_tech_filter / /activity然后在res/xml目录下新建nfc_tech_filter.xmlresources xmlns:xliffhttp://schemas.android.com/apk/res/android tech-list techandroid.nfc.tech.MifareClassic/tech techandroid.nfc.tech.NfcA/tech /tech-list /resources这样手机靠近门禁卡时系统会自动打开MainActivity并把NFC标签对象传进来。2.2 读取NFC卡片信息的核心代码读取卡片信息是整个项目的“体检”环节你需要通过这段代码拿到UID、协议类型、卡片容量等关键信息。下面这份MainActivity.java是我自己常用的版本兼容Kotlin和Java混合项目这里用Java写方便你直接对照。public class MainActivity extends AppCompatActivity { private NfcAdapter nfcAdapter; private TextView tvResult; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvResult findViewById(R.id.tv_result); nfcAdapter NfcAdapter.getDefaultAdapter(this); if (nfcAdapter null) { tvResult.setText(设备不支持NFC); return; } if (!nfcAdapter.isEnabled()) { tvResult.setText(请先在系统设置中开启NFC); } else { tvResult.setText(NFC已就绪请将卡片靠近手机背面); } } Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); handleTag(intent); } Override protected void onResume() { super.onResume(); if (nfcAdapter ! null) { nfcAdapter.enableForegroundDispatch( this, new PendingIntent( getActivity(), 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT ), new IntentFilter[]{}, new String[][]{ {NfcA.class.getName()}, {MifareClassic.class.getName()} } ); } } Override protected void onPause() { super.onPause(); if (nfcAdapter ! null) { nfcAdapter.disableForegroundDispatch(this); } } private void handleTag(Intent intent) { if (!NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) !NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()) !NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { return; } Tag tag intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag null) { tvResult.setText(没有读取到标签数据); return; } StringBuilder sb new StringBuilder(); sb.append(UID: ).append(bytesToHex(tag.getId())).append(\n); sb.append(Tag的技术列表:\n); for (String tech : tag.getTechList()) { sb.append( ).append(tech).append(\n); } MifareClassic mfc MifareClassic.get(tag); if (mfc ! null) { try { mfc.connect(); sb.append(卡片类型: ) .append(mfc.getType() MifareClassic.TYPE_CLASSIC ? Mifare Classic : 其他类型) .append(\n); sb.append(扇区数: ).append(mfc.getSectorCount()).append(\n); sb.append(块数: ).append(mfc.getBlockCount()).append(\n); sb.append(容量: ).append(mfc.getSize()).append( 字节\n); mfc.close(); } catch (IOException e) { sb.append(连接失败: ).append(e.getMessage()).append(\n); } } tvResult.setText(sb.toString()); } private String bytesToHex(byte[] bytes) { StringBuilder sb new StringBuilder(); for (byte b : bytes) { sb.append(String.format(%02X, b)); } return sb.toString(); } }注意代码里PendingIntent的两个标志位FLAG_MUTABLE是API 31之后必须加的不加会直接抛异常FLAG_ONE_SHOT要慎用如果加上这个标志读取器每次只能触发一次Intent第二次近距离触发时会失败。我因为这个坑调试了半天把FLAG_ONE_SHOT去掉就好了。R.layout.activity_main里只需要一个TextView用于显示结果这里不再占篇幅贴布局文件。2.3 HCE模式Android 4.4之后的官方卡模拟方案如果你确认自己的门禁卡走的是ISO 14443-4 Type A协议就可以用HCE实现软卡模拟。HCE的实现分两大部分一个是Service文件负责接收和处理外部的APDU指令另一个是APDU配置文件用来声明这张“虚拟卡”支持的应用IDAID。先定义一个CardEmulationService类public class CardEmulationService extends HostApduService { private static final String TAG CardEmuService; private static final byte[] SELECT_OK new byte[]{(byte) 0x90, 0x00}; private static final byte[] UNKNOWN_CMD new byte[]{0x6A, (byte) 0x82}; private static final byte[] AID new byte[]{ (byte) 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10 }; Override public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) { Log.d(TAG, 收到APDU指令: bytesToHex(commandApdu)); if (commandApdu.length 5 commandApdu[0] 0x00 commandApdu[1] (byte) 0xA4) { byte[] selectAid extractAid(commandApdu); if (selectAid ! null Arrays.equals(selectAid, AID)) { return SELECT_OK; } return UNKNOWN_CMD; } if (isReadCommand(commandApdu)) { byte[] blockData buildBlockData(); return buildReadResponse(blockData); } return UNKNOWN_CMD; } Override public void onDeactivated(int reason) { Log.d(TAG, 卡片模拟被取消reason reason); } private byte[] extractAid(byte[] commandApdu) { int aidLength commandApdu[4] 0xFF; byte[] aid new byte[aidLength]; System.arraycopy(commandApdu, 5, aid, 0, aidLength); return aid; } private boolean isReadCommand(byte[] commandApdu) { // 这里按实际门禁卡的指令格式判断多数Mifare读块指令是0x30开头的Binary Read return commandApdu[0] 0x30; } private byte[] buildBlockData() { byte[] block new byte[16]; // 在这里填充你希望返回给门禁读卡器的扇区数据 // 注意这必须是你有合法权限的数据 return block; } private byte[] buildReadResponse(byte[] blockData) { byte[] response new byte[blockData.length 2]; System.arraycopy(blockData, 0, response, 0, blockData.length); response[blockData.length] (byte) 0x90; response[blockData.length 1] 0x00; return response; } private String bytesToHex(byte[] bytes) { StringBuilder sb new StringBuilder(); for (byte b : bytes) { sb.append(String.format(%02X, b)); } return sb.toString(); } }看到buildBlockData方法里是空的你可能已经猜到要完整模拟一张卡你必须知道你那张合法卡片每个扇区的原始数据才能正确应答读卡器的读指令。对于加密的扇区你还要在App里实现对应的密钥认证流程否则读卡器要求你先认证再读块时你这边无法完成认证握手门禁系统就会认为通信失败。这也就是我在前面强调“你得先搞清楚自己卡片的加密情况”的另一个原因。非加密扇区的门禁卡HCE完全能搞定加密扇区被门禁系统写入了自定义数据的卡HCE的难度会一下子变得很高因为你需要完整实现对方的私有协议。2.4 APDU服务配置让系统知道你的虚拟卡存在光有Service还不够你需要在res/xml目录下创建apduservice.xml用来告诉系统这个Service要监听哪些AIDhost-apdu-service xmlns:androidhttp://schemas.android.com/apk/res/android android:descriptionstring/app_name android:requireDeviceUnlockfalse aid-group android:description门禁模拟 android:categoryother aid-filter android:nameA0000000031010 / /aid-group /host-apdu-service然后在AndroidManifest.xml里给Service加上intent-filter和meta-dataservice android:name.CardEmulationService android:exportedtrue android:permissionandroid.permission.BIND_NFC_SERVICE intent-filter action android:nameandroid.nfc.cardemulation.action.HOST_APDU_SERVICE / /intent-filter meta-data android:nameandroid.nfc.cardemulation.application android:resourcexml/apduservice / /serviceAID的值必须和代码里的一致并且要保证唯一性。门禁读卡器会优先尝试选择它自己规定的AID如果App里监听不到对应的AID整个卡模拟过程根本不会启动。这里有个疑惑点为什么HCE模拟Mifare Classic特别困难因为HCE走的是ISO 14443-4 Type A上层框架而Mifare Classic用的是ISO 14443-3层级的认证命令。门禁读卡器在发起Mifare Classic认证时触发的指令流不是标准APDU格式而是Mifare私有命令而这些命令底层是NFC控制器芯片直接处理的根本不会传递给HCE层的App进程。换句话讲HCE能模拟的卡是那些懂得“上层对话”的卡片而Mifare Classic这类有专有协议的卡App很难在纯软件层直接接管。3. 手把手实操从读取到模拟的全流程3.1 实操第一步读取卡片类型和UID到这里假设你已经把上面的读取代码跑起来了。把门禁卡贴到手机背面如果代码没问题屏幕上会显示类似这样的信息UID: 4A3B2C1D Tag的技术列表: android.nfc.tech.MifareClassic android.nfc.tech.NfcA android.nfc.tech.Ndef 卡片类型: Mifare Classic 扇区数: 16 块数: 64 容量: 1024 字节这段信息能解释很多问题。UID是你这张卡的身份证号后面模拟时大概率要用到。技术列表里能看到MifareClassic就说明它是经典的Mifare卡。如果你读到的是ISO 14443-4 Type A或者NfcB那就可能是新一代门禁卡模拟方式完全不同。还要多说一句有些NFC工具App也能读这些信息比如NFC Tools。如果你不想先写代码可以先用这类工具快速确诊卡片类型等确认自己的场景适合HCE后再上手写代码。3.2 实操第二步根据卡片类型选择模拟方案看完卡片信息你需要做一个决策。这里我把常见场景整理成一张表方便你对照选择卡片类型底层协议推荐模拟方案难度Mifare Classic 1K/4KS50/S70ISO 14443-3硬件写卡器写入UID卡 / 换支持模拟的手机高ISO 14443-4 Type A如DesfireISO 14443-4HCE卡模拟中高NFC Type 2如NTAG213ISO 14443-3HCE或NDEF模拟低公交卡等金融卡复杂私有协议不建议自行模拟极高你可能要问既然HCE只适合ISO 14443-4那大量使用Mifare Classic的老式门禁怎么办老实说普通App基本搞不定除非你的手机芯片原生支持Mifare Classic卡模拟目前很多主流手机并不开放这个能力。如果你确实有权限替换或复制自己的门禁卡最省心的路子是买一张UID可写卡配合读卡器把合法卡的数据克隆到新卡里再把新卡贴到手机壳里或者作为备用卡携带。这虽然不是“手机模拟”但也解决了实际问题。如果你的门禁读卡器支持ISO 14443-4或者你手里是新一代的公交卡、门禁卡那HCE就是最靠谱的选择。HCE方案的好处是不需要额外硬件一个App就搞定。3.3 实操第三步完整跑通HCE模拟流程HCE方案的关键在于APDU数据包的交互节奏具体流程如下。第一步把手机贴近门禁读卡器。此时读卡器可能会先发起“寻卡”流程你的手机NFC芯片以“虚拟卡”形式回应。第二步门禁读卡器发送SELECT AID指令比如00A40400 07 A0000000031010。这个指令翻译过来就是我选择应用ID为A0000000031010的卡片应用。你的CardEmulationService里的processCommandApdu会被调用并收到这串字节。第三步你返回90 00代表选择成功。如果返回6A82读卡器就知道这张虚拟卡不支持该应用后续操作直接中断。第四步读卡器发送READ BINARY指令如30开头读取你虚拟卡对应扇区块的数据。你要返回16字节的块数据并在末尾拼上90 00状态码。第五步读卡器实际验证数据。如果数据与门禁后台系统匹配闸机就开了。这里最考验人的是第四步。要正常应答你必须有合法卡片每个扇区块的明文数据。如果这些扇区没加密你可以用读卡器直接dump出来如果扇区加密了你就需要合法密钥而密钥本身大概率是门禁系统管理员下发的。我们需要在这里停下来因为盗取加密密钥和读取他人加密数据已经明显超出“模拟自己卡片”的合理边界。3.4 实操第四步NFC标签写入方案如果你的门禁卡信息可以复制而且你也确实只有一张卡的权限另一个思路是直接把卡数据写入一枚NFC标签把标签贴在手机壳里。这种做法其实比写完整Android App省事适用于NFC Type 2标签。写入时需要用到Ndef.writeNdefMessage接口示例代码private void writeNdefTag(Tag tag, String uid) { try { Ndef ndef Ndef.get(tag); if (ndef ! null) { ndef.connect(); NdefMessage msg new NdefMessage( NdefRecord.createUri(https://example.com/access/ uid) ); ndef.writeNdefMessage(msg); Toast.makeText(this, 写入成功, Toast.LENGTH_SHORT).show(); ndef.close(); } else { Toast.makeText(this, 该标签不支持NDEF写入, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { Toast.makeText(this, 写入失败: e.getMessage(), Toast.LENGTH_SHORT).show(); } }写NDEF格式的标签只能让具备NDEF解析能力的门禁系统识别。很多门禁系统根本不读NDEF只按自己私有格式读块数据。所以这个方案比较挑门禁系统真正适配场景反而不如HCE广。不过我见过一些创意玩法在自己的工位上贴一枚NDEF标签手机碰到就直接打开一个网页或者触发一个自动化流程这个倒很实用。它不算“门禁卡模拟”但属于NFC的合理应用扩展顺带提一下。4. 常见问题与排查技巧实录4.1 手机半天扫描不到卡片问题出在哪很多人第一步就卡住手机靠近门禁卡页面毫无反应。这种情况我先建议你按下面清单逐项排查手机NFC功能是否开启。在系统设置里搜索NFC确认开关是打开状态。卡片是否贴在手机背面偏上的位置。NFC天线一般在手机后盖上部靠近摄像头区域不是整个背面都能感应。系统是否弹出“不支持的标签”或“无法识别”的提示。如果提示不支持说明硬件层面对这张卡的技术类型不兼容。是不是贴了金属手机壳。金属壳会屏蔽NFC信号合成材质或者厚硅胶壳也可能影响感应距离。卡片本身损坏。这个容易忽略部分卡用久了内部线圈断裂读卡器都读不到更别说手机。按我自己的经验八成以上“读不到卡”都是NFC天线位置没对准。可以把卡片贴着手机背部慢慢平移找到最灵敏的位置记住那个位置后面每次都贴那里。4.2 模拟成功但门禁不认多半是这几个原因代码跑通、卡片也读到了但把手机放到门禁读卡器上就是不开门。这个阶段的排查我会先从通信层面做起。先确定门禁系统使用的协议是不是你的模拟方案覆盖得了的。如果读卡器是Mifare Classic模式而你的HCE走ISO 14443-4那两者根本不会建立连接读卡器会一直处于“找不到卡”的状态。再看AID是否正确。HCE要求虚拟卡的AID和门禁读卡器期望的AID一致。你用读卡器工具抓数据时能看到它发出来的SELECT命令拿这个命令里的AID和apduservice.xml里的AID对比一下。然后看数据内容是否完整。很多门禁卡在读卡器发起认证或读数据指令时你返回的是空数据或者错误的块数据门禁系统会认为这张卡没有权限。此时唯一的解决办法是拿到自己合法卡片的全部明文扇区数据照着数据去填充buildBlockData。还有一类隐性问题是时序。HCE是纯软件处理数据返回速度可能比物理卡片慢上几十毫秒甚至上百毫秒部分对时序敏感的门禁读卡器会判定通信超时。这种情况比较难调只能通过优化代码逻辑、减少日志输出、避免在主线程做耗时操作来尽量缩短响应时间。4.3 关于加密卡、UID兼容性我把话挑明前面多次提到加密扇区和UID这里专门总结几条避坑经验。第一别指望破解Mifare Classic的加密扇区。Crypto-1算法存在已知缺陷网上确实有工具可以在获得足够密文后破解密钥但这件事在绝大多数场景下既不合法也不合理。如果你的门禁卡是加密扇区找正规途径申请一张合法卡或者让管理员帮忙处理。第二UID不等于一切。很多门禁系统除了验证UID还会读特定扇区的数据只有UID长得一样但扇区数据完全不匹配的卡也会被拒之门外。所以“复制卡”这类操作重点不在UID而在完整数据。第三关于“中继攻击”这类概念做安全研究的人可能听过但这不是普通用户该碰的方向。利用NFC中继设备把读卡器信号和卡片信号拉远绕过门禁距离限制这种行为在正式门禁系统里属于破坏安全管理机制风险很大也扰乱安全秩序。本文所有方案都限定在合法使用自己的卡片不涉及也不探讨攻击性应用。第四手机厂商对NFC的底层封装程度不一样。同一个App在一台手机上能读Mifare Classic在另一台上可能根本读不到。这些差异根源于NFC控制器芯片和系统驱动层的实现。遇到这种问题换个手机测试往往比调代码更快见效。4.4 调试技巧用日志和数据包把问题放在明面上排查NFC问题时日志是最重要的线索。建议在processCommandApdu和handleTag里都加上Log.d输出把收到的每个字节都打印出来。Log.d(NFC_LOG, 收到APDU: bytesToHex(commandApdu));这样你就能看到门禁读卡器发过来的完整指令序列包括SELECT、READ、WRITE等。指令序列看清了模拟逻辑才有依据。还要提一个很多人忽略的点在真机上调试NFC模拟时不要同时开启其他NFC应用。比如系统自带钱包、支付宝的NFC碰一碰会在后台抢占卡模拟通道导致你的App收不到指令。调试前可以把不相关的NFC应用关掉把系统默认支付应用改成本App或不指定。我自己调试时习惯把App装在两台手机上一台只做读卡器用NFC Reader类工具持续发送指令另一台跑HCE服务。这样能分开观察两端的行为大大加快定位问题的速度。5. 从代码到可靠运行还需要这几步5.1 让App常驻后台避免被系统清理HCE服务本身不需要你打开App界面手机息屏状态下也能工作。但国产ROM的后台管理比较激进可能会把没有前台界面的Service杀掉。要保证模拟功能稳定你要做两件事。第一在MainActivity里调用startForegroundService启动一个前台服务并在通知栏显示常驻通知。这样系统会认为App正在持续运行降低被清理的概率。第二在系统设置里把App加入自启动白名单、后台运行白名单。不同品牌的设置路径不一样但基本都在“电池优化”或“应用管理”里能找到。代码示例Context context getApplicationContext(); Intent serviceIntent new Intent(context, CardEmulationService.class); if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); }并且要在CardEmulationService里重写onStartCommand返回START_STICKY让系统在内存紧张时尽量重建服务。5.2 多张门禁卡切换怎么做实际使用中你可能有几张不同的卡要模拟比如单元门卡、公司门禁卡。HCE支持配置多个AID但在同一时刻一张虚拟卡只能回应一个通信会话。你想切换不同卡比较实用的做法是在App里维护一张卡配置列表每个配置项对应一个AID和一套扇区数据。然后在selectAid时根据读卡器发来的AID匹配对应的卡配置返回相应的数据。这样只要不同门禁系统用的AID不同App就能自动识别并切换到对应的模拟内容。如果两张卡用的AID恰好相同那就只能手动切换了。这时候可以做一个通知栏快捷方式或桌面小组件用AppWidget控制在当前生效的卡之间切换。5.3 用户态细节App名称、图标和签名这种自用工具AppUI不需要太复杂。但有几个细节建议做好App名称起一个不显眼的名字比如“门禁工具”或者“NFC助手”。图标随意但不要用系统默认的机器人图标看起来太像半成品。签名要稳定别用debug签名凑合。因为后续每次安装更新都要同一个签名换签名就只能卸载重装配置的AID和后台白名单可能全部失效。签名操作在Android Studio的Build - Generate Signed App Bundle or APK里完成跟着引导走就行。如果App只给自己用直接选APK、选release、选V1和V2签名方案即可。5.4 精度提升的进阶方向模拟ISO 14443-4 Type A如果你的门禁系统是新一代设备支持ISO 14443-4 Type A那HCE是完全兼容的。此时你需要更细致地处理APDU交互不只是响应SELECT和READ还要处理套擦指令、块编号、数据长度等细节。建议多抓几次真卡和门禁之间的通信日志摸清指令规律后再逐条填充到processCommandApdu里。一个比较实用的小技巧是先用读卡器工具比如带NFC功能的开发板或者另一个Android手机装读卡App去读真卡把每次读块指令的位置、长度、返回数据全记录下来建立一张“地址表”。之后你在HCE里照着这张表响应即可。这个方法可能有点笨但在没有更多协议资料的情况下最管用。6. 关于手机模拟门禁卡我的几点体会折腾完这一圈我很清楚一点手机模拟门禁卡这件事真正的瓶颈从来不是写代码而是读卡器和你手里那张卡之间的协议是否兼容。HCE的机制设计得很好但它不是万能的遇到Mifare Classic这类私有协议卡只能另想办法。如果让我给后来者一个最直接的顺序建议大概是先读卡确认类型再评估门禁读卡器协议然后选HCE或者硬件方案最后才动手写代码。跳过前面几步直接写代码大概率会白费力气。从实际角度看这项技术最实用的地方在于当你有权利使用某一张卡、却经常忘带卡的时候把手机变成一张备用卡应付日常进出非常方便。我自己的卡片是ISO 14443-4的Type A格式用HCE模拟之后现在基本告别实体卡了上下电梯、走门禁掏出手机一贴就过偶尔手机没电的时候才会老老实实翻工牌。最后再分享一个小经验HCE服务调试时尽量别在同一个App里同时跑前台服务和卡模拟服务有些机型会把前台通知和卡模拟状态搞混导致贴在读卡器上没反应。把前台通知的文案写得明确一些比如“NFC门禁服务运行中”这样系统界面上一眼就能看出服务是否正常排查起来省很多事。行就写到这里。你如果真卡在某个环节手头有具体的卡片类型信息和代码日志可以顺着这篇文章里的思路再排查一遍。折腾NFC本身就是个边试边学的活耐心一点多抓数据多打Log总会找出方案的。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。