Android Studio NFC读取CPU卡信息

Android手持机PAD IC卡读写、RFID及PSAM卡验证DEMO 基本寻卡界面可寻到14443A(包含CPU卡)类型卡和15693类型卡,点击开始读卡则可读取天线附近的卡,再次点击停止读卡。更多操作包括:基本寻卡、M1卡、15693卡、PSAM卡、CPU卡、关于软件、退出功能。读块,起始块号0, 块数为1,也就是读一个块的数据,如果要读2个块的数据,只需要在块数那里输入2即可。15693卡有如下操作:寻卡、选卡、读、写,流程为:寻卡--选卡---读/写。m1卡操作流程为寻卡--认证--读/写,以块为单位进行读写。cpu卡操作有三项:激活卡片、发送APDU指令、关闭卡片。 阅读详情

今天接到一个NFC读CPU卡的需求,做个总结。

总结之前,吐槽一下,一定要查一下测试机支不支持NFC功能,我拿乐视S3开发了一上午,以为哪里出了BUG或者权限没加,查了无数资料,下了十几个demo,死活就是连不上NFC,下午换了小米8se来测,发现还是连不上。。。万念俱灰之际,找了同事的小米MIX2来测,发现可以了!!!!!!!!!!

测试之前一定要查一下自己的测试机支不支持NFC功能!!!!!!!!
测试之前一定要查一下自己的测试机支不支持NFC功能!!!!!!!!
测试之前一定要查一下自己的测试机支不支持NFC功能!!!!!!!!

言归正传,上代码

步骤一:Mainfext中添加权限

<uses-permission android:name="android.permission.NFC" />

在activity中添加

 <activity
            android:name=".NfcActivity"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED" />
            </intent-filter>

            <meta-data
                android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/tag_type" />

        </activity>

在res文件夹中新建一个xml文件夹,取名叫:tag_type

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
</resources>


主activity中

package com.soullistener.nfcdemo;

import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;

import java.io.IOException;

