Android NfcManager 之NFC接入与介绍

2023-04-12


NFC:全称是近场通信(Near Field Communication),是一种短距离无线技术


Android Beam是一个基于近场通信所做的新功能,这个功能可以为其他手机分享你正在使用的功能。 Android升级到4.1后,Android Beam现在可以在两台支持NFC的Android设备间分享照片和视频,还可以与支持NFC的蓝牙设备相连。


Android NFC同时支持三个主要的操作模式:

1.设备读/写模式,允许NFC设备的读/写NFC目标设备;


2.P2P模式,使NFC设备与其他NFC节点交换数据;这种运作模式被使用在Android Beam中;


3/卡仿真模式,使NFC设备本身作为一个NFC卡。然后模拟NFC卡可以通过一个外部的NFC读写访问,如销售终端NFC点。


4.NDEF数据

从NFC便签读取NDEF格式的数据
向NFC标签写入NDEF格式的数据
通过Android Beam技术将NDEF数据发送到另一部NFC设备


5.NFC的三重过滤机制intent-filter

两个终端设备要想读写数据,会有个短暂配对的时间,数据接收端会根据具体的数据格式和标签类型调用相应的Activity(Tag Dispatch),这个activity需要定义一个intent filter中指定不同的过滤机制,分三个等级,所以叫NFC的三重过滤机制


5.1NDEF_DISCOVERED

只过滤固定格式的NDEF数据,比如纯文本,指定协议(HTTP FTP SMB等)的URI


5.2TECH_DISCOVERED

当ACTION_NDEF_DISCOVERED指定的过滤机制无法匹配Tag时,就会使用这种过滤机制进行匹配,这种过滤机制并不是通过Tag的数据进行匹配的,而是根据Tag支持的数据存储格式进行匹配,因此这种机制使用范围很广


5.3TAG_DISCOVERED

这种机制用来处理未识别的Tag


接入流程:


1.首选NFC依赖硬件,这个就需要权限支持

外获取NFC设备数据需要在内添加如下内容



    


nfc_tech_filter是在res/xml文件下的自定义xml文件:




    
        android.nfc.tech.IsoDep
    
    
        android.nfc.tech.NfcA
    

2.三种模式的Demo运行


NFC的数据NfcAdapter来管理的,NfcAdapter有两种途径获取



NfcManager manager=(NfcManager)getSystemService(Context.NFC_SERVICE);
NfcAdapter adapter=  manager.getDefaultAdapter();



这是通过NFCmanager获取,



NfcManger的构造器 也是通过NcfAdapter.getNfcAdapter(Context)获取adapter的实例。


同理:我们也可以直接获取NfcAdapter,不通过NfcManger来获取。


NcfAdapter adapter=NfcAdapter.getDefaultAdapter(Context context)



这个adapter其实也是需要通过NfcManager来获取,直接调用静态方法,针对不熟悉getSystemService来说,可以直接使用封装现成的。


adapter的内部会有一个:


static HashMap sNfcAdapters = new HashMap(); //guard by NfcAdapter.class


,所以NFC是独立于Activity,因为adapter内部有一个静态变量,会把当前山下文使用的nfcadapter缓存起来。


2.Tag接收页面的启动模式


    
        AIN" />

        

        
    

    

launchMode:因为NFC是一个独立特性,所有页面启动需要保持一个,singleTask或者




singleTop




NdefMessage:

主要是描写叙述NDEF格式的信息


NdefRecord:

这个是秒速NDEF信息的一个信息段


这两个都是Android NCF技术的核心类,不管是读写NFC标签还是通过Android Beam技术传递数据都须要这两个类


小牛刀


1.获取Tag对象

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);


正常该intent通过Activity这个方法获取:


protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    
}

因为任何页面被打开多少次,都会执行onNewIntent方法


2.推断NFC标签的数格式

Ndef ndef = Ndef.get(tag);


3.写入数据

ndef.wrriteNdefMessage(ndefMessage);


4.NdefRecord:NDEF格式数据,创建对象提供如下

public static NdefRecord createApplicationRecord(String packageName)

public static NdefRecord createUri(Uri uri)

public static NdefRecord createUri(String uriString)

public static NdefRecord createMime(String mimeType, byte[] mimeData)

public static NdefRecord createExternal(String domain, String type, byte[] data) 

public static NdefRecord createTextRecord(String languageCode, String text) 

public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload)

