Android系统修改 设置->关于平板电脑(手机)->状态信息中显示的SN序列号

Android系统修改 设置->关于平板电脑(手机)->状态信息中显示的序列号


1、本文全志A64为例,platform -> sun50iwp1   kernel -> linux-3.10  uboot: uboot-2014.07


---------------------------------kernel层-----------------------------------------------------------

2、 由于发现cat /sys/class/android_usb/android0/iSerial的number是等同于android系统状态信息中显示的SN序列号。
   因此在sys_config.fex中又找到以下配置:

;--------------------------------
;---     序列号标志
;--------------------------------
[serial_feature]
sn_filename = "sn.txt"


于是根据子键值名sn_filename 找到位于uboot-2014.07/board/sunxi/common/sunxi_serial.c

int sunxi_set_serial_num(void) 
{
        char serial[128] = {0};
        if(get_serial_num_from_file(serial))           =================> 方法1
        {
                get_serial_num_from_chipid(serial);    =================> 方法2
        }
        printf("serial is: %s\n",serial);
        if(setenv("sunxi_serial", serial))             =================>向kernel env中注册serialno信息
        {
                printf("error:set variable [sunxi_serial] fail\n");
        }


        return 0;
}

sunxi_set_serial_num是获取serialno的函数,其中定义了一个serial数组,而有两种获取serialno的方法;


方法1:
int get_serial_num_from_file(char* serial)
{
		........
		........
		ret = fdt_getprop_string(working_fdt,nodeoffset,"sn_filename",&filename);
        if((ret < 0) || (strlen(filename)== 0) )
        {
                printf("sunxi_serial: sn_filename is not exist\n");
                return -1;
        }
        //check private partition info
        partno = sunxi_partition_get_partno_byname("private");
        if(partno < 0)
        {
                return -1;
        }


        //get data from file
        sprintf(part_info,"%d:0", partno);
        sprintf(addr_info,"%lx", (ulong)serial); 
        sprintf(file_info,"%s", filename);
		
		.........
}


由上可知道方法1的serialno是由以下几部分组成:part_info、addr_info、file_info组成。
(1).通过在private分区中根据子键sn_filename中的值查找device tree获取file_info.
(2).通过强转serial数组的地址作为addr_info.
(3).通过获取private分区的number作为part_info.


方法2:

由上可知道方法2是通过读取特定寄存器的值来作为serialno(20位)。!!!最后通过sunxi_set_serial_num函数中的setenv向kernel env中注册serialno信息。

---------------------------------android层-----------------------------------------------------------

3、内核启动后android启动的第一个进程便是init进程,源码位于system/core/init/init.c中在init.c中,系统从main函数执行。

int main(int argc, char **argv)
{
    ........
	........
	
    property_init();


    get_hardware_name(hardware, &revision);


    process_kernel_cmdline(); ============>关键函数 step1
    ........
}


static void process_kernel_cmdline(void)
{
	/* don't expose the raw commandline to nonpriv processes */
	chmod("/proc/cmdline", 0440);


	/* first pass does the common stuff, and finds if we are in qemu.
	 * second pass is only necessary for qemu to export all kernel params
	 * as props.
	 */
	import_kernel_cmdline(0, ); =============>step2 在此函数中想kernel中获取注册的param 保存到类似ro.boot.xx的字符串中。
	if (qemu[0])
		import_kernel_cmdline(1, import_kernel_nv);


	/* now propogate the info given on command line to internal variables
	 * used by init as well as the current required properties
	 */
	export_kernel_boot_props(); =============>step3 重点关注,对ro.serialno进行赋值
}

static void export_kernel_boot_props(void)
{
    char tmp[PROP_VALUE_MAX];
    char serialno[PROP_VALUE_MAX] = {0}; //add
    int ret;
    unsigned i;
    struct {
        const char *src_prop;
        const char *dest_prop;
        const char *def_val;
    } prop_map[] = {
        { "ro.boot.serialno", "ro.serialno", "", },
        { "ro.boot.mode", "ro.bootmode", "unknown", },
        { "ro.boot.baseband", "ro.baseband", "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
    };
#####################################################################################################################################################

 
    for (i = 1; i < ARRAY_SIZE(prop_map); i++) { //modfied by tsb  因此此处我把i的值由 0 修改为 1.
					         =====>可以看到通过调用property_get获取ro.boot.xx字符串中的param。
						 =====>然后通过property_set来设置ro.xx字符的param。
#######而对于R58机器而言,不存在以上kernel层1、2步骤,ro.serialno的param来源于调用static int get_chip_id(char *buf, size_t size)函数获取/proc/cpuinfo下的serial号
#######而对于R16机器而言,不存在以上kernel层1、2步骤,ro.serialno的param来源于调用static int get_cpu_id(char* buf, size_t size)函数获取/proc/cpuinfo下的serial号 	
        ret = property_get(prop_map[i].src_prop, tmp);                                                                                                                                           
                                                 =====>!!!!!!!property_set函数只对没有设置过的ro.xx字符串设置param有效。
		                                 =====>!!!!!!!即一个ro.xx字符串一旦被设置过,即再次调用property_set函数无效,本人就在此犯过一次错误!!!!
        if (ret > 0)                             =====>具体可查看property_set实现。
            property_set(prop_map[i].dest_prop, tmp); 
        else
            property_set(prop_map[i].dest_prop, prop_map[i].def_val);
    }

    strlcpy(serialno, "12345678901234597890", sizeof("12345678901234567890"));//add by tsb 自定义serialno 即SN序列号12345678901234567890。
    property_set(prop_map[0].dest_prop, serialno);//add by tsb   设置ro.serialno的值
   
    ..........	
}


4.在frameworks/base/core/java/android/os/Build.java中定义了一个Build类

public class Build
 {   
	.........
	public static final String SERIAL = getString("ro.serialno");
	.........
 }

5.系统设置->关于平板电脑(手机)->状态信息在 packages/apps/Settings/src/com/android/settings/deviceinfo/Staus.java进 行设置

import android.os.Build; ==>包含了frameworks/base/core/java/android/os/Build.java中定义的一个Build类

public class Status extends PreferenceActivity {

 .........
 
	 protected void onCreate(Bundle icicle) {
			..........
			
			String serial = Build.SERIAL;           ==========>获取字符串
			if (serial != null && !serial.equals("")) {
				setSummaryText(KEY_SERIAL_NUMBER, serial); ===========>设置显示
			} else {
				removePreferenceFromScreen(KEY_SERIAL_NUMBER);
			}
			........
	}
 ...........
}

6.至此,整个流程结束~


 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/tsb151/article/details/70386040