现在的位置: 首页 > 综合 > 正文

通过jni写二进制文件失败总结

2017年10月29日 ⁄ 综合 ⁄ 共 3390字 ⁄ 字号 评论关闭

问题背景:
产线用到的CIT、PCBA、RUNIN等标志位,工模apk做完测试之后调用so动态库文件写入二进制文件。Write方法失败。
问题分析:
之前的项目标志位是存在productinfo/producinfo.bin二进制文件中,可以直接写成功,SC8830 android2.3平台是存在/dev/ubi0_miscdata 裸分区中,使用ubi方案,该裸分区必须root权限才可以去写,但是我们工程模式apk具有system权限,通过jni调用底层so动态库文件去写,该so库文件也是system权限,导致写操作失败。返回-1.
问题解决:
1. 在产线down完版本PC端发AT指令去写sn号时,将/dev/ubi0_miscdata同步到productinfo/producinfo.bin文件(这两个二进制文件的结构相同),结构定义如下所示:
typedef struct _tagSP09_PHASE_CHECK
{
unsigned long Magic;                
// "SP09"   (ÀÏ\u0153Ó¿ÚΪSP05)
char    
SN1[SP09_MAX_SN_LEN]; // SN , SN_LEN=24
char    
SN2[SP09_MAX_SN_LEN];    // add for Mobile
int     StationNum;                
// the test station number of the testing
char    
StationName[SP09_MAX_STATION_NUM][SP09_MAX_STATION_NAME_LEN];
unsigned char
Reserved[13];               // 
unsigned char
SignFlag;
char    
szLastFailDescription[SP09_MAX_LAST_DESCRIPTION_LEN];
unsigned short  iTestSign;
// Bit0~Bit14 ---> station0~station 14 
               
   //if tested. 0: tested, 1: not tested
unsigned short  iItem;    // part1: Bit0~ Bit_14 indicate test Station,1±íÊ\u0178Pass,    

}SP09_PHASE_CHECK_T, *LPSP09_PHASE_CHECK_T;

写完SN号后,PC端会发AT指令去读取SN号,此时是从dev中读取的,需要在读取时判断producinfo.bin文件是否为空,如果为空读取dev,并且将dev中的内容复制到productinfo.bin
,否则读取productinfo.bin文件。在写的时候直接去写pruductinfo文件和dev,但是该方法不能同时去写dev和productinfo文件,该方法失败。
2. 上层应用使用Runtime.getRuntime().exec()执行bin文件
在本模块中增加定义一个engcit 生成一个system/bin/engcit.bin文件如下
+#ENGCIT
+CAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PRELINK_MODULE    := false
+LOCAL_SHARED_LIBRARIES  := libcutils
+LOCAL_STATIC_LIBRARIES  :=
+LOCAL_LDLIBS        += -Idl
+LOCAL_CFLAGS        += -static 
+
+LOCAL_C_INCLUDES    += engclient.h \
+                       engopt.h \
+                       engphasecheck.h
+                                         
+LOCAL_SRC_FILES     := eng_cit.c
+
+LOCAL_MODULE := engcit
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
新增一个eng_cit.c文件用于接收参数执行写操作。关键代码如下:
while ( -1 != (opt = getopt(argc, argv, "t:"))) { //获取参数
+               switch (opt) {
+                       case 't':
+                               type = atoi(optarg);
+                               index = get_index(type);
+                               break;
+                       default:
+                               exit(-1);
+               }
+       }
+       eng_writephasecheck_cit(&phase_check);
+       return 0;//写完 return,关闭该service
在上层apk中直接调用Runtime.getRuntime().exec() 来执行该bin文件执行写操作。
Runtime使得直接调用底层Linux下的可执行程序或脚本成为可能
比如Linux下写个测试工具,直接编译后apk中通过Runtime来调用
或者写个脚本,apk中直接调用,省去中间层或者JNI
具体如下:
try {  
            Process p = Runtime.getRuntime().exec(system/bin/engcit –t 1);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
由于上层应用时system权限,调用bin文件也只有system权限,所以该方法也失败。
3.Init.rc中定义具有root权限的service ,启动上面定义的engcit.bin,通过jni调用到c层,控制该service执行写操作,init.rc中如下
+service engcitfull /system/bin/engcit -t 1
+    user root
+    group system
+    disabled
+    oneshot
+service engcitpcba /system/bin/engcit -t 2
….
+service engcitrunin /system/bin/engcit -t 3 
…..
+service engcitfreset /system/bin/engcit -t 4
……
权限配置为user root(root权限) group system(如果为root权限,system权限不能启动root权限的service)
通过JNI调用到c层so库后,操作如下
if(test == 1){//FULL TEST
+                       property_set("ctl.start", "engcitfull"); 
+               }else if (test == 2){//PCBA TEST
+                       property_set("ctl.start", "engcitpcba");        
+               }else if (test == 3){//RUNIN TEST
+                       property_set("ctl.start", "engcitrunin");       
+               }else if (test == 4){//FACTORY RESET 
+                       property_set("ctl.start", "engcitfreset");      
+               }
通过参数不同,写操作的结果不同。通过验证,该方法生效。该service用完即刻销毁,不占用内存空间。
4.Init.rc中定义一个具有root权限的service,常驻内存,利用socket通信建立连接,service作为服务端循环等待消息,收到消息后执行写操作。上层通过JNI调用到C层so库文件,作为客户端发消息,与service建立socket通信。该方法理论上可以解决该问题,但是考虑到service需要常驻内存,该项目内存比较紧张,所以没有采用该方法。对于一些特殊事件,可以考虑使用该方法。

抱歉!评论已关闭.