Android Studio NFC讀取CPU卡信息

今天接到一個NFC讀CPU卡的需求,做個總結。

總結之前,吐槽一下,一定要查一下測試機支不支持NFC功能,我拿樂視S3開發了一上午,以爲哪裏出了BUG或者權限沒加,查了無數資料,下了十幾個demo,死活就是連不上NFC,下午換了小米8se來測,發現還是連不上。。。萬念俱灰之際,找了同事的小米MIX2來測,發現可以了!!!!!!!!!!

測試之前一定要查一下自己的測試機支不支持NFC功能!!!!!!!!
測試之前一定要查一下自己的測試機支不支持NFC功能!!!!!!!!
測試之前一定要查一下自己的測試機支不支持NFC功能!!!!!!!!

言歸正傳,上代碼

步驟一:Mainfext中添加權限

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

在activity中添加

 <activity
            android:name=".NfcActivity"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <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/tag_type" />

        </activity>

在res文件夾中新建一個xml文件夾,取名叫:tag_type

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
</resources>


主activity中

package com.soullistener.nfcdemo;

import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;

import java.io.IOException;

public class NfcActivity extends AppCompatActivity {
    private NfcAdapter mNfcAdapter;
    private Tag mTag;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mNfcAdapter = M1CardUtils.isNfcAble(this);
        M1CardUtils.setPendingIntent(PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()), 0));
        mTag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);

        TextView textView = findViewById(R.id.tv_content);
        textView.setMovementMethod(ScrollingMovementMethod.getInstance());
        //M1卡類型
        findViewById(R.id.btn_read_m1).setOnClickListener(v -> {
            if (M1CardUtils.hasCardType(mTag, this, "MifareClassic")) {
                try {
                    StringBuilder stringBuilder = new StringBuilder();
                    String[][] m1Content = M1CardUtils.readCard(mTag);
                    for (int i = 0; i < m1Content.length; i++) {
                        for (int j = 0; j < m1Content[i].length; j++) {
                            stringBuilder.append(m1Content[i][j]+"\n");
                        }
                    }
                    textView.setText(stringBuilder.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        //CPU
        findViewById(R.id.btn_read_cpu).setOnClickListener(v->{
            if (M1CardUtils.hasCardType(mTag, this, "IsoDep")) {
                try {
                    textView.setText(M1CardUtils.readIsoCard(mTag));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        mNfcAdapter = M1CardUtils.isNfcAble(this);
        M1CardUtils.setPendingIntent(PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()), 0));
        Log.e("onNewIntent","onNewIntent");
        mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(this);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mNfcAdapter != null) {
            mNfcAdapter.enableForegroundDispatch(this, M1CardUtils.getPendingIntent(),
                    null, null);
        }
    }

}

添加兩個工具類

package com.soullistener.nfcdemo;

import android.app.Activity;
import android.app.PendingIntent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;

public class M1CardUtils {

    private static PendingIntent pendingIntent;
    public static PendingIntent getPendingIntent(){
        return pendingIntent;
    }

    public static void setPendingIntent(PendingIntent pendingIntent){
        M1CardUtils.pendingIntent = pendingIntent;
    }

    /**
     * 判斷是否支持NFC
     * @return
     */
    public static NfcAdapter isNfcAble(Activity mContext){
        NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
        if (mNfcAdapter == null) {
            Toast.makeText(mContext, "設備不支持NFC!", Toast.LENGTH_LONG).show();
        }else if (!mNfcAdapter.isEnabled()) {
            Toast.makeText(mContext, "請在系統設置中先啓用NFC功能!", Toast.LENGTH_LONG).show();
        }

        return mNfcAdapter;
    }

    /**
     * 監測是否支持cardType類型卡
     * @param tag
     * @param activity
     * @param cardType
     * @return
     */
    public static boolean hasCardType(Tag tag,Activity activity,String cardType){

        if (tag == null){
            Toast.makeText(activity,"請貼卡",Toast.LENGTH_LONG).show();
            return false;
        }

        String[] techList = tag.getTechList();

        boolean hasCardType = false;
        for (String tech : techList) {
            Log.e("TagTech",tech);
            if (tech.contains(cardType)) {
                hasCardType = true;
                break;
            }
        }

        if (!hasCardType) {
            Toast.makeText(activity, "不支持"+cardType+"卡", Toast.LENGTH_LONG).show();
        }

        return hasCardType;
    }

    /**
     * CPU卡信息讀取
     * @param tag
     * @return
     * @throws IOException
     */
    public static String readIsoCard(Tag tag) throws IOException {
        IsoDep isoDep = IsoDep.get(tag);
        if (!isoDep.isConnected()){
            isoDep.connect();
        }

        String result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00A40000023F00")));
        Log.e("readIsoCard",result);
        result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00A40000020005")));
        Log.e("readIsoCard",result);
        result = StringUtil.bytesToHexString(isoDep.transceive(StringUtil.hex2Bytes("00B0000016")));
        Log.e("readIsoCard",result);
        isoDep.close();
        return result;
    }

    /**
     * M1讀取卡片信息
     * @return
     */
    public static String[][] readCard(Tag tag)  throws IOException{
        MifareClassic mifareClassic = MifareClassic.get(tag);
        try {
            mifareClassic.connect();
            String[][] metaInfo = new String[16][4];
            // 獲取TAG中包含的扇區數
            int sectorCount = mifareClassic.getSectorCount();
            for (int j = 0; j < sectorCount; j++) {
                int bCount;//當前扇區的塊數
                int bIndex;//當前扇區第一塊
                if (m1Auth(mifareClassic,j)) {
                    bCount = mifareClassic.getBlockCountInSector(j);
                    bIndex = mifareClassic.sectorToBlock(j);
                    for (int i = 0; i < bCount; i++) {
                        byte[] data = mifareClassic.readBlock(bIndex);
                        String dataString = bytesToHexString(data);
                        metaInfo[j][i] = dataString;
                        Log.e("獲取到信息",dataString);
                        bIndex++;
                    }
                } else {
                    Log.e("readCard","密碼校驗失敗");
                }
            }
            return metaInfo;
        } catch (IOException e){
            throw new IOException(e);
        } finally {
            try {
                mifareClassic.close();
            }catch (IOException e){
                throw new IOException(e);
            }
        }
    }

    /**
     * 改寫數據
     * @param block
     * @param blockbyte
     */
    public static boolean writeBlock(Tag tag, int block, byte[] blockbyte) throws IOException {
        MifareClassic mifareClassic = MifareClassic.get(tag);
        try {
            mifareClassic.connect();
            if (m1Auth(mifareClassic,block/4)) {
                mifareClassic.writeBlock(block, blockbyte);
                Log.e("writeBlock","寫入成功");
            } else {
                Log.e("密碼是", "沒有找到密碼");
                return false;
            }
        } catch (IOException e){
            throw new IOException(e);
        } finally {
            try {
                mifareClassic.close();
            }catch (IOException e){
                throw new IOException(e);
            }
        }
        return true;

    }

    /**
     * 密碼校驗
     * @param mTag
     * @param position
     * @return
     * @throws IOException
     */
    public static boolean m1Auth(MifareClassic mTag,int position) throws IOException {
        if (mTag.authenticateSectorWithKeyA(position, MifareClassic.KEY_DEFAULT)) {
            return true;
        } else if (mTag.authenticateSectorWithKeyB(position, MifareClassic.KEY_DEFAULT)) {
            return true;
        }
        return false;
    }

    private static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        char[] buffer = new char[2];
        for (int i = 0; i < src.length; i++) {
            buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
            buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
            System.out.println(buffer);
            stringBuilder.append(buffer);
        }
        return stringBuilder.toString();
    }
}

第二個

package com.soullistener.nfcdemo;

import java.io.UnsupportedEncodingException;
/**
 *
 * @description 字符串處理工具類
 */
public class StringUtil {

    /**
     * 判斷字符串是否爲空
     *
     * @param value
     * @return
     */
    public static boolean isEmpty(String value) {
        if (value != null && !"".equalsIgnoreCase(value.trim())
                && !"null".equalsIgnoreCase(value.trim())) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 十六進制的ASCII碼轉字符串
     * @param hex
     * @return
     */
    public static String convertHexToString(String hex){
        StringBuilder sb = new StringBuilder();
        StringBuilder temp = new StringBuilder();
        for( int i=0; i<hex.length()-1; i+=2 ){
            String output = hex.substring(i, (i + 2));
            int decimal = Integer.parseInt(output, 16);
            sb.append((char)decimal);
            temp.append(decimal);
        }
        return sb.toString();
    }

    /**
     * 字節數組轉十六進制字符串
     * */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 16進制轉ascii
     * @param string
     * @return
     */
    public static String converStringtoAscll(String string){
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0;i<string.length();i++){
            stringBuilder.append((int)string.charAt(i));
        }
        return stringBuilder.toString();
    }

    /**
     *
     * @param hexString
     * @return 將十六進制轉換爲字節數組
     */
    public static String hexStr =  "0123456789ABCDEF";
    public static byte[] hexStringToBinary(String hexString){
        int len = hexString.length()/2;
        byte[] bytes = new byte[len];
        byte high = 0;
        byte low = 0;

        for(int i=0;i<len;i++){
            high = (byte)((hexStr.indexOf(hexString.charAt(2*i)))<<4);
            low = (byte)hexStr.indexOf(hexString.charAt(2*i+1));
            bytes[i] = (byte) (high|low);
        }
        return bytes;
    }

    /**左補指定元素*/
    public static String fillLeft(String value, String element, int count){
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<count - value.length();i++){
            sb.append(element);
        }
        sb.append(value);
        return sb.toString();
    }

    /**右補指定元素*/
    public static String fillRight(String value, String element, int count){
        StringBuffer sb = new StringBuffer();
        sb.append(value);
        for(int i=0;i<count - value.length();i++){
            sb.append(element);
        }
        return sb.toString();
    }

    //10進制轉16進制
    public static String IntToHex(int n){
        char[] ch = new char[20];
        int nIndex = 0;
        while ( true ){
            int m = n/16;
            int k = n%16;
            if ( k == 15 )
                ch[nIndex] = 'F';
            else if ( k == 14 )
                ch[nIndex] = 'E';
            else if ( k == 13 )
                ch[nIndex] = 'D';
            else if ( k == 12 )
                ch[nIndex] = 'C';
            else if ( k == 11 )
                ch[nIndex] = 'B';
            else if ( k == 10 )
                ch[nIndex] = 'A';
            else
                ch[nIndex] = (char)('0' + k);
            nIndex++;
            if ( m == 0 )
                break;
            n = m;
        }
        StringBuffer sb = new StringBuffer();
        sb.append(ch, 0, nIndex);
        sb.reverse();
//        String strHex = new String("0x");
//        strHex += sb.toString();
        return sb.toString();
    }

    // 16進制轉10進制
    public static int HexToInt(String strHex) {
        int nResult = 0;
        if (!IsHex(strHex))
            return nResult;
        String str = strHex.toUpperCase();
        if (str.length() > 2) {
            if (str.charAt(0) == '0' && str.charAt(1) == 'X') {
                str = str.substring(2);
            }
        }
        int nLen = str.length();
        for (int i = 0; i < nLen; ++i) {
            char ch = str.charAt(nLen - i - 1);
            try {
                nResult += (GetHex(ch) * GetPower(16, i));
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return nResult;
    }

    // 計算16進制對應的數值
    public static int GetHex(char ch) throws Exception {
        if (ch >= '0' && ch <= '9')
            return (int) (ch - '0');
        if (ch >= 'a' && ch <= 'f')
            return (int) (ch - 'a' + 10);
        if (ch >= 'A' && ch <= 'F')
            return (int) (ch - 'A' + 10);
        throw new Exception("error param");
    }

    // 計算冪
    public static int GetPower(int nValue, int nCount) throws Exception {
        if (nCount < 0)
            throw new Exception("nCount can't small than 1!");
        if (nCount == 0)
            return 1;
        int nSum = 1;
        for (int i = 0; i < nCount; ++i) {
            nSum = nSum * nValue;
        }
        return nSum;
    }

    // 判斷是否是16進制數
    public static boolean IsHex(String strHex) {
        int i = 0;
        if (strHex.length() > 2) {
            if (strHex.charAt(0) == '0'
                    && (strHex.charAt(1) == 'X' || strHex.charAt(1) == 'x')) {
                i = 2;
            }
        }
        for (; i < strHex.length(); ++i) {
            char ch = strHex.charAt(i);
            if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
                    || (ch >= 'a' && ch <= 'f'))
                continue;
            return false;
        }
        return true;
    }

    /**
     * 轉字符串成十六進制的ASCII
     * @param str
     * @return
     */
    public static String convertStringToHex(String str){
        char[] chars = str.toCharArray();
        StringBuffer hex = new StringBuffer();
        for(int i = 0; i < chars.length; i++){
            hex.append(Integer.toHexString((int)chars[i]));
        }
        return hex.toString();
    }

    /**
     * 十六進制字符串轉換成bytes
     *
     * @param hexStr
     * @return
     */
    public static byte[] hexStr2Bytes(String hexStr) {
        int l = hexStr.length();
        if (l % 2 != 0) {
            StringBuilder sb = new StringBuilder(hexStr);
            sb.insert(hexStr.length() - 1, '0');
            hexStr = sb.toString();
        }
        byte[] b = new byte[hexStr.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hexStr.charAt(j++);
            char c1 = hexStr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }

    private static int parse(char c) {
        if (c >= 'a')
            return (c - 'a' + 10) & 0x0f;
        if (c >= 'A')
            return (c - 'A' + 10) & 0x0f;
        return (c - '0') & 0x0f;
    }

    /**解析44域漢字錯誤信息描述*/
    public static String parseErrorMsg(String msg){
        String parseStr = null;
        try {
            parseStr = new String(hexStringToByte(msg.toUpperCase()),"GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return parseStr;
    }

    public static byte[] hexStringToByte(String hex) {
        int len = hex.length() / 2;
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();

        for(int i = 0; i < len; ++i) {
            int pos = i * 2;
            result[i] = (byte)(toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }

        return result;
    }

    private static byte toByte(char c) {
        byte b = (byte)"0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**格式姓名*/
    public static String formateName(String name){
        String nameStr = "";
        int length = name.length();
        if(length == 2){
            nameStr = name.substring(0,1);
            nameStr += "*";
        }else if(length == 3){
            nameStr = name.substring(0,1);
            nameStr += "**";
        }

        return nameStr;
    }

    /**
     * 16進制數字取反
     *
     * @param hexString
     * @return
     */
    public static String hexBalanceNo(String hexString) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < hexString.length(); i++) {
            stringBuilder.append(Integer.toHexString(~Integer.valueOf(hexString, 16)).substring(7, 8));
        }
        return stringBuilder.toString();
    }

    /**
     * 16進制數字取反
     *
     * @param hexString
     * @return
     */
    public static String hexNo(String hexString) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < hexString.length(); i++) {
            String noHex = Integer.toHexString(~Integer.valueOf(String.valueOf(hexString.charAt(i)), 16));
            stringBuilder.append(noHex.substring(7, 8));
        }
        return stringBuilder.toString();
    }

    public static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[3] = (byte) ((value >> 24) & 0xFF);
        src[2] = (byte) ((value >> 16) & 0xFF);
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * 十六進制轉int 大端模式
     *
     * @param hex
     * @return
     */
    public static int hex2Int(String hex) {
        byte[] bytes = hex2Bytes(hex);
        int len = bytes.length;
        int rec = 0;
        for (int i = 0; i < len; i++) {
            int temp = bytes[i] & 0xff;
            int off = (len - 1 - i) * 8;
            rec |= (temp << off);
        }
        return rec;
    }

    /**
     * 十六進制轉Byte
     *
     * @param hexStr
     * @return
     */
    public static byte[] hex2Bytes(String hexStr) {
        int len = hexStr.length();
        if (len % 2 != 0) {
            throw new RuntimeException("length error");
        }
        byte[] b = new byte[hexStr.length() / 2];
        int bLen = b.length;
        int j = 0;
        for (int i = 0; i < bLen; i++) {
            char c0 = hexStr.charAt(j++);
            char c1 = hexStr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章