public NdefRecord(byte[] data)
Uri支持格式如下:
private static final String[] URI_PREFIX_MAP = new String[] {
        "", // 0x00
        "http://www.", // 0x01
        "https://www.", // 0x02
        "http://", // 0x03
        "https://", // 0x04
        "tel:", // 0x05
        "mailto:", // 0x06
        "ftp://anonymous:anonymous@", // 0x07
        "ftp://ftp.", // 0x08
        "ftps://", // 0x09
        "sftp://", // 0x0A
        "smb://", // 0x0B
        "nfs://", // 0x0C
        "ftp://", // 0x0D
        "dav://", // 0x0E
        "news:", // 0x0F
        "telnet://", // 0x10
        "imap:", // 0x11
        "rtsp://", // 0x12
        "urn:", // 0x13
        "pop:", // 0x14
        "sip:", // 0x15
        "sips:", // 0x16
        "tftp:", // 0x17
        "btspp://", // 0x18
        "btl2cap://", // 0x19
        "btgoep://", // 0x1A
        "tcpobex://", // 0x1B
        "irdaobex://", // 0x1C
        "file://", // 0x1D
        "urn:epc:id:", // 0x1E
        "urn:epc:tag:", // 0x1F
        "urn:epc:pat:", // 0x20
        "urn:epc:raw:", // 0x21
        "urn:epc:", // 0x22
        "urn:nfc:", // 0x23
};

三、核心业务

1.action的校验

String action = intent.getAction();
        if (TextUtils.equals(action, NfcAdapter.ACTION_TAG_DISCOVERED)) {

            return true;
        }

2.读

private void readNfcTag(Intent intent) {
    if (intent == null)
        return;
    Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    if (parcelables == null || parcelables.length == 0)
        return;
    NdefMessage mNdefMsg = (NdefMessage) parcelables[0];
    NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
    if (mNdefRecord != null) {

        try {
            String msg = new String(mNdefRecord.getPayload(), "utf-8");

        } catch (Exception e) {

        }

    }


}

3.写

//NFC写入
    private void writeNFC(Tag tag) {
        //null不执行操作,强调敲代码的逻辑性
        if (tag == null) {
            return;
        }

        NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createApplicationRecord(mPackNmae)});

        //获得写入大小
        int size = ndefMessage.toByteArray().length;
        //2.推断是否是NDEF标签
        try {
            Ndef ndef = Ndef.get(tag);
            if (ndef != null) {
                //说明是NDEF标签,開始连接
                ndef.connect();
                //推断是否可写
                if (!ndef.isWritable()) {
                    showToast("当前设备不支持写入");
                    return;
                }
                //推断大小
                if (ndef.getMaxSize() < size) {
                    showToast("容量太小了");
                    return;
                }
                //写入
                ndef.writeNdefMessage(ndefMessage);

                showToast("写入成功");

            }
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

加密卡芯片的读写MifareClassic


MifareClassic:


“MIFARE Classic是恩智浦半导体开发的可用于非接触式智能卡,符合ISO/IEC 14443 A类标准。用于公共交通票证等应用,还可用于各类其他应用有S20,S50(M1),S70几种规格,主要是根据存储器容量划分,存储器容量分别有320B,1K,4K


这种卡和其他的不同,是有密码校验,不同厂家可以定制不同的密码和数据格式,简单的介绍如下


Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        MifareClassic mifareClassic = MifareClassic.get(tag);
        try {
            boolean connected = mifareClassic.isConnected();
            if (connected) {
                int count = mifareClassic.getSectorCount();

                for (int index = 0; index < count; index++) {
                    //获取善区数

                    boolean keyAopen = mifareClassic.authenticateSectorWithKeyA(index, MifareClassic.KEY_DEFAULT);
                    if (keyAopen) {
                        //获取扇区里面块的数量
                        int bCount = mifareClassic.getBlockCountInSector(index);
                        int bIndex = mifareClassic.sectorToBlock(index);
                        for (int position = 0; position < bCount; position++) {
                            byte[] data = mifareClassic.readBlock(bIndex + position);//进行了读卡
                            msgBuffer.append("块" + (bIndex + position) + "数据:").append(ByteArrayToHexString(data)).append("\r\n");

                            //修改KeyA和KeyB
                            if ((bIndex + position) == (4 * index + 3)) {
                                //将所有扇区的最后一个Block修改为111111111111ff078069111111111111
                                mifareClassic.writeBlock(bIndex + position, new byte[]{(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0xff, 0x07, (byte) 0x80, (byte) 0x69, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11});
//                                Log.e("onNewIntent:",(bIndex+position)+"块加密成功");

                            }


                        }

                    }


                }

            }

        } catch (Exception e) {

        }

关于Mifare Classic也是用的比较多的地方,暂时先写到这,以后将会整理一份详细的介绍。


本文仅代表作者观点,版权归原创者所有,如需转载请在文中注明来源及作者名字。

免责声明:本文系转载编辑文章,仅作分享之用。如分享内容、图片侵犯到您的版权或非授权发布,请及时与我们联系进行审核处理或删除,您可以发送材料至邮箱:service@tojoy.com