zoukankan      html  css  js  c++  java
  • 3.非标准的NDEF格式数据解析--IsoDep

    1.使用目的:正常开发是针对NDEF格式数据进行开发,但实际情况并非如此,以厦门公交卡为例,厦门公交卡保存的是非NDEF格式数据。其类型是IsoDep类型。

    2.非标准的NDEF格式数据流程:当厦门公交卡放到NFC上时,手机会捕获该厦门公交卡标签信息,自动获得该tag能支持的技术支持,其中标签的数据将封装到Intent中,并启动相关的Activity处理该标签信息,判断该标签类型为IsoDep类型后,使用该IsoDep类对标签进行操作。

    You can use the getTechList() method to determine the technologies supported by the tag and create the corresponding TagTechnology object with one of classes provided by android.nfc.tech

    你可以使用getTechList()方法来确定该tag能支持的技术,创建由android.nfc.tech包中提供的能与之匹配的的TagTechnology对象类。

    NFC针对非标准的NDEF格式数据使用步骤:

    1.获取NFC权限/添加Intent过滤器

    2.获取NFC适配器

    3.捕获NFC Intent

    4.处理该Intent(获取信息Tag)--> 判断标签类型为IsoDep类型,并执行相关操作

    5.IsoDep类的核心函数: transceive(byte[]) 函数。通过该函数发送相关指令,得到返回值。-- 需要了解底层的指令

    例如:

    底层的指令:

    通过NFC读取公交卡的余额和交易记录

    读取分四个步骤:
    1.select PSF (1PAY.SYS.DDF01)
    选择支付系统文件,它的名字是1PAY.SYS.DDF01。

    byte[] DFN_PSE = { (byte) '1', (byte) 'P',
    (byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y',
    (byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F',
    (byte) '0', (byte) '1', };

    2.选择公交卡应用的名字或者ID

    长安通:
    byte[] DFN_SRV = { (byte) 0xA0, (byte) 0x00,
    (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98,
    (byte) 0x07, (byte) 0x01, };

    3.读取余额 
    发送命令读取电子钱包的余额: 

    final byte[] cmd = { (byte) 0x80, // CLA Class 
    (byte) 0x5C, // INS Instruction 
    (byte) 0x00, // P1 Parameter 1 
    (byte) 0x02, // P2 Parameter 2 
    (byte) 0x04, // Le 
    }; 

    获取到的余额数据是byte[] data, 前4字节合并成int,再除以100(两个小数点),得到的结果就是余额。

    4.读取交易记录 

    一次性读取命令,在不知道有多少条记录的时候,用这个命令: 

    byte[] cmd = { (byte) 0x00, // CLA Class 

    (byte) 0xB2, // INS Instruction 

    (byte) 0x01, // P1 Parameter 1 

    (byte) 0xC5, // P2 Parameter 2 

    (byte) 0x00, // Le 

    }; 

    返回所有的记录byte[] data,每23个字节代表一条记录 

    也可以一条一条的读取: 

    cmd = { (byte) 0x00, // CLA Class 

    (byte) 0xB2, // INS Instruction 

    (byte) index, // P1 Parameter 1 

    (byte) 0xC4, // P2 Parameter 2 

    (byte) 0x00, // Le 

    }; 

    一条记录是23个字节byte[] data,对其解码如下 

    data[0]-data[1]:index 

    data[2]-data[4]:over,金额溢出? 

    data[5]-data[8]:交易金额 

    data[9]:如果等于0x06或者0x09,表示刷卡;否则是充值 

    data[10]-data[15]:刷卡机或充值机编号 

    data[16]-data[22]:日期String.format("%02X%02X.%02X.%02X %02X:%02X:%02X",data[16], data[17], data[18], data[19], data[20], data[21], data[22]); 

    实例解析:

    1.获取NFC权限

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

    添加Intent过滤器

    <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <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/nfc_tech_filter" />
                <intent-filter>
                    <action android:name="android.nfc.action.TAG_DISCOVERED" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
    View Code

    nfc_tech_filter.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    
        <tech-list>
            <tech>android.nfc.tech.IsoDep</tech>
        </tech-list>
        <!-- 以下只是显示怎么添加多个nfc支持类 -->
        <tech-list>
            <tech>android.nfc.tech.NfcV</tech>
        </tech-list>
        <tech-list>
            <tech>android.nfc.tech.NfcF</tech>
        </tech-list>
    
    </resources>
    View Code

    2.获取NFC适配器

    private NfcAdapter nfcAdapter; // NFC适配器
    ...
    ......
            // 获取默认的NFC控制器,并进行判断
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            if (nfcAdapter == null) {
                Log.d("h_bl", "设备不支持NFC!");
                return;
            }
            if (!nfcAdapter.isEnabled()) {
                Toast.makeText(getApplicationContext(), "请在系统设置中先启用NFC功能!", Toast.LENGTH_SHORT).show();
                Log.d("h_bl", "请在系统设置中先启用NFC功能!");
                return;
            }

    3.捕获NFC Intent

            Intent intent = this.getIntent(); // 捕获NFC Intent
            String nfcAction = intent.getAction(); // 解析该Intent的Action

    4.处理该Intent(获取信息Tag) -- 厦门公交卡为IsoDep类,详见IsoDep类的使用。

            if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(nfcAction)) {
                Log.d("h_bl", "ACTION_TECH_DISCOVERED");
                Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  // 获取Tag标签,既可以处理相关信息
                for (String tech : tag.getTechList()) {
                    Log.d("h_bl", "tech=" + tech);
                }
                IsoDep isoDep = IsoDep.get(tag);
                String str = "";
                try {
                    isoDep.connect(); // 连接
                    if (isoDep.isConnected()) {
                        Log.d("h_bl", "isoDep.isConnected"); // 判断是否连接上
                        // 1.select PSF (1PAY.SYS.DDF01)
                        // 选择支付系统文件,它的名字是1PAY.SYS.DDF01。
                        byte[] DFN_PSE = { (byte) '1', (byte) 'P', (byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y', (byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F', (byte) '0', (byte) '1', };
                        isoDep.transceive(getSelectCommand(DFN_PSE));
                        // 2.选择公交卡应用的名称
                        byte[] DFN_SRV = { (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98, (byte) 0x07, (byte) 0x01, };
                        isoDep.transceive(getSelectCommand(DFN_SRV));
                        // 3.读取余额
                        byte[] ReadMoney = { (byte) 0x80, // CLA Class
                                (byte) 0x5C, // INS Instruction
                                (byte) 0x00, // P1 Parameter 1
                                (byte) 0x02, // P2 Parameter 2
                                (byte) 0x04, // Le
                        };
                        byte[] Money = isoDep.transceive(ReadMoney);
                        if (Money != null && Money.length > 4) {
                            int cash = byteToInt(Money, 4);
                            float ba = cash / 100.0f;
                            show_msg.setText("余额:" + ba);
                        }
                        // 4.读取所有交易记录
                        byte[] ReadRecord = { (byte) 0x00, // CLA Class
                                (byte) 0xB2, // INS Instruction
                                (byte) 0x01, // P1 Parameter 1
                                (byte) 0xC5, // P2 Parameter 2
                                (byte) 0x00, // Le
                        };
                        byte[] Records = isoDep.transceive(ReadRecord);
                        // 处理Record
                        Log.d("h_bl", "总消费记录" + Records);
                        ArrayList<byte[]> ret = parseRecords(Records);
                        List<String> retList = parseRecordsToStrings(ret);
                        show_msg.append("
    " + "消费记录如下:");
                        for (String string : retList) {
                            Log.d("h_bl", "消费记录" + string);
                            show_msg.append("
    " + string);
                        }
    
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (isoDep != null) {
                        try {
                            isoDep.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

    完整代码:

    package com.example.nfcdemo;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.support.v7.app.ActionBarActivity;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.content.IntentFilter.MalformedMimeTypeException;
    import android.nfc.NfcAdapter;
    import android.nfc.Tag;
    import android.nfc.tech.IsoDep;
    import android.nfc.tech.NfcF;
    import android.nfc.tech.NfcV;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;
    import android.widget.Toast;
    import com.example.nfcdemo2.R;
    
    public class MainActivity extends ActionBarActivity {
    
        private NfcAdapter nfcAdapter; // NFC适配器
        // NFC前台调度系统
        private PendingIntent pendingIntent = null;
        private TextView show_msg; // 显示数据
        protected final static byte TRANS_CSU = 6; // 如果等于0x06或者0x09,表示刷卡;否则是充值
        protected final static byte TRANS_CSU_CPX = 9; // 如果等于0x06或者0x09,表示刷卡;否则是充值
        public String[][] tenchlists;
        public IntentFilter[] filters;
    
        @SuppressWarnings("unchecked")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            show_msg = (TextView) findViewById(R.id.show_msg);
            tenchlists = new String[][] { { IsoDep.class.getName() }, { NfcV.class.getName() }, { NfcF.class.getName() }, };
    
            try {
                filters = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };
            } catch (MalformedMimeTypeException e1) {
                e1.printStackTrace();
            }
            // 初始化PendingIntent,当有NFC设备连接上的时候,就交给当前Activity处理
            pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
            // 获取默认的NFC控制器,并进行判断
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            if (nfcAdapter == null) {
                Log.d("h_bl", "设备不支持NFC!");
                finish();
                return;
            }
            if (!nfcAdapter.isEnabled()) {
                Toast.makeText(getApplicationContext(), "请在系统设置中先启用NFC功能!", Toast.LENGTH_SHORT).show();
                Log.d("h_bl", "请在系统设置中先启用NFC功能!");
                return;
            }
            Intent intent = this.getIntent(); // 捕获NFC Intent
            praseIntent(intent);
        }
    
        private void praseIntent(Intent intent) {
    
            String nfcAction = intent.getAction(); // 解析该Intent的Action
            if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(nfcAction)) {
                Log.d("h_bl", "ACTION_TECH_DISCOVERED");
                Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // 获取Tag标签,既可以处理相关信息
                for (String tech : tag.getTechList()) {
                    Log.d("h_bl", "tech=" + tech);
                }
                IsoDep isoDep = IsoDep.get(tag);
                String str = "";
                try {
                    isoDep.connect(); // 连接
                    if (isoDep.isConnected()) {
                        Log.d("h_bl", "isoDep.isConnected"); // 判断是否连接上
                        // 1.select PSF (1PAY.SYS.DDF01)
                        // 选择支付系统文件,它的名字是1PAY.SYS.DDF01。
                        byte[] DFN_PSE = { (byte) '1', (byte) 'P', (byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y', (byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F', (byte) '0', (byte) '1', };
                        isoDep.transceive(getSelectCommand(DFN_PSE));
                        // 2.选择公交卡应用的名称
                        byte[] DFN_SRV = { (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98, (byte) 0x07, (byte) 0x01, };
                        isoDep.transceive(getSelectCommand(DFN_SRV));
                        // 3.读取余额
                        byte[] ReadMoney = { (byte) 0x80, // CLA Class
                                (byte) 0x5C, // INS Instruction
                                (byte) 0x00, // P1 Parameter 1
                                (byte) 0x02, // P2 Parameter 2
                                (byte) 0x04, // Le
                        };
                        byte[] Money = isoDep.transceive(ReadMoney);
                        if (Money != null && Money.length > 4) {
                            int cash = byteToInt(Money, 4);
                            float ba = cash / 100.0f;
                            show_msg.setText("余额:" + ba);
                        }
                        // 4.读取所有交易记录
                        byte[] ReadRecord = { (byte) 0x00, // CLA Class
                                (byte) 0xB2, // INS Instruction
                                (byte) 0x01, // P1 Parameter 1
                                (byte) 0xC5, // P2 Parameter 2
                                (byte) 0x00, // Le
                        };
                        byte[] Records = isoDep.transceive(ReadRecord);
                        // 处理Record
                        Log.d("h_bl", "总消费记录" + Records);
                        ArrayList<byte[]> ret = parseRecords(Records);
                        List<String> retList = parseRecordsToStrings(ret);
                        show_msg.append("
    " + "消费记录如下:");
                        for (String string : retList) {
                            Log.d("h_bl", "消费记录" + string);
                            show_msg.append("
    " + string);
                        }
    
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (isoDep != null) {
                        try {
                            isoDep.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        @Override
        protected void onPause() {
            if (nfcAdapter != null)
                nfcAdapter.disableForegroundDispatch(this);
            super.onPause();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            nfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, tenchlists);
        }
    
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
            // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
            if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
                Log.d("h_bl", "onNewIntent");
                praseIntent(intent);
            }
    
        }
    
        public static byte byteToHex(byte arg) {
            byte hex = 0;
            if (arg >= 48 && arg <= 57) {
                hex = (byte) (arg - 48);
            } else if (arg >= 65 && arg <= 70) {
                hex = (byte) (arg - 55);
            } else if (arg >= 97 && arg <= 102) {
                hex = (byte) (arg - 87);
            }
            return hex;
        }
    
        private byte[] getSelectCommand(byte[] aid) {
            final ByteBuffer cmd_pse = ByteBuffer.allocate(aid.length + 6);
            cmd_pse.put((byte) 0x00) // CLA Class
                    .put((byte) 0xA4) // INS Instruction
                    .put((byte) 0x04) // P1 Parameter 1
                    .put((byte) 0x00) // P2 Parameter 2
                    .put((byte) aid.length) // Lc
                    .put(aid).put((byte) 0x00); // Le
            return cmd_pse.array();
        }
    
        /**
         * 整条Records解析成ArrayList<byte[]>
         * 
         * @param Records
         * @return
         */
        private ArrayList<byte[]> parseRecords(byte[] Records) {
            int max = Records.length / 23;
            Log.d("h_bl", "消费记录有" + max + "条");
            ArrayList<byte[]> ret = new ArrayList<byte[]>();
            for (int i = 0; i < max; i++) {
                byte[] aRecord = new byte[23];
                for (int j = 23 * i, k = 0; j < 23 * (i + 1); j++, k++) {
                    aRecord[k] = Records[j];
                }
                ret.add(aRecord);
            }
            for (byte[] bs : ret) {
                Log.d("h_bl", "消费记录有byte[]" + bs); // 有数据。解析正确。
            }
            return ret;
        }
    
        /**
         * ArrayList<byte[]>记录分析List<String> 一条记录是23个字节byte[] data,对其解码如下
         * data[0]-data[1]:index data[2]-data[4]:over,金额溢出??? data[5]-data[8]:交易金额
         * ??代码应该是(5,4) data[9]:如果等于0x06或者0x09,表示刷卡;否则是充值
         * data[10]-data[15]:刷卡机或充值机编号
         * data[16]-data[22]:日期String.format("%02X%02X.%02X.%02X %02X:%02X:%02X"
         * ,data[16], data[17], data[18], data[19], data[20], data[21], data[22]);
         * 
         * @param logs
         * @return
         */
        private List<String> parseRecordsToStrings(ArrayList<byte[]>... Records) {
            List<String> recordsList = new ArrayList<String>();
            for (ArrayList<byte[]> record : Records) {
                if (record == null)
                    continue;
                for (byte[] v : record) {
                    StringBuilder r = new StringBuilder();
                    int cash = Util.toInt(v, 5, 4);
                    char t = (v[9] == TRANS_CSU || v[9] == TRANS_CSU_CPX) ? '-' : '+';
                    r.append(String.format("%02X%02X.%02X.%02X %02X:%02X ", v[16], v[17], v[18], v[19], v[20], v[21], v[22]));
                    r.append("   " + t).append(Util.toAmountString(cash / 100.0f));
                    String aLog = r.toString();
                    recordsList.add(aLog);
                }
            }
            return recordsList;
        }
    
        // byteArray转化为int
        private int byteToInt(byte[] b, int n) {
            int ret = 0;
            for (int i = 0; i < n; i++) {
                ret = ret << 8;
                ret |= b[i] & 0x00FF;
            }
            if (ret > 100000 || ret < -100000)
                ret -= 0x80000000;
            return ret;
        }
    
    }
    View Code

    Util:

    public final class Util {
        private final static char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    
        private Util() {
        }
    
        public static byte[] toBytes(int a) {
            return new byte[] { (byte) (0x000000ff & (a >>> 24)), (byte) (0x000000ff & (a >>> 16)), (byte) (0x000000ff & (a >>> 8)), (byte) (0x000000ff & (a)) };
        }
    
        public static int toInt(byte[] b, int s, int n) {
            int ret = 0;
    
            final int e = s + n;
            for (int i = s; i < e; ++i) {
                ret <<= 8;
                ret |= b[i] & 0xFF;
            }
            return ret;
        }
    
        public static int toIntR(byte[] b, int s, int n) {
            int ret = 0;
    
            for (int i = s; (i >= 0 && n > 0); --i, --n) {
                ret <<= 8;
                ret |= b[i] & 0xFF;
            }
            return ret;
        }
    
        public static int toInt(byte... b) {
            int ret = 0;
            for (final byte a : b) {
                ret <<= 8;
                ret |= a & 0xFF;
            }
            return ret;
        }
    
        public static String toHexString(byte[] d, int s, int n) {
            final char[] ret = new char[n * 2];
            final int e = s + n;
    
            int x = 0;
            for (int i = s; i < e; ++i) {
                final byte v = d[i];
                ret[x++] = HEX[0x0F & (v >> 4)];
                ret[x++] = HEX[0x0F & v];
            }
            return new String(ret);
        }
    
        public static String toHexStringR(byte[] d, int s, int n) {
            final char[] ret = new char[n * 2];
    
            int x = 0;
            for (int i = s + n - 1; i >= s; --i) {
                final byte v = d[i];
                ret[x++] = HEX[0x0F & (v >> 4)];
                ret[x++] = HEX[0x0F & v];
            }
            return new String(ret);
        }
    
        public static int parseInt(String txt, int radix, int def) {
            int ret;
            try {
                ret = Integer.valueOf(txt, radix);
            } catch (Exception e) {
                ret = def;
            }
    
            return ret;
        }
    
        public static String toAmountString(float value) {
            return String.format("%.2f", value);
        }
    }
    View Code
  • 相关阅读:
    Javascript 运动中Offset的bug——逐行分析代码,让你轻松了解运动的原理
    Javascript 多物体运动——逐行分析代码,让你轻松了解运动的原理
    thymeleaf设置网页脚本里面的值
    win10系统瞬间黑屏问题
    Spring boot启动端口设置
    idea打包jar包
    java socket使用例子
    java使用socket读取网页
    java读取网页
    Error:Unexpected lock protocol found in lock file. Expected 3, found 0
  • 原文地址:https://www.cnblogs.com/H-BolinBlog/p/5377425.html
Copyright © 2011-2022 走看看