Android rom开发:自定义序列号ro.serialno

本文基于Android 5.1版本SDK。

客户需求:自定义sn,统一规则编号。

第一部分:背景知识

Android的sn由SystemProperties.get("ro.serialno", "");而来。

ro.serialno并不像常见的其他属性一样存在于build.prop等prop文件中,而是在/system/core/init/init.c里由ro.boot.serialno转换而来,转换的函数是export_kernel_boot_props,源码如下:

static void export_kernel_boot_props(void)
{
    char tmp[PROP_VALUE_MAX];
    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 = 0; i < ARRAY_SIZE(prop_map); i++) {
        ret = property_get(prop_map[i].src_prop, tmp);
        if (ret > 0)
            property_set(prop_map[i].dest_prop, tmp);
        else
            property_set(prop_map[i].dest_prop, prop_map[i].def_val);
    }

    ret = property_get("ro.boot.console", tmp);
    if (ret)
        strlcpy(console, tmp, sizeof(console));

    /* save a copy for init's usage during boot */
    property_get("ro.bootmode", tmp);
    strlcpy(bootmode, tmp, sizeof(bootmode));

    /* if this was given on kernel command line, override what we read
     * before (e.g. from /proc/cpuinfo), if anything */
    ret = property_get("ro.boot.hardware", tmp);
    if (ret)
        strlcpy(hardware, tmp, sizeof(hardware));
    property_set("ro.hardware", hardware);

    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
    property_set("ro.revision", tmp);

    /* TODO: these are obsolete. We should delete them */
    if (!strcmp(bootmode,"factory"))
        property_set("ro.factorytest", "1");
    else if (!strcmp(bootmode,"factory2"))
        property_set("ro.factorytest", "2");
    else
        property_set("ro.factorytest", "0");
}

重点看for循环部分,可以明显看到,ro.serialno的值是由ro.boot.serialno而来。

export_kernel_boot_props在process_kernel_cmdline中调用

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, import_kernel_nv);
    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();
}

process_kernel_cmdline中还调用了import_kernel_nv,import_kernel_nv的方法体如下:

static void import_kernel_nv(char *name, int for_emulator)
{
    char *value = strchr(name, '=');
    int name_len = strlen(name);

    if (value == 0) return;
    *value++ = 0;
    if (name_len == 0) return;

    if (for_emulator) {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char buff[PROP_NAME_MAX];
        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );

        if (len < (int)sizeof(buff))
            property_set( buff, value );
        return;
    }

    if (!strcmp(name,"qemu")) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;

        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}

代码很清晰,从cmdline中将androidboot.*的属性拿出来设置给了ro.boot.*的属性。在终端里面执行cat /proc/cmdline,如下:

shell@msm8909:/ # cat /proc/cmdline
sched_enable_hmp=1 console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x3F ehci-hcd.pa
rk=3 androidboot.bootdevice=7824900.sdhci lpm_levels.sleep_disabled=1 earlyprintk androidboot.selinux=permissive androidboot.emmc=true androidbo
ot.serialno=c336a56 androidboot.baseband=msm mdss_mdp3.panel=1:dsi:0:qcom,mdss_dsi_jd9366_800p_video:1:none

可以看到androidboot.serialno=c336a56,说明sn是在bootloader中获得,通过cmdline传递到内核。

至此,sn的传递和赋值流程基本梳理清楚。

第二部分:实现重新对sn赋值

自定义sn的思路是在系统的persist分区(persist分区的内容不会因为恢复出厂设置而被清除,重新刷机会清除)里面预设一个文本文件,系统启动之后,从app里面向这个文本文件写入按规则编号的sn,然后重启系统,init进程重新读取文本里面的值来设置ro.serialno和ro.boot.serialno。

首先在init.c里面修改,屏蔽掉ro.boot.serialno赋值的逻辑,然后在export_kernel_boot_props的for循环里面,i=0的时候是对ro.serialno进行赋值,在这里增加需要的代码:fopen打开文本获取到文件指针fp->fgets从fp读取一行内容到buf里面->调用property_set将buf的值设置给ro.serialno和ro.boot.serialno。

修改之后,重新编译boot.img。

make bootimage -j8

然后重刷boot:

adb reboot bootloader
fastboot flash boot boot.img
fastboot reboot

重启进系统,dmesg | grep init查看打印,尴尬的问题来了,fp是null。看来在init进程启动的时候,persist分区还没挂载,因此访问不到persist分区。在init.c里读取文本这个路子行不通。

在这里插入图片描述

换个思路

init.rc里面on boot之后,以root权限执行脚本,脚本中读取文本内容,然后将内容设置给ro.serialno和ro.boot.serialno。

脚本内容和init.rc增加的代码都很简单:

on boot
    start setsn

service setsn /sbin/busybox sh /system/etc/setsn.sh service
    oneshot
    disabled
    user root
    group root

**********************************
**********************************
#!/system/bin/sh

SN_FILE_PATH=/persist/sn.txt
SN_FILE_VALUE=`cat $SN_FILE_PATH`
SN_BACKUP_VALUE=`getprop persist.serialno.backup`

if [[ "$SN_FILE_VALUE" != "" ]];
then
    SN=$SN_FILE_VALUE
else
    SN=$SN_BACKUP_VALUE
fi

setprop ro.serialno $SN
setprop ro.boot.serialno $SN

此方法实测有效。

PS:
在调试过程中发现,init进程中对ro.serialno和ro.boot.serialno只能设值一次,如果不屏蔽init.c里面设值的逻辑,那么执行脚本将不会改变sn。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值