| | |
| | | import android.nfc.tech.MifareClassic; |
| | | import android.util.Log; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | |
| | | import com.dayu.baselibrary.bean.BaseManagerToUserCard; |
| | | import com.dayu.baselibrary.bean.BaseUserCardCard; |
| | | import com.dayu.baselibrary.tools.HexUtil; |
| | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * author: zuo |
| | | * Date: 2024-09-25 |
| | | * Time: 10:33 |
| | | * 备注:原生读写卡方式 |
| | | * 原生NFC读卡工具类 |
| | | * |
| | | * 功能说明: |
| | | * 1. 使用Android原生NFC API读取Mifare Classic卡片数据 |
| | | * 2. 支持读取用户卡信息、管理卡信息、卡号、卡类型等 |
| | | * 3. 支持扇区级别的数据读取和验证 |
| | | * 4. 提供多种密钥验证方式(KeyA验证) |
| | | * 5. 支持单例模式,确保资源正确管理 |
| | | * |
| | | * 使用场景: |
| | | * - 用户卡信息读取 |
| | | * - 管理卡信息读取 |
| | | * - 卡片类型识别 |
| | | * - 卡号获取 |
| | | * - 扇区数据读取 |
| | | * |
| | | * 注意事项: |
| | | * - 需要确保NFC功能已开启 |
| | | * - 读取前需要进行密钥验证 |
| | | * - 使用完毕后需要关闭连接,释放资源 |
| | | * - 异常情况下要正确处理连接关闭 |
| | | * |
| | | * @author zuo |
| | | * @date 2024-09-25 |
| | | * @time 10:33 |
| | | */ |
| | | public class NativeNfcReadHelper extends BaseNfcReadHelper { |
| | | |
| | | /** NFC标签对象,包含卡片的基本信息 */ |
| | | private Tag tag; |
| | | // private NFCCallback callback; |
| | | |
| | | /** 单例实例 */ |
| | | private static NativeNfcReadHelper helper; |
| | | |
| | | |
| | | /** |
| | | * 设置Intent,从中提取NFC标签信息 |
| | | * |
| | | * @param intent 包含NFC标签信息的Intent |
| | | */ |
| | | @Override |
| | | public void setIntent(Intent intent) { |
| | | this.tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 单例初始化 |
| | | * |
| | | * @param intent |
| | | * @return |
| | | * 获取单例实例 |
| | | * |
| | | * @param intent 包含NFC标签信息的Intent |
| | | * @param activity 当前Activity实例 |
| | | * @return NativeNfcReadHelper实例 |
| | | */ |
| | | public static NativeNfcReadHelper getInstence(Intent intent, Activity activity) { |
| | | if (helper == null) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取用户卡信息 |
| | | * |
| | | * @return |
| | | * 获取用户卡数据(默认从扇区1读取) |
| | | * |
| | | * @param userCardCard 用户卡数据模板对象,用于解析读取的字节数据 |
| | | * @return 解析后的用户卡数据对象,失败返回null |
| | | */ |
| | | public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) { |
| | | return getUserCardData(1, userCardCard); |
| | | } |
| | | |
| | | /** |
| | | * 从指定扇区获取用户卡信息 |
| | | * |
| | | * 读取流程: |
| | | * 1. 建立NFC连接 |
| | | * 2. 使用密钥验证扇区访问权限 |
| | | * 3. 读取扇区内所有块的数据 |
| | | * 4. 将字节数据解析为用户卡对象 |
| | | * 5. 关闭连接并返回结果 |
| | | * |
| | | * @param sectorIndex 要读取的扇区索引(从0开始) |
| | | * @param userCardCard 用户卡数据模板对象 |
| | | * @return 解析后的用户卡数据对象,失败返回null |
| | | */ |
| | | @Override |
| | | public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) { |
| | | public BaseUserCardCard getUserCardData(int sectorIndex, BaseUserCardCard userCardCard) { |
| | | if (userCardCard != null) { |
| | | BaseUserCardCard userCard = null; |
| | | Map<String, List<byte[]>> map = new HashMap<>(); |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | //链接NFC |
| | | // 建立NFC连接 |
| | | mfc.connect(); |
| | | //获取扇区数量 |
| | | int count = mfc.getSectorCount(); |
| | | //存储空间 |
| | | int size = mfc.getSize(); |
| | | //用于判断时候有内容读取出来 |
| | | |
| | | // 用于判断是否有内容读取出来 |
| | | boolean flag = false; |
| | | List<byte[]> list = new ArrayList<>(); |
| | | //验证扇区密码,否则会报错(链接失败错误) |
| | | |
| | | // 验证扇区密码,否则会报错(连接失败错误) |
| | | boolean isOpen = false; |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) { |
| | | if (!listKeyA.isEmpty()) { |
| | | // 遍历所有可用的KeyA密钥进行验证 |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(sectorIndex, listKeyA.get(i))) { |
| | | isOpen = true; |
| | | break; |
| | | } |
| | | } |
| | | } else if (!listA_PS.isEmpty()) { |
| | | // 使用默认密钥或特定扇区密钥进行验证 |
| | | if (mfc.authenticateSectorWithKeyA(sectorIndex, defauleKey)) { |
| | | isOpen = true; |
| | | break; |
| | | } else if (mfc.authenticateSectorWithKeyA(sectorIndex, listA_PS.get(sectorIndex))) { |
| | | isOpen = true; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | //获取扇区里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(1); |
| | | //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) |
| | | int bIndex = mfc.sectorToBlock(1); |
| | | //String data1 = ""; |
| | | // 获取扇区里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(sectorIndex); |
| | | // 获取扇区第一个块对应芯片存储器的位置 |
| | | int bIndex = mfc.sectorToBlock(sectorIndex); |
| | | |
| | | // 读取扇区内所有块的数据 |
| | | for (int j = 0; j < bCount; j++) { |
| | | //读取数据 |
| | | byte[] data = null; |
| | | try { |
| | | data = mfc.readBlock(bIndex); |
| | |
| | | } |
| | | |
| | | if (flag) { |
| | | // 将读取的字节数据解析为用户卡对象 |
| | | userCard = userCardCard.getBean(list); |
| | | return userCard; |
| | | } |
| | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | // 确保连接被正确关闭,释放资源 |
| | | try { |
| | | mfc.close(); |
| | | } catch (Exception e) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取卡片类型 |
| | | * |
| | | * 读取扇区1第一个块的第一个字节作为卡片类型标识 |
| | | * |
| | | * @return 卡片类型的十六进制字符串,失败返回错误码 |
| | | */ |
| | | @Override |
| | | public String getCradType() { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | |
| | | try { |
| | | mfc.connect(); |
| | | boolean isOpen = false; |
| | | |
| | | // 使用KeyA密钥验证扇区1的访问权限 |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) { |
| | | isOpen = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | // 读取扇区1第一个块的数据 |
| | | int bIndex = mfc.sectorToBlock(1); |
| | | byte[] data = mfc.readBlock(bIndex + 0); |
| | | if (data != null && data.length > 0) { |
| | | // 将第一个字节转换为十六进制字符串作为卡片类型 |
| | | String hex = HexUtil.byteToHex(data[0]); |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | return hex; |
| | | } |
| | | |
| | | } else { |
| | | Log.i("NFCWreatActivity", "isOpen===" + isOpen); |
| | | return BaseCommon.CARD_TYPE_ERROR2; |
| | | return BaseCommon.CARD_TYPE_ERROR2; // 密钥验证失败 |
| | | } |
| | | } catch (IOException e) { |
| | | return BaseCommon.CARD_TYPE_ERROR1; |
| | | return BaseCommon.CARD_TYPE_ERROR1; // IO异常 |
| | | } finally { |
| | | try { |
| | | mfc.close(); |
| | |
| | | |
| | | /** |
| | | * 读取NFC卡的全部信息 |
| | | * |
| | | * @param callback |
| | | * |
| | | * 遍历所有扇区,读取每个扇区的所有块数据 |
| | | * |
| | | * @param callback 数据读取完成后的回调接口 |
| | | */ |
| | | @Override |
| | | public void getAllData(final NFCCallMapback callback) { |
| | |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | //链接NFC |
| | | // 建立NFC连接 |
| | | mfc.connect(); |
| | | //获取扇区数量 |
| | | // 获取扇区数量 |
| | | int count = mfc.getSectorCount(); |
| | | //存储空间 |
| | | // 存储空间大小 |
| | | int size = mfc.getSize(); |
| | | //用于判断时候有内容读取出来 |
| | | // 用于判断是否有内容读取出来 |
| | | boolean flag = false; |
| | | |
| | | // 遍历所有扇区 |
| | | for (int i = 0; i < count; i++) { |
| | | List<byte[]> list = new ArrayList<>(); |
| | | //验证扇区密码,否则会报错(链接失败错误) |
| | | // 验证扇区密码 |
| | | boolean isOpen = false; |
| | | for (int j = 0; j < listKeyA.size(); j++) { |
| | | if (mfc.authenticateSectorWithKeyA(i, listKeyA.get(i))) { |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | //获取扇区里面块的数量 |
| | | // 获取扇区里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(i); |
| | | //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) |
| | | // 获取扇区第一个块对应芯片存储器的位置 |
| | | int bIndex = mfc.sectorToBlock(i); |
| | | //String data1 = ""; |
| | | |
| | | // 读取扇区内所有块的数据 |
| | | for (int j = 0; j < bCount; j++) { |
| | | //读取数据 |
| | | byte[] data = null; |
| | | try { |
| | | data = mfc.readBlock(bIndex); |
| | |
| | | } |
| | | map.put(i + "", list); |
| | | } |
| | | |
| | | if (flag) { |
| | | callback.callBack(map); |
| | | } else { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取1扇区信息 |
| | | * |
| | | * @param callback |
| | | * 获取扇区1的信息 |
| | | * |
| | | * 专门用于读取扇区1的所有块数据,通常用于读取用户卡信息 |
| | | * |
| | | * @param callback 数据读取完成后的回调接口 |
| | | */ |
| | | @Override |
| | | public void getOneSectorData(NFCCallListback callback) { |
| | | |
| | | |
| | | MifareClassic mfc = null; |
| | | try { |
| | | mfc = MifareClassic.get(tag); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | if (null != mfc) { |
| | | try { |
| | | //链接NFC |
| | | // 建立NFC连接 |
| | | mfc.connect(); |
| | | //用于判断时候有内容读取出来 |
| | | // 用于判断是否有内容读取出来 |
| | | boolean flag = false; |
| | | List<byte[]> list = new ArrayList<>(); |
| | | //验证扇区密码,否则会报错(链接失败错误) |
| | | |
| | | // 验证扇区1的密码 |
| | | boolean isOpen = false; |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) { |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | //获取扇区里面块的数量 |
| | | // 获取扇区1里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(1); |
| | | //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) |
| | | // 获取扇区1第一个块对应芯片存储器的位置 |
| | | int bIndex = mfc.sectorToBlock(1); |
| | | //String data1 = ""; |
| | | |
| | | // 读取扇区1内所有块的数据 |
| | | for (int j = 0; j < bCount; j++) { |
| | | //读取数据 |
| | | byte[] data = null; |
| | | try { |
| | | data = mfc.readBlock(bIndex); |
| | |
| | | } |
| | | flag = true; |
| | | } |
| | | |
| | | if (flag) { |
| | | callback.callBack(list); |
| | | } else { |
| | |
| | | callback.error(CardCommonState.ERROR); |
| | | e.printStackTrace(); |
| | | } finally { |
| | | |
| | | try { |
| | | mfc.close(); |
| | | } catch (Exception e) { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 读取NFC卡的特定扇区信息 |
| | | * |
| | | * @param a 扇区 |
| | | * @param b 块 |
| | | * @param callback |
| | | * 读取NFC卡的特定扇区特定块信息 |
| | | * |
| | | * @param a 扇区索引(从0开始) |
| | | * @param b 块索引(从0开始,相对于扇区内的块) |
| | | * @param callback 数据读取完成后的回调接口 |
| | | */ |
| | | @Override |
| | | public void getData(final int a, final int b, final NFCCallByteback callback) { |
| | |
| | | try { |
| | | mfc.connect(); |
| | | int count = mfc.getSectorCount(); |
| | | |
| | | // 验证扇区索引是否有效 |
| | | if (a < 0 || a > count - 1) { |
| | | callback.error(CardCommonState.ERROR); |
| | | return; |
| | | } |
| | | |
| | | int bCount = mfc.getBlockCountInSector(a); |
| | | // 验证块索引是否有效 |
| | | if (b < 0 || b > bCount - 1) { |
| | | callback.error(CardCommonState.ERROR); |
| | | return; |
| | | } |
| | | |
| | | int type = mfc.getType();//获取TAG的类型 |
| | | // 获取TAG的类型(用于调试) |
| | | int type = mfc.getType(); |
| | | String typeS = ""; |
| | | switch (type) { |
| | | case MifareClassic.TYPE_CLASSIC: |
| | |
| | | typeS = "TYPE_UNKNOWN"; |
| | | break; |
| | | } |
| | | |
| | | // 验证扇区密码 |
| | | boolean isOpen = false; |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(a, listKeyA.get(i))) { |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | // 读取指定扇区指定块的数据 |
| | | int bIndex = mfc.sectorToBlock(a); |
| | | byte[] data = mfc.readBlock(bIndex + b); |
| | | callback.callBack(data); |
| | |
| | | }).start(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取卡号(读取后自动关闭连接) |
| | | * |
| | | * 从扇区0第一个块读取卡的UID作为卡号 |
| | | * |
| | | * @return 卡号的十六进制字符串(大写),失败返回错误码或空字符串 |
| | | */ |
| | | @Override |
| | | public String getCardNumber() { |
| | | if (tag == null) { |
| | | return ""; |
| | | } |
| | | |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | mfc.connect(); |
| | | //获取当前卡号 |
| | | // 获取当前卡号 |
| | | boolean isOpen = false; |
| | | if (!listKeyA.isEmpty()) { |
| | | // 使用KeyA密钥验证扇区0 |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | isOpen = true; |
| | |
| | | } |
| | | } |
| | | } else if (!listA_PS.isEmpty()) { |
| | | // 使用默认密钥或特定密钥验证扇区0 |
| | | if (mfc.authenticateSectorWithKeyA(0, defauleKey)) { |
| | | isOpen = true; |
| | | } else if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))) { |
| | |
| | | } |
| | | |
| | | if (isOpen) { |
| | | // 读取扇区0第一个块的数据 |
| | | int bIndex = mfc.sectorToBlock(0); |
| | | byte[] data = mfc.readBlock(bIndex + 0); |
| | | if (data != null && data.length > 0) { |
| | | // 提取前4个字节作为UID |
| | | String hex = HexUtil.bytesToHex(Arrays.copyOfRange(data, 0, 4)); |
| | | hex = HexUtil.spaceHex(hex); |
| | | hex = HexUtil.HighLowHex(hex); |
| | | hex = HexUtil.spaceHex(hex); // 添加空格分隔 |
| | | hex = HexUtil.HighLowHex(hex); // 高低位转换 |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | return hex.toUpperCase(); |
| | | } |
| | | } else { |
| | | return BaseCommon.CARD_TYPE_ERROR2; // 密钥验证失败 |
| | | } |
| | | |
| | | } catch (IOException e) { |
| | | return BaseCommon.CARD_TYPE_ERROR1; |
| | | return BaseCommon.CARD_TYPE_ERROR1; // IO异常 |
| | | } finally { |
| | | try { |
| | | mfc.close(); |
| | | mfc.close(); // 确保关闭连接 |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取卡号(不关闭连接) |
| | | * |
| | | * 与getCardNumber()功能相同,但不关闭NFC连接 |
| | | * 适用于需要连续进行多次NFC操作的场景 |
| | | * |
| | | * @return 卡号的十六进制字符串(大写),失败返回错误码或空字符串 |
| | | */ |
| | | public String getCardNumberNoClose() { |
| | | if (tag == null) { |
| | | return ""; |
| | | } |
| | | |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | mfc.connect(); |
| | | //获取当前卡号 |
| | | // 获取当前卡号 |
| | | boolean isOpen = false; |
| | | if (!listKeyA.isEmpty()) { |
| | | // 使用KeyA密钥验证扇区0 |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | isOpen = true; |
| | |
| | | } |
| | | } |
| | | } else if (!listA_PS.isEmpty()) { |
| | | // 使用默认密钥或特定密钥验证扇区0 |
| | | if (mfc.authenticateSectorWithKeyA(0, defauleKey)) { |
| | | isOpen = true; |
| | | } else if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))) { |
| | |
| | | } |
| | | |
| | | if (isOpen) { |
| | | // 读取扇区0第一个块的数据 |
| | | int bIndex = mfc.sectorToBlock(0); |
| | | byte[] data = mfc.readBlock(bIndex + 0); |
| | | if (data != null && data.length > 0) { |
| | | // 提取前4个字节作为UID |
| | | String hex = HexUtil.bytesToHex(Arrays.copyOfRange(data, 0, 4)); |
| | | hex = HexUtil.spaceHex(hex); |
| | | hex = HexUtil.HighLowHex(hex); |
| | | hex = HexUtil.spaceHex(hex); // 添加空格分隔 |
| | | hex = HexUtil.HighLowHex(hex); // 高低位转换 |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | return hex.toUpperCase(); |
| | | } |
| | | } else { |
| | | return BaseCommon.CARD_TYPE_ERROR2; // 密钥验证失败 |
| | | } |
| | | |
| | | } catch (IOException e) { |
| | | Log.i("NFCWreatActivity", e.toString()); |
| | | return BaseCommon.CARD_TYPE_ERROR1; |
| | | return BaseCommon.CARD_TYPE_ERROR1; // IO异常 |
| | | } |
| | | // 注意:这里不关闭连接 |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /** |
| | | * 获取卡片类型和卡号(默认参数) |
| | | * |
| | | * @return "卡号,卡片类型"格式的字符串 |
| | | */ |
| | | @Override |
| | | public String getCradTypeAndCardNumber() { |
| | | return getCradTypeAndCardNumber(1); |
| | | return getCradTypeAndCardNumber(1, 0, 0); |
| | | } |
| | | |
| | | /** |
| | | * 获取卡片类型和卡号(自动关闭连接) |
| | | * |
| | | * @param sectorIndex 读取卡片类型的扇区索引 |
| | | * @param blockIndex 读取卡片类型的块索引 |
| | | * @param cardTypeIndex 卡片类型字节在块中的位置索引 |
| | | * @return "卡号,卡片类型"格式的字符串 |
| | | */ |
| | | public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex) { |
| | | return getCradTypeAndCardNumber(sectorIndex, blockIndex, cardTypeIndex, true); |
| | | } |
| | | |
| | | /** |
| | | * 获取卡片类型和卡号 |
| | | * |
| | | * @return |
| | | * |
| | | * 读取流程: |
| | | * 1. 从扇区0读取卡号 |
| | | * 2. 从指定扇区读取卡片类型 |
| | | * 3. 返回"卡号,卡片类型"格式的字符串 |
| | | * |
| | | * @param sectorIndex 读取卡片类型的扇区索引 |
| | | * @param blockIndex 读取卡片类型的块索引 |
| | | * @param cardTypeIndex 卡片类型字节在块中的位置索引 |
| | | * @param isClose 是否在读取完成后关闭连接 |
| | | * @return "卡号,卡片类型"格式的字符串,失败返回错误码 |
| | | */ |
| | | |
| | | public String getCradTypeAndCardNumber(int sectorIndex) { |
| | | |
| | | public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex, boolean isClose) { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | mfc.connect(); |
| | | StringBuilder strData = new StringBuilder(); |
| | | //获取当前卡号 |
| | | |
| | | // 第一步:获取当前卡号(从扇区0读取) |
| | | boolean isOpen = false; |
| | | if (!listKeyA.isEmpty()) { |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | |
| | | isOpen = true; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | int bIndex = mfc.sectorToBlock(0); |
| | | byte[] data = mfc.readBlock(bIndex + 0); |
| | | if (data != null && data.length > 0) { |
| | | // 提取卡号并格式化 |
| | | String hex = HexUtil.bytesToHex(Arrays.copyOfRange(data, 0, 4)); |
| | | hex = HexUtil.spaceHex(hex); |
| | | hex = HexUtil.HighLowHex(hex); |
| | | strData.append(hex); |
| | | strData.append(","); |
| | | strData.append(","); // 分隔符 |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | } |
| | | } |
| | | //获取卡片类型 |
| | | |
| | | // 第二步:获取卡片类型(从指定扇区读取) |
| | | if (!listKeyA.isEmpty()) { |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(sectorIndex, listKeyA.get(i))) { |
| | |
| | | isOpen = true; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | int bIndex = mfc.sectorToBlock(sectorIndex); |
| | | byte[] data = mfc.readBlock(bIndex + sectorIndex); |
| | | byte[] data = mfc.readBlock(bIndex + 0); |
| | | if (data != null && data.length > 0) { |
| | | String hex = HexUtil.byteToHex(data[0]); |
| | | // 提取指定位置的字节作为卡片类型 |
| | | String hex = HexUtil.byteToHex(data[cardTypeIndex]); |
| | | strData.append(hex); |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | return strData.toString().toUpperCase(); |
| | | } |
| | | } else { |
| | | Log.i("NFCWreatActivity", "isOpen===" + isOpen); |
| | | return BaseCommon.CARD_TYPE_ERROR2; |
| | | return BaseCommon.CARD_TYPE_ERROR2; // 密钥验证失败 |
| | | } |
| | | } catch (IOException e) { |
| | | return BaseCommon.CARD_TYPE_ERROR1; |
| | | e.printStackTrace(); |
| | | return BaseCommon.CARD_TYPE_ERROR1; // IO异常 |
| | | } finally { |
| | | try { |
| | | mfc.close(); |
| | | if (isClose) { |
| | | mfc.close(); // 根据参数决定是否关闭连接 |
| | | } |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取扇区1的数据(返回字节数组列表) |
| | | * |
| | | * @return 扇区1所有块的字节数据列表,失败返回null |
| | | */ |
| | | @Override |
| | | public List<byte[]> getOnesectorData() { |
| | | MifareClassic mfc = null; |
| | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | List<byte[]> list = new ArrayList<>(); |
| | | if (null != mfc) { |
| | | try { |
| | | //链接NFC |
| | | // 建立NFC连接 |
| | | mfc.connect(); |
| | | //用于判断时候有内容读取出来 |
| | | // 用于判断是否有内容读取出来 |
| | | boolean flag = false; |
| | | |
| | | //验证扇区密码,否则会报错(链接失败错误) |
| | | // 验证扇区1的密码 |
| | | boolean isOpen = false; |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) { |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (isOpen) { |
| | | //获取扇区里面块的数量 |
| | | // 获取扇区1里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(1); |
| | | //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) |
| | | // 获取扇区1第一个块对应芯片存储器的位置 |
| | | int bIndex = mfc.sectorToBlock(1); |
| | | //String data1 = ""; |
| | | |
| | | // 读取扇区1内所有块的数据 |
| | | for (int j = 0; j < bCount; j++) { |
| | | //读取数据 |
| | | byte[] data = null; |
| | | try { |
| | | data = mfc.readBlock(bIndex); |
| | |
| | | } finally { |
| | | try { |
| | | mfc.close(); |
| | | return list; |
| | | return list; // 在finally中返回结果 |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取用户卡信息 |
| | | * |
| | | * @return |
| | | * 获取管理卡转用户卡的数据 |
| | | * |
| | | * 用于读取管理卡中存储的用户卡信息,通常用于管理卡向用户卡的数据转换 |
| | | * |
| | | * @param baseManagerToUserCard 管理卡数据模板对象 |
| | | * @return 解析后的管理卡数据对象,失败返回null |
| | | */ |
| | | @Override |
| | | public BaseManagerToUserCard getManagerToUserCardData(BaseManagerToUserCard baseManagerToUserCard) { |
| | | |
| | | BaseManagerToUserCard managerToUserCard = null; |
| | | Map<String, List<byte[]>> map = new HashMap<>(); |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | //链接NFC |
| | | // 建立NFC连接 |
| | | mfc.connect(); |
| | | //获取扇区数量 |
| | | // 获取扇区数量 |
| | | int count = mfc.getSectorCount(); |
| | | //存储空间 |
| | | // 存储空间大小 |
| | | int size = mfc.getSize(); |
| | | //用于判断时候有内容读取出来 |
| | | // 用于判断是否有内容读取出来 |
| | | boolean flag = false; |
| | | List<byte[]> list = new ArrayList<>(); |
| | | //验证扇区密码,否则会报错(链接失败错误) |
| | | |
| | | // 验证扇区1的密码(先尝试默认密钥,再尝试公司密钥) |
| | | boolean isOpen = mfc.authenticateSectorWithKeyA(1, defauleKey); |
| | | if (!isOpen) { |
| | | isOpen = mfc.authenticateSectorWithKeyA(1, companyKey); |
| | | } |
| | | |
| | | if (isOpen) { |
| | | //获取扇区里面块的数量 |
| | | // 获取扇区1里面块的数量 |
| | | int bCount = mfc.getBlockCountInSector(1); |
| | | //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) |
| | | // 获取扇区1第一个块对应芯片存储器的位置 |
| | | int bIndex = mfc.sectorToBlock(1); |
| | | //String data1 = ""; |
| | | |
| | | // 读取扇区1内所有块的数据 |
| | | for (int j = 0; j < bCount; j++) { |
| | | //读取数据 |
| | | byte[] data = null; |
| | | try { |
| | | data = mfc.readBlock(bIndex); |
| | |
| | | } |
| | | |
| | | if (flag) { |
| | | // 将读取的字节数据解析为管理卡对象 |
| | | managerToUserCard = baseManagerToUserCard.getBean(list); |
| | | return managerToUserCard; |
| | | } |
| | |
| | | e.printStackTrace(); |
| | | } finally { |
| | | try { |
| | | mfc.close(); |
| | | mfc.close(); // 确保连接被正确关闭 |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | } |