Android 系统序列号从哪里来,以及客制化序列号

  • 系统获取序列号过程
  • 客制化序列号


系统获取序列号过程

Android系统的SN号,实际是从“cmdline”里面的“androidboot.serialno”获取到的,那么在给系统传递“cmdline”之前,就需要获取到SN号。

以MTK的一个Android 5.1系统为例,MTK的SN号,通过写号工具,和IMEI等参数,一起写入“NV(Non-Volatile)”中,然后开机再读出来用。

读取SN号的部分,在lk中,代码如下,文件:bootable/bootloader/lk/app/mt_boot/mt_boot.c:

void mt_boot_init(const struct app_descriptor *app)
{
	unsigned usb_init = 0;
	unsigned sz = 0;
	int sec_ret = 0;
#ifdef CONFIG_MTK_USB_UNIQUE_SERIAL
	u64 key;
	u32 chip_code;
#endif
	char serial_num[SERIALNO_LEN];

#ifdef CONFIG_MTK_USB_UNIQUE_SERIAL
	/* Please enable EFUSE clock in platform.c before reading sn key */
	/* serial string adding */
	key = readl(SERIAL_KEY_HI);
	key = (key << 32) | readl(SERIAL_KEY_LO);
	chip_code = DRV_Reg32(APHW_CODE);
	//如果有使用写号工具去写号,那么“key”就不为零,这里具体计算SN号的算法,都是平台厂商定义的东西
	//估计是写号的时候,使用逆向的算法得到“key”,读的时候使用“key”再去算SN号
	if (key != 0)
		get_serial(key, chip_code, serial_num);		//这里用到厂商定义的算法,算出来SN号来
	else
		memcpy(serial_num, DEFAULT_SERIAL_NUM, SN_BUF_LEN);		//如果没有写号,使用默认值“DEFAULT_SERIAL_NUM”
	/* copy serial from serial_num to sn_buf */
	memcpy(sn_buf, serial_num, SN_BUF_LEN);		//只需要“SN_BUF_LEN”长度做系统SN号
			dprintf(CRITICAL,"serial number %s\n",serial_num);
#else
			memcpy(sn_buf, DEFAULT_SERIAL_NUM, strlen(DEFAULT_SERIAL_NUM));
#endif
	sn_buf[SN_BUF_LEN] = '\0';
	surf_udc_device.serialno = sn_buf;

	......
}

读取到SN号之后,传递给“cmdline”的“androidboot.serialno”,代码如下,依然在mt_boot.c文件:

/* Append androidboot.serialno=xxxxyyyyzzzz in cmdline */
    sprintf(cmdline, "%s%s%s", cmdline, " androidboot.serialno=", sn_buf);

然后,init进程启动之后,会去处理“cmdline”,得到SN号。
代码如下,文件:system/core/init/init.c

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

void import_kernel_cmdline(int in_qemu,
                           void (*import_kernel_nv)(char *name, int in_qemu))
{
    char cmdline[2048];
    char *ptr;
    int fd;

    fd = open("/proc/cmdline", O_RDONLY);
    if (fd >= 0) {
        int n = read(fd, cmdline, sizeof(cmdline) - 1);
        if (n < 0) n = 0;

        /* get rid of trailing newline, it happens */
        if (n > 0 && cmdline[n-1] == '\n') n--;

        cmdline[n] = 0;
        close(fd);
    } else {
        cmdline[0] = 0;
    }

    ptr = cmdline;
    while (ptr && *ptr) {
        char *x = strchr(ptr, ' ');
        if (x != 0) *x++ = 0;
        import_kernel_nv(ptr, in_qemu);
        ptr = x;
    }
}

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;

#ifdef MTK_TC7_COMMON_DEVICE_INTERFACE
    if (!strcmp(name,"AdbAutoEnable")) {
        char buff[PROP_NAME_MAX];
        int len = snprintf( buff, sizeof(buff), "ro.usb.adb.auto_enable" );

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

    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);
		}
    }
}

在函数“import_kernel_nv”里,获取到“androidboot.serialno”的值,然后设置到系统属性“ro.boot.serialno”,这样Android就获取到了SN号。

客制化序列号

既然知道了SN号的获取流程,那么客制化就可以针对的去修改。

对于Android系统的SN号,只要在设置系统属性“ro.boot.serialno”的时候或之前,进行客制化修改就可以改变系统SN号;
对于fastboot功能的SN号,就要从lk读取SN号的时候,进行对应修改。

比如使用EMMC的id作为SN号,在lk中的修改的话,就要在如下这个位置之前,改变“serial_num”的值。

/* copy serial from serial_num to sn_buf */
	memcpy(sn_buf, serial_num, SN_BUF_LEN);

理论上lk这里是可以读EMMC的id的,可以参考preloader中读EMMC的id部分代码,我是在init.c的函数“import_kernel_nv”里实现的,如下:

static void import_kernel_nv(char *name, int for_emulator)
{
	......

    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)
		{
#define EMMC_ID_LEN   32
#define SN_BUF_LEN    10
			int fd;
			char emmc_id[EMMC_ID_LEN+1];
			char sn_buf[SN_BUF_LEN+1];

			//如果现在是要处理SN号
			if( !strcmp(boot_prop_name,"serialno") )
			{
				//通过驱动节点读取EMMC的id
				fd = open("/sys/block/mmcblk0/device/cid", O_RDONLY);
				if (fd < 0)
				{
					ERROR("can't open: %s\n", "/sys/block/mmcblk0/device/cid");
					property_set(prop, value);		//打开失败,使用默认值
					return;
				}
				//读取EMMC的id
				cnt = read(fd, (void *)&emmc_id, EMMC_ID_LEN);
				close(fd);
				emmc_id[EMMC_ID_LEN] = 0;
				if(cnt != EMMC_ID_LEN)
				{
					ERROR("read emmc id len error: %d:%d\n", EMMC_ID_LEN, cnt);
					property_set(prop, value);		//读取不正确,使用默认值
					return;
				}
				//只需要前面10个字符
				emmc_id[SN_BUF_LEN] = 0;
				cnt = snprintf(sn_buf, sizeof(sn_buf), "%s", emmc_id);
				sn_buf[SN_BUF_LEN] = 0;
				if(cnt != SN_BUF_LEN)
				{
					ERROR("set sn buf len error: %d:%d\n", SN_BUF_LEN, cnt);
					property_set(prop, value);		//长度不正确,使用默认值
					return;
				}
				//使用新SN号设置系统属性
				property_set(prop, sn_buf);
			}
			//如果现在要处理的不是SN号
			else
			{
				property_set(prop, value);
			}
		}
    }
}