public class NfcActivity extends AppCompatActivity {
    private NfcAdapter mNfcAdapter;
    private Tag mTag;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mNfcAdapter = M1CardUtils.isNfcAble(this);
        M1CardUtils.setPendingIntent(PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()), 0));
        mTag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);

        TextView textView = findViewById(R.id.tv_content);
        textView.setMovementMethod(ScrollingMovementMethod.getInstance());
        //M1卡类型
        findViewById(R.id.btn_read_m1).setOnClickListener(v -> {
            if (M1CardUtils.hasCardType(mTag, this, "MifareClassic")) {
                try {
                    StringBuilder stringBuilder = new StringBuilder();
                    String[][] m1Content = M1CardUtils.readCard(mTag);
                    for (int i = 0; i < m1Content.length; i++) {
                        for (int j = 0; j < m1Content[i].length; j++) {
                            stringBuilder.append(m1Content[i][j]+"\n");
                        }
                    }
                    textView.setText(stringBuilder.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        //CPU
        findViewById(R.id.btn_read_cpu).setOnClickListener(v->{
            if (M1CardUtils.hasCardType(mTag, this, "IsoDep")) {
                try {
                    textView.setText(M1CardUtils.readIsoCard(mTag));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        mNfcAdapter = M1CardUtils.isNfcAble(this);
        M1CardUtils.setPendingIntent(PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()), 0));
        Log.e("onNewIntent","onNewIntent");
        mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(this);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mNfcAdapter != null) {
            mNfcAdapter.enableForegroundDispatch(this, M1CardUtils.getPendingIntent(),
                    null, null);
        }
    }

}

添加两个工具类

package com.soullistener.nfcdemo;

import android.app.Activity;
import android.app.PendingIntent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;

public class M1CardUtils {

    private static PendingIntent pendingIntent;
    public static PendingIntent getPendingIntent(){
        return pendingIntent;
    }

    public static void setPendingIntent(PendingIntent pendingIntent){
        M1CardUtils.pendingIntent = pendingIntent;
    }

    /**
     * 判断是否支持NFC
     * @return
     */
    public static NfcAdapter isNfcAble(Activity mContext){
        NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
        if (mNfcAdapter == null) {
            Toast.makeText(mContext, "设备不支持NFC!", Toast.LENGTH_LONG).show();
        }else if (!mNfcAdapter.isEnabled()) {
            Toast.makeText(mContext, "请在系统设置中先启用NFC功能!", Toast.LENGTH_LONG).show();
        }

        return mNfcAdapter;
    }

    /**
     * 监测是否支持cardType类型卡
     * @param tag
     * @param activity
     * @param cardType
     * @return
     */
    public static boolean hasCardType(Tag tag,Activity activity,String cardType){

        if (tag == null){
            Toast.makeText(activity,"请贴卡",Toast.LENGTH_LONG).show();
            return false;
        }

        String[] techList = tag.getTechList();

        boolean hasCardType = false;
        for (String tech : techList) {
            Log.e("TagTech",tech);
            if (tech.contains(cardType)) {
                hasCardType = true;
                break;
            }
        }

        if (!hasCardType) {
            Toast.makeText(activity, "不支持"+cardType+"卡", Toast.LENGTH_LONG).show();
        }

        return hasCardType;
    }

    /**
     * CPU卡信息读取
     * @param tag
     * @return
     * @throws IOException
     */
    public static String readIsoCard(Tag tag) throws IOException {
        IsoDep isoDep = IsoDep.get(tag);
        if (!isoDep.isConnected()){
            isoDep.connect();
        }

        String result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00A40000023F00")));
        Log.e("readIsoCard",result);
        result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00A40000020005")));
        Log.e("readIsoCard",result);
        result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00B0000016")));
        Log.e("readIsoCard",result);
        isoDep.close();
        return result;
    }

    /**
     * M1读取卡片信息
     * @return
     */
    public static String[][] readCard(Tag tag)  throws IOException{
        MifareClassic mifareClassic = MifareClassic.get(tag);
        try {
            mifareClassic.connect();
            String[][] metaInfo = new String[16][4];
            // 获取TAG中包含的扇区数
            int sectorCount = mifareClassic.getSectorCount();
            for (int j = 0; j < sectorCount; j++) {
                int bCount;//当前扇区的块数
                int bIndex;//当前扇区第一块
                if (m1Auth(mifareClassic,j)) {
                    bCount = mifareClassic.getBlockCountInSector(j);
                    bIndex = mifareClassic.sectorToBlock(j);
                    for (int i = 0; i < bCount; i++) {
                        byte[] data = mifareClassic.readBlock(bIndex);
                        String dataString = bytesToHexString(data);
                        metaInfo[j][i] = dataString;
                        Log.e("获取到信息",dataString);
                        bIndex++;
                    }
                } else {
                    Log.e("readCard","密码校验失败");
                }
            }
            return metaInfo;
        } catch (IOException e){
            throw new IOException(e);
        } finally {
            try {
                mifareClassic.close();
            }catch (IOException e){
                throw new IOException(e);
            }
        }
    }

    /**
     * 改写数据
     * @param block
     * @param blockbyte
     */
    public static boolean writeBlock(Tag tag, int block, byte[] blockbyte) throws IOException {
        MifareClassic mifareClassic = MifareClassic.get(tag);
        try {
            mifareClassic.connect();
            if (m1Auth(mifareClassic,block/4)) {
                mifareClassic.writeBlock(block, blockbyte);
                Log.e("writeBlock","写入成功");
            } else {
                Log.e("密码是", "没有找到密码");
                return false;
            }
        } catch (IOException e){
            throw new IOException(e);
        } finally {
            try {
                mifareClassic.close();
            }catch (IOException e){
                throw new IOException(e);
            }
        }
        return true;

    }

    /**
     * 密码校验
     * @param mTag
     * @param position
     * @return
     * @throws IOException
     */
    public static boolean m1Auth(MifareClassic mTag,int position) throws IOException {
        if (mTag.authenticateSectorWithKeyA(position, MifareClassic.KEY_DEFAULT)) {
            return true;
        } else if (mTag.authenticateSectorWithKeyB(position, MifareClassic.KEY_DEFAULT)) {
            return true;
        }
        return false;
    }

    private static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        char[] buffer = new char[2];
        for (int i = 0; i < src.length; i++) {
            buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
            buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
            System.out.println(buffer);
            stringBuilder.append(buffer);
        }
        return stringBuilder.toString();
    }
}

第二个

package com.soullistener.nfcdemo;

import java.io.UnsupportedEncodingException;
/**
 *
 * @description 字符串处理工具类
 */
public class StringUtil {

    /**
     * 判断字符串是否为空
     *
     * @param value
     * @return
     */
    public static boolean isEmpty(String value) {
        if (value != null && !"".equalsIgnoreCase(value.trim())
                && !"null".equalsIgnoreCase(value.trim())) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 十六进制的ASCII码转字符串
     * @param hex
     * @return
     */
    public static String convertHexToString(String hex){
        StringBuilder sb = new StringBuilder();
        StringBuilder temp = new StringBuilder();
        for( int i=0; i<hex.length()-1; i+=2 ){
            String output = hex.substring(i, (i + 2));
            int decimal = Integer.parseInt(output, 16);
            sb.append((char)decimal);
            temp.append(decimal);
        }
        return sb.toString();
    }

    /**
     * 字节数组转十六进制字符串
     * */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 16进制转ascii
     * @param string
     * @return
     */
    public static String converStringtoAscll(String string){
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0;i<string.length();i++){
            stringBuilder.append((int)string.charAt(i));
        }
        return stringBuilder.toString();
    }

    /**
     *
     * @param hexString
     * @return 将十六进制转换为字节数组
     */
    public static String hexStr =  "0123456789ABCDEF";
    public static byte[] hexStringToBinary(String hexString){
        int len = hexString.length()/2;
        byte[] bytes = new byte[len];
        byte high = 0;
        byte low = 0;

        for(int i=0;i<len;i++){
            high = (byte)((hexStr.indexOf(hexString.charAt(2*i)))<<4);
            low = (byte)hexStr.indexOf(hexString.charAt(2*i+1));
            bytes[i] = (byte) (high|low);
        }
        return bytes;
    }

    /**左补指定元素*/
    public static String fillLeft(String value, String element, int count){
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<count - value.length();i++){
            sb.append(element);
        }
        sb.append(value);
        return sb.toString();
    }

    /**右补指定元素*/
    public static String fillRight(String value, String element, int count){
        StringBuffer sb = new StringBuffer();
        sb.append(value);
        for(int i=0;i<count - value.length();i++){
            sb.append(element);
        }
        return sb.toString();
    }

    //10进制转16进制
    public static String IntToHex(int n){
        char[] ch = new char[20];
        int nIndex = 0;
        while ( true ){
            int m = n/16;
            int k = n%16;
            if ( k == 15 )
                ch[nIndex] = 'F';
            else if ( k == 14 )
                ch[nIndex] = 'E';
            else if ( k == 13 )
                ch[nIndex] = 'D';
            else if ( k == 12 )
                ch[nIndex] = 'C';
            else if ( k == 11 )
                ch[nIndex] = 'B';
            else if ( k == 10 )
                ch[nIndex] = 'A';
            else
                ch[nIndex] = (char)('0' + k);
            nIndex++;
            if ( m == 0 )
                break;
            n = m;
        }
        StringBuffer sb = new StringBuffer();
        sb.append(ch, 0, nIndex);
        sb.reverse();
//        String strHex = new String("0x");
//        strHex += sb.toString();
        return sb.toString();
    }

    // 16进制转10进制
    public static int HexToInt(String strHex) {
        int nResult = 0;
        if (!IsHex(strHex))
            return nResult;
        String str = strHex.toUpperCase();
        if (str.length() > 2) {
            if (str.charAt(0) == '0' && str.charAt(1) == 'X') {
                str = str.substring(2);
            }
        }
        int nLen = str.length();
        for (int i = 0; i < nLen; ++i) {
            char ch = str.charAt(nLen - i - 1);
            try {
                nResult += (GetHex(ch) * GetPower(16, i));
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return nResult;
    }

    // 计算16进制对应的数值
    public static int GetHex(char ch) throws Exception {
        if (ch >= '0' && ch <= '9')
            return (int) (ch - '0');
        if (ch >= 'a' && ch <= 'f')
            return (int) (ch - 'a' + 10);
        if (ch >= 'A' && ch <= 'F')
            return (int) (ch - 'A' + 10);
        throw new Exception("error param");
    }

    // 计算幂
    public static int GetPower(int nValue, int nCount) throws Exception {
        if (nCount < 0)
            throw new Exception("nCount can't small than 1!");
        if (nCount == 0)
            return 1;
        int nSum = 1;
        for (int i = 0; i < nCount; ++i) {
            nSum = nSum * nValue;
        }
        return nSum;
    }

    // 判断是否是16进制数
    public static boolean IsHex(String strHex) {
        int i = 0;
        if (strHex.length() > 2) {
            if (strHex.charAt(0) == '0'
                    && (strHex.charAt(1) == 'X' || strHex.charAt(1) == 'x')) {
                i = 2;
            }
        }
        for (; i < strHex.length(); ++i) {
            char ch = strHex.charAt(i);
            if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
                    || (ch >= 'a' && ch <= 'f'))
                continue;
            return false;
        }
        return true;
    }

    /**
     * 转字符串成十六进制的ASCII
     * @param str
     * @return
     */
    public static String convertStringToHex(String str){
        char[] chars = str.toCharArray();
        StringBuffer hex = new StringBuffer();
        for(int i = 0; i < chars.length; i++){
            hex.append(Integer.toHexString((int)chars[i]));
        }
        return hex.toString();
    }

    /**
     * 十六进制字符串转换成bytes
     *
     * @param hexStr
     * @return
     */
    public static byte[] hexStr2Bytes(String hexStr) {
        int l = hexStr.length();
        if (l % 2 != 0) {
            StringBuilder sb = new StringBuilder(hexStr);
            sb.insert(hexStr.length() - 1, '0');
            hexStr = sb.toString();
        }
        byte[] b = new byte[hexStr.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hexStr.charAt(j++);
            char c1 = hexStr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }

    private static int parse(char c) {
        if (c >= 'a')
            return (c - 'a' + 10) & 0x0f;
        if (c >= 'A')
            return (c - 'A' + 10) & 0x0f;
        return (c - '0') & 0x0f;
    }

    /**解析44域汉字错误信息描述*/
    public static String parseErrorMsg(String msg){
        String parseStr = null;
        try {
            parseStr = new String(hexStringToByte(msg.toUpperCase()),"GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return parseStr;
    }

    public static byte[] hexStringToByte(String hex) {
        int len = hex.length() / 2;
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();

        for(int i = 0; i < len; ++i) {
            int pos = i * 2;
            result[i] = (byte)(toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }

        return result;
    }

    private static byte toByte(char c) {
        byte b = (byte)"0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**格式姓名*/
    public static String formateName(String name){
        String nameStr = "";
        int length = name.length();
        if(length == 2){
            nameStr = name.substring(0,1);
            nameStr += "*";
        }else if(length == 3){
            nameStr = name.substring(0,1);
            nameStr += "**";
        }

        return nameStr;
    }

    /**
     * 16进制数字取反
     *
     * @param hexString
     * @return
     */
    public static String hexBalanceNo(String hexString) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < hexString.length(); i++) {
            stringBuilder.append(Integer.toHexString(~Integer.valueOf(hexString, 16)).substring(7, 8));
        }
        return stringBuilder.toString();
    }

    /**
     * 16进制数字取反
     *
     * @param hexString
     * @return
     */
    public static String hexNo(String hexString) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < hexString.length(); i++) {
            String noHex = Integer.toHexString(~Integer.valueOf(String.valueOf(hexString.charAt(i)), 16));
            stringBuilder.append(noHex.substring(7, 8));
        }
        return stringBuilder.toString();
    }

    public static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[3] = (byte) ((value >> 24) & 0xFF);
        src[2] = (byte) ((value >> 16) & 0xFF);
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * 十六进制转int 大端模式
     *
     * @param hex
     * @return
     */
    public static int hex2Int(String hex) {
        byte[] bytes = hex2Bytes(hex);
        int len = bytes.length;
        int rec = 0;
        for (int i = 0; i < len; i++) {
            int temp = bytes[i] & 0xff;
            int off = (len - 1 - i) * 8;
            rec |= (temp << off);
        }
        return rec;
    }

    /**
     * 十六进制转Byte
     *
     * @param hexStr
     * @return
     */
    public static byte[] hex2Bytes(String hexStr) {
        int len = hexStr.length();
        if (len % 2 != 0) {
            throw new RuntimeException("length error");
        }
        byte[] b = new byte[hexStr.length() / 2];
        int bLen = b.length;
        int j = 0;
        for (int i = 0; i < bLen; i++) {
            char c0 = hexStr.charAt(j++);
            char c1 = hexStr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }
}

车载NFC技术解析:从原理到Android实现与安全防御 近场通信(NFC)是一种基于13.56MHz的短距离无线技术,依靠电磁感应实现厘米级数据交换。凭借“近”与“低功耗”的天然属性,NFC在身份认证领域具有独特优势,尤其在智能汽车中,已成为数字钥匙、无钥匙进入、驾驶员识别和车载支付等场景的关键技术。本文从NFC工作原理与协议体系出发,梳理车载NFC的硬件架构与软件栈,并结合Android平台演示标签读写、NDEF解析与HCE卡模拟的工程实现。同时针对业界关注的中继攻击与卡片克隆风险,提出多层防护设计与安全建议,帮助开发者构建安全可靠的车载近场通信方案。 阅读详情

相关推荐

基于XIAO nRF54LM20A Sense的NFC开发实战:从原理到应用

近场通信(NFC)作为一种短距离、低功耗的无线通信技术,其核心原理基于13.56MHz频率的磁场感应耦合,支持读卡器、卡模拟和点对点三种工作模式。这项技术的工程价值在于简化了设备间的安全数据交换流程,极大降低了嵌入式设备集成无线通信功能的硬件复杂度和开发门槛。在物联网和嵌入式开发领域,NFC广泛应用于智能门禁、移动支付、设备快速配对及无源传感数据采集等场景。本文以Seeed Studio XIAO nRF54LM20A Sense开发板为例,深入解析其集成的NFC控制器与天线设计,并通过实战代码演示如何利用

weixin_34072637的博客 336

Android NFC标签 开发深度解析 触碰的艺术

本文转自:http://blog.youkuaiyun.com/smartbetter/article/details/53173217 有几天没有更新博客了,不过本篇却准备了许久,希望能带给每一位开发者最简单高效的学习方式。废话到此为止,下面开始正文。  NFC(Near Field Communication,近场通信)是一种数据传输技术。与Wi-Fi、蓝牙、红外线等数据传输技术的一个主要差异就是

BillyLu的博客 2723

基于XIAO nRF52840的NFC标签开发:从原理到实践

近场通信(NFC)作为一种短距离无线通信技术,通过电磁感应实现设备间的非接触式数据交换。其核心原理基于13.56MHz射频场耦合,支持读写器与标签两种模式,遵循ISO/IEC 14443标准。在物联网和嵌入式领域,NFC技术因其低功耗、高安全性和便捷的触碰交互特性,成为设备快速配网、身份认证和数据传输的理想选择。通过NDEF数据交换格式,NFC能够封装文本、URI、智能海报等多种类型信息,实现跨平台兼容。本文以Seeed Studio XIAO nRF52840开发板为例,深入解析其内置NFCT模块的硬件架

weixin_30321709的博客 367

Android Studio安卓读写NFC Ntag标签源码

Android Studio安卓读写NFC Ntag标签源码,读取标签UID、签名信息、计数器、设置静态锁、设置标签密钥保护机制、轻松读取标签内数据、轻松写数据。

1714

近场通信技术车载集成:离线兜底与Android实现

近场通信技术是一种工作于13.56MHz频段的短距离无线通信技术,通过物理接触式的数据交换,在身份认证场景中具有独特优势。与蓝牙、UWB等方案相比,NFC无需配对、不受网络影响,甚至在手机低电量关机时仍可用于刷卡操作,因此成为数字钥匙体系中不可或缺的离线兜底通道。在车载场景中,NFC主要支持读写模式、卡模拟模式与点对点模式,其中卡模拟模式常用于车门解锁、车辆启动、共享汽车授权等应用。基于Android平台的NFC标签读写与HCE卡模拟开发,能够帮助开发者快速实现车钥匙原型,验证从标签读取到APDU指令响应的

weixin_34294649的博客 377

XIAO nRF52840 NFC标签模拟实战:从原理到动态应用开发

NFC(近场通信)作为一种短距离无线通信技术,其核心原理基于13.56MHz频率的电磁感应耦合,实现设备间的非接触式数据交换。在物联网和嵌入式开发领域,NFC标签模拟技术因其低功耗、便捷交互的特点,展现出重要的技术价值,广泛应用于智能设备配网、身份识别与场景触发等应用场景。本文聚焦于Seeed Studio XIAO nRF52840开发板,深入解析其内置的NFCT(NFC标签前端)外设,指导开发者利用Arduino环境,实现符合NFC Forum Type 2 Tag标准的标签模拟。通过结合Adafrui

340

Android HCE实战:手机模拟NFC标签实现碰一碰数据传输

NFC数据交换通常依赖物理标签或写卡器,而Android HCE(主机卡模拟)技术让手机本身就能模拟NFC卡,动态生成NDEF消息。其原理是通过注册AID、响应APDU指令,在ISO-DEP协议上返回标准NDEF字节流,实现无需网络和硬件的点对点通信。这项技术的价值在于零成本、高灵活性和跨设备兼容性,适用于设备配网、内部工具传参和演示环境数据分发等场景。本文深入解析AID注册、APDU分片与NDEF构造的完整实现,并分享多机型兼容性调试经验,帮助开发者快速落地HCE方案。

weixin_33957648的博客 221

基于ESP32-C6与NFC的智能名片设计:硬件选型、低功耗与交互实现

近场通信(NFC)作为一种短距离无线通信技术,其原理基于射频识别,允许设备在非接触状态下进行数据交换。这项技术的核心价值在于简化设备配对与信息传输流程,提升交互的便捷性与效率。在物联网和嵌入式开发领域,NFC常与微控制器结合,用于智能门禁、移动支付、设备配对等场景。惯性测量单元(IMU)则通过加速度计、陀螺仪等传感器感知物体的运动姿态,其数据融合与校准是确保测量精度的关键,广泛应用于机器人、无人机和可穿戴设备中。本文将NFC与IMU技术融入日常载体,以智能名片为例,探讨如何在极致尺寸与功耗约束下,通过ESP

weixin_34332905的博客 754

Android HCE 模拟 NFC 标签实现跨设备 NDEF 数据共享

NFC 标签在 Android 设备上常因兼容性问题导致读取不稳定,而 HCE(主机卡模拟)技术通过软件模拟物理标签,实现了动态数据响应。其原理基于 APDU 指令交互和 AID 路由机制,在 HostApduService 中处理标准指令,从而支持跨设备 NDEF 数据共享。这项技术为物联网设备配网、身份凭证传递和离线数据交换等场景提供了便捷、安全的解决方案。

weixin_34409357的博客 258

VersaLogic Android评估套件解析:从AOSP到工业嵌入式实战

在工业嵌入式领域,Android正从消费端走向工控端,其底层Linux内核与成熟应用框架的结合,让设备交互、生态复用和开发效率获得质的提升。AOSP作为开源底座,配合BSP定制、驱动适配和系统裁剪,构成了一套完整的技术链路。x86平台跑Android在性能与外设兼容性上具备独特优势,尤其适合工业HMI、医疗终端、边缘计算网关等场景。面对具体项目,开发人员还需掌握fastboot烧录、ADB调试、SELinux策略配置、OTA升级等实操要点。VersaLogic推出的Android Demo/Eval Kit

weixin_30800807的博客 268

Android NFC公交卡数据读取实战

在开发Android应用之前,安装和配置一个高效的开发环境是至关重要的。Android Studio是官方推荐的开发环境,它集成了Android SDK并提供了丰富的开发工具。安装过程相对简单,但正确配置可以避免后续开发中出现的问题。在安装Android Studio之前,请确保您的计算机满足以下最低要求:- 操作系统:Windows 7/8/10(32位或64位)、macOS 10.8.5或更高版本、Linux(64位)

weixin_42323064的博客 1333

DouyinLiveRecorder 完整指南:40+平台直播录制软件的配置方法与排坑思路

昨晚十点,你惦记的主播开播了,可你人在洗澡。等刷到提醒,这场两小时的分享已经播完,而平台回放随时可能下架。手机录屏画质差、还发烫,手动点“下载”根本来不及——这类“错过就没了”的内容,其实可以交给软件替你盯着。DouyinLiveRecorder 正是一款可循环值守的多平台直播录制软件:把直播间地址填进配置文件后,它会自动监测开播、解析直播源、调用 FFmpeg 把视频完整存到本地 `downlo

gitblog_00323的博客 227

android NFC (读取M1卡内容)

android,需要手机有NFC功能,可以读写M1卡,获得UID

Android HCE门禁卡模拟全解析:从协议原理到APDU实战踩坑

NFC技术早已融入日常生活的方方面面,从移动支付到智能门禁,手机替代实体卡已成为趋势。在Android平台上,主机卡模拟(HCE)是一项关键能力,它允许应用在缺少安全芯片的情况下,通过软件层直接响应读卡器的指令,让手机扮演一张CPU卡。然而,HCE并非万能,它基于ISO 14443-4协议工作,与大量门禁系统使用的Mifare Classic(ISO 14443-3 + NXP私有协议)并不兼容。理解APDU指令交互、AID路由规则以及系统配置细节,是评估HCE方案可行性的基础。对于开发者与硬件选型工程师而

weixin_34409357的博客 575

Android studio RFID NFC读写源码

1、读取ID卡卡号; 2、读写M1IC卡扇区数据、修改卡片密钥、写入UID 3、读写15693协议icode2卡片 4、读写NXP Ntag卡片 5、读写低频T5557卡片 6、读写低频em4305卡片 7、读写低频em4469卡片 8、读写NDEF智能海报

android_nfc_demo.zip_AndroidBeamDemo_Androidnfc巡考_NFC P2P_NFC an

Android-NFC P2P学习源码,非常实用的~

安卓系统从底层架构到开发适配全面解析

安卓系统基于Linux内核与Binder通信机制构建,通过HAL层屏蔽硬件差异,形成从内核到应用的五层体系。随着版本演进,分区存储、动态权限、前台服务类型等限制不断收紧,开发者需要理解系统行为变化才能避免适配陷阱。同时,国产ROM的深度定制带来了后台调度、推送通道等方面的额外差异。掌握版本演进规律、架构分层逻辑和本地化兼容策略,是移动开发者和系统工程师高效完成多版本适配、提升应用稳定性的关键出发点。本文围绕这些核心机制与工程实践展开,提供系统性的认知框架和可落地的排查方法。

weixin_33843947的博客 1577

警惕!数字签名伪造的攻防演练:用SigThief克隆证书的真实测试(Windows环境)

本文深入探讨了Windows环境下数字签名伪造的攻防技术,详细介绍了使用SigThief工具进行签名提取与克隆的实战步骤。通过真实测试案例,揭示了数字签名安全机制的潜在漏洞,并提供了有效的防御策略和检测技术,帮助安全人员提升系统防护能力。

weixin_42530732的博客 408

前端直连Android硬件:WebSocket网关架构实战

WebUSB尚不成熟,而直接在前端调用Android SDK(如ADK)会遭遇ClassLoader隔离、Context生命周期绑定和线程模型冲突等系统级限制。本文聚焦‘前端与Android设备通信’这一基础工程问题,解析如何绕过SDK移植困境,将Android降级为轻量通信网关,通过本地WebSocket实现低延迟、高可靠的数据帧透传。技术价值在于解耦平台依赖、规避权限死锁、统一事件协议;典型应用于IoT管理后台、工业扫码Web控制台及Android工控板远程交互场景。核心实践涵盖USB/NFC设备发现、

372

PaddleOCR在医疗票据识别中的实战:从部署到业务字段提取

本文详细介绍了PaddleOCR在医疗票据识别中的实战应用,重点阐述了其离线部署方案与PP-OCRv3模型选型优势。针对医疗票据敏感数据不出本地的合规要求,文章提供了从环境搭建、模型配置到核心字段提取策略的完整流程,并分享了性能优化与系统集成的实践经验,有效解决了医疗票据自动化录入的难题。

weixin_29271053的博客 236

Apollo Planning2.0决策规划算法代码详细解析 (1):环境搭建

1、官方提供连接:git clone https://github.com/ApolloAuto/application-pnc.git。此外,也更新了dreamview对pnc的支持,使得调试更加方便。apollo开源团队近期更新了planning版本,对代码进行了一定程度上的重构。(2)进入docker,并编译整个工程:./apollo.sh build_dbg。下载项目环境后使用aem进行环境配置;2、有一定基础建议直接拉取最新代码,自己手动进行编译。(1)环境安装: ./dev_start.sh。

自动驾驶Player的博客 1841
上一篇: Android Studio string字符串大小写转换
下一篇: Android studio 按钮按压效果
书信一封~
博客等级 码龄10年 11粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值