Linux java jni调用C++封装动态库

        由于项目中java需要调用第三方提供的C++动态库;由于第三方动态库传入的参数较多,还伴随着指针传入操作,导致java调用极为不便!因此催生出对于第三方的C++动态库进行二次封装。java调用只需按结构传入一个结构化的string即可。话不多说开干!

第三方库

        第三方C++的头文件HD10_safe.h如下:

#ifndef _HD10_safe_H_
#define _HD10_safe_H_

#ifdef _cplusplus
extern "C" {
#endif

typedef	enum VKeyGenResultEx
{
	KGRE_OK 	,
  	KGRE_NOT_OK
}KeyGenResult;


typedef enum ECUS
{
	BCM=1, DCM, IC_6000, GW, VWM, LDWS, DMS, VCU, RCU, ACC, 
	RWCD, PSU, BBM, AC_6000, TPMS, VMS, PEPS, IMMO, EMS, ESCL,
	MMI, BMS, FC, EC, ECM, CPD_3000, AFS, TCO, TCU, DCU,
	ECAS, ABS, EBS, EPB, AEBS, EVM, TCU_SH, ADAS, BS_LKA, CIM,
	RIM, FIM, MCU, CABS, DSW, VCU_x5_M3S, SWG, CCU, WP_ECU, CMS_ECU,
	EHBS, ACR, TBOX_5G, CSC, HCU, LKA, ADCU, CPD_6000, IC_3000, AC_3000,
	LE_IC, LE_AC, LE_BCM, LE_GW, LE_VCU, LE_TPMS, LE_DCM, VDCU, PDU, DCDC, DCAC,         DSSAD
}ECUs;

typedef unsigned char	uint8; 
typedef unsigned short	uint16;       
// Function
//
KeyGenResult HD10GenerateKeyEx(uint8*       iSeedArray,	/* Array for the seed [in] */
                          uint8			iSeedArraySize,	/* Length of the array for the seed [in] */
                          const uint8	iSecurityLevel,	/* Security level [in] */
                          uint8 *		iKeyArray,	/* Array for the key [in, out] */
                          uint8 *		iKeySize,	/* Length of the key [out] */
						  ECUs			e			/* enum ECUS elements */
                          );

#ifdef _cplusplus
}
#endif

#endif // _HD10_safe_H_

        第三方动态库 libTXJsafe.so 如下:

C++调用

        写一个C++ demo 调用动态库 test.cpp 代码如下:

#include <stdio.h>
#include "HD10_safe.h"

int main()
{
	uint8 arr[2] = {0x12, 0x34};
	uint8 seed[2] = {0x00, 0x00};
	uint8 seedLen = 0;
	
	int ret = HD10GenerateKeyEx(arr, 2, 7, seed, &seedLen, TCU);
	
	printf("ret : %d, seedLen:%d \n", ret, seedLen);
	
	for(int i = 0; i < seedLen; i++)
	{
		printf("seed[%d] : 0x%02x ;", i, seed[i]);
	}
	printf("\n");
	
	return 0;
}

        运行结果如下:

jni二次封装动态库

        通过jni二次封装动态库 mysafelib.cpp 代码如下:

#include <iostream>
#include <stdio.h>
#include <jni.h>
#include <string.h>
#include "HD10_safe.h"

// iString 输入参数格式:level(1byte) + ECUS(1byte) + SeedSize(1byte) + SeedValue(nbyte)
// rString 返回参数格式:keysize(1byte) + keyValue(nbyte)
extern "C" {
    JNIEXPORT jstring JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv *env, jobject obj, jstring iString);
}

// 十六进制字符串转字十六进制 "12" --> 0x12
int Str2Hex(char *p_hexstr, int iHexLen, char *pdststr);

// 十六进制数值转十六进制字符串 0x12 --> "12"
int Hex2Str(const char *p_strstr, int iStrLen, char *pdststr);

JNIEXPORT jstring JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv *env, jobject obj, jstring iString)
{
	char myRString[66] = {0};
	char iHexStrOrg[64] = {0};
	char iHexStr[32] = {0};
	
	const char *iStrData = env->GetStringUTFChars(iString, 0);
	memcpy(iHexStrOrg, iStrData, strlen(iStrData) > 64 ? 64 : strlen(iStrData));
	env->ReleaseStringUTFChars(iString, iStrData);
	
	int iHexStrLen = Str2Hex(iHexStrOrg, strlen(iHexStrOrg), iHexStr);
	
	// std::cout << "strlen(iHexStrOrg) : " << strlen(iHexStrOrg) << std::endl;
	
	if(iHexStrLen < 4)	return env->NewStringUTF(myRString);
	
	// std::cout << "iHexStrLen : " << iHexStrLen << std::endl;
	
	uint8 seed[32] = {0};
	uint8 key[33] = {0};
	uint8 keyLen = 0;
	
	uint8 u8Level		= *(uint8 *)iHexStr;
	uint8 u8ECU	 		= *(uint8 *)(iHexStr + 1);
	uint8 u8SeedSize	= *(uint8 *)(iHexStr + 2);
	
	// printf("u8SeedSize : %d \n", u8SeedSize);
	
	if(u8SeedSize > 32)	return env->NewStringUTF(myRString);

	memcpy(seed, iHexStr + 3, u8SeedSize);
	
	KeyGenResult ret = (KeyGenResult)HD10GenerateKeyEx(seed, u8SeedSize, u8Level, key + 1, &keyLen, (ECUs)u8ECU);
	
	if(ret != KGRE_OK || keyLen > 32)	return env->NewStringUTF(myRString);
	
	// printf("ret : %d, keyLen:%d \n", (int)ret, keyLen);
	
	// for(int i = 1; i <= keyLen; i++)	printf("key[%d] : %02x; ", i, key[i]);
	
	key[0] = keyLen * 2;
	
	Hex2Str((const char*)key, keyLen + 1, myRString);

	jstring rString = env->NewStringUTF(myRString);
	
	printf("\n---------------successful-----------------\n");
	
    return rString;
}

