ADD software version display
ADD software version display
1. Problem Description
2. Analysis
3. Solution
3.1 处理升级前后不改变的版本号
3.2 处理升级前后改变的版本号
4. Summary
1. Problem Description
在手机拨号盘输入暗码*#xxx#,弹出对话框,显示手机各image版本号。
2. Analysis
已知条件:
- 手机各个分区版本已经写入到了工程源码的指定文件中(一般是放在development/version/include/version.inc文件中),其具体的形式如下所示:
#define SCATTER_VER "K4H5EMMCCE00"#define PRELOADER_VER "P4H5H0H0CE00"#define UBOOT_VER "U4H5H0H0CE00"#define ANDROID_BOOT_VER "B4H5H0H0CE00"#define RECOVERY_VER "R4H5H0H0CE00"#define ANDROID_SYS_VER "Y4H5H0H0CE00"#define CACHE_VER "H4H5H0H0CE00"#define USRDATA_VER "S4H5H0H0CE00"#define LOGO_VER "L4H5H0H0CE00"#define FAT_VER "F4H5H0H0CE00"#define CUSTPACK_VER "C4H5HEU0CE00"#define SIMLOCK_VER "X4H0H0H0CE00"#define AP_DATABASE_VER "A4H5H0H0CE00"#define MODEM_DATABASE_VE "O4H5H0H0CE00"#define TRUSTZONE_VER "T4H5H0H0CE00"
- 输入暗码的dialog软件为第三方apk,该软件使用读系统属性的方法来获取版本信息,提供的系统属性接口如下:
ro.tct.cust.ver /*保存custpack分区版本号的系统属性*/ro.tct.non.ver /*保存modem分区版本号的系统属性*/ro.tct.sys.ver /*保存system分区版本号的系统属性*/ro.tct.boot.ver /*保存boot分区版本号的系统属性*/ro.tct.preloader.ver /*保存preloader分区版本号的系统属性*/ro.tct.bootloader.ver /*保存bootloader分区版本号的系统属性*/ro.tct.reco.ver /*保存recovery分区版本号的系统属性*/
即当输入*#xxx#后,该apk会读取设备中的各个系统属性,然后将读到的值通过AlertDialog显示出来。
分析思路
既然已经知道了各个分区版本号所存储的文件位置,又知道三方apk读取信息的接口,那么需要做的就是:
- 读取文件version.inc中的数据;
- 将读到的值存入到对应的系统属性中;
问题似乎已经得到了解决,但是system、boot、preloader、bootloader、recovery分区的版本号在fota版本升级后会变化。也就是说通过上面的做法,将编译好的系统刷如设备中,该设备的各个分区版本号是永远不能改变的,那么上面的做法就无法满足版本升级后部分分区版本号改变的要求了。
因此,可以分为2种不同情况来处理,具体如下图所示:

