之前对nfc对cpu卡的操作这块,走了许多弯路,所以写下这篇文章。一方面做下总结,另一方面希望能帮助到一些人。
本文目的在于让读者了解如何在android的程序开发中对接cpu卡、m1卡,对于nfc和cpu卡、m1卡本身的技术原理不做探讨,读者可自行百度。
android对cpu卡、m1卡的识别
1、设置权限
<uses-permission android:name="android.permission.NFC" />
2、设置感兴趣的技术列表
public NfcModel(Activity activity, Class<? extends TagTechnology>...techs){
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
techLists = new String[techs.length][];
for(int i=0; i < techs.length; ++i){
techLists[i] = new String[]{ techs[i].getName() };
}
this.techs = techs;
try{
filters = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };
}catch (IntentFilter.MalformedMimeTypeException e){
throw new RuntimeException(e);
}
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
pendingIntent = PendingIntent.getActivity(
activity,
0,
new Intent( activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
onNewIntent(activity.getIntent());
}
}
3、读取数据
public void load(Parcelable parcelable) {
final Tag tag = (Tag) parcelable;
if(listener!=null){
/**
* 这里一定要关闭一下,防止因程序原因导致未关闭
*/
close();
for(Class<? extends TagTechnology> t : techs){
try {
Method method = t.getMethod("get",Tag.class);
TagTechnology result = (TagTechnology) method.invoke(null,tag);
if(result==null){
continue;
}
technology = result;
listener.onNfcEvent(result);
} catch (Exception e) {
if(NfcUtil.debug){
throw new RuntimeException("回调nfc事件发生错误",e);
}else{
//生产环境
e.printStackTrace();
}
}
}
}
}
4、在activity中调用
public class NfcTestActivity extends AppCompatActivity implements NfcListener {
private NfcModel nfcModel;
private TextView result;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfc);
result = (TextView) findViewById(R.id.result);
//
nfcModel = new NfcModel( this, IsoDep.class, MifareClassic.class);
nfcModel.setListener(this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
nfcModel.onNewIntent(intent);
}
@Override
protected void onResume() {
super.onResume();
nfcModel.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
nfcModel.onPause(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
nfcModel.destroy();
}
@Override
public void onNfcEvent(TagTechnology tag) {
try{
tag.connect();
if(tag instanceof IsoDep){
DepTagAdapter adapter = new DefaultDepTagAdapter((IsoDep) tag);
String[] results = adapter.send(new String[]{"00a40000023f00"});
result.setText("cpu:"+results[0]);
}else{
MifareOneTagAdapter adapter = new DefaultMifareOneTagAdapter((MifareClassic) tag);
adapter.authenticateSectorWithKeyA(0, HexUtil.decodeHex("A0A1A2A3A4A5"));
byte[] bytes = adapter.readBlock(0);
result.setText("m1:"+HexUtil.encodeHexStr(bytes));
}
}catch (IOException e){
Toast.makeText(this,"请重新贴卡",Toast.LENGTH_SHORT);
}catch (NfcException e){
result.setText("error:"+e.getMessage());
}
}
}
贴卡调用app
1、使用权限
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
2、在需要贴卡后直接调用的activity中增加如下配置:
···
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</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" />
</activity>
···
nfc_tech_filter内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.MifareClassic</tech>
</tech-list>
</resources>
进行react-native封装
···
protected DepTagAdapter cpu;
protected MifareOneTagAdapter m1;
public static final String CPU = "cpu";
protected NfcModel model;
public NfcModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addLifecycleEventListener(this);
reactContext.addActivityEventListener(this);
}
@Override
public String getName() {
return "NfcModule";
}
@ReactMethod
public void close(){
if(cpu !=null){
cpu.close();
cpu = null;
}
if(m1!=null){
m1.close();
m1 = null;
}
}
@ReactMethod
public void isAvailable(Callback callback){
callback.invoke(NfcUtil.isAvailable(getCurrentActivity()));
}
/**
* m1卡写卡流程
*
* 参数格式为
*
* [
*
* {
* cmd:'write',
* data:'',
* keyb:'',
* sector:0
* },
* {
* cmd:'inc',
* data: 1,
* keyb:'',
* sector:1,
* },
* {
* cmd:'dec',
* data: 1,
* keyb:'',
* sector:1,
* },
* {
* cmd:'transfer',
* data: 1,
* keyb:'',
* sector:1,
* }
*
*
* ]
*
*
*
*/
@ReactMethod
public void write(ReadableArray command, Promise promise){
if(m1 ==null){
promise.reject("io","closed");
return;
}
}
/**
* 恢复流程
* @param command
* @param promise
*
*
* 参数格式为
*
* [
*
*
* 恢复流程中的拷贝
* {
* cmd:'copy',
* src:1, //blockIndex
* dest:2 //blockIndex
* keya:''
* },
*
* 恢复流程中的恢复
* {
* cmd:'restore',
* src:1, //blockIndex
* dest:2 //blockIndex
* keya:''
* },
*
* 恢复流程中的设置
*
* {
* cmd:'set',
* dest:2, //blockIndex
* data:'',
* keya:'',
* },
* ]
*/
@ReactMethod
public void restore(ReadableArray command, Promise promise){
if(m1 ==null){
promise.reject("io","closed");
return;
}
}
@ReactMethod
public void readBlock(ReadableArray command, Promise promise){
//传输进来的格式为: [ "keya0", null, "keya1",... ]
if(m1 ==null){
promise.reject("io","closed");
return;
}
try {
m1.connect();
WritableArray arr = Arguments.createArray();
for(int i=0 , c = command.size(); i < c; ++i){
try{
if(command.isNull(i)){
arr.pushNull();
continue;
}
String cmd = command.getString(i);
m1.authenticateSectorWithKeyA(i, HexUtil.decodeHex(cmd));
byte[] bytes = m1.readBlock(i);
arr.pushString( HexUtil.encodeHexStr(bytes) );
}catch (IOException e){
promise.reject("io",e);
return;
}
}
promise.resolve(arr);
} catch (IOException e) {
promise.reject("io",e);
}
}
@ReactMethod
public void apdu(ReadableArray command, Promise promise){
if(cpu ==null){
promise.reject("io","closed");
return;
}
try {
cpu.connect();
WritableArray arr = Arguments.createArray();
for(int i=0 , c = command.size(); i < c; ++i){
try{
String cmd = command.getString(i);
if(cmd.contains(",")){
String[] args = cmd.split(",");
NfcResponse response = null;
for(String arg : args){
response = cpu.send(arg);
}
//只用最后一个为主
String result = response.getStr();
arr.pushString(result);
}else{
NfcResponse response = cpu.send(command.getString(i));
String result = response.getStr();
arr.pushString(result);
}
}catch (IOException e){
promise.reject("io",e);
return;
}catch (NfcException e){
promise.reject("nfc",e);
return;
}
}
promise.resolve(arr);
} catch (IOException e) {
promise.reject("io",e);
}
}
@Override
public void onHostResume() {
if(model==null){
model = new NfcModel(getCurrentActivity(), IsoDep.class, MifareClassic.class);
model.setListener(this);
}
model.onResume(getCurrentActivity());
}
@Override
public void onHostPause() {
if(model!=null)
model.onPause(getCurrentActivity());
}
@Override
public void onHostDestroy() {
if(model!=null){
model.destroy();
model = null;
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
}
@Override
public void onNewIntent(Intent intent) {
if(model!=null)
model.onNewIntent(intent);
}
@Override
public void onNfcEvent(TagTechnology tag) {
if(tag instanceof IsoDep){
cpu = new DefaultDepTagAdapter( (IsoDep)tag);
//通知感知到了
notifyEvent("nfcTag","cpu");
}else if(tag instanceof MifareClassic){
m1 = new DefaultMifareOneTagAdapter( (MifareClassic)tag ) ;
notifyEvent("nfcTag","m1");
}
}
protected void notifyEvent(String eventName,@Nullable Object data){
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,data);
}
···
源码
https://github.com/jzoom/jzoom-nfc
网友评论