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>
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>
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("\n" + "消费记录如下:");
for (String string : retList) {
Log.d("h_bl", "消费记录" + string);
show_msg.append("\n" + 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("\n" + "消费记录如下:");
for (String string : retList) {
Log.d("h_bl", "消费记录" + string);
show_msg.append("\n" + 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;
} }
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);
}
}
3.非标准的NDEF格式数据解析--IsoDep的更多相关文章
- Android之JSON格式数据解析
查看原文:http://blog.csdn.net/hantangsongming/article/details/42234293 JSON:JavaScript 对象表示法(JavaScript ...
- (5)微信二次开发 之 XML格式数据解析
1.首先理解一下html html的全名是:HyperText Transfer markup language 超级文本标记语言,html本质上是一门标记(符合)语言,在html里,这些标记是事先定 ...
- html中通过js获取接口JSON格式数据解析以及跨域问题
前言:本人自学前端开发,一直想研究下js获取接口数据在html的实现,顺利地找到了获取数据的方法,但是有部分接口在调用中出现无法展示数据.经查,发现时跨域的问题,花费了一通时间,随笔记录下过程,以方便 ...
- 让SNIPER-MXNet从标准的COCO格式数据集中直接使用file_name作为图片路径
告别项目中“依index生成路径”的方法,直接使用我们在生成.json标签时就已经写入的图片路径(这里我写入的是绝对路径 full path)来获取图片. 需要做的,用以下代码替换SNIPER/lib ...
- ini格式数据生成与解析具体解释
ini格式数据生成与解析具体解释 1.ini格式数据长啥样? watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/ ...
- android nfc中Ndef格式的读写
1. 在onCreate()中获取NfcAdapter对象: NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); 2.在onNewI ...
- Wireshark分析非标准端口号流量
Wireshark分析非标准端口号流量 2.2.2 分析非标准端口号流量Wireshark分析非标准端口号流量 应用程序运行使用非标准端口号总是网络分析专家最关注的.关注该应用程序是否有意涉及使用非 ...
- 13、NFC技术:读写非NDEF格式的数据
MifareUltralight数据格式 将NFC标签的存储区域分为16个页,每一个页可以存储4个字节,一个可存储64个字节(512位).页码从0开始(0至15).前4页(0至3)存储了NFC标签相关 ...
- Python解析非标准JSON(Key值非字符串)
采集数据的时候经常碰到一些JSON数据的Key值不是字符串,这些数据在JavaScript的上下文中是可以解析的,但在Python中,没有该部分数据的上下文,无法采用json.loads(JSON)的 ...
随机推荐
- php利用PHPExcel类导出导入Excel用法
PHPExcel类是php一个excel表格处理插件了,下面我来给大家介绍利用PHPExcel类来导入与导出excel表格的应用方法,有需要了解的朋友不防参考参考(PHPExcel自己百度下载这里不介 ...
- JavaWeb笔记(十一)Maven
什么是Maven Maven是Apache旗下一款开源自动化的项目管理工具,它使用java语言编写,因此Maven是一款跨平台的项目管理工具. 主要功能 项目构建 在实际开发中,不仅仅是写完代码项目就 ...
- CodeForces Round #515 Div.3 C. Books Queries
http://codeforces.com/contest/1066/problem/C You have got a shelf and want to put some books on it. ...
- user namespace 2
昨天又试了一下, echo 0 1000 1 > /proc/<PID>/uid_map 想着直接把一个进程的uid设置成root,这样他就可以在自己的user namespace ...
- 如何使能diskquota
quotacheck -avug quotaon -avug setquota -u test1 10000 20000 /mountpoint quota -uv test1
- 洛谷 P3765 总统选举 解题报告
P3765 总统选举 题目背景 黑恶势力的反攻计划被小C成功摧毁,黑恶势力只好投降.秋之国的人民解放了,举国欢庆.此时,原秋之国总统因没能守护好国土,申请辞职,并请秋之国人民的大救星小C钦定下一任.作 ...
- 【马克-to-win】学习笔记—— 第五章 异常Exception
第五章 异常Exception [学习笔记] [参考:JDK中文(类 Exception)] java.lang.Object java.lang.Throwable java.lang.Except ...
- Java并发编程--AQS
概述 抽象队列同步器(AbstractQueuedSynchronizer,简称AQS)是用来构建锁或者其他同步组件的基础框架,它使用一个整型的volatile变量(命名为state)来维护同步状态, ...
- 语音提示辅助类MySoundAlertUtil
package com.jlb.scan.util; import android.content.Context; import android.media.AudioManager; import ...
- 接下来打算写一下visual stuido 2013使用git进行远端管理。
虽然我有了vs的账号,也vs2013开始已经可以进行远端的账户管理了,可是vs的版控毕竟有些依赖vs,想想还是用git吧 今天把这个环境的整套都弄地基本熟了.记录一下,算是一个小结.开始搭建系统框架