美文网首页React Native实战
react-native调用nfc读写cpu卡、m1卡-andr

react-native调用nfc读写cpu卡、m1卡-andr

作者: 追梦3000 | 来源:发表于2017-10-22 15:18 被阅读917次

    之前对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

    参考文献

    Android NFC识别CPU卡和m1卡

    android m1卡读写

    CPU卡相关资料收集总结

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

    简单apdu指令返回局分析

    相关文章

      网友评论

        本文标题:react-native调用nfc读写cpu卡、m1卡-andr

        本文链接:https://www.haomeiwen.com/subject/nbnyuxtx.html