block格式差分包合并方法解析

采用合并差分包的方式,不过要做一些处理,以下是一些分析

1、做出两个差分包

对比两个升级包可以看出,我们只需要处理system  和vendor部分,其他驱动文件原生处理为了整包升级,所以01-03 和 02-03 中是一样的

2、修改01-03升级包,

修改updater-scrypt,以以下格式处理脚本文件

if getprop("ro.build.date.utc") == "1562576172" then

01-03升级脚本

......

else

02-03升级脚本

......

endif;

 

修改01-03升级包中system vendor文件的名称

system01.transfer.list system01.new.dat system01.patch.dat

vendor01.transfer.list vendor01.new.dat vendor01.patch.dat

同时修改updater-scrypt 中01-03脚本中的system vendor的名称

 

3、将02-03中升级包的updater-scrypt中的内容直接复制到else 后的部分,这里不用做任何的更改,全部脚本复制

 

4、将02-03中system 和 vendor的相关文件放入到 之前处理的的升级包中

处理后的截图如下

block格式差分包合并方法解析_第1张图片

这样处理完后验证,发现刷01版本 可以正常升级到03 ,但是刷机到02 却无法升级到03,报错patch失败

recovery的log如下,看错误确实走进了else方法,因为02-03的cache判断是25841664 needed

 

[    6.998341] SELinux: Loaded file_contexts
[    6.998551] Source: LEAGOO/Z10/Z10:8.1.0/O11019/1553496128:user/release-keys
[    6.998579] Target: LEAGOO/Z10/Z10:8.1.0/O11019/1565838105:user/release-keys
[    6.998732] Verifying current system...
[    6.998805] 108990464 bytes free on /cache (25841664 needed)
[   19.534887] Verified system image...
[   20.454253] Verified vendor image...
[   20.454349] Patching system image after verification.
[   20.454449] performing update
[   20.462901] blockimg version is 4
[   20.462951] maximum stash entries 0
[   20.465919] creating stash /cache/recovery/3a0104144fd74fc8745d8bb2737492c5611934b0/
[   20.466837] 108986368 bytes free on /cache (25841664 needed)
[   20.466929]  erasing 68874 blocks
[   20.592881] stashing 284 overlapping blocks to a7201da77719855292aa705ec24e88804b4c1a68
[   20.592944] 108986368 bytes free on /cache (1163264 needed)
[   20.593019]  writing 284 blocks to /cache/recovery/3a0104144fd74fc8745d8bb2737492c5611934b0/a7201da77719855292aa705ec24e88804b4c1a68
[   20.667376] patching 284 blocks to 284
[   20.667434] Not a bsdiff patch.
[   20.667448] bspatch failed, result: 2
[   20.667460] Patch may be corrupted, offset: 0, SHA1: 03110be20c1d315bc6814caa3e1086b042f907ae
[   20.667475] Failed to apply bsdiff patch.
[   20.667488] failed to execute command [bsdiff 0 210 a7201da77719855292aa705ec24e88804b4c1a68 a819a07d4258d79f6fa909984c0933983f211e9b 2,189705,189989 284 2,189705,189989]
[   20.669709] script aborted: E1001: Failed to update system image.
[   20.669829] Patch application failed, retry update.

这里报错是patch文件不对,起初怀疑是不是用错了02的包,但是实际测试02-03原始升级包是可以升级成功的

目前针对这个错误判断是我们拷贝后的dat文件损坏了,但是对比跟02-03原包是一样,所以基本上是卡这儿了,只能填坑了

 

5、生成差分包dat文件处理

既然我们这样手动放入或者用zip命令不行,只能看最初的生成差分包时,是怎么把dat文件放入到升级包中

ota_from_target_file.py

def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
  ......
  ......
  system_diff.WriteScript(script, output_zip,
                          progress=0.8 if vendor_diff else 0.9)

  if vendor_diff:
    vendor_diff.WriteScript(script, output_zip, progress=0.1)

common.py

WriteScript方法

  def WriteScript(self, script, output_zip, progress=None):
    if not self.src:
      # write the output unconditionally
      script.Print("Patching %s image unconditionally..." % (self.partition,))
    else:
      script.Print("Patching %s image after verification." % (self.partition,))

    if progress:
      script.ShowProgress(progress, 0)
    self._WriteUpdate(script, output_zip)
    if OPTIONS.verify:
      self._WritePostInstallVerifyScript(script)

self._WriteUpdate(script, output_zip) 

看到我们想要的代码了 ,处理new.dat 和 patch.dat部分

  def _WriteUpdate(self, script, output_zip):
    ZipWrite(output_zip,
             '{}.transfer.list'.format(self.path),
             '{}.transfer.list'.format(self.partition))

    # For full OTA, compress the new.dat with brotli with quality 6 to reduce its size. Quailty 9
    # almost triples the compression time but doesn't further reduce the size too much.
    # For a typical 1.8G system.new.dat
    #                       zip  | brotli(quality 6)  | brotli(quality 9)
    #   compressed_size:    942M | 869M (~8% reduced) | 854M
    #   compression_time:   75s  | 265s               | 719s
    #   decompression_time: 15s  | 25s                | 25s

    if not self.src:
      bro_cmd = ['bro', '--quality', '6',
                 '--input', '{}.new.dat'.format(self.path),
                 '--output', '{}.new.dat.br'.format(self.path)]
      print("Compressing {}.new.dat with brotli".format(self.partition))
      p = Run(bro_cmd, stdout=subprocess.PIPE)
      p.communicate()
      assert p.returncode == 0,\
          'compression of {}.new.dat failed'.format(self.partition)

      new_data_name = '{}.new.dat.br'.format(self.partition)
      ZipWrite(output_zip,
               '{}.new.dat.br'.format(self.path),
               new_data_name,
               compress_type=zipfile.ZIP_STORED)
    else:
      new_data_name = '{}.new.dat'.format(self.partition)
      ZipWrite(output_zip, '{}.new.dat'.format(self.path), new_data_name)

    ZipWrite(output_zip,
             '{}.patch.dat'.format(self.path),
             '{}.patch.dat'.format(self.partition),
             compress_type=zipfile.ZIP_STORED)

