点击打开链接
文档目的
在Android手机生产过程中,有时需要做一些器件测试或者其他压力测试,并且保存测试结果,即使手机恢复出场设置或者重新下载版本也不会擦出这些结果,这个时候就要用到NV来保存。本文主要介绍高通平台上层通过调用jni层方法保存NV和读取NV的方法。
java层方法定义
使用高通的工具QXDM连接手机,用NV Browser查看手机的NV数据:
接下来以NVID[02497] Factory Data Storage Area 1为例。
新建一个java文件: NvWriter.java
- public class NvWriter {
- private static final String TAG = "NvWriter-TAG";
- private static NvWriter sInstance = null;
-
-
- public static final char PASS = 'P';
- public static final char FAIL = 'F';
- public static final char NA = ' ';
-
- static {
- System.loadLibrary("nvwriter_jni");
- }
- private native String native_readflag_NV();
- private native void native_writeflag_NV(int index,char result);
-
- public static NvWriter getInstance() {
- if (sInstance == null) {
- sInstance = new NvWriter();
- }
- return sInstance;
- }
-
- public String readFlagNV() {
- String mFlagNv = native_readflag_NV();
- Log.i(TAG,"readFlagNV: mFlagNv = " + mFlagNv);
- return mFlagNv;
- }
-
- public void writeFlagNV(int index,char result) {
- native_writeflag_NV(index,result);
- }
-
- public char getFlag(int index) {
- String mFlagNv = readFlagNV();
- char flag = NA;
- if (mFlagNv != null && mFlagNv.length() >= index) {
- flag = mFlagNv.charAt(index);
- }
- Log.i(TAG,index + ": flag = " + flag);
- return flag;
- }
- }
这个类中只有几个简单的方法:
1.一个单实例的构造方法
2.3个public的操作NV的java方法
3.2个native方法
这样我们在需要操作NV的地方,可以像下面这样:
1.写NV:
- NvWriter.getInstance().writeFlagNV(index,NvWriter.PASS);
2.读取NV:
- NvWriter.getInstance().getFlag(index);
上述方法具体实现都是在jni层,我们接下来看一下jni层是如何实现的。
Jni层方法实现
新建一个cpp文件nvwriter.cpp:
- #include <unistd.h>
- #include <utils/Log.h>
- #include <cutils/log.h>
- #include <jni.h>
- #include <JNIHelp.h>
- #include <stdlib.h>
- #include "android_runtime/AndroidRuntime.h"
-
- #include <android/log.h>
- #include "nv.h"
- #ifdef LOG_TAG
- #undef LOG_TAG
- #endif
- #define LOG_TAG "NVWriter-TAG-JNI"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
-
- static int SHOW_LOG = 1;
- static int SHOW_NV_LOG = 0;
- static const char* const className = "com/xxx/nv/NvWriter";
-
- jint NativeInit() {
-
- if(!Diag_LSM_Init(NULL)) {
- LOGI("Diag_LSM_Init() failed.");
- return -1;
- }
-
- LOGI("Diag_LSM_Init succeeded. \n");
-
- register_callback();
- return 1;
- }
-
- jstring CharTojstring(JNIEnv* env,const char* str) {
- jsize len = strlen(str);
- jclass clsstring = env->FindClass("java/lang/String");
- jstring strencode = env->NewStringUTF("utf-8");
- jmethodID mid = env->GetMethodID(clsstring,"<init>","([BLjava/lang/String;)V");
- jbyteArray barr = env->NewByteArray(len);
- env->SetByteArrayRegion(barr,0,len,(jbyte*)str);
- return (jstring)env->NewObject(clsstring,mid,barr,strencode);
- }
-
- void android_native_writeflag_NV(JNIEnv *env,jobject this_,jint index,jchar result) {
- if (SHOW_LOG) {
- LOGI("android_native_writeflag_NV");
- }
- if (NativeInit() < 0) {
- return;
- }
- unsigned char tmp[22] = { 0 };
- unsigned char after[20] = { 0 };
- nv_items_enum_type nvId = NV_FACTORY_DATA_3_I;
- memset(tmp, 0, sizeof(tmp));
- memset(after, 0, sizeof(after));
- diag_nv_read(nvId,tmp, sizeof(tmp));
- for(int m=0;m < sizeof(tmp)-3;m++) {
- if (tmp[m+3] == NULL) {
- after[m] = ' ';
- } else {
- after[m] = tmp[m+3];
- }
- }
- LOGI("android_native_writeflag_NV index = %d\n",(int)index);
- after[sizeof(tmp)-3+1] = '\0';
- after[index] = result;
- if (SHOW_NV_LOG) {
- for (int n=0;n < sizeof(after);n++) {
- LOGI("android_native_writeflag_NV,after[%d] = %02x \n",n,after[n]);
- }
- }
- diag_nv_write(nvId,after, sizeof(after));
- }
-
- jstring android_native_readflag_NV(JNIEnv *env) {
- if (SHOW_LOG) {
- LOGI("android_native_readflag_NV");
- }
- if (NativeInit() < 0) {
- return NULL;
- }
- unsigned char tmp[23] = { 0 };
- unsigned char after[21] = { 0 };
- memset(tmp, 0, sizeof(tmp));
- memset(after, 0, sizeof(after));
- nv_items_enum_type nvId = NV_FACTORY_DATA_3_I;
- diag_nv_read(nvId,tmp, sizeof(tmp));
- for(int m=0;m < sizeof(tmp)-3;m++) {
- if (tmp[m+3] == NULL) {
- after[m] = ' ';
- } else {
- after[m] = tmp[m+3];
- }
- }
- after[sizeof(tmp)-3+1] = '\0';
- const char* p = (const char*)(char*)after;
- if (SHOW_NV_LOG) {
- for(int i=0;i < sizeof(after);i++) {
- LOGI("android_native_readflag_NV p[%d] = %02x\n",i,p[i]);
- }
- }
-
- jstring flag_string = CharTojstring(env,p);
- return flag_string;
- }
-
- JNINativeMethod gMethods[] = {
- { "native_writeflag_NV", "(IC)V",(void*) android_native_writeflag_NV },
- { "native_readflag_NV", "()Ljava/lang/String;",(void*) android_native_readflag_NV }
- };
-
- int registerNativeMethods(JNIEnv* env) {
-
- jclass clazz;
- clazz = env->FindClass(className);
- if (env->RegisterNatives(clazz, gMethods,
- sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
- return -1;
- }
- return 0;
- }
-
- jint JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv* env = NULL;
- jint result = -1;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
- if (NULL != env && registerNativeMethods(env) == 0) {
- result = JNI_VERSION_1_4;
- }
- }
- return result;
- }
这个cpp要做的事情主要有以下几点:
1.注册JNI方法
2.调用高通平台自带的操作nv的方法进行读取和写NV
3.处理返回的值并传递给java层
有三点需要注意:
1.diag_nv_read读取返回的nv值前三位要去掉
2.返回的char要转化为jstring才能传递给上层
3.nv操作必须要先调用Diag_LSM_Init(NULL)初始化,并register_callback();
总结一下:
由于高通平台自带的源码中就有操作NV的函数,但是是位于cpp中的(vendor/qcom/proprietary/fastmmi和vendor/qcom/proprietary/diag中),但是我们自己的java文件无法直接调用高通的cpp文件,所以我们需要写一个jni文件进行中转。
即:我们的java->我们的jni->高通的cpp
最终实现我们的需求。
最后再注:
在jni文件对应的Android.mk中要加入以下语句,不然会编译报错:
- LOCAL_C_INCLUDES += \
- vendor/qcom/proprietary/fastmmi/libmmi \
- external/libcxx/include \
- external/skia/include/core \
- external/libxml2/include \
- external/icu/icu4c/source/common \
- $(QC_PROP_ROOT)/diag/include \
- $(QC_PROP_ROOT)/diag/src/ \
- $(TARGET_OUT_HEADERS)/common/inc
-
- LOCAL_SHARED_LIBRARIES := \
- libutils \
- libcutils \
- libc \
- libmmi \
- libdiag