refactor(nfc): 重构 NFC 读写助手类
-移除了 HuaZhiNfcReadHelper 类
- 更新了 BaseNfcReadHelper 和 BaseNfcWriteHelper 接口
-重构了 NativeNfcReadHelper 和 NativeNfcWriteHelper 类
- 更新了 NfcReadAdapter 和 NfcWriteAdapter 类
-调整了 ModelUtils 类中的 getModelType 方法
| | |
| | | android { |
| | | signingConfigs { |
| | | debug { |
| | | storeFile file('../dycz.jks') |
| | | storeFile file(myValue) |
| | | storePassword 'dycz@2023' |
| | | keyAlias 'dayu' |
| | | keyPassword 'dycz@2023' |
| | |
| | | import android.nfc.NfcAdapter; |
| | | import android.os.Bundle; |
| | | |
| | | import com.dayu.baselibrary.tools.nfc.HuaZhiNfcReadHepler; |
| | | import com.dayu.baselibrary.utils.ModelUtils; |
| | | import com.dayu.baselibrary.utils.TipUtil; |
| | | import com.pos.device.picc.PiccReader; |
| | | import com.pos.device.picc.PiccReaderCallback; |
| | | |
| | | /** |
| | | * author: zuo |
| | |
| | | case ModelUtils.defaultType: |
| | | case ModelUtils.ShangMiType: |
| | | adapterType = ModelUtils.defaultType; |
| | | break; |
| | | case ModelUtils.HuaZhiRongHaiType: |
| | | adapterType = ModelUtils.HuaZhiRongHaiType; |
| | | PiccReader.getInstance().startSearchCard(HuaZhiNfcReadHepler.TIMEOUT, new PiccReaderCallback() { |
| | | @Override |
| | | public void onSearchResult(int result, int cardType) { |
| | | if (result == PiccReaderCallback.SUCCESS) { |
| | | switch (cardType) { |
| | | //以下为M1卡 |
| | | case PiccReader.MIFARE_ONE_S50: |
| | | case PiccReader.MIFARE_ONE_S70: |
| | | onNfcBack(null); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | break; |
| | | } |
| | | |
| | |
| | | package com.dayu.baselibrary.tools.nfc; |
| | | |
| | | import android.content.Intent; |
| | | |
| | | import com.dayu.baselibrary.bean.BaseManagerToUserCard; |
| | | import com.dayu.baselibrary.bean.BaseUserCardCard; |
| | | |
| | |
| | | public abstract class BaseNfcReadHelper extends BaseNFCHelper { |
| | | |
| | | |
| | | public abstract void setIntent(Intent intent); |
| | | |
| | | public abstract String getCradType(); |
| | | |
| | | /** |
| | |
| | | package com.dayu.baselibrary.tools.nfc; |
| | | |
| | | import android.content.Intent; |
| | | import android.nfc.tech.MifareClassic; |
| | | |
| | | import com.dayu.baselibrary.bean.BaseUserCardCard; |
| | |
| | | * 备注: |
| | | */ |
| | | public abstract class BaseNfcWriteHelper extends BaseNFCHelper { |
| | | |
| | | public abstract void setIntent(Intent intent); |
| | | |
| | | /** |
| | | * 写卡 |
| | | * |
| | | * @param userCard 用户卡内容 |
| | | * @param |
| | | * @param sector 书写的扇区 (从0开始数) |
| | | */ |
| | | public abstract boolean writeUserData(BaseUserCardCard userCard); |
| | | public abstract boolean writeUserData(BaseUserCardCard userCard,int sector); |
| | | |
| | | /** |
| | | * 写卡 |
| | |
| | | |
| | | |
| | | public NativeNfcReadHelper(Intent intent, Activity activity) { |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void setIntent(Intent intent) { |
| | | this.tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 单例初始化 |
| | |
| | | if (helper == null) { |
| | | helper = new NativeNfcReadHelper(intent, activity); |
| | | } |
| | | helper.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) { |
| | | if (userCardCard!=null){ |
| | | if (userCardCard != null) { |
| | | BaseUserCardCard userCard = null; |
| | | Map<String, List<byte[]>> map = new HashMap<>(); |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String getCradType() { |
| | |
| | | mfc.connect(); |
| | | //获取当前卡号 |
| | | boolean isOpen = false; |
| | | if (!listKeyA.isEmpty()){ |
| | | if (!listKeyA.isEmpty()) { |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | isOpen = true; |
| | | break; |
| | | } |
| | | } |
| | | }else if (!listA_PS.isEmpty()){ |
| | | if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))){ |
| | | } else if (!listA_PS.isEmpty()) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))) { |
| | | isOpen = true; |
| | | } |
| | | } |
| | |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | public String getCardNumberNoClose() { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | | try { |
| | | mfc.connect(); |
| | | //获取当前卡号 |
| | | boolean isOpen = false; |
| | | if (!listKeyA.isEmpty()) { |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | isOpen = true; |
| | | break; |
| | | } |
| | | } |
| | | } else if (!listA_PS.isEmpty()) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))) { |
| | | 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); |
| | | Log.i("NFCWreatActivity", "hex===" + hex); |
| | | return hex.toUpperCase(); |
| | | } |
| | | } |
| | | |
| | | } catch (IOException e) { |
| | | Log.i("NFCWreatActivity", e.toString()); |
| | | return BaseCommon.CARD_TYPE_ERROR1; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /** |
| | | * 获取卡片类型和卡号 |
| | | * |
| | |
| | | private static NativeNfcWriteHelper helper; |
| | | |
| | | public NativeNfcWriteHelper(Intent intent, Activity activity) { |
| | | } |
| | | |
| | | public void setIntent(Intent intent) { |
| | | this.tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); |
| | | } |
| | | |
| | |
| | | if (helper == null) { |
| | | helper = new NativeNfcWriteHelper(intent, activity); |
| | | } |
| | | helper.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | * @param |
| | | */ |
| | | @Override |
| | | public boolean writeUserData(BaseUserCardCard userCard) { |
| | | public boolean writeUserData(BaseUserCardCard userCard, int sector) { |
| | | if (userCard != null) { |
| | | int a = 1; |
| | | int a = sector; |
| | | try { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | |
| | | @Override |
| | | public boolean writeData(byte[] str, int a, int b, NFCCallBack callBack) { |
| | | Log.i("NFCWreatActivity", "writeData: a=" + a + " b=" + b); |
| | | if (str.length <= 16) { |
| | | if (str.length == 16) { |
| | | try { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | | if (null != mfc) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (isOpen) { |
| | | int bIndex = mfc.sectorToBlock(a); |
| | | //写卡 |
| | |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | callBack.isSusses(false, a + "扇区写卡报错" + e.getMessage()); |
| | | return false; |
| | | } |
| | | } else { |
| | | callBack.isSusses(false, a + "扇区写卡报错,byte数组大小不为16"); |
| | | } |
| | | return false; |
| | | } |
| | |
| | | |
| | | NativeNfcReadHelper nativeNfcReadHelper; |
| | | |
| | | @Override |
| | | public void setIntent(Intent intent) { |
| | | nativeNfcReadHelper.setIntent(intent); |
| | | } |
| | | |
| | | public NfcReadAdapter(Intent intent, Activity activity) { |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcReadHelper.getCardNumber(); |
| | | |
| | | |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | public String getCardNumberNoClose() { |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcReadHelper.getCardNumberNoClose(); |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String getCradTypeAndCardNumber() { |
| | | switch (BaseNfcActivity.adapterType) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public boolean writeUserData(BaseUserCardCard userCard) { |
| | | public void setIntent(Intent intent) { |
| | | nativeNfcWriteHelper.setIntent(intent); |
| | | } |
| | | |
| | | @Override |
| | | public boolean writeUserData(BaseUserCardCard userCard,int sector) { |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcWriteHelper.writeUserData(userCard); |
| | | return nativeNfcWriteHelper.writeUserData(userCard, sector); |
| | | |
| | | } |
| | | return false; |
| | |
| | | //判断是哪个机型 |
| | | public static int getModelType() { |
| | | int type; |
| | | try { |
| | | Class.forName("com.pos.device.config.DevConfig"); |
| | | type = ModelUtils.HuaZhiRongHaiType; |
| | | return type; |
| | | } catch (ClassNotFoundException e) { |
| | | // try { |
| | | // Class.forName("com.pos.device.config.DevConfig"); |
| | | // type = ModelUtils.HuaZhiRongHaiType; |
| | | // return type; |
| | | // } catch (ClassNotFoundException e) { |
| | | type = ModelUtils.ShangMiType; |
| | | return type; |
| | | } |
| | | // } |
| | | } |
| | | } |
| | |
| | | id 'com.android.application' version '7.2.2' apply false |
| | | id 'com.android.library' version '7.2.2' apply false |
| | | id 'org.jetbrains.kotlin.android' version '1.9.0' apply false |
| | | } |
| | | |
| | | ext { |
| | | myValue = 'C:\\Users\\User\\StudioProjects\\charge\\dycz.jks' |
| | | } |
| | |
| | | <activity android:name="com.dayu.general.activity.NewCardActivity" /> |
| | | <activity android:name="com.dayu.general.activity.ManageListActivity" /> |
| | | <activity android:name="com.dayu.general.activity.SearchUserActivity"/> |
| | | <activity android:name="com.dayu.general.activity.NfcWreatActivity" /> |
| | | <activity android:name="com.dayu.general.activity.NfcWreatActivity" |
| | | android:exported="false" |
| | | android:launchMode="singleTop" |
| | | > |
| | | <intent-filter> |
| | | <action android:name="android.nfc.action.ACTION_NDEF_DISCOVERED" /> |
| | | <category android:name="android.intent.category.DEFAULT" /> |
| | | <data android:mimeType="text/plain" /> |
| | | </intent-filter> |
| | | </activity> |
| | | <activity android:name=".activity.MainActivity"/> |
| | | <activity |
| | | android:name=".activity.ManagerReadActivity" |
| | | android:screenOrientation="portrait" |
| | | android:windowSoftInputMode="adjustResize"/> |
| | | android:exported="false" |
| | | android:launchMode="singleTop"> |
| | | <intent-filter> |
| | | <action android:name="android.nfc.action.ACTION_NDEF_DISCOVERED" /> |
| | | <category android:name="android.intent.category.DEFAULT" /> |
| | | <data android:mimeType="text/plain" /> |
| | | </intent-filter> |
| | | </activity> |
| | | |
| | | |
| | | |
| | |
| | | #include <android/log.h> |
| | | #include <string.h> |
| | | #include <jni.h> |
| | | #include <sstream> |
| | | #include <iomanip> |
| | | |
| | | static jclass contextClass; |
| | | static jclass signatureClass; |
| | | static jclass packageNameClass; |
| | | static jclass packageInfoClass; |
| | | // 声明全局静态变量,用于存储Java类的引用 |
| | | static jclass contextClass; // Android Context类的引用 |
| | | static jclass signatureClass; // Android Signature类的引用 |
| | | static jclass packageNameClass; // Android PackageManager类的引用 |
| | | static jclass packageInfoClass; // Android PackageInfo类的引用 |
| | | |
| | | // 定义发布版本的签名常量 |
| | | const char *RELEASE_SIGN = "308202b8308201a0020101300d06092a864886f70d01010b05003022310f300d06035504030c06e5a4a7e7a6b9310f300d06035504070c06e5a4a9e6b4a5301e170d3233313132303035333131325a170d3438313131333035333131325a3022310f300d06035504030c06e5a4a7e7a6b9310f300d06035504070c06e5a4a9e6b4a530820122300d06092a864886f70d01010105000382010f003082010a0282010100a0924f3d618e4a622def691e16e54ce5bdfd035bd73e7cb947d2bf3bd0c00afa26e52963e0299fc06d76d153be696c5285d630577e1dcb2b740a72b6d904482217de308fb91c8435441ed05e844ced1e5c3446d82cb8f38751049df26a42adcfc33f1f12c2ce03f676e5d148aad800ace89670b87835e2c02a8570a0b6740d9c0669d4cb3c597d0b2dd49fc0904e885773b6d3a87d9f1e73eb526e0d1a9e9e3c48d986938286cd824151b5a6214faf89d3e699524511b23c86d3b110a7f0bb56a6d2436f69816538a62a38cb1fee6eb685d267cc200df8af51b936bd280beaa2023f75678d77a11ac6de734b30af63d394c8b63bccf2115a47ea15c9212c740d0203010001300d06092a864886f70d01010b05000382010100307cafa9b14be91ba6424cfcc6aed75b069a1c4d6eb646eab0de93f372f236f5f0a6097499df99391075d6ced18d419a2b15adb041890e2b56a3bfbd6be40efee99c5c713ba8ea1d45da09b67916106116e96eb735271c4d53e0739f753145cbc42e149ad3d9507d422ec1c6f1a7f792a4542f9a64f0de3d4f4af69f0fb3390ef3577dcf8844cf744426d173b0934d879148062c5ca64022dc99af370dbfeaf2b5d4a279b20c54a361bca12c25bf185c2885519bbbc36e46ddb083080f0cc5b1f2eafe964ebce5071b0ae7d92a34a9193861b996d2c0299b1993f41063a27038199365a6e3cb27a02ffa9facdc48a63713eb5fbf90e9fd73056aba16b28e5fee"; |
| | | |
| | | // 定义XOR加密密钥数组 |
| | | const unsigned char XOR_KEY[] = {0x7A, 0xB3, 0xC9, 0x7B, 0xE3, 0x78}; // 修改后三个字节的密钥 |
| | | |
| | | // 加密密钥 |
| | | const unsigned char XOR_KEY[] = {0x7A, 0xB3, 0xC9, 0xD5, 0xE1, 0xF8, 0x42, 0x91, 0x37, 0x8F, 0x5D, |
| | | 0x2E}; |
| | | // 定义后半部分XOR密钥数组 |
| | | const unsigned char XOR_KEY_BACK[] = {0x4D, 0xE4, 0x7C, 0x2A, 0x8B, 0x7F}; |
| | | |
| | | // 加密后的M1卡密码数组 |
| | | // 定义加密后的M1卡扇区密钥数组 |
| | | const unsigned char ENCRYPTED_SECTOR_KEYS[] = { |
| | | // 扇区0密码 (加密后) |
| | | 0x9C, 0x45, 0x36, 0x55, 0x8C, 0x7F, 0x35, 0x93, 0x4B, 0x78, 0x2A, 0x79, |
| | | // 扇区1密码 (加密后) |
| | | 0xB4, 0x52, 0x91, 0x45, 0x99, 0x87, 0x3C, 0xA3, 0x6E, 0x5F, 0x1D, 0x5E, |
| | | // 以下类似格式存储其他扇区密码... |
| | | 0x1E, 0x85, 0xEB, 0x51, 0xB9, 0x39, 0x02, 0x2E, 0x77, 0x1F, 0x3E, 0x93, |
| | | 0x22, 0x62, 0xBD, 0x83, 0x99, 0x2A, 0x2F, 0x19, 0x95, 0x07, 0x29, 0x2E, |
| | | 0x90, 0x18, 0xE0, 0x6A, 0xB7, 0x68, 0x34, 0x89, 0x42, 0x5B, 0x16, 0x47, |
| | | 0xA8, 0x0B, 0xB5, 0x97, 0xB4, 0x43, 0x1D, 0x62, 0x89, 0x73, 0x33, 0x2E, |
| | | 0x24, 0x9D, 0xB7, 0x46, 0x8D, 0x64, 0x49, 0x7A, 0x52, 0x41, 0x1C, 0x91, |
| | | 0x31, 0x78, 0x95, 0x52, 0x95, 0x39, 0x19, 0x4C, 0x81, 0x2A, 0x0D, 0x47, |
| | | 0xF5, 0x64, 0xA7, 0x93, 0xB9, 0x51, 0x27, 0x82, 0x43, 0x68, 0x31, 0x3F, |
| | | 0x55, 0x93, 0xB7, 0x68, 0x97, 0x9A, 0x37, 0x71, 0x52, 0x49, 0x17, 0x1E, |
| | | 0x8E, 0x93, 0xB5, 0x44, 0x8D, 0x42, 0x22, 0x84, 0x95, 0x33, 0x22, 0x93, |
| | | 0x42, 0x82, 0xA3, 0x35, 0x91, 0x33, 0x13, 0x93, 0x71, 0x21, 0x01, 0x71, |
| | | 0x37, 0x9F, 0xA6, 0x68, 0x92, 0x86, 0x46, 0x72, 0x43, 0x62, 0x12, 0x52, |
| | | 0x5D, 0x85, 0x93, 0x86, 0x82, 0x46, 0x31, 0x86, 0x57, 0x48, 0x16, 0x88, |
| | | 0x97, 0x73, 0xB5, 0x47, 0x95, 0x55, 0x36, 0x69, 0x49, 0x58, 0x18, 0x6A, |
| | | 0xEA, 0x46, 0x84, 0x93, 0x82, 0x19, 0x29, 0x91, 0x31, 0x1C, 0x0C, 0x7D |
| | | // 扇区0密码 (加密后) |
| | | 0x9C, 0x45, 0x36, 0x55, 0x8C, 0x7F, 0x35, 0x93, 0x4B, 0x78, 0x2A, 0x79, |
| | | // 扇区1密码 (加密后) |
| | | 0xB4, 0x52, 0x91, 0x45, 0x99, 0x87, 0x3C, 0xA3, 0x6E, 0x5F, 0x1D, 0x5E, |
| | | // 以下类似格式存储其他扇区密码... |
| | | 0x1E, 0x85, 0xEB, 0x51, 0xB9, 0x39, 0x02, 0x2E, 0x77, 0x1F, 0x3E, 0x93, |
| | | 0x22, 0x62, 0xBD, 0x83, 0x99, 0x2A, 0x2F, 0x19, 0x95, 0x07, 0x29, 0x2E, |
| | | 0x90, 0x18, 0xE0, 0x6A, 0xB7, 0x68, 0x34, 0x89, 0x42, 0x5B, 0x16, 0x47, |
| | | 0xA8, 0x0B, 0xB5, 0x97, 0xB4, 0x43, 0x1D, 0x62, 0x89, 0x73, 0x33, 0x2E, |
| | | 0x24, 0x9D, 0xB7, 0x46, 0x8D, 0x64, 0x49, 0x7A, 0x52, 0x41, 0x1C, 0x91, |
| | | 0x31, 0x78, 0x95, 0x52, 0x95, 0x39, 0x19, 0x4C, 0x81, 0x2A, 0x0D, 0x47, |
| | | 0xF5, 0x64, 0xA7, 0x93, 0xB9, 0x51, 0x27, 0x82, 0x43, 0x68, 0x31, 0x3F, |
| | | 0x55, 0x93, 0xB7, 0x68, 0x97, 0x9A, 0x37, 0x71, 0x52, 0x49, 0x17, 0x1E, |
| | | 0x8E, 0x93, 0xB5, 0x44, 0x8D, 0x42, 0x22, 0x84, 0x95, 0x33, 0x22, 0x93, |
| | | 0x42, 0x82, 0xA3, 0x35, 0x91, 0x33, 0x13, 0x93, 0x71, 0x21, 0x01, 0x71, |
| | | 0x37, 0x9F, 0xA6, 0x68, 0x92, 0x86, 0x46, 0x72, 0x43, 0x62, 0x12, 0x52, |
| | | 0x5D, 0x85, 0x93, 0x86, 0x82, 0x46, 0x31, 0x86, 0x57, 0x48, 0x16, 0x88, |
| | | 0x97, 0x73, 0xB5, 0x47, 0x95, 0x55, 0x36, 0x69, 0x49, 0x58, 0x18, 0x6A, |
| | | 0xEA, 0x46, 0x84, 0x93, 0x82, 0x19, 0x29, 0x91, 0x31, 0x1C, 0x0C, 0x7D |
| | | }; |
| | | |
| | | // 解密函数 |
| | | void decrypt_key(const unsigned char *encrypted, char *decrypted, size_t len) { |
| | | // 将字节数组转换为十六进制字符串的函数 |
| | | std::string bytesToHexString(const unsigned char* data, size_t len) { |
| | | std::stringstream ss; // 创建字符串流对象 |
| | | ss << std::hex << std::uppercase << std::setfill('0'); // 设置输出格式为十六进制,大写,不足两位补0 |
| | | for (size_t i = 0; i < len; i++) { |
| | | decrypted[i] = encrypted[i] ^ XOR_KEY[i % sizeof(XOR_KEY)]; |
| | | ss << std::setw(2) << static_cast<int>(data[i]); // 将每个字节转换为两位十六进制 |
| | | } |
| | | decrypted[len] = '\0'; |
| | | return ss.str(); // 返回转换后的字符串 |
| | | } |
| | | |
| | | //extern "C" JNIEXPORT jstring |
| | | // |
| | | //JNICALL |
| | | //Java_com_yglx_testjni_MainActivity_getKey( |
| | | // JNIEnv *env, |
| | | // jobject /* this */) { |
| | | // return env->NewStringUTF(AUTH_KEY); |
| | | //} |
| | | // 解密函数:使用XOR密钥解密数据 |
| | | jstring decrypt_key(JNIEnv* env, const unsigned char* encrypted_data, int length) { |
| | | if (!encrypted_data || length < 6) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "GeBaseHelper", "Invalid input data"); |
| | | return nullptr; |
| | | } |
| | | |
| | | // 为不同扇区使用不同的XOR密钥 |
| | | const unsigned char XOR_KEYS[16][6] = { |
| | | {0x7A, 0xB3, 0xC9, 0x72, 0xFC, 0x78}, // 扇区0 |
| | | {0x7A, 0xB3, 0xC9, 0x5A, 0x05, 0xB7}, // 扇区1 |
| | | {0x7A, 0xB3, 0x63, 0x10, 0xF9, 0xA4}, // 扇区2 |
| | | {0x7A, 0x73, 0x35, 0xD1, 0xFE, 0x71}, // 扇区3 - 修改第3个字节 |
| | | {0x2A, 0x93, 0xF8, 0x13, 0xB1, 0x31}, // 扇区4 |
| | | {0x7A, 0xB3, 0x79, 0x6D, 0x40, 0x18}, // 扇区5 |
| | | {0x7A, 0xB1, 0x19, 0xDA, 0xD6, 0x9A}, // 扇区6 |
| | | {0x7A, 0x13, 0xDD, 0x63, 0x3E, 0x63}, // 扇区7 |
| | | {0x7A, 0x73, 0x71, 0x5D, 0x89, 0x11}, // 扇区8 |
| | | {0x7A, 0x73, 0xF1, 0xE2, 0xFF, 0xB1}, // 扇区9 |
| | | {0x7A, 0x6D, 0xE9, 0x42, 0xBE, 0xA0}, // 扇区10 |
| | | {0x7A, 0x43, 0xB1, 0x78, 0x76, 0x3D}, // 扇区11 |
| | | {0x79, 0x43, 0xD1, 0xD1, 0x70, 0xEB}, // 扇区12 |
| | | {0x7A, 0xB3, 0xBF, 0x49, 0x6B, 0xB1}, // 扇区13 |
| | | {0x2B, 0xA3, 0xB3, 0x00, 0x19, 0x2C}, // 扇区14 |
| | | {0x7A, 0xF3, 0xDA, 0x8B, 0x98, 0x15} // 扇区15 |
| | | }; |
| | | |
| | | // 获取当前扇区号 |
| | | int sector = -1; |
| | | for (int i = 0; i < 16; i++) { |
| | | if (memcmp(encrypted_data, &ENCRYPTED_SECTOR_KEYS[i * 12], 6) == 0) { |
| | | sector = i; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (sector == -1) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "GeBaseHelper", "Unknown sector"); |
| | | return nullptr; |
| | | } |
| | | |
| | | // __android_log_print(ANDROID_LOG_INFO, "GeBaseHelper", "Decrypting sector %d", sector); |
| | | // __android_log_print(ANDROID_LOG_INFO, "GeBaseHelper", "Encrypted data: %02X %02X %02X %02X %02X %02X", |
| | | // encrypted_data[0], encrypted_data[1], encrypted_data[2], |
| | | // encrypted_data[3], encrypted_data[4], encrypted_data[5]); |
| | | // __android_log_print(ANDROID_LOG_INFO, "GeBaseHelper", "Using XOR key: %02X %02X %02X %02X %02X %02X", |
| | | // XOR_KEYS[sector][0], XOR_KEYS[sector][1], XOR_KEYS[sector][2], |
| | | // XOR_KEYS[sector][3], XOR_KEYS[sector][4], XOR_KEYS[sector][5]); |
| | | |
| | | unsigned char decrypted[6]; |
| | | for (int i = 0; i < 6; i++) { |
| | | decrypted[i] = encrypted_data[i] ^ XOR_KEYS[sector][i]; |
| | | // __android_log_print(ANDROID_LOG_INFO, "GeBaseHelper", "Byte %d: %02X ^ %02X = %02X", |
| | | // i, encrypted_data[i], XOR_KEYS[sector][i], decrypted[i]); |
| | | } |
| | | |
| | | std::string result; |
| | | result.reserve(13); // 6 bytes * 2 chars + null terminator |
| | | char hex[3]; |
| | | for (int i = 0; i < 6; i++) { |
| | | snprintf(hex, sizeof(hex), "%02X", decrypted[i]); |
| | | result += hex; |
| | | } |
| | | |
| | | // __android_log_print(ANDROID_LOG_INFO, "GeBaseHelper", "Decrypted result: %s", result.c_str()); |
| | | return env->NewStringUTF(result.c_str()); |
| | | } |
| | | |
| | | // JNI库加载时的初始化函数 |
| | | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { |
| | | |
| | | JNIEnv *env = NULL; |
| | | jint result = -1; |
| | | JNIEnv *env = NULL; // 声明JNI环境指针 |
| | | jint result = -1; // 初始化返回值为错误状态 |
| | | // 获取JNI环境 |
| | | if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) |
| | | return result; |
| | | |
| | | // 获取并保存Java类的全局引用 |
| | | contextClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/Context")); |
| | | signatureClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/Signature")); |
| | | packageNameClass = (jclass) env->NewGlobalRef( |
| | |
| | | packageInfoClass = (jclass) env->NewGlobalRef( |
| | | (env)->FindClass("android/content/pm/PackageInfo")); |
| | | |
| | | __android_log_print(ANDROID_LOG_DEBUG, "jw", "sss"); |
| | | return JNI_VERSION_1_4; |
| | | __android_log_print(ANDROID_LOG_DEBUG, "M1Card", "JNI_OnLoad completed"); // 输出初始化完成日志 |
| | | return JNI_VERSION_1_4; // 返回JNI版本 |
| | | } |
| | | |
| | | |
| | | // 获取M1卡扇区密钥的JNI函数 |
| | | extern "C" |
| | | JNIEXPORT jstring JNICALL |
| | | Java_com_dayu_general_tool_GeBaseHelper_getM1SectorKeySecure(JNIEnv *env, jobject instance, |
| | | jobject contextObject, |
| | | jint sectorIndex) { |
| | | // 参数有效性检查 |
| | | if (!contextObject || !env) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Invalid parameters"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取包管理器相关方法ID |
| | | jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager", |
| | | "()Landroid/content/pm/PackageManager;"); |
| | | jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo", |
| | | "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); |
| | | jmethodID getPackageManagerId = env->GetMethodID(contextClass, "getPackageManager", |
| | | "()Landroid/content/pm/PackageManager;"); |
| | | jmethodID getPackageNameId = env->GetMethodID(contextClass, "getPackageName", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID signToStringId = env->GetMethodID(signatureClass, "toCharsString", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID getPackageInfoId = env->GetMethodID(packageNameClass, "getPackageInfo", |
| | | "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); |
| | | |
| | | // 检查方法ID获取是否成功 |
| | | if (!getPackageManagerId || !getPackageNameId || !signToStringId || !getPackageInfoId) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get method IDs"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取应用签名信息 |
| | | jobject packageManagerObject = (env)->CallObjectMethod(contextObject, getPackageManagerId); |
| | | jstring packNameString = (jstring) (env)->CallObjectMethod(contextObject, getPackageNameId); |
| | | jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId, |
| | | packNameString, 64); |
| | | jfieldID signaturefieldID = (env)->GetFieldID(packageInfoClass, "signatures", |
| | | "[Landroid/content/pm/Signature;"); |
| | | jobjectArray signatureArray = (jobjectArray) (env)->GetObjectField(packageInfoObject, |
| | | signaturefieldID); |
| | | jobject signatureObject = (env)->GetObjectArrayElement(signatureArray, 0); |
| | | jobject packageManagerObject = env->CallObjectMethod(contextObject, getPackageManagerId); |
| | | if (!packageManagerObject) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get PackageManager"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | jstring packNameString = (jstring) env->CallObjectMethod(contextObject, getPackageNameId); |
| | | if (!packNameString) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get package name"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | jobject packageInfoObject = env->CallObjectMethod(packageManagerObject, getPackageInfoId, |
| | | packNameString, 64); |
| | | if (!packageInfoObject) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get PackageInfo"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取签名数组字段ID |
| | | jfieldID signaturefieldID = env->GetFieldID(packageInfoClass, "signatures", |
| | | "[Landroid/content/pm/Signature;"); |
| | | if (!signaturefieldID) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get signature field ID"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取签名数组 |
| | | jobjectArray signatureArray = (jobjectArray) env->GetObjectField(packageInfoObject, |
| | | signaturefieldID); |
| | | if (!signatureArray || env->GetArrayLength(signatureArray) == 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "No signatures found"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取第一个签名对象 |
| | | jobject signatureObject = env->GetObjectArrayElement(signatureArray, 0); |
| | | if (!signatureObject) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get signature object"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 获取签名字符串 |
| | | const char *signStrng = (env)->GetStringUTFChars( |
| | | (jstring) (env)->CallObjectMethod(signatureObject, signToStringId), 0); |
| | | jstring signatureString = (jstring) env->CallObjectMethod(signatureObject, signToStringId); |
| | | if (!signatureString) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get signature string"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 调试日志输出 |
| | | __android_log_print(ANDROID_LOG_DEBUG, "M1Card", "Current signature: %s", signStrng); |
| | | // 获取签名字符串的UTF-8字符 |
| | | const char *signStrng = env->GetStringUTFChars(signatureString, 0); |
| | | if (!signStrng) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to get signature UTF chars"); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | |
| | | // 输出当前签名用于调试 |
| | | // __android_log_print(ANDROID_LOG_DEBUG, "M1Card", "Current signature: %s", signStrng); |
| | | |
| | | // 验证签名并返回所有扇区密码 |
| | | if (strcmp(signStrng, RELEASE_SIGN) == 0) { |
| | | size_t numKeys = sizeof(ENCRYPTED_SECTOR_KEYS) / 12; // 每个密钥12字节 |
| | | char *allKeys = new char[numKeys * 13]; // 为每个密钥预留12个字符+1个分隔符 |
| | | allKeys[0] = '\0'; |
| | | // 计算密钥数量 |
| | | size_t numKeys = sizeof(ENCRYPTED_SECTOR_KEYS) / 12; |
| | | std::string result; |
| | | result.reserve(numKeys * 13); // 每个密钥12个字符+1个分隔符 |
| | | |
| | | // 解密并转换每个扇区密钥 |
| | | for (size_t i = 0; i < numKeys; i++) { |
| | | char decrypted[13]; |
| | | decrypt_key(ENCRYPTED_SECTOR_KEYS + (i * 12), decrypted, 12); |
| | | |
| | | strcat(allKeys, decrypted); |
| | | if (i < numKeys - 1) { |
| | | strcat(allKeys, ","); |
| | | // __android_log_print(ANDROID_LOG_DEBUG, "M1Card", "处理扇区 %zu 的密钥", i); |
| | | |
| | | // 确保不会越界访问 |
| | | if (i * 12 + 6 > sizeof(ENCRYPTED_SECTOR_KEYS)) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Index out of bounds"); |
| | | break; |
| | | } |
| | | |
| | | // 获取解密后的字符串 |
| | | jstring decrypted_str = decrypt_key(env, ENCRYPTED_SECTOR_KEYS + (i * 12), 6); |
| | | |
| | | if (!decrypted_str) { |
| | | // __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Failed to decrypt key for sector %zu", i); |
| | | continue; |
| | | } |
| | | |
| | | // 转换成C字符串 |
| | | const char* decrypted_chars = env->GetStringUTFChars(decrypted_str, 0); |
| | | |
| | | if (i > 0) { |
| | | result += ","; // 添加分隔符 |
| | | } |
| | | result += decrypted_chars; // 使用解密后的数据 |
| | | |
| | | // 释放资源 |
| | | env->ReleaseStringUTFChars(decrypted_str, decrypted_chars); |
| | | env->DeleteLocalRef(decrypted_str); |
| | | } |
| | | |
| | | jstring result = (env)->NewStringUTF(allKeys); |
| | | delete[] allKeys; |
| | | return result; |
| | | // 释放资源并返回结果 |
| | | env->ReleaseStringUTFChars(signatureString, signStrng); |
| | | return env->NewStringUTF(result.c_str()); |
| | | } else { |
| | | return (env)->NewStringUTF("signature_verification_failed"); |
| | | // 签名验证失败 |
| | | __android_log_print(ANDROID_LOG_ERROR, "M1Card", "Signature verification failed"); |
| | | env->ReleaseStringUTFChars(signatureString, signStrng); |
| | | return env->NewStringUTF(""); |
| | | } |
| | | } |
| | | |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.content.Intent |
| | | import android.os.Bundle |
| | | import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE |
| | | import com.dayu.general.databinding.ActivityManageListGeBinding |
| | | import com.dayu.general.tool.CardCommon |
| | | |
| | | class ManageListActivity : BaseActivity() { |
| | | |
| | |
| | | |
| | | fun initView() { |
| | | binding?.titleBar?.setOnItemclickListner(ClickType_LEFT_IMAGE) { this.finish() } |
| | | binding?.tvCleanCard?.setOnClickListener { |
| | | var intent = Intent(this, ManagerReadActivity::class.java) |
| | | intent.putExtra("cardType", CardCommon.CLEAN_CARD_TYPE) |
| | | startActivity(intent) |
| | | } |
| | | binding?.tvCheckCard?.setOnClickListener { |
| | | var intent = Intent(this, ManagerReadActivity::class.java) |
| | | intent.putExtra("cardType", CardCommon.CHECK_CARD) |
| | | startActivity(intent) |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.content.Intent |
| | | import android.os.Bundle |
| | | import android.view.View |
| | | import androidx.databinding.DataBindingUtil |
| | | import com.dayu.baselibrary.net.subscribers.SubscriberListener |
| | | import com.dayu.baselibrary.utils.BaseCommon |
| | | import com.dayu.baselibrary.utils.ToastUtil |
| | | import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE |
| | | import com.dayu.general.R |
| | | import com.dayu.general.databinding.ActivityManagerReadBinding |
| | | import com.dayu.general.model.CardInfoModel |
| | | import com.dayu.general.net.ApiManager |
| | | import com.dayu.general.net.BaseResponse |
| | | import com.dayu.general.tool.NfcReadHelper |
| | | |
| | | class ManagerReadActivity : BaseNfcActivity() { |
| | | |
| | | private lateinit var binding: ActivityManagerReadBinding |
| | | private val viewModel: CardInfoModel = CardInfoModel() |
| | | var cardType = "" |
| | | |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | |
| | | // 初始化数据绑定 |
| | | binding = DataBindingUtil.setContentView(this, R.layout.activity_manager_read) |
| | | binding.lifecycleOwner = this |
| | | binding.viewModel = viewModel |
| | | if (intent.hasExtra("cardType")) { |
| | | cardType = intent.getStringExtra("cardType").toString() |
| | | } |
| | | |
| | | initView() |
| | | initListener() |
| | | } |
| | | |
| | | private fun initView() { |
| | | binding.titleBar.setCenterText("读卡") |
| | | binding.titleBar.setOnItemclickListner(ClickType_LEFT_IMAGE) { this.finish() } |
| | | binding.btnNext.setOnClickListener { |
| | | if (viewModel.cardNumber.value.isNullOrEmpty()) { |
| | | ToastUtil.show("请先读取卡号") |
| | | } else { |
| | | postCardData(cardType, viewModel.cardNumber.value!!, getRemark()) |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | private fun initListener() { |
| | | binding.titleBar.setOnItemclickListner(ClickType_LEFT_IMAGE) { finish() } |
| | | } |
| | | |
| | | /** |
| | | * 设置卡号 |
| | | */ |
| | | fun setCardNumber(cardNumber: String) { |
| | | viewModel.setCardNumber(cardNumber) |
| | | } |
| | | |
| | | /** |
| | | * 获取备注信息 |
| | | */ |
| | | fun getRemark(): String = viewModel.remark.value ?: "" |
| | | |
| | | override fun onNfcBack(intent: Intent) { |
| | | // 处理NFC读取结果 |
| | | NfcReadHelper.getInstance(intent, this).getCardNumber().let { cardNumber -> |
| | | if (cardNumber.isNotEmpty() && !cardNumber.contains(BaseCommon.CARD_TYPE_ERROR2)) { |
| | | binding.btnNext.visibility = View.VISIBLE |
| | | binding.dataLayout.visibility = View.VISIBLE |
| | | binding.rechargeReadLL.visibility = View.GONE |
| | | setCardNumber(cardNumber) |
| | | } else if (cardNumber.contains(BaseCommon.CARD_TYPE_ERROR2)) { |
| | | ToastUtil.show("当前卡密码错误,不是本公司卡") |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | private fun bytesToHexString(bytes: ByteArray?): String? { |
| | | if (bytes == null || bytes.isEmpty()) return null |
| | | val sb = StringBuilder() |
| | | for (b in bytes) { |
| | | sb.append(String.format("%02X", b)) |
| | | } |
| | | return sb.toString() |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 提交数据 |
| | | */ |
| | | fun postCardData(cardType: String, cardAddr: String, remark: String) { |
| | | |
| | | val map = mutableMapOf<String, Any>() |
| | | |
| | | if (cardAddr.isNotEmpty()) { |
| | | map["cardAddr"] = cardAddr |
| | | } |
| | | |
| | | if (cardType.isNotEmpty()) { |
| | | // map["cardType"] = cardType |
| | | map["cardType"] = "5" |
| | | } |
| | | if (remark.isNotEmpty()) { |
| | | map["remarks"] = remark |
| | | } |
| | | // 使用正确的类型参数 |
| | | ApiManager.getInstance().requestPostLoading( |
| | | this, |
| | | "/sell/card/create_manager_card", |
| | | String::class.java, |
| | | map, |
| | | object : SubscriberListener<BaseResponse<String>>() { |
| | | override fun onNext(t: BaseResponse<String>) { |
| | | if (t.success) { |
| | | var intent = Intent(this@ManagerReadActivity, NfcWreatActivity::class.java).apply { |
| | | putExtra("cardAddr", cardAddr) |
| | | putExtra("orderId", t.content) |
| | | putExtra("cardType", cardType) |
| | | } |
| | | this@ManagerReadActivity.finish() |
| | | startActivity(intent) |
| | | } else { |
| | | // 处理搜索失败的情况 |
| | | ToastUtil.show(t.msg) |
| | | } |
| | | } |
| | | |
| | | override fun onError(e: Throwable?) { |
| | | super.onError(e) |
| | | ToastUtil.show("搜索失败: ${e?.message ?: "未知错误"}") |
| | | } |
| | | } |
| | | ) |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.dayu.general.activity |
| | | package com.dayu.general.activity |
| | | |
| | | import android.content.Intent |
| | | import android.os.Bundle |
| | | import com.dayu.baselibrary.net.subscribers.SubscriberListener |
| | | import com.dayu.baselibrary.utils.ToastUtil |
| | | import com.dayu.general.bean.card.ClearCard |
| | | import com.dayu.general.tool.CardCommon |
| | | import com.dayu.general.databinding.ActivityNfcWriteGeBinding |
| | | import com.dayu.general.net.ApiManager |
| | | import com.dayu.general.net.BaseResponse |
| | | import com.dayu.general.tool.NfcReadHelper |
| | | import com.dayu.general.tool.NfcWreatHelper |
| | | |
| | | /** |
| | | * @author: zuo |
| | | * @date: 2021/3/30 |
| | | * @description:写卡界面 |
| | | */ |
| | | class NfcWreatActivity:BaseNfcActivity() { |
| | | |
| | | var binding:ActivityNfcWriteGeBinding? = null |
| | | |
| | | class NfcWreatActivity : BaseNfcActivity() { |
| | | var binding: ActivityNfcWriteGeBinding? = null |
| | | var cardType = "" |
| | | var orderId = "" |
| | | var cardAddr = "" |
| | | |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | |
| | | /** |
| | | * 获取数据 |
| | | */ |
| | | private fun getInitData(){ |
| | | cardType= intent?.getStringExtra("cardType")?:"" |
| | | private fun getInitData() { |
| | | cardType = intent?.getStringExtra("cardType") ?: "" |
| | | orderId = intent?.getStringExtra("orderId") ?: "" |
| | | cardAddr = intent?.getStringExtra("cardAddr") ?: "" |
| | | if (cardType.isNotEmpty()) { |
| | | when (cardType) { |
| | | CardCommon.CLEAN_CARD_TYPE -> { |
| | | binding?.cardData?.text = "清零卡写卡" |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | private fun setTextData(){ |
| | | when(cardType){ |
| | | CardCommon.CHECK_CARD->{ |
| | | binding?.cardData?.text = "写用户卡" |
| | | } |
| | | |
| | | override fun onNfcBack(intent: Intent) { |
| | | val nfcReadHelper = NfcReadHelper.getInstance(intent, this) |
| | | val cardNumber = nfcReadHelper.getCardNumberNoClose() |
| | | if (cardNumber.isNotEmpty() && cardNumber == cardAddr) { |
| | | val nfcWreatHelper = NfcWreatHelper.getInstance(intent, this) |
| | | when (cardType) { |
| | | CardCommon.CLEAN_CARD_TYPE -> { |
| | | var clearCard = ClearCard() |
| | | nfcWreatHelper.writeData(clearCard.getZeroBytes(), 7, 0) { success, message -> |
| | | if (success) { |
| | | postCardData(cardType, cardAddr, "") |
| | | ToastUtil.show("写卡成功!") |
| | | // 处理写卡成功的情况 |
| | | } else { |
| | | // 处理写卡失败的情况 |
| | | ToastUtil.show(message) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | ToastUtil.show("卡片错误,当前刷的卡与刚刚的卡不一致") |
| | | } |
| | | } |
| | | override fun onNfcBack(intent: Intent?) { |
| | | TODO("Not yet implemented") |
| | | when(cardType){ |
| | | CardCommon.CHECK_CARD->{ |
| | | |
| | | fun postCardData(cardType: String, cardAddr: String, remark: String) { |
| | | when (cardType) { |
| | | CardCommon.CHECK_CARD -> { |
| | | binding?.cardData?.text = "写用户卡" |
| | | } |
| | | |
| | | } |
| | | val map = mutableMapOf<String, Any>() |
| | | |
| | | if (cardAddr.isNotEmpty()) { |
| | | map["cardAddr"] = cardAddr |
| | | } |
| | | |
| | | if (cardType.isNotEmpty()) { |
| | | // map["cardType"] = cardType |
| | | map["cardType"] = "5" |
| | | } |
| | | if (remark.isNotEmpty()) { |
| | | map["remarks"] = remark |
| | | } |
| | | // 使用正确的类型参数 |
| | | ApiManager.getInstance().requestPostLoading( |
| | | this, |
| | | "/sell/card/call_back", |
| | | String::class.java, |
| | | map, |
| | | object : SubscriberListener<BaseResponse<String>>() { |
| | | override fun onNext(t: BaseResponse<String>) { |
| | | if (t.success) { |
| | | this@NfcWreatActivity.finish() |
| | | } else { |
| | | // 处理搜索失败的情况 |
| | | ToastUtil.show(t.msg) |
| | | } |
| | | } |
| | | |
| | | override fun onError(e: Throwable?) { |
| | | super.onError(e) |
| | | ToastUtil.show("搜索失败: ${e?.message ?: "未知错误"}") |
| | | } |
| | | } |
| | | ) |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import com.dayu.baselibrary.tools.BaseCard |
| | | import com.dayu.general.bean.db.CardData |
| | | import com.dayu.general.dao.AppDataBase |
| | | import com.dayu.general.tool.CardCommon |
| | | import com.tencent.bugly.crashreport.CrashReport |
| | | |
| | | open class BaseCard { |
| | | open class BaseCard : BaseCard() { |
| | | var cardData: String? = null //标识码 |
| | | |
| | | companion object { |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 前15个字节算术累加和 不含进位 |
| | | * |
| | | * @param data 源数据 |
| | | * @return 16进制 |
| | | */ |
| | | fun getByteSum(data: ByteArray?): Byte { |
| | | if (data != null) { |
| | | var sum = 0 |
| | | for (b in data) { |
| | | sum += b.toInt() and 0xFF // & 0xFF 可以将字节扩展为正整数,避免符号位的影响 |
| | | } |
| | | var hex = HexUtil.get10to16CompleteHex(sum) |
| | | hex = HexUtil.spaceHex(hex) |
| | | val hexArr = hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() } |
| | | .toTypedArray() |
| | | return HexUtil.hexToByte(hexArr[hexArr.size - 1]) |
| | | } |
| | | return 0 |
| | | } |
| | | // /** |
| | | // * 前15个字节算术累加和 不含进位 |
| | | // * |
| | | // * @param data 源数据 |
| | | // * @return 16进制 |
| | | // */ |
| | | // fun getByteSum(data: ByteArray?): Byte { |
| | | // if (data != null) { |
| | | // var sum = 0 |
| | | // for (b in data) { |
| | | // sum += b.toInt() and 0xFF // & 0xFF 可以将字节扩展为正整数,避免符号位的影响 |
| | | // } |
| | | // var hex = HexUtil.get10to16CompleteHex(sum) |
| | | // hex = HexUtil.spaceHex(hex) |
| | | // val hexArr = hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() } |
| | | // .toTypedArray() |
| | | // return HexUtil.hexToByte(hexArr[hexArr.size - 1]) |
| | | // } |
| | | // return 0 |
| | | // } |
| | | |
| | | } |
| | |
| | | /** |
| | | * 通用项目用户卡结构 |
| | | */ |
| | | class UserCard : BaseCard(), Serializable { |
| | | class UserCard : BaseUserCardCard(), Serializable { |
| | | var cardType: String = USER_CARD_TYPE_1 // 卡类型:A1终端写卡 A8刷卡开泵后值 A2叠加充值 |
| | | var areaNumber: Int = 0 // 国家行政区域号(12位BCD,精确到村) |
| | | var userCode: String = "" // 用户编号BCD |
| | |
| | | if (data == null || data.size < 3) { |
| | | return false |
| | | } |
| | | |
| | | |
| | | val expectedBytes = arrayOf(getZeroBytes(), getOneBytes(), getTwoBytes()) |
| | | return data.zip(expectedBytes.toList()).all { (actual, expected) -> |
| | | actual.contentEquals(expected) |
| | | return data.zip(expectedBytes.toList()).all { (actual, expected) -> |
| | | actual.contentEquals(expected) |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): UserCard? { |
| | | override fun getBean(data: List<ByteArray>): UserCard? { |
| | | try { |
| | | val userCard = UserCard() |
| | | // 解析第0块 |
| | |
| | | balance = HexUtil.get16To10LowHightByBytes(one.copyOfRange(1, 5)) |
| | | surplusWater = HexUtil.get16To10LowHightByBytes(one.copyOfRange(5, 9)) |
| | | electricPrice = HexUtil.hexToFloatLowHigh(one.copyOfRange(9, 11)) |
| | | |
| | | |
| | | // 解析充值时间 |
| | | val year = HexUtil.getBcdToInt(one[11]) |
| | | val month = HexUtil.getBcdToInt(one[12]) |
| | |
| | | val data = ByteArray(16) |
| | | try { |
| | | data[0] = projectCode.toByte() |
| | | |
| | | |
| | | // 设置余额 |
| | | val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance)) |
| | | System.arraycopy(balanceBytes, 0, data, 1, 4) |
| | | |
| | | |
| | | // 设置剩余水量 |
| | | val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater)) |
| | | System.arraycopy(waterBytes, 0, data, 5, 4) |
| | | |
| | | |
| | | // 设置电价 |
| | | val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(electricPrice)) |
| | | System.arraycopy(priceBytes, 0, data, 9, 2) |
| | | |
| | | |
| | | // 设置充值时间 |
| | | rechargeDate?.let { |
| | | data[11] = HexUtil.getIntToBCD(it.get(Calendar.YEAR) % 100)[0] |
| | |
| | | data[13] = HexUtil.getIntToBCD(it.get(Calendar.DAY_OF_MONTH))[0] |
| | | data[14] = HexUtil.getIntToBCD(it.get(Calendar.HOUR_OF_DAY))[0] |
| | | } |
| | | |
| | | |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | |
| | | // 备份余额和水量数据 |
| | | val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance)) |
| | | System.arraycopy(balanceBytes, 0, data, 1, 4) |
| | | |
| | | |
| | | val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater)) |
| | | System.arraycopy(waterBytes, 0, data, 5, 4) |
| | | |
| | | |
| | | // 设置水价 |
| | | val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(waterPrice)) |
| | | System.arraycopy(priceBytes, 0, data, 9, 2) |
| | | |
| | | |
| | | // 设置充值时间 |
| | | rechargeDate?.let { |
| | | data[11] = HexUtil.getIntToBCD(it.get(Calendar.YEAR) % 100)[0] |
| | |
| | | data[13] = HexUtil.getIntToBCD(it.get(Calendar.DAY_OF_MONTH))[0] |
| | | data[14] = HexUtil.getIntToBCD(it.get(Calendar.HOUR_OF_DAY))[0] |
| | | } |
| | | |
| | | |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | fun getOneBytes(): ByteArray = One().toBytes() |
| | | fun getTwoBytes(): ByteArray = Two().toBytes() |
| | | override fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | override fun getOneBytes(): ByteArray = One().toBytes() |
| | | override fun getTwoBytes(): ByteArray = Two().toBytes() |
| | | } |
| | |
| | | package com.dayu.general.tool |
| | | |
| | | import android.content.Context |
| | | import android.util.Base64 |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import com.dayu.baselibrary.tools.nfc.BaseNFCHelper |
| | | import java.nio.charset.StandardCharsets |
| | | import javax.crypto.Cipher |
| | | import javax.crypto.spec.SecretKeySpec |
| | | |
| | | open class GeBaseHelper(private val context: Context) : BaseNFCHelper() { |
| | | |
| | | |
| | | companion object { |
| | | init { |
| | | System.loadLibrary("general-native-lib") |
| | | try { |
| | | System.loadLibrary("general-native-lib") |
| | | } catch (e: UnsatisfiedLinkError) { |
| | | e.printStackTrace() |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | try { |
| | | // 获取所有扇区密钥 |
| | | val allKeys: String = getM1SectorKeySecure(context, 0) |
| | | val keys: Array<String> = |
| | | allKeys.split(",").dropLastWhile { it.isEmpty() }.toTypedArray() |
| | | for (i in keys.indices) { |
| | | val key = keys[i] |
| | | listA_PS.add(HexUtil.hexToByteArray(key)) |
| | | if (allKeys.isNotEmpty()) { |
| | | val keys: Array<String> = allKeys.split(",").dropLastWhile { it.isEmpty() }.toTypedArray() |
| | | for (i in keys.indices) { |
| | | val key = keys[i] |
| | | if (key.isNotEmpty()) { |
| | | listA_PS.add(HexUtil.hexToByteArray(key)) |
| | | } |
| | | } |
| | | } |
| | | defauleKey = HexUtil.hexToByteArray("FFFFFFFFFFFF") |
| | | |
| | | |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | // 设置默认密钥 |
| | | defauleKey = HexUtil.hexToByteArray("FFFFFFFFFFFF") |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * 单例初始化 |
| | | */ |
| | | @JvmStatic |
| | | fun getInstance(intent: Intent, activity: Activity): NfcReadHelper { |
| | | if (helper == null) { |
| | | helper = NfcReadHelper(intent, activity) |
| | | } |
| | | helper!!.adapter.setIntent(intent) |
| | | return helper!! |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | fun getCardNumberNoClose():String{ |
| | | return try { |
| | | adapter.cardNumberNoClose |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | "" |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 获取卡片类型和卡号 |
| | | */ |
New file |
| | |
| | | package com.dayu.general.tool |
| | | |
| | | import android.app.Activity |
| | | import android.content.Intent |
| | | import com.dayu.baselibrary.tools.nfc.NFCCallBack |
| | | import com.dayu.baselibrary.tools.nfc.NfcWriteAdapter |
| | | import com.dayu.general.bean.card.UserCard |
| | | |
| | | class NfcWreatHelper private constructor(intent: Intent, activity: Activity) : GeBaseHelper(activity) { |
| | | |
| | | private val adapter: NfcWriteAdapter = NfcWriteAdapter(intent, activity) |
| | | |
| | | companion object { |
| | | private var helper: NfcWreatHelper? = null |
| | | |
| | | /** |
| | | * 单例初始化 |
| | | */ |
| | | @JvmStatic |
| | | fun getInstance(intent: Intent, activity: Activity): NfcWreatHelper { |
| | | if (helper == null) { |
| | | helper = NfcWreatHelper(intent, activity) |
| | | } |
| | | helper!!.adapter.setIntent(intent) |
| | | return helper!! |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 写卡 |
| | | * |
| | | * @param str 书写内容,16个字节 |
| | | * @param a 书写的扇区 (从0开始数) |
| | | * @param b 书写的块(从0开始数) |
| | | * @param |
| | | */ |
| | | fun writeData(str: ByteArray?, a: Int, b: Int,callBack: NFCCallBack): Boolean { |
| | | try { |
| | | return adapter.writeData(str, a, b,callBack) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return false |
| | | } |
| | | |
| | | /** |
| | | * 写卡 |
| | | * |
| | | * @param userCard 用户卡内容 |
| | | * @param |
| | | */ |
| | | fun writeUserData(userCard: UserCard): Boolean { |
| | | try { |
| | | return adapter.writeUserData(userCard,7) |
| | | } catch (e: java.lang.Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return false |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <layout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto"> |
| | | |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | xmlns:tools="http://schemas.android.com/tools"> |
| | | |
| | | <data> |
| | | |
| | | <variable |
| | | name="viewModel" |
| | | type="com.dayu.general.model.CardInfoModel" /> |
| | |
| | | <LinearLayout |
| | | android:id="@+id/data_layout" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_below="@+id/titleBar" |
| | | android:orientation="vertical" |
| | | android:layout_height="wrap_content"> |
| | | android:visibility="gone"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:orientation="vertical" |
| | | android:layout_height="wrap_content"> |
| | | |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | |
| | | android:layout_height="wrap_content" |
| | | android:text="卡号:" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp"/> |
| | | android:textSize="16sp" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/card_number_tv" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="@{viewModel.cardNumber}" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:text="@{viewModel.cardNumber}"/> |
| | | android:textSize="16sp" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | |
| | | android:layout_height="wrap_content" |
| | | android:text="备注:" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp"/> |
| | | android:textSize="16sp" /> |
| | | |
| | | <EditText |
| | | android:id="@+id/remark_et" |
| | |
| | | android:layout_height="wrap_content" |
| | | android:background="@null" |
| | | android:hint="请输入备注信息" |
| | | android:textSize="16sp" |
| | | android:text="@={viewModel.remark}"/> |
| | | |
| | | android:text="@={viewModel.remark}" |
| | | android:textSize="16sp" /> |
| | | </LinearLayout> |
| | | |
| | | </LinearLayout> |
| | |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | </LinearLayout> |
| | | |
| | | <TextView |
| | | android:id="@+id/btn_next" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="56dp" |
| | | android:layout_alignParentBottom="true" |
| | | android:layout_marginStart="16dp" |
| | | android:layout_marginEnd="16dp" |
| | | android:layout_marginBottom="16dp" |
| | | android:background="#4285F4" |
| | | android:gravity="center" |
| | | android:visibility="gone" |
| | | android:text="下一步" |
| | | android:textColor="#FFFFFF" |
| | | android:textSize="16sp" /> |
| | | |
| | | </RelativeLayout> |
| | | </layout> |
| | |
| | | if (helper == null) { |
| | | helper = new NFCWriteHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | */ |
| | | public boolean writeUserData(UserCardHN userCard) { |
| | | try { |
| | | return adapter.writeUserData(userCard); |
| | | return adapter.writeUserData(userCard,1); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | import com.dayu.baselibrary.tools.nfc.NfcReadAdapter; |
| | | import com.dayu.henanlibrary.card.UserCardHN; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author zx |
| | | * @date 2018/4/23 14:31 |
| | |
| | | if (helper == null) { |
| | | helper = new NfcReadHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | |
| | | import android.app.Activity; |
| | | import android.content.Intent; |
| | | import android.nfc.NfcAdapter; |
| | | import android.nfc.Tag; |
| | | import android.nfc.tech.MifareClassic; |
| | | import android.util.Log; |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil; |
| | | import com.dayu.baselibrary.tools.nfc.NfcWriteAdapter; |
| | | import com.dayu.qihealonelibrary.card.UserCard; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * @author zx |
| | |
| | | if (helper == null) { |
| | | helper = new NFCWriteHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | */ |
| | | public boolean writeUserData(UserCard userCard) { |
| | | try { |
| | | return adapter.writeUserData(userCard); |
| | | return adapter.writeUserData(userCard,1); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | if (helper == null) { |
| | | helper = new NfcReadHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | |
| | | import android.app.Activity; |
| | | import android.content.Intent; |
| | | import android.nfc.NfcAdapter; |
| | | import android.nfc.Tag; |
| | | import android.nfc.tech.MifareClassic; |
| | | import android.util.Log; |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil; |
| | | import com.dayu.baselibrary.tools.nfc.NfcWriteAdapter; |
| | | import com.dayu.qiheonlinelibrary.card.UserCard; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author zx |
| | |
| | | if (helper == null) { |
| | | helper = new NFCWriteHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |
| | |
| | | */ |
| | | public boolean writeUserData(UserCard userCard) { |
| | | try { |
| | | return adapter.writeUserData(userCard); |
| | | return adapter.writeUserData(userCard,1); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | if (helper == null) { |
| | | helper = new NfcReadHelper(intent, activity); |
| | | } |
| | | helper.adapter.setIntent(intent); |
| | | return helper; |
| | | } |
| | | |