之前接了一个API 广告反作弊的需求,需要客户端获取bootMark,updateMark值给渠道商,这两个值是通过JNI调用内核的boo_id值。我们的APP只打了armeabi-v7a 的so,经过测试发现,在vivo机型上遇到了奔溃,其他机型则没有问题.
奔溃信息如下:
2021-09-24 18:23:00.800 19123-19123/? E/aid.demoprojec: Unknown bits set in runtime_flags: 0x8000
2021-09-24 18:23:00.955 19123-19164/com.aaid.demoproject E/VPerformance: Fail to get file list com.aaid.demoproject
2021-09-24 18:23:00.955 19123-19164/com.aaid.demoproject E/VPerformance: Fail to get file list com.aaid.demoproject
2021-09-24 18:23:01.098 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xfb
2021-09-24 18:23:01.098 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] string: '8a5fbf21-182e-49af-97a8-ee3590ce20d4
2021-09-24 18:23:01.099 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] �|e����#�ѡ��С��6�'
2021-09-24 18:23:01.099 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] input: '0x38 0x61 0x35 0x66 0x62 0x66 0x32 0x31 0x2d 0x31 0x38 0x32 0x65 0x2d 0x34 0x39 0x61 0x66 0x2d 0x39 0x37 0x61 0x38 0x2d 0x65 0x65 0x33 0x35 0x39 0x30 0x63 0x65 0x32 0x30 0x64 0x34 0x0a <0xfb> 0x7c 0x65 0xf1 0xf8 0xe9 0xc4 0x19 0x1b 0x15 0x23 0xf0 0x18 0xd1 0xa1 0xff 0xa4 0xd0 0xa1 0xff 0x19 0xe6 0x36 0xf0 0x07'
2021-09-24 18:23:01.099 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] in call to NewStringUTF
2021-09-24 18:23:01.099 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] from java.lang.String com.aaid.demoproject.MainActivity.stringFromJNI2()
2021-09-24 18:23:01.283 19123-19123/com.aaid.demoproject A/aid.demoprojec: runtime.cc:630] Runtime aborting...
2021-09-24 18:23:01.283 19123-19123/com.aaid.demoproject A/aid.demoprojec: runtime.cc:630] Dumping all threads without mutator lock held
2021-09-24 18:23:01.283 19123-19123/com.aaid.demoproject A/aid.demoprojec: runtime.cc:630] All threads:
2021-09-24 18:23:01.283 19123-19123/com.aaid.demoproject A/aid.demoprojec: runtime.cc:630] DALVIK THREADS (17):
通过java_vm_ext.cc:570] in call to NewStringUTF
2021-09-24 18:23:01.099 19123-19123/com.aaid.demoproject A/aid.demoprojec: java_vm_ext.cc:570] from java.lang.String com.aaid.demoproject.MainActivity.stringFromJNI2()的日志,我们马上就能定位到报错的地方
static jstring getBoot(JNIEnv *env, jclass) {
FILE *fp = fopen("/proc/sys/kernel/random/boot_id", "r");
char boot[TID1_LEN];
if (fp == NULL) {
//获取失败
} else {
unsigned char c;
int i = 0;
while (i < TID1_LEN) {
c = fgetc(fp);
boot[i] = c;
i = i + 1;
}
if (ferror(fp)) {
//获取失败
}
}
// fgets(t1,sizeof(t1),fp);
std::string sboot = boot;
return env->NewStringUTF(sboot.c_str());
}
在看错误日志,分析得出乱码导致了en->NewStringUT(sboot.c_str())方法报错了.那其他手机为何正常,vivo报错了呢,继续深入分析,乱码现象是只有在vivo上有吗?验证华为,小米等手机,乱码也有,但是没有报错.大概能猜到vivo肯定是修改了底层的校验方法。知道了原因之后,解决办法也简单,就是将bood_id值进行utf8 编码,这里编码直接调用了java层的String类的API,jni层处理有点麻烦的,改造之后的方法如下
static jstring getBoot(JNIEnv *env, jclass) {
FILE *fp = fopen("/proc/sys/kernel/random/boot_id", "r");
char boot[TID1_LEN];
if (fp == NULL) {
//获取失败
} else {
unsigned char c;
int i = 0;
while (i < TID1_LEN) {
c = fgetc(fp);
boot[i] = c;
i = i + 1;
}
if (ferror(fp)) {
//获取失败
}
}
// fgets(t1,sizeof(t1),fp);
//std::string sboot = boot;
//return env->NewStringUTF(sboot.c_str());
//替换下面的代码
jclass strClass = (env)->FindClass("java/lang/String");
jstring encoding = (env)->NewStringUTF("UTF-8");
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray((jsize) strlen(boot));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, (jsize) strlen(boot), (jbyte *) boot);
return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}
运行改造后的代码,vivo上就没崩溃了.但是崩溃问题解决了,乱码问题还没解决,如图:
java层调用jni方法获取的仍旧是乱码存在,不过这里就处理更简单了,直接截取好了。boot_id前36位是正常的,那就截取前36位的即可,当然测试了过款机型,都是36位,后面也不保证截取36位是正确的,所以java层截取的时候,最好用try,catch 保护下.一行代码解决:str.substring(0,36).
因为只记录一个bug的解决,现在工程代码就不传了.如果后面谁有需要,我可以传到github上
贴一下完整的获取代码
#include <jni.h>
#include <string>
#include <sys/stat.h>
const int TID1_LEN=37;
static jstring getUpdate(JNIEnv *env, jclass) {
struct stat sb;
int updates,updatens;
if (stat("/data/data", &sb) == -1) {
//获取失败
} else {
updatens = (int) sb.st_atim.tv_nsec;
updates = (int) sb.st_atim.tv_sec;
}
std::string idRes = std::to_string(updates) + "." + std::to_string(updatens);
return env->NewStringUTF(idRes.c_str());
}
static jstring getBoot(JNIEnv *env, jclass) {
FILE *fp = fopen("/proc/sys/kernel/random/boot_id", "r");
char boot[TID1_LEN];
if (fp == NULL) {
//获取失败
} else {
unsigned char c;
int i = 0;
while (i < TID1_LEN) {
c = fgetc(fp);
boot[i] = c;
i = i + 1;
}
if (ferror(fp)) {
//获取失败
}
}
// fgets(t1,sizeof(t1),fp);
//std::string sboot = boot;
//return env->NewStringUTF(sboot.c_str());
//替换下面的代码
jclass strClass = (env)->FindClass("java/lang/String");
jstring encoding = (env)->NewStringUTF("UTF-8");
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray((jsize) strlen(boot));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, (jsize) strlen(boot), (jbyte *) boot);
return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}
static const char *classPathName = "com/aaid/demoproject/MainActivity";
static const JNINativeMethod gMethods[] = {
{"stringFromJNI1", "()Ljava/lang/String;", (void *) getUpdate},
{"stringFromJNI2", "()Ljava/lang/String;", (void *) getBoot}
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if((vm->GetEnv((void **) &env, JNI_VERSION_1_4)) != JNI_OK ){
return JNI_FALSE;
}
jclass clazz = env->FindClass(classPathName);
if(clazz == NULL){
return JNI_FALSE;
}
if((env->RegisterNatives(clazz, gMethods, 2) < 0)){
return JNI_FALSE;
}
return JNI_VERSION_1_4;
}