ZipWrite方法

#
def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
             compress_type=None):
  import datetime

  # http://b/18015246
  # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
  # for files larger than 2GiB. We can work around this by adjusting their
  # limit. Note that `zipfile.writestr()` will not work for strings larger than
  # 2GiB. The Python interpreter sometimes rejects strings that large (though
  # it isn't clear to me exactly what circumstances cause this).
  # `zipfile.write()` must be used directly to work around this.
  #
  # This mess can be avoided if we port to python3.
  saved_zip64_limit = zipfile.ZIP64_LIMIT
  zipfile.ZIP64_LIMIT = (1 << 32) - 1

  =================
  
  /tmp/tmpBmzgWk/system.patch.dat
  system.patch.dat
  0
  =================
  print("=================")  
  print(zip_file)
  print(filename)
  print(arcname)
  print(compress_type)
  print("=================")
  
  if compress_type is None:
    compress_type = zip_file.compression
  if arcname is None:
    arcname = filename

  saved_stat = os.stat(filename)

  try:
    # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
    # file to be zipped and reset it when we're done.
    os.chmod(filename, perms)

    # Use a fixed timestamp so the output is repeatable.
    epoch = datetime.datetime.fromtimestamp(0)
    timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
    os.utime(filename, (timestamp, timestamp))

    zip_file.write(filename, arcname=arcname, compress_type=compress_type)
  finally:
    os.chmod(filename, saved_stat.st_mode)
    os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
    zipfile.ZIP64_LIMIT = saved_zip64_limit

通过打印我们知道了进入ZipWrite的参数,同时我看到了compress_type=zipfile.ZIP_STORED这样一个参数,对于patch.dat,查询后发现这是zipfile模块的一个参数

zipfile.ZIP_STORED

表示非压缩的常量。

那现在就比较明了了,对于dat文件,虽然是放进升级包,但是不做任何压缩的处理,而我们不管是怎么手动放,都对dat进行了压缩,所以我们现在要用zipfile模块写一个脚本去处理dat文件的拷贝

 

6、形成脚本处理升级包

import os
import os.path
import re
import subprocess
import sys
import shlex
import shutil
import tempfile
import threading
import time
import zipfile
import datetime


def ZipWrite(input_zip, filename, arcname=None, perms=0o644):

  # http://b/18015246
  # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
  # for files larger than 2GiB. We can work around this by adjusting their
  # limit. Note that `zipfile.writestr()` will not work for strings larger than
  # 2GiB. The Python interpreter sometimes rejects strings that large (though
  # it isn't clear to me exactly what circumstances cause this).
  # `zipfile.write()` must be used directly to work around this.
  #
  # This mess can be avoided if we port to python3.
  saved_zip64_limit = zipfile.ZIP64_LIMIT
  zipfile.ZIP64_LIMIT = (1 << 32) - 1
  
  saved_stat = os.stat(filename)

  try:
    # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
    # file to be zipped and reset it when we're done.
    os.chmod(filename, perms)

    # Use a fixed timestamp so the output is repeatable.
    # 处理文件的时间戳
    epoch = datetime.datetime.fromtimestamp(0)
    timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
    os.utime(filename, (timestamp, timestamp))
    #将传入的文件生成为zipfile对象,传入参数a 代表可以追加文件
    zip_file = zipfile.ZipFile(input_zip, "a")
    #如果我们要追加的文件不在zip_file 文件列表中,执行追加
    if arcname not in zip_file.namelist():
      #使用write方法追加文件,并给到参数zipfile.ZIP_STORED
      zip_file.write(filename, arcname, zipfile.ZIP_STORED)
    else:
      print >> sys.stderr, "%s is already exist,do nothing..." % (arcname)
  finally:
    os.chmod(filename, saved_stat.st_mode)
    os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
    zipfile.ZIP64_LIMIT = saved_zip64_limit
    #处理好后关闭
    zip_file.close()

  return True

def main(argv):

  if len(argv) != 3:
    print(__doc__)
    sys.exit(1)

  input_zip = argv[0]
  filename = argv[1]
  arcname = argv[2]
  #传入的三个参数,1、升级包 2、需要放入的文件 3、放入后文件的名称
  if not ZipWrite(input_zip, filename, arcname):
    print >> sys.stderr, "error: failed to zip %s to %s" % (arcname,zip_file)
    exit(1)


if __name__ == '__main__':
  main(sys.argv[1:])

执行脚本:

./build/tools/releasetools/adups_zipfile_for_dat.py update.zip '/home/adups/FOTA/2019090901/02/target_files-package02/update002/vendor.transfer.list'  vendor.transfer.list

 

使用重新生成的差分包测试01-03 和 02-03 均验证OK,问题解决

你可能感兴趣的:(Andrdoid,OTA升级)