Add files via upload

This commit is contained in:
DXL
2020-08-27 12:33:42 +08:00
committed by GitHub
parent 592db75ac3
commit 95b0e5c311
7 changed files with 2103 additions and 0 deletions
@@ -0,0 +1,179 @@
package com.proxgrind.chameleon.utils.mifare;
import com.proxgrind.chameleon.packets.DataPackets;
import com.proxgrind.chameleon.utils.system.LogUtils;
import com.proxgrind.chameleon.utils.tools.HexUtil;
import com.proxgrind.devices.BleSerialControl;
import java.io.IOException;
import java.util.Arrays;
public class ChameleonBatchAdapterImpl implements BatchAdapter {
// 持有一个原生通讯的句柄!
private BleSerialControl control = BleSerialControl.get();
// 一个空的数组!
public static final byte[] EMPTY_DATA_ONE = new byte[]{0x00};
/**
* 读取函数,变色龙的读取实现,依赖起始块
*
* @param startBlock 起始块,如果当前的模式是读取单块模式,则此参数是需要读取的块
* @param isKeyA 当前是否是用keyA来验证并且读取的
* @param key 当前被用来读取的秘钥
* @param isReadSector 当前是否是读取全部扇区模式,注意,如果是读取扇区模式,
* 遇到4K的大扇区的情况下,需要进行每次4个块的读取,也就是
* 从startBlock开始表示读取第一个大块
* 从startBlock + 4 开始表示读取第二个大块
* 从startBlock + 8 开始表示读取第三个大块
* 从startBlock + 12 开始表示读取第四个大块
*/
@Override
public byte[][] read(int startBlock, boolean isKeyA, byte[] key, boolean isReadSector) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(isReadSector ? 2 : 1), // 置入类型,如果当前是读取扇区,则设置操作标志为2,否则设置为1
EMPTY_DATA_ONE, // 置入状态码!
new byte[]{(byte) startBlock}, // 置入起始块!
isKeyA(isKeyA), // 置入当前验证的秘钥类型!
key, // 置入秘钥!
isReadSector ? new byte[16 * 4] : new byte[16] // 置入空字节
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
LogUtils.d("发送的数据长度: " + dataOfParam.length);
// 发送数据!
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, dataOfParam.length);
if (respDatas != null && respDatas.length == dataOfParam.length) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte status = respDatas[1];
checkTagStatus(status);
if (status == 0) {
LogUtils.d("读取失败");
return null;
}
if (status == 1) {
return HexUtil.splitBytes(
Arrays.copyOfRange(respDatas, 10, respDatas.length),
16
);
}
}
return null;
}
@Override
public boolean write(int sector, boolean isKeyA, byte[] key, byte[] dataGroup) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(3), // 置入类型!
EMPTY_DATA_ONE, // 置入状态码!
new byte[]{(byte) sector}, // 置入扇区!
isKeyA(isKeyA), // 置入当前验证的秘钥类型!
key, // 置入秘钥!
dataGroup // 置入数据!
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
// 发送数据!
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, dataOfParam.length);
if (respDatas != null && respDatas.length == dataOfParam.length) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte status = respDatas[1];
checkTagStatus(status);
if (status == 0) {
LogUtils.d("写入失败");
return false;
}
return status == 1;
}
return false;
}
/**
* 经过测试,以35个秘钥为一组进行验证比较好!
*/
@Override
public byte[] verity(int sector, byte[][] keysGroup, boolean isKeyA) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(0x04), // 置入类型! 04
EMPTY_DATA_ONE, // 置入状态码! 00
getBlock(sector), // 置入扇区! ~3F
isKeyA(isKeyA), // 置入当前验证的秘钥类型! 00
getKeyCount(keysGroup), // 获得当前的秘钥总数!
HexUtil.bytesMerge(keysGroup) // 合并秘钥!
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, 11);
if (respDatas != null && respDatas.length == 11) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte index = respDatas[1];
checkTagStatus(index);
// 只有当索引大于1的时候,才是真正的有应答,当应答FF的时候,则是卡片失联了
if (index > 0 && index <= keysGroup.length) {
return keysGroup[index - 1];
}
}
return null;
}
public void checkTagStatus(int statusCode) throws IOException {
if (statusCode == 0xFF || statusCode == -1) {
throw new IOException("Tag lost.");
}
}
public byte[] sendAndReadResponse(byte[] data, int dataLength, int acceptRespLength) throws IOException {
// 发送数据!
int timeout = getTimeout();
// 刷新缓冲区且发送
control.flush();
int maxLen = BleSerialControl.MTU;
if (control.write(data, 0, dataLength, timeout) == dataLength) {
// 读取应答数据
byte[] responseBuffer = new byte[acceptRespLength <= 0 ? maxLen : acceptRespLength];
// 直接读取
control.read(
responseBuffer,
0,
Math.max(acceptRespLength, 0),
timeout
);
return responseBuffer;
}
return null;
}
/**
* 获取byte数组型的类型,分别可能是
* 0 读取
* 1 写入
* 2 验证
*/
public byte[] getType(int type) {
return new byte[]{(byte) type};
}
/**
* 将单个字节的sector转为数组!
*/
public byte[] getBlock(int sector) {
return new byte[]{(byte) MfDataUtils.get_trailer_block(MfDataUtils.sectorToBlock(sector))};
}
public byte[] isKeyA(boolean isKeyA) {
return new byte[]{(byte) (isKeyA ? 0 : 1)};
}
public byte[] getKeyCount(byte[][] keys) {
return new byte[]{(byte) keys.length};
}
@Override
public int getTimeout() {
return 2333;
}
@Override
public void setTimeout(int timeout) {
}
}
@@ -0,0 +1,266 @@
package com.proxgrind.chameleon.utils.mifare;
import android.nfc.tech.MifareClassic;
import com.proxgrind.chameleon.packets.DataPackets;
import com.proxgrind.chameleon.utils.system.LogUtils;
import com.proxgrind.chameleon.utils.tools.HexUtil;
import java.io.IOException;
import java.util.Arrays;
public class ChameleonMifareAdapter implements MifareAdapter {
private ChameleonBatchAdapterImpl batchAdapter = new ChameleonBatchAdapterImpl();
private byte[] uid;
private byte[] atqa;
private int sak = -1;
private byte[] ats;
@Override
public boolean rescantag() throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
batchAdapter.getType(0x00), // 置入类型! 04
ChameleonBatchAdapterImpl.EMPTY_DATA_ONE, // 置入状态码! 00
new byte[10], // 卡号
new byte[1], // 卡号长度
new byte[2], // ATQA
new byte[1], // SAK
new byte[1] // ATS长度
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
byte[] respDatas = batchAdapter.sendAndReadResponse(dataOfFinal, dataOfFinal.length, 0);
// byte[] respDatas = HexUtil.hexStringToByteArray("0001505C8E04000000000000040400080B12b12b12b12b12b121b2121b12b12b1212b1bb32513b513513b5131b4141b1");
if (respDatas != null && respDatas.length > 2) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte status = respDatas[1];
// 只有当索引大于1的时候,才是真正的有应答,当应答FF或者0的时候,则是卡片失联了
batchAdapter.checkTagStatus(status);
if (status > 0) {
// 我们需要进行信息截取!
if (respDatas.length >= 13) { // 可以截取有效的UID!
uid = Arrays.copyOfRange(respDatas, 2, 12);
uid = Arrays.copyOf(uid, respDatas[12]);
LogUtils.d("UID: " + HexUtil.toHexString(uid));
}
if (respDatas.length >= 15) { // 可以截取有效的ATQA
atqa = Arrays.copyOfRange(respDatas, 13, 15);
LogUtils.d("ATQA: " + HexUtil.toHexString(atqa));
}
if (respDatas.length >= 16) { // 可以截取有效的SAK
sak = Arrays.copyOfRange(respDatas, 15, 16)[0];
LogUtils.d("SAK: " + sak);
}
int atsLen = 0;
if (respDatas.length >= 17) {
atsLen = Arrays.copyOfRange(respDatas, 16, 17)[0];
}
LogUtils.d("ATS长度: " + atsLen);
if (atsLen > 0 && respDatas.length - 17 >= atsLen) {
ats = Arrays.copyOfRange(respDatas, 17, 17 + atsLen);
LogUtils.d("ATS: " + HexUtil.toHexString(ats));
}
return true;
}
}
return false;
}
@Override
public boolean connect() throws IOException {
return rescantag();
}
@Override
public void close() throws IOException {
// 不需要做任何操作!
}
@Override
public byte[] read(int block) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public boolean write(int blockIndex, byte[] data) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public boolean authA(int sectorIndex, byte[] key) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public boolean authB(int sectorIndex, byte[] key) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public void increment(int blockIndex, int value) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public void decrement(int blockIndex, int value) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public void restore(int blockIndex) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public void transfer(int blockIndex) throws IOException {
throw new IOException("Unsupported operation.");
}
@Override
public byte[] getUid() {
try {
if (uid == null)
rescantag();
} catch (IOException e) {
e.printStackTrace();
}
return uid;
}
@Override
public byte[] getAts() {
if (ats == null) {
try {
rescantag();
} catch (IOException e) {
e.printStackTrace();
}
}
return ats;
}
@Override
public byte[] getAtqa() {
if (atqa == null) {
try {
rescantag();
} catch (IOException e) {
e.printStackTrace();
}
}
return atqa;
}
@Override
public byte[] getSak() {
if (sak == -1) {
try {
rescantag();
} catch (IOException e) {
e.printStackTrace();
}
}
return new byte[]{(byte) sak};
}
@Override
public int getType() {
try {
if (sak == -1)
rescantag();
} catch (IOException e) {
e.printStackTrace();
return -1;
}
switch (sak) {
case 0x01:
case 0x08:
case 0x09:
// Seclevel = SL2
case 0x18:
case 0x19:
case 0x28:
//mIsEmulated = true;
case 0x38:
case 0x88:
return MifareClassic.TYPE_CLASSIC;
case 0x10:
// SecLevel = SL2
case 0x11:
return MifareClassic.TYPE_PLUS;
// NXP-tag: false
case 0x98:
case 0xB8:
return MifareClassic.TYPE_PRO;
default:
// Stack incorrectly reported a MifareClassic. We cannot handle this
// gracefully - we have no idea of the memory layout. Bail.
return -1;
}
}
@Override
public int getSectorCount() {
switch (getType()) {
case 0:
return 16;
case 1:
return 32;
case 2:
return 40;
}
return 0;
}
@Override
public int getBlockCount() {
switch (getType()) {
case 0:
return MifareClassic.SIZE_1K / MifareClassic.BLOCK_SIZE;
case 1:
return MifareClassic.SIZE_2K / MifareClassic.BLOCK_SIZE;
case 2:
return MifareClassic.SIZE_4K / MifareClassic.BLOCK_SIZE;
}
return 0;
}
@Override
public void setTimeout(int ms) {
}
@Override
public int getTimeout() {
return 2333;
}
@Override
public BatchAdapter getBatchImpl() {
return batchAdapter;
}
@Override
public boolean isConnected() {
try {
return rescantag();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean isEmulated() {
return false;
}
@Override
public boolean isSpecialTag() {
return false;
}
@Override
public boolean isTestSupported() {
return false;
}
}
File diff suppressed because it is too large Load Diff
+105
View File
@@ -0,0 +1,105 @@
package com.proxgrind.chameleon.utils.mifare;
/*
* MifareClassic标签用得到的工具!
* */
public class MfDataUtils {
public static boolean validateSector(int sector) {
// Do not be too strict on upper bounds checking, since some cards
// have more addressable memory than they report. For example,
// MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
// MIFARE Classic compatibility mode.
// Note that issuing a command to an out-of-bounds block is safe - the
// tag should report error causing IOException. This validation is a
// helper to guard against obvious programming mistakes.
int NR_TRAILERS_4k = 40;
if (sector < 0 || sector >= NR_TRAILERS_4k) {
return false;
}
return true;
}
public static boolean validateBlock(int block) {
// Just looking for obvious out of bounds...
int NR_BLOCKS_4k = 0xFF;
if (block < 0 || block >= NR_BLOCKS_4k) {
return false;
}
return true;
}
public static boolean validateValueOperand(int value) {
if (value < 0) {
return false;
}
return true;
}
public static int blockToSector(int blockIndex) {
if (!validateBlock(blockIndex)) return 0;
if (blockIndex < 32 * 4) {
return (blockIndex / 4);
} else {
return (32 + (blockIndex - 32 * 4) / 16);
}
}
public static int sectorToBlock(int sectorIndex) {
if (!validateSector(sectorIndex)) {
return -1;
}
if (sectorIndex < 32) {
return (sectorIndex * 4);
} else {
return (32 * 4 + (sectorIndex - 32) * 16);
}
}
public static boolean isFirstBlock(int uiBlock) {
// 测试我们是否处于小扇区或者大扇区?
if (uiBlock < 128)
return ((uiBlock) % 4 == 0);
else
return ((uiBlock) % 16 == 0);
}
public static boolean isTrailerBlock(int uiBlock) {
// 测试我们处于小区块还是大扇区
if (uiBlock < 128)
return ((uiBlock + 1) % 4 == 0);
else
return ((uiBlock + 1) % 16 == 0);
}
public static int getBlockCountInSector(int sectorIndex) {
if (!validateSector(sectorIndex)) return -1;
if (sectorIndex < 32) {
return 4;
} else {
return 16;
}
}
public static int get_trailer_block(int uiFirstBlock) {
// Test if we are in the small or big sectors
int trailer_block;
if (uiFirstBlock < 128) {
trailer_block = uiFirstBlock + (3 - (uiFirstBlock % 4));
} else {
trailer_block = uiFirstBlock + (15 - (uiFirstBlock % 16));
}
return trailer_block;
}
public static int getIndexOnSector(int block, int sector) {
int index = 0;
//得到当前的块在扇区中的具体索引!
for (int i = 0; i < getBlockCountInSector(sector); i++) { //得到当前扇区的块总数!
if (block == (sectorToBlock(block) + i)) break;
++index;
}
return index;
}
}
+180
View File
@@ -0,0 +1,180 @@
package com.proxgrind.chameleon.utils.mifare;
import java.io.IOException;
import java.io.Serializable;
/**
* MifareClassic定义
*/
public interface MifareAdapter extends Serializable {
/*
* 一个标准的MifareClassic
* 是可以被链接,被验证,被读取,被写入,被增值,被减值,被重置的!
* */
/**
* 重新查找获取标签!
*
* @return 查找结果!
*/
boolean rescantag() throws IOException;
/**
* 链接标签
*
* @return 链接结果!
*/
boolean connect() throws IOException;
/**
* 断开标签
*/
void close() throws IOException;
/**
* 读取标签
*
* @param block 读取的块
* @return 读取结果, 可能为null
*/
byte[] read(int block) throws IOException;
/**
* 写入标签
*
* @param blockIndex 写入的块,
* @param data 将被写入的数据,必须是16字节长度的Hex字符串!
* @return 写入结果!
*/
boolean write(int blockIndex, byte[] data) throws IOException;
/**
* 验证密钥A
*
* @param sectorIndex 被验证的块
* @param key 用来验证的密钥
* @return 验证结果!
*/
boolean authA(int sectorIndex, byte[] key) throws IOException;
/**
* 验证密钥B
*
* @param sectorIndex 被验证的块
* @param key 用来验证的密钥
* @return 验证结果!
*/
boolean authB(int sectorIndex, byte[] key) throws IOException;
/**
* 增值
*
* @param blockIndex 被增值的块
* @param value 非负递增的值
*/
void increment(int blockIndex, int value) throws IOException;
/**
* 增值
*
* @param blockIndex 被增值的块
* @param value 非负递减的值
*/
void decrement(int blockIndex, int value) throws IOException;
/**
* 恢复增值减值操作
*
* @param blockIndex 被恢复的块
*/
void restore(int blockIndex) throws IOException;
/**
* 转移值数据到块
*
* @param blockIndex 被转移的块
*/
void transfer(int blockIndex) throws IOException;
/**
* 获得UID
*
* @return UID字节数组
*/
byte[] getUid();
byte[] getAts();
byte[] getAtqa();
byte[] getSak();
/**
* 获得类型
*
* @return 1024 or 2048 or 4096
*/
int getType();
/**
* 获得扇区数量
*
* @return 卡片支持的扇区容量
*/
int getSectorCount();
/**
* 获得块数量
*
* @return 卡片支持的块容量
*/
int getBlockCount();
/**
* 设置操作超时
*
* @param ms 被设置的超时
*/
void setTimeout(int ms);
/**
* 获取操作超时
*
* @return 超时参数
*/
int getTimeout();
/**
* 获取批量操作的实现!
* 如果返回null,则不支持批量!
*/
BatchAdapter getBatchImpl();
/**
* 是否是链接状态!
*
* @return 如果卡片已经链接(在部分实现机制上)
* 否则你可以获取结果为true的返回值,否则为false
*/
boolean isConnected();
/**
* 是否是仿真卡
*
* @return true为仿真
*/
boolean isEmulated();
/**
* 是否是后门卡!
*
* @return 是否是后门卡
*/
boolean isSpecialTag();
/**
* 是否是支持测试!
*/
boolean isTestSupported();
}
+321
View File
@@ -0,0 +1,321 @@
package com.proxgrind.chameleon.utils.mifare;
import android.content.Context;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.os.Build;
import com.proxgrind.chameleon.utils.stream.IOUtils;
import com.proxgrind.chameleon.utils.system.LogUtils;
import com.proxgrind.chameleon.utils.system.SystemUtils;
import com.proxgrind.chameleon.utils.tools.GlobalTag;
import java.io.File;
import java.io.IOException;
public class StdMifareImpl implements MifareAdapter, GlobalTag.OnNewTagListener {
private static MifareClassic mMfTag = null;
private final static StdMifareImpl stdMifareImpl = new StdMifareImpl();
/*
* 此方法需要传入一个TAG,这个TAG将被用来初始化一个MifareClassic标签!
* */
private StdMifareImpl() {
mMfTag = getMfOfGlobalTag();
GlobalTag.addListener(this);
}
public static StdMifareImpl getInstance() {
synchronized (stdMifareImpl) {
mMfTag = getMfOfGlobalTag();
}
return stdMifareImpl;
}
private static MifareClassic getMfOfGlobalTag() {
if (mMfTag != null) IOUtils.close(mMfTag);
Tag tag = GlobalTag.getTag();
if (tag != null) {
return MifareClassic.get(tag);
}
return null;
}
/**
* Check if the device supports the MIFARE Classic technology.
* In order to do so, there is a first check ensure the device actually has a NFC hardware
* After this, this function will check if there are files
* like "/dev/bcm2079x-i2c" or "/system/lib/libnfc-bcrm*". Files like
* these are indicators for a NFC controller manufactured by Broadcom.
* Broadcom chips don't support MIFARE Classic.
*
* @return True if the device supports MIFARE Classic. False otherwise.
*/
public static boolean hasMifareClassicSupport(Context context) {
// Check for the MifareClassic class.
// It is most likely there on all NFC enabled phones.
// Therefore this check is not needed.
/*
try {
Class.forName("android.nfc.tech.MifareClassic");
} catch( ClassNotFoundException e ) {
// Class not found. Devices does not support MIFARE Classic.
return false;
}
*/
// Check if ther is any NFC hardware at all.
if (NfcAdapter.getDefaultAdapter(context) == null) {
return false;
}
// Check if there is the NFC device "bcm2079x-i2c".
// Chips by Broadcom don't support MIFARE Classic.
// This could fail because on a lot of devices apps don't have
// the sufficient permissions.
// Another exception:
// The Lenovo P2 has a device at "/dev/bcm2079x-i2c" but is still
// able of reading/writing MIFARE Classic tags. I don't know why...
// https://github.com/ikarus23/MifareClassicTool/issues/152
boolean isLenovoP2 = Build.MANUFACTURER.equals("LENOVO")
&& Build.MODEL.equals("Lenovo P2a42");
File device = new File("/dev/bcm2079x-i2c");
if (!isLenovoP2 && device.exists()) {
return false;
}
// Check if there is the NFC device "pn544".
// The PN544 NFC chip is manufactured by NXP.
// Chips by NXP support MIFARE Classic.
device = new File("/dev/pn544");
if (device.exists()) {
return true;
}
// Check if there are NFC libs with "brcm" in their names.
// "brcm" libs are for devices with Broadcom chips. Broadcom chips
// don't support MIFARE Classic.
File libsFolder = new File("/system/lib");
File[] libs = libsFolder.listFiles();
if (libs != null) {
for (File lib : libs) {
if (lib.isFile()
&& lib.getName().startsWith("libnfc")
&& lib.getName().contains("brcm")
// Add here other non NXP NFC libraries.
) {
return false;
}
}
} else {
return false;
}
return true;
}
public static boolean isNfcOpened(Context context) {
if (hasMifareClassicSupport(context)) {
//判断设备是否支持NFC
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context);
// 可能出现adapter为空的情况!
if (adapter != null) {
return adapter.isEnabled();
} else {
LogUtils.d("isNfcOpenedadapter is null. ");
}
}
LogUtils.d("isNfcOpenedMifareClassic unsupported. ");
return false;
}
public MifareClassic getMf() {
return mMfTag;
}
@Override
public boolean rescantag() throws IOException {
Tag tag = GlobalTag.getTag();
if (mMfTag != null) mMfTag.close();
if (tag != null) {
mMfTag = MifareClassic.get(tag);
}
return mMfTag != null;
}
@Override
public boolean connect() throws IOException {
if (mMfTag.isConnected()) mMfTag.close();
if (mMfTag != null && !isConnected()) {
mMfTag.connect();
return true;
}
return false;
}
@Override
public void close() throws IOException {
if (mMfTag != null)
if (isConnected())
mMfTag.close();
}
@Override
public byte[] read(int block) throws IOException {
if (mMfTag != null)
return mMfTag.readBlock(block);
return null;
}
@Override
public boolean write(int blockIndex, byte[] data) throws IOException {
if (mMfTag != null) {
mMfTag.writeBlock(blockIndex, data);
return true;
}
return false;
}
@Override
public boolean authA(int sectorIndex, byte[] key) throws IOException {
if (mMfTag != null) {
return mMfTag.authenticateSectorWithKeyA(sectorIndex, key);
}
return false;
}
@Override
public boolean authB(int sectorIndex, byte[] key) throws IOException {
if (mMfTag != null)
return mMfTag.authenticateSectorWithKeyB(sectorIndex, key);
return false;
}
@Override
public void increment(int blockIndex, int value) throws IOException {
if (mMfTag != null)
mMfTag.increment(blockIndex, value);
}
@Override
public void decrement(int blockIndex, int value) throws IOException {
if (mMfTag != null)
mMfTag.decrement(blockIndex, value);
}
@Override
public void restore(int blockIndex) throws IOException {
if (mMfTag != null)
mMfTag.restore(blockIndex);
}
@Override
public void transfer(int blockIndex) throws IOException {
if (mMfTag != null)
mMfTag.transfer(blockIndex);
}
@Override
public byte[] getUid() {
if (mMfTag != null)
return mMfTag.getTag().getId();
return null;
}
@Override
public byte[] getAts() {
return new byte[0];
}
@Override
public byte[] getAtqa() {
return new byte[0];
}
@Override
public byte[] getSak() {
return new byte[0];
}
@Override
public int getType() {
if (mMfTag != null)
return mMfTag.getType();
return -1;
}
@Override
public int getSectorCount() {
if (mMfTag != null)
return mMfTag.getSectorCount();
return -1;
}
@Override
public int getBlockCount() {
if (mMfTag != null)
return mMfTag.getBlockCount();
return -1;
}
@Override
public void setTimeout(int ms) {
if (mMfTag != null)
mMfTag.setTimeout(ms);
}
@Override
public int getTimeout() {
if (mMfTag != null)
return mMfTag.getTimeout();
return -1;
}
@Override
public BatchAdapter getBatchImpl() {
// 标准NFC不支持批量验证,所以需要返回null
return null;
}
@Override
public boolean isConnected() {
synchronized (stdMifareImpl) {
if (mMfTag == null) {
LogUtils.d("MfTag对象为空,将会直接返回NULL");
return false;
}
LogUtils.d("MfTag对象不为空,将会直接返回链接状态: " + mMfTag.isConnected());
return mMfTag.isConnected();
}
}
@Override
public boolean isEmulated() {
return false;
}
@Override
public boolean isSpecialTag() {
//自带的NFC不支持特殊的后门标签直接读写,因此直接返回false即可!
return false;
}
@Override
public boolean isTestSupported() {
return false;
}
@Override
public void onNewTag(Tag tag) {
synchronized (stdMifareImpl) {
if (mMfTag != null) {
if (mMfTag.isConnected()) IOUtils.close(mMfTag);
mMfTag = getMfOfGlobalTag();
LogUtils.d("StdMifareImpl收到了卡片通知,将会进行卡片更新处理!");
}
}
}
}
+60
View File
@@ -0,0 +1,60 @@
package com.proxgrind.chameleon.utils.mifare;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.nfc.tech.MifareClassic;
public class StdMifareIntent {
private NfcAdapter mAdapter = null;
public StdMifareIntent(Context context) {
NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
//判断设备是否支持NFC
if (manager == null) return;
mAdapter = manager.getDefaultAdapter();
}
/*
* 获取当前操作的适配器!
* */
public NfcAdapter getAdapter() {
return mAdapter;
}
/*
* 注册前台
* */
public void enableForegroundDispatch(Activity targetAct) {
if (mAdapter == null) return;
try {
//进行前台广播拦截!
Intent intent = new Intent(targetAct,
targetAct.getClass()).addFlags(
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(
targetAct, 0, intent, 0);
mAdapter.enableForegroundDispatch(targetAct, pendingIntent, null, new String[][]{
new String[]{MifareClassic.class.getName()}});
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 解注册前台
* */
public void disableForegroundDispatch(Activity activity) {
if (mAdapter == null) return;
try {
//解注册前台拦截
mAdapter.disableForegroundDispatch(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}