Android系统更新防互刷功能实现与分析

写在前面:

    为了帮助理解,这里首先描述一个应用场景。

    一个项目有两个版本(一个项目两个版本的原因或许是由于硬件不同导致的,如不同容量电池,不同分辨率摄像头等),在升级的时候很容易将相同项目的两个版本的升级包混淆,因此需要实现两个版本的防互刷功能,那么在该应用场景下需要如何实现呢?

    注意,这里肯定会有疑问了,既然一个项目两个版本容易混淆更新包,那么把它作为两个项目来实施不是有效避免这个问题了吗?当然,把一个项目由于不同版本分拆为不同的项目来做当然可以有效的避免更新包混淆的问题,这也是一个比较快捷的方案。因此该博文是在实施一个项目的场景下来开展的。


第一步:了解校验原理

熟悉ota_from_target_files、edify_generator.py或则了解install.c和updater-script脚本执行过程的朋友应该很清楚在更新包安装的一开始会对一些属性信息进行校验,这里贴出部分脚本生成器edify_generator.py的相关的函数:

  #校验时间戳

  def AssertOlderBuild(self, timestamp, timestamp_text):
    """Assert that the build on the device is older (or the same as)
    the given timestamp."""
    self.script.append(
        (‘(!less_than_int(%s, getprop("ro.build.date.utc"))) || ‘
         ‘abort("Can\‘t install this package (%s) over newer ‘
         ‘build (" + getprop("ro.build.date") + ").");‘
         ) % (timestamp, timestamp_text))

校验时间戳,我们需要理解上面的方法,上面的函数只是生成一条edify语句,实际上在生成的updater-script脚本中,也就是下面这条语句:

(!less_than_int(%s, getprop("ro.build.date.utc")))||abort("Can\‘t install this package (%s) over newer build (" + getprop("ro.build.date") + ").");

arg1:timestamp, arg2:timestamp_text//参数1为世界时间,参数2为日期格式

上面的代码告诉我们如果①的值为True则执行下一步,否则执行语句②,语句①对比时间戳大小,语句②为中断语句。也就是说如果当前更新包的打包时间如果比手机Build的时间older,就执行中止语句。

#校验设备信息
  def AssertDevice(self, device):
    """Assert that the device identifier is the given string."""
    cmd = (‘getprop("ro.product.device") == "%s" || ‘
           ‘abort("This package is for \\"%s\\" devices; ‘
           ‘this is a \\"" + getprop("ro.product.device") + "\\".");‘
           ) % (device, device)
    self.script.append(cmd)

若更新包中打包的设备信息如果与手机Build中的设备信息不一致就会执行中止语句。

#校验系统指纹

  def AssertSomeFingerprint(self, *fp):
    """Assert that the current system build fingerprint is one of *fp."""
    if not fp:
      raise ValueError("must specify some fingerprints")
    cmd = (
           ‘ ||\n    ‘.join([(‘file_getprop("/system/build.prop", ‘
                         ‘"ro.build.fingerprint") == "%s"‘)
                        % i for i in fp]) +
           ‘ ||\n    abort("Package expects build fingerprint of %s; this ‘
           ‘device has " + getprop("ro.build.fingerprint") + ".");‘
           ) % (" or ".join(fp),)
    self.script.append(cmd)

若更新包中新旧版本的系统指纹与当前手机中的系统指纹不一致就会执行中止语句。

无论是时间戳还是设备信息还是系统指纹都是从build.prop中取值。但是,这里呢,要对fingerprint信息简单介绍一下,fingerprint就是我们常说的系统指纹,我们可以在adb模式下能否通过getprop ro.build.fingerprint的方式进行查看或者参考alps\frameworks\base\core\java\android\os\Build.java下对应的FINGERPRINT的取值方式在代码中进行取值。每个项目的每个版本Fingerprint都不同,按照Google的要求,前后不允许带有空格、空行、断行,字符不超过91个,

Fingerprint字段定义格式如下:
BUILD_FINGERPRINT :=
$(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
如:Google/Y100-T00/GoogleY100-T:4.2.2/GoogleY100-T00/CHSC01B001:user/release-keys
TCT/TCL_J900T/Camry2_TD:4.2.1/JOP40D/TCL_J900T_V2.0:user/release-keys
具体配置请按以下:
1.PRODUCT_BRAND为getprop文件中的相关字段:ro.product.brand的信息,全字母大写or首字母大写均可。Eg:Google
2.TARGET_PRODUCT为getprop文件中的相关字段:ro.product.name的信息,全字母大写,Eg:Y516-T00。
3.TARGET_DEVICE为getprop文件中的相关字段:ro.product.device的信息
4.ID为getprop文件中的相关字段:ro.build.id的信息,
5.version.incremental为getprop文件中的相关字段:ro.build.version.incremental的信息。要求version.incremental是内部版本号(真实版本号)
6.VERSION.RELEASE为getprop文件中的相关字段:ro.build.version.release的信息,一般为Android系统版本号
7.TYPE必须是user版本,不能是eng版本。

8.TAGS必须是release-keys,不能是test-keys,(将宏MTK_SIGNATURE_CUSTOMIZATION = yes)。如果该版本带有或者后续将开发OTA升级功能,须使用ota-rel-keys,release-keys,普通版本请使用release-keys。

那么系统指纹在差分升级的时候才会校验,设备信息和签名一般来说同一个项目后者同一个产品对于客户来讲都是不允许修改的,如果客户允许这些也可以作为防互刷的手段。那么可能到这里你或许已经明白这篇博文的目的了。

那么,对的,本篇博文主要是通过增加新的系统属性来标识相同项目不同版本防互刷的功能。那么该如何实现呢?

第二步:添加校验标识

一、添加系统属性

这里需要在/vendor/{项目}/{项目分支}/config/system.prop文件,当然不同平台不同公司对应的system.prop路径不一致

这里我在system.prop中最后一行添加如下属性:

ro.update.version=a1

二、修改脚本

首先在脚本生成器edifty_generator.py文件中定义如下方法:

  def AssertUpdateVersion(self, update_version):
    """Assert that the update_version identifier is the given string."""
    cmd = ('getprop("ro.update.version") == "%s" || '
           'abort("This package is for \\"%s\\" update_version; '
           'this is a \\"" + getprop("ro.update.version") + "\\".");'
           ) % (update_version, update_version)
    self.script.append(cmd)
然后在ota_from_target_file文件中的AppendAssertions函数中添加如下:(红色为新添加)
def AppendAssertions(script, info_dict):
      device = GetBuildProp("ro.product.device", info_dict)
      script.AssertDevice(device)
      update_version = GetBuildProp("ro.update.version", info_dict)//从字典中去除ro.update.version的值
      script.AssertUpdateVersion(update_version)//在升级脚本中添加验证新的属性函数

并修改WriteFullOTAPackage()函数,如下:

def WriteFullOTAPackage(input_zip, output_zip):
  # TODO: how to determine this?  We don‘t know what version it will
  # be installed on top of.  For now, we expect the API just won‘t
  # change very often.
  script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)


  metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
                                         OPTIONS.info_dict),
              "pre-device": GetBuildProp("ro.product.device",
                                         OPTIONS.info_dict),
              "post-timestamp": GetBuildProp("ro.build.date.utc",
                                             OPTIONS.info_dict),
              "update-version": GetBuildProp("ro.update.version",
                                             OPTIONS.info_dict),//定义新的属性

              }

三、验证(注!不要偷懒,直接修改update包中的build.pro,否则会校验签名不过的)

那么接下来就需要进行验证了,这里呢首先使用ro.update.version值为a1的版本制作整包,然后在手机中download属性ro.update.version值为a2的版本进行sd卡整包升级,在升级的过程中会对update-version进行校验,如果值不一样则校验失败。

下面贴出校验失败的log(也就是防互刷功能成功实现的效果)

script aborted: This package is for "a1" update_version; this is a "a2".
This package is for "a1" update_version; this is a "a2".
E:Error in /sdcard/dload/update.zip

(Status 7)
Installation aborted.


I:no boot messages recovery
I:[1]check the otaupdate is done!
factory_poweroff_flag=0
demo_mode_flag=0


下面是对应的updater-script脚本中防互刷的语句。

getprop("ro.update.version") == "a1" || abort("This package is for \"a1\" update_version; this is a \"" + getprop("ro.update.version") + "\".");

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。