// 十六进制字符串转字十六进制 "12" --> 0x12
int Str2Hex(char *p_hexstr, int iHexLen, char *pdststr)
{
	int iret = 0;
	while(p_hexstr != NULL && pdststr != NULL && iHexLen > 1)
	{
		char cTemp = '0';
		
		// printf("1:%c, 2:%c\n", p_hexstr[0], p_hexstr[1]);
		
		// 小写统一转大写
		if(p_hexstr[0] >= '0' && p_hexstr[0] <= '9')
		{
			cTemp = p_hexstr[0] - '0';
		}
		else if(p_hexstr[0] >= 'A' && p_hexstr[0] <= 'F')
		{
			cTemp = p_hexstr[0] - 'A' + 10;
		}
		else if(p_hexstr[0] >= 'a' && p_hexstr[0] <= 'f')
		{
			cTemp = p_hexstr[0] - 'a' + 10;
		}
		else
		{
			printf("the hex str is error!\n");
			break;
		}
		
		*pdststr = cTemp * 16;
		
		if(p_hexstr[1] >= '0' && p_hexstr[1] <= '9')
		{
			cTemp = p_hexstr[1] - '0';
		}
		else if(p_hexstr[1] >= 'A' && p_hexstr[1] <= 'F')
		{
			cTemp = p_hexstr[1] - 'A' + 10;
		}
		else if(p_hexstr[1] >= 'a' && p_hexstr[1] <= 'f')
		{
			cTemp = p_hexstr[1] - 'a' + 10;
		}
		else
		{
			printf("the hex str is error!\n");
			break;
		}
		*pdststr += cTemp;
		
		// printf("---iHexLen:%d, pdststr:%c\n", iHexLen, *pdststr);
		
		iHexLen -= 2;
		p_hexstr += 2;
		pdststr++;
		iret++;
	}
	
	// printf("iret : %d\n", iret);
	
	return iret;
}
 
// 十六进制数值转十六进制字符串 0x12 --> "12"
int Hex2Str(const char *p_strstr, int iStrLen, char *pdststr)
{
	int index_str = 0, index_hex = 0;
 
	const char cHex[] = "0123456789ABCDEF";
	while(index_str < iStrLen && p_strstr != NULL && pdststr != NULL)
	{
		pdststr[index_hex++] = cHex[((uint8)p_strstr[index_str])/16];
		pdststr[index_hex++] = cHex[((uint8)p_strstr[index_str++])%16];
	}
 
	return index_hex;
}

        编译生成对应二次封装的动态库如下:

java调用二次封装动态库

        java调用二次封装库的demo文件如下:

package com.example;

public class MyClass {
    static {
        System.load("/home/lijd/testlib3/sodir/libmysafe.so"); // 加载C++动态库
    }

    private native String nativeMethod(String str); // 声明本地方法

    public static void main(String[] args) {
        byte[] seedValue = new byte[]{0x12, 0x34};
        int seedSize = 2;//字节长度
        int level = 7;
        int ECUS = 29;

        StringBuffer param = new StringBuffer();
        param.append(byteToString(intToByte1(level)));
        param.append(byteToString(intToByte1(ECUS)));
        param.append(byteToString(intToByte1(seedSize)));
        param.append(byteToString(seedValue));
        String result = new MyClass().nativeMethod(param.toString()); // 调用本地方法
		System.out.println("---MyClass---" + result.length() + " : " + result);
    }


    /**
     * @title byteToString
     * @description 将字节数组转为字符串
     * @param buff
     *            字节数据
     * @return String 装换后的字符串数据
     */
    public static final String byteToString(byte[] buff) {
        StringBuilder sb = new StringBuilder();
        if (null != buff && buff.length > 0) {
            for (byte b : buff) {
                short t = b;
                if (t < 0)
                    t += 256;
                short h = (short) (t / 16);
                short l = (short) (t % 16);

                switch (h) {
                    case 10:
                        sb.append("A");
                        break;
                    case 11:
                        sb.append("B");
                        break;
                    case 12:
                        sb.append("C");
                        break;
                    case 13:
                        sb.append("D");
                        break;
                    case 14:
                        sb.append("E");
                        break;
                    case 15:
                        sb.append("F");
                        break;
                    default:
                        sb.append(h);
                        break;
                }

                switch (l) {
                    case 10:
                        sb.append("A");
                        break;
                    case 11:
                        sb.append("B");
                        break;
                    case 12:
                        sb.append("C");
                        break;
                    case 13:
                        sb.append("D");
                        break;
                    case 14:
                        sb.append("E");
                        break;
                    case 15:
                        sb.append("F");
                        break;
                    default:
                        sb.append(l);
                        break;
                }

            }
        }

        return sb.toString();
    }

    public static byte[] intToByte1(int value) {
        byte[] b = new byte[1];

        for (int i = 0; i < 1; ++i) {
            int offset = (b.length - 1 - i) * 8;
            b[i] = (byte) (value >>> offset & 255);
        }
        return b;
    }
}

        执行javac 编译源文件生产class文件如下:

        运行java的class文件如下:

        至此,一切搞定!通过windows测试这样也完全可以。

上一篇:STM32启动过程分析