对于在fota升级前后不需要改变的版本号,直接读取该版本号,然后将它写入对应系统属性中。
对于在fota升级前后需要改变的版本号,首先,将它读取出来写入到一个中间文件(对于boot和system版本号是存放.ver文件中,其他三个的版本号是放在对应的.img文件中特定位置并且加有标识字符)中,;然后,在手机每次启动时,动态的读取这个中间文件的值,并将读到的值写入对应系统属性中;如果有fota升级,在升级时会同时替换升级分区的中间文件,这样就可以保证每次显示版本号时,都是读取的最新版本号。
3. Solution
3.1 处理升级前后不改变的版本号
- 修改/build/core/Makefile文件
从文件development/version/include/version.inc中分别读取modem分区版本号和custpack分区版本号,然后写入系统属性中,具体代码如下:
VERSIONDEF := development/version/include/version.inc #引进version.inc文件VERSIONLEN := 12CUSTPACK_VER := $(shell awk ' /CUSTPACK_VER/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))MODEM_DATABASE_VE := $(shell awk ' /MODEM_DATABASE_VE/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)"......CUSTPACK_VER="$(CUSTPACK_VER)" \MODEM_DATABASE_VE="$(MODEM_DATABASE_VE)" \......
- 修改文件build/tools/buildinfo.sh
echo "ro.tct.cust.ver=$CUSTPACK_VER"echo "ro.tct.non.ver=$MODEM_DATABASE_VE“
到现在modem分区版本号和custpack分区版本号已经写入到文件out目录下build.prop文件中,通过打开该文件可以看到从version.inc中读到的版本号。
3.2 处理升级前后改变的版本号
- 修改/build/core/Makefile文件
#write boot version to boot.verif [ -f $(TOPDIR)$(VERSIONDEF) ];#将boot分区版本号写入到临时文件boot.ver中thenawk ' /ANDROID_BOOT_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(TOPDIR)$(VERSIONDEF) > $(TARGET_ROOT_OUT)/boot.ver ;fi#write boot version to boot.ver......define build-systemimage-target#write system version to system.verif [ -f $(TOPDIR)$(VERSIONDEF) ];#将system分区版本号写入到临时文件boot.ver中thenawk ' /ANDROID_SYS_VER/ { print substr($$NF, 2,$(VERSIONLEN)) }' $(TOPDIR)$(VERSIONDEF) > $(TARGET_OUT)/system.ver;fi#write system version to system.ver#write recovery version and the string of JRD_VERSION_MARK_ to recovery.imgRECOVER_VERSION = JRD_VERSION_MARK_$(shell awk ' /RECOVERY_VER/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))# Assumes this has already been strippedifdef BOARD_KERNEL_CMDLINEINTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE) $(RECOVER_VERSION)"elseINTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(RECOVER_VERSION)"Endif#write recovery version and the string of JRD_VERSION_MARK_ to recovery.img
- 修改文件/vendor/mediatek/proprietary/bootable/bootloader/lk/Android.mk在指定头文件中定义一个宏定义,编译后再打开该version.h文件,可以看到如
#define LK_VER JRD_VERSION_MARK_U4HB3004CE00样子的宏定义。
VERSIONDEF := development/version/include/version.incVERSIONLEN := 12if [ -f $(LK_ROOT_DIR)/$(VERSIONDEF) ];thenecho -n "#define LK_VER JRD_VERSION_MARK_" > $(LK_ROOT_DIR)/vendor/mediatek/proprietary/bootable/bootloader/lk/include/version.h ;awk ' /UBOOT_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(LK_ROOT_DIR)/$(VERSIONDEF) >> $(LK_ROOT_DIR)/vendor/mediatek/proprietary/bootable/bootloader/lk/include/version.h ;fi
- 修改文件vendor/mediatek/proprietary/bootable/bootloader/lk/kernel/main.c,将前面的宏定义
LK_VER写入到对应的.img文件中去。
#include "version.h"ststatic int bootstrap2(void *arg){......#ifdef LK_VER#define xstr(s) str(s)#define str(s) #sprintf("%s\n", xstr(LK_VER));/*大概是只要使用了该宏,就会为该宏分配存储空间并存储在目标文件img中*/#endif......}
- 修改文件vendor/mediatek/proprietary/bootable/bootloader/preloader/Android.mk在指定头文件中定义一个宏定义,编译后再打开该version.h文件,可以看到如
#define PRELOADER_VER JRD_VERSION_MARK_P4HB3004CE00样子的宏定义。
VERSIONDEF := development/version/include/version.incVERSIONLEN := 12if [ -f $(PRELOADER_ROOT_DIR)/$(VERSIONDEF) ];thenecho -n "#define PRELOADER_VER JRD_VERSION_MARK_" > $(PRELOADER_DIR)/platform/mt6735/src/core/inc/version.h ;awk ' /PRELOADER_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(PRELOADER_ROOT_DIR)/$(VERSIONDEF) >> $(PRELOADER_DIR)/platform/mt6735/src/core/inc/version.h ;fi
- 修改文件vendor/mediatek/proprietary/bootable/bootloader/preloader/platform/mt6735/src/core/main.c,将前面的宏定义
PRELOADER_VER写入到对应的.img文件中去。
#include "version.h"static void bldr_pre_process(void){......#ifdef PRELOADER_VER#define xstr(s) str(s)#define str(s) #sprintf("%s\n", xstr(PRELOADER_VER));#endif......}
- 修改文件vendor/jrdcom/proprietary/traceability/main.c,将.ver文件中的版本号读取出来,然后写入相应的系统属性.ro中
#include <stdio.h>#include <string.h>#include <errno.h>#include <stdlib.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>#define __ALLOCATE_CFG_AUDIO_DEFAULT_H#include "libnvram.h"#include "CFG_PRODUCT_INFO_File.h"#include "Custom_NvRam_LID.h"#include <cutils/properties.h>//#define printf(x...) do { KLOG_ERROR("!!!!!!!!!!!!!!", x); } while (0)#include <android/log.h>#define LOG_TAG "gettra"#define printf(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)//VERSION parameter#define VERSION_FILE_PATCH "/system/system.ver"#define VERSION_OFFSET 0#define VERSION_SIZE 12#define VERSION_ANBOOT_FILE_PATCH "/boot.ver"int main(int argc, char *argv[]){int fid_ver;unsigned char ver[13]={0};unsigned char anbootver[13]={0};int fid_anbootver;memset(ver, 0x00, 12);memset(anbootver, 0x00, 12);//get version and set ro.tct.sys.verfid_ver = open( VERSION_FILE_PATCH, O_RDONLY);if(fid_ver < 0){perror("open:error");}else{lseek(fid_ver, VERSION_OFFSET , SEEK_SET);read(fid_ver, ver, VERSION_SIZE);close(fid_ver);}fid_anbootver = open( VERSION_ANBOOT_FILE_PATCH, O_RDONLY);if(fid_anbootver < 0){//perror("open:error");printf("fid_anbootver error\n");}else{printf("fid_anbootver \n");lseek(fid_anbootver, VERSION_OFFSET , SEEK_SET);read(fid_anbootver, anbootver, VERSION_SIZE);close(fid_anbootver);}property_set("ro.tct.sys.ver", ver);property_set("ro.tct.boot.ver", anbootver);printf("gettracability exe over!\n");return 1;}#undef __ALLOCATE_CFG_AUDIO_DEFAULT_H
- 修改文件vendor/jrdcom/proprietary/version/main.c,将.img文件中的版本号读取出来,然后写入相应的系统属性.ro中
/** Get version number from raw partition image, output to one file.*/#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <sys/system_properties.h>#include <utils/Log.h>#define printf ALOGE#define PRELOADER "/dev/block/mmcblk0boot0"#define BOOTLOADER "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/lk"#define RECOVERYIMG "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/recovery"#define PRELOADER_SIZE 0x40000#define BOOTLOADER_SIZE 0x60000#define RECOVERYIMG_SIZE 0x900000#define JRD_VERSION_MARK "JRD_VERSION_MARK_"#define JRD_VERSION_MARK_LEN 17#define JRD_VERSION_LEN 12static char buff[RECOVERYIMG_SIZE];/*这个方法就是先从指定img文件中读size个字节到一个临时数组ver中,然后根据size的大小,将数组中的值存入相应的系统属性.ro文件中*/int getver(char *img, int size){int ret = 0;char ver[JRD_VERSION_MARK_LEN + JRD_VERSION_LEN + 1];char setver[JRD_VERSION_MARK_LEN + JRD_VERSION_LEN + 1]={'\0'};int f;ssize_t s;int p;/*为数组分配存储空间大小为 17+12+1*/memset(ver, 0, JRD_VERSION_MARK_LEN);/*先讲表示字段"JRD_VERSION_MARK_"拷贝到数组中*/strncpy(ver, JRD_VERSION_MARK, JRD_VERSION_MARK_LEN);// read imagef = open(img, O_RDONLY);if (f < 0) {printf("can not open image!!!!!!!!!!!!!!!!!!\n");return f;}/*将.img文件中的前size个字符拷贝到buff中*/s = read(f, buff, size);printf("read out byte cnt: %d!!!!!!!!!!!!!!!!!!\n", s);f = close(f);// search the mark stringfor(p = 0; p < size; p++) {// match the first charif (buff[p] == ver[0]) {int t;for(t = 0; t < JRD_VERSION_MARK_LEN; t++) {if(buff[p + t] != ver[t])break;}// match whole mark stringif(t == JRD_VERSION_MARK_LEN)break;}}// not findif (p == size)return ret;/*p + strlen(JRD_VERSION_MARK)为版本号在buff中的偏移位*/// find itstrncpy(ver, buff + p + strlen(JRD_VERSION_MARK), JRD_VERSION_LEN);ver[JRD_VERSION_LEN] = '\n';if(size == PRELOADER_SIZE){strncpy(setver,ver, JRD_VERSION_LEN);setver[JRD_VERSION_LEN] = '\0';、/*将版本号写入系统属性ro.tct.preloader.ver中*/if(property_set("ro.tct.preloader.ver", setver) <0){printf("can not set ro.tct.preloader.ver!!!!!!!!!!!!!!!\n");}}else if (size == BOOTLOADER_SIZE){strncpy(setver,ver, JRD_VERSION_LEN);setver[JRD_VERSION_LEN] = '\0';/*将版本号写入系统属性ro.tct.bootloader.ver中*/if(property_set("ro.tct.bootloader.ver", setver) <0){printf("can not set ro.tct.bootloader.ver!!!!!!!!!!!!!!!\n");}}else{strncpy(setver,ver, JRD_VERSION_LEN);setver[JRD_VERSION_LEN] = '\0';/*将版本号写入系统属性ro.tct.reco.ver中*/if(property_set("ro.tct.reco.ver", setver) <0){printf("can not set ro.tct.reco.ver!!!!!!!!!!!!!!!\n");}}return ret;}int main(int argc, char *argv[]){int ret = 0;printf("PRELOADER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");ret = getver(PRELOADER, PRELOADER_SIZE);printf("BOOTLOADER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");ret = getver(BOOTLOADER, BOOTLOADER_SIZE);printf("RECOVERYIMG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");ret = getver(RECOVERYIMG, RECOVERYIMG_SIZE);return ret;}
到现在基本改完了,然后编译系统并将编好系统刷入设备中,验证发现无法显示出所有的版本号。难到上面的分析有问题?通过log分析发现,问题出在SELinux权限的方面。那什么是SELinux呢?说简单点,就是针对某个进程,它是否具有访问某个文件资源的权限。而在上面运行vendor/jrdcom/proprietary/version/main.c代码的进程需要访问lk、preloader、recovery这个3个分区的文件资源。我们需要将该进程读这3个分区的权限放开,才能获得分区中的版本号,不然无法获取版本号的值。
需要注意的是要具体查看项目中到底使用到哪个目录下的sepolicy规则,可以通过查看BoardConfig.mk文件来确定
#SELinux Policy File Configurationifeq ($(strip $(MTK_BASIC_PACKAGE)), yes)BOARD_SEPOLICY_DIRS += \device/mediatek/mt6735/sepolicy/basicendififeq ($(strip $(MTK_BSP_PACKAGE)), yes)BOARD_SEPOLICY_DIRS += \device/mediatek/mt6735/sepolicy/basic \device/mediatek/mt6735/sepolicy/bspendififneq ($(strip $(MTK_BASIC_PACKAGE)), yes)ifneq ($(strip $(MTK_BSP_PACKAGE)), yes)BOARD_SEPOLICY_DIRS += \device/mediatek/mt6735/sepolicy/basic \device/mediatek/mt6735/sepolicy/bsp \device/mediatek/mt6735/sepolicy/fullendifendif
具体的selinux规则修改如下:
- 在文件
[sepolicy规则目录]/file.te中定义一个type,具体如下:
type getver_data_file, file_type;
- 在文件
[sepolicy规则目录]/device.te中定义一个type,具体如下:
type lk_block_device, dev_type;
- 在文件
[sepolicy规则目录]/file_contexts中定义定义文件的安全上下文,具体如下:
#lk文件的安全上下文/dev/block/platform/mtk-msdc\.0/[0-9]+\.msdc0/by-name/lk u:object_r:lk_block_device:s0#getver文件的安全上下文/system/bin/getver u:object_r:getver_exec:s0/data/imgver u:object_r:getver_data_file:s0
- 在文件
[sepolicy规则目录]下创建一个getver.te文件,具体如下:
# getver#为getver应用定义域type getver, domain;#定义一个getver_exec类型的typetype getver_exec, exec_type, file_type;init_daemon_domain(getver)# Give getver a place where only getver can store files; everyone else is off limitsfile_type_auto_trans(getver, system_data_file, getver_data_file)#定义getver应用对文件的访问规则allow getver getver_data_file:file {create rw_file_perms};allow getver block_device:file {open rw_file_perms};allow getver block_device:dir {search rw_file_perms};allow getver nvram_data_file:file {open rw_file_perms};allow getver nvram_data_file:lnk_file {r_file_perms};allow getver nvram_data_file:dir {search r_file_perms};allow getver nvdata_file:dir {search rw_file_perms};allow getver userdata_block_device:blk_file {rw_file_perms};allow getver preloader_block_device:blk_file {r_file_perms};allow getver recovery_block_device:blk_file {r_file_perms};allow getver lk_block_device:blk_file {open r_file_perms};#allow getver system_data_file:dir {create search rw_file_perms add_name};allow getver getver_data_file:file {create open read write};#added by xuanfeng.ye for Task1304435 beginallow getver system_file:file {execute_no_trans};#added by xuanfeng.ye for Task1304435 end#20170204-jrdhz-yaosen.lin-add-for-t3695219-to-show version-brandallow getver property_socket:sock_file { write };allow getver system_prop:property_service { set };allow getver init:unix_stream_socket { connectto };
经过上面的修改,运行vendor/jrdcom/proprietary/version/main.c代码的进程就可以读取img文件中的版本号,并且将版本号写入对应的系统属性中了。而三方dialog apk就可以通过读取对应的系统属性来在界面上显示各个版本号了,对于系统属性读写需不需要加selinux访问规则就要看项目具体的配置了,如果项目对系统属性也使用了selinux,则要对系统属性加上对应selinux规则才能正确显示版本号。
4. Summary
这做这个defect的时候,需要区分软件升级前后要改变的版本号和不需要改变的版本号,对应升级前后一直不变的版本号,只需要将它们读出来,静态的写入到文件中,在需要使用的时候再拿出来就可以了。而对应升级前后需要改变的版本号,可以通过中间文件的形式来处理即在每次开机的时候通过读取中间文件来获取版本号,同时在软件升级时候同时更新中间文件来确保版本号也一起更新过,而在做这个问题的时候,遇到的难点则是selinux部分,因为对selinux的知识也不是很熟悉,写的selinux规则也是照猫画虎将别人的搬过来,而没有实际理解它们的含义,所以导致编译N次都没有成功的读取出img部分的版本号。整个问题的解决思路还是比较简单的,特别需要的就是各个地方的细节,比如最开始写的selinux规则,不管怎么写都没有效果,最后发现原来是使用的那个sepolicy目录根据就没有编译进系统。
ADD software version display的更多相关文章
- cordova platform add specified version
cordova platform add specified version 命令格式 cordova platform add android@4.0 可用的版本 Valid install tar ...
- 【转】System.Data.OracleClient requires Oracle client software version 8.1.7 or greater
安装完ASP.NET,Oracle9i客户端后,使用System.Data.OracleClient访问Oracle数据库如果出现这种错误:System.Data.OracleClient requi ...
- Software Version
Software Version Time Limit : 3000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) To ...
- C# System.Data.OracleClient requires Oracle client software version 8.1.7 or greater
好好的程序,突然出现了错误,原因是:System.Data.OracleClient requires Oracle client software version 8.1.7 or greater, ...
- Eclipse执行import命令导入maven项目时报错:Add a version or custom suffix using "Name template" in "Advanced" settings
新建了两个maven项目在E盘workspace目录,后面移到workspace/app_engine目录下提交svn,再通过Eclipse的File->import导入时报错了: Projec ...
- Data Base System.Data.OracleClient requires Oracle client software version 8.1.7 or greater解决方案
System.Data.OracleClient requires Oracle client software version 8.1.7 or greater解决方案 一.问题: 1.通过Syst ...
- System.Data.OracleClient requires Oracle client software version 8.1.7 or greater
It is a security issue, so to fix it simply do the following: Go to the Oracle folder. 1- Right Clic ...
- HDOJ(HDU) 1976 Software Version(简单判断)
Problem Description 相信大家一定有过在网上下载软件而碰到多个不同版本的情况. 一般来说,软件的版本号由三个部分组成,主版本号(Major Version Number),子版本号( ...
- Software Version --hdu1976
#include using namespace std; int main() { int T; cin>>T; int a1,b1,c1; int a2,b2,c2; while(T- ...
随机推荐
- mysql的MVCC多版本并发控制机制
MVCC多版本并发控制机制 全英文名:Multi-Version Concurrency Control MVCC不会通过加锁互斥来保证隔离性,避免频繁的加锁互斥. 而在串行化隔离级别为了保证较高的隔 ...
- MQTT协议 - arduino ESP32 通过精灵一号 MQTT Broker 进行通讯的代码详解
前言 之前研究了一段时间的 COAP 协议结果爱智那边没有测试工具,然后 arduino 也没有找到合适的库,我懒癌发作也懒得修这库,就只能非常尴尬先暂时放一放了.不过我在 爱智APP -> 设 ...
- axb_2019_fmt32
一道格式字符串的题目,拿到题目例行检查一下 32位的程序开机了nx的保护,将程序放入ida中 发现没有system函数于是进入main主函数进行代码审计 可以看到printf存在明显的格式字符串漏洞 ...
- tomcat Address already in use: JVM_Bind
运行多个tomcat时,出现tomcat Address already in use: JVM_Bind这个错误,可以按照如下方式解决: 修改F:\tomcat20111101\apache-tom ...
- CentOS7学习笔记(六) 用户权限管理
用户.用户组与文件的关系 在了解权限管理之前先创建一些用户和用户组便于后续学习,在root用户下操作: # 创建两个用户组 [root@localhost data]# groupadd kaifa ...
- 分布式系统一致性算法(Raft)
过去, Paxos一直是分布式协议的标准,但是Paxos难于理解,更难以实现,Google的分布式锁系统Chubby作为Paxos实现曾经遭遇到很多坑. 来自Stanford的新的分布式协议研究称为R ...
- react 结合gitte 创建项目(详情步骤)
创建项目第一步 基本搭建 在创建之前,需要有一个git 仓库,我们要把项目搭建到git 中 目录介绍 cd 到某个盘 mkdir workspace 创建workspace文件夹 cd works ...
- RenderFlex children have non-zero flex but incoming height constraints are unbounded.
问题 Flexible 里用了 Column, 使得高度无法确定 解决方案 将Flexible替换为ConstrainedBox, 并设定maxHeight 代码 ConstrainedBox( co ...
- MyBatis学习(四)MyBatis一对一关联查询
一对一关联查询即.两张表通过外键进行关联.从而达到查询外键直接获得两张表的信息.本文基于业务拓展类的方式实现. 项目骨架 配置文件conf.xml和db.properties前几节讲过.这里就不细说了 ...
- 【LeetCode】264. Ugly Number II 解题报告(Java & Python)
标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ https://leetcode.com/prob ...