OTA升级学习笔记

这篇具有很好参考价值的文章主要介绍了OTA升级学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

记录下近期学习的OTA升级相关内容

1、OTA是什么

OTA(Over-the-Air Technology)简单来说就是一种无线升级的技术

2、OTA升级包制作

2.1 升级包生成方式

整编完成后使用make otapackage会生成target_files压缩包(包含完整的image数据)和可用于升级的ota update压缩包。如果不想在编译的时候生成升级包,可以将TARGET_SKIP_OTA_PACKAGE置成false(编译脚本build/core/Makefile中)

//android/build/core/Makefile
ifeq ($(BUILD_OS),darwin)
  build_ota_package := false
  build_otatools_package := false
else
  # set build_ota_package, and allow opt-out below`在这里插入代码片`
  build_ota_package := true
  ifeq ($(TARGET_SKIP_OTA_PACKAGE),true)`在这里插入代码片`
//可改动在编译时不自动生成升级包
    build_ota_package := false
  endif
  .....
endif

....
//make otapackage制作升级包
ifeq ($(build_ota_package),true)
# -----------------------------------------------------------------
# OTA update package

# $(1): output file
# $(2): additional args
define build-ota-package-target
# 调用ota_from_target_files脚本制作升级包
PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
   build/make/tools/releasetools/ota_from_target_files -v \
   --block \
   --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
   -p $(HOST_OUT) \
   $(if $(OEM_OTA_CONFIG), -o $(OEM_OTA_CONFIG)) \
   $(2) \
   $(BUILT_TARGET_FILES_PACKAGE) $(1)
endef

name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
  name := $(name)_debug
endif
//升级包名称
name := $(name)-ota-$(FILE_NAME_TAG)
//升级包目录
INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip

INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata
.....

OTA升级包(ota update)是使用Google提供的脚本解析target_files制作出来的。升级包分为是整包和差分包。两者执行的命令略有不同,在android目录下输入:

整包:build/tools/releasetools/ota_from_target_files out/dist/merged-qssi_kona-target_files.zip ota.zip
差分包:build/tools/releasetools/ota_from_target_files -i out/dist/A.zip out/dist/B.zip ota.zip

命令可拆分为三部分来看,以整包为例:build/tools/releasetools/ota_from_target_files是Google提供的脚本路径,可以进入build/tools/releasetools/看下还有其他脚本,out/dist/merged-qssi_kona-target_files.zip是系统编译生成的target_files路径,ota.zip为自己命的升级包名称,可在前面增加保存路径,不加默认在android路径下。

2.2 升级包生成流程解析

在源码下执行该命令生成update.zip包主要分成两步:
(1) 根据build/core/Makefile执行编译生成一个target update原包(zip格式)
(2) 运行一个python脚本,并以上一步准备的zip包作为输入,最终生成我们需要的升级包

2.2.1 Makefile编译生成target原包

这个原包在实际编译过程中有两个作用:
(1) 用来生成OTA update升级包
(2) 用来生成系统镜像
编译脚本build/core/Makefile中(参考otapackage/Makefile注释)

# -----------------------------------------------------------------
# 一个zip压缩包用于映射target filesystem
# 这个压缩包可以用来做ota升级包或者filesystem image作为构建后的一个步骤
name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
  name := $(name)_debug
endif
# android/build/make/core/main.mk ---> FILE_NAME_TAG := eng.$(BUILD_USERNAME)
# target原包包名
# 例如:productName-target_files-eng.UserName
name := $(name)-target_files-$(FILE_NAME_TAG)
//进入该路径 obj/PACKAGING/target_files_intermediates
intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip
$(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates)
//target文件的目录
$(BUILT_TARGET_FILES_PACKAGE): \
	    zip_root := $(intermediates)/$(name)

# $(1): Directory to copy
# $(2): Location to copy it to
# The "ls -A" is to prevent "acp s/* d" from failing if s is empty.
define package_files-copy-root
  if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \
    mkdir -p $(2) && \
    $(ACP) -rd $(strip $(1))/* $(2); \
  fi
endef

built_ota_tools :=
.....
# 在此处添加后就会生成到target原始包中
$(BUILT_TARGET_FILES_PACKAGE): \
	    $(INSTALLED_RAMDISK_TARGET) \
	    $(INSTALLED_BOOTIMAGE_TARGET) \
	    $(INSTALLED_RECOVERYIMAGE_TARGET) \
	    $(FULL_SYSTEMIMAGE_DEPS) \
	    $(INSTALLED_USERDATAIMAGE_TARGET) \
	    $(INSTALLED_CACHEIMAGE_TARGET) \
	    $(INSTALLED_VENDORIMAGE_TARGET) \
	    $(INSTALLED_PRODUCTIMAGE_TARGET) \
	    $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET) \
	    $(INSTALLED_VBMETAIMAGE_TARGET) \
        ....
//image拷贝到target包的images路径下
ifdef BOARD_PREBUILT_VENDORIMAGE
	$(hide) mkdir -p $(zip_root)/IMAGES
	$(hide) cp $(INSTALLED_VENDORIMAGE_TARGET) $(zip_root)/IMAGES/
endif
ifdef BOARD_PREBUILT_PRODUCTIMAGE
	$(hide) mkdir -p $(zip_root)/IMAGES
	$(hide) cp $(INSTALLED_PRODUCTIMAGE_TARGET) $(zip_root)/IMAGES/
endif
.....
ifneq ($(BOARD_SUPER_PARTITION_GROUPS),)
	$(hide) echo "super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)" > $(zip_root)/META/dynamic_partitions_info.txt
	@# Remove 'vendor' from the group partition list if the image is not available. This should only
	@# happen to AOSP targets built without vendor.img. We can't remove the partition from the
	@# BoardConfig file, as it's still needed elsewhere (e.g. when creating super_empty.img).
	$(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \
	    $(eval _group_partition_list := $(BOARD_$(call to-upper,$(group))_PARTITION_LIST)) \
	    $(if $(INSTALLED_VENDORIMAGE_TARGET),,$(eval _group_partition_list := $(filter-out vendor,$(_group_partition_list)))) \
	    echo "$(group)_size=$(BOARD_$(call to-upper,$(group))_SIZE)" >> $(zip_root)/META/dynamic_partitions_info.txt; \
	    $(if $(_group_partition_list), \
	        echo "$(group)_partition_list=$(_group_partition_list)" >> $(zip_root)/META/dynamic_partitions_info.txt;))
endif # BOARD_SUPER_PARTITION_GROUPS
	@# TODO(b/134525174): Remove `-r` after addressing the issue with recovery patch generation.

	# 调用add_img_to_target_files脚本,将img添加到target files包中
	$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
	    build/make/tools/releasetools/add_img_to_target_files -a -r -v -p $(HOST_OUT) $(zip_root)
	@# Zip everything up, preserving symlinks and placing META/ files first to
	@# help early validation of the .zip file while uploading it.
	$(hide) find $(zip_root)/META | sort >$@.list
	$(hide) find $(zip_root) -path $(zip_root)/META -prune -o -print | sort >>$@.list
	$(hide) $(SOONG_ZIP) -d -o $@ -C $(zip_root) -l $@.list

2.2.2 ota_frome_target_files.py脚本

在编译过程中若生成OTA update升级包时会调用一个名为ota_from_target_files的python脚本,位置在/build/tools/releasetools/ota_from_target_files。
这个脚本的作用是以第一步生成的zip原始包作为输入,最终生成可用的OTA升级zip包。

2.2.3 ota_frome_target_files

Path:android/build/make/tools/releasetools/ota_from_target_files.py

这个脚本开始部分的帮助文档:

Usage: ota_from_target_files [flags] input_target_files output_ota_package
    -b 过时的。
    -k 签名所使用的密钥
    -i 生成增量OTA包时使用此选项。后面我们会用到这个选项来生成OTA增量包。
    -w 是否清除userdata分区
    -n 在升级时是否不检查时间戳,缺省要检查,即缺省情况下只能基于旧版本升级。
    -e 是否有额外运行的脚本
    -m 执行过程中生成脚本(updater-script)所需要的格式,目前有两种即amend和edify。对应上两种版本升级时会采用不同的解释器。缺省会同时生成两种格式的脚 本。
    -p 定义脚本用到的一些可执行文件的路径。
    -s 定义额外运行脚本的路径。
    -x 定义额外运行的脚本可能用的键值对。
    -v 执行过程中打印出执行的命令。
    -h 命令帮助
/build/tools/releasetools/ota_from_target_files.py脚本:

主函数main是python的入口函数,从main函数开始看,大概看一下main函数里的流程:
(1)在main函数的开头,首先将用户设定的option选项存入OPTIONS变量中,它是一个python中的类。紧接着判断有没有额外的脚本,如果有就读入到OPTIONS变量中。
(2)解压缩输入的zip包,即我们在上文生成的原始zip包。然后判断是否用到device-specific extensions(设备扩展)如果用到,随即读入到OPTIONS变量中。
(3)判断是否签名,然后判断是否有新内容的增量源,有的话就解压该增量源包放入一个临时变量中(source_zip)。自此,所有的准备工作已完毕,随即会调用该脚本中最主要的函数

WriteFullOTAPackage(input_zip,output_zip)
def main(argv):
  # 将用户设定的opthin选项存入OPTIONS变量中,它是一个python类
  def option_handler(o, a):
    if o in ("-k", "--package_key"):
      OPTIONS.package_key = a
    elif o in ("-i", "--incremental_from"):
      OPTIONS.incremental_source = a
	  .....
    # target原始包
    elif o == "--extracted_input_target_files":
      OPTIONS.extracted_input = a
	  .....
#直接从 zip 或提取的输入加载构建信息字典目录。 我们不需要解压缩整个目标文件 zip,因为它们 A/B OTA 不需要(brillo_update_payload 自行完成)。
# 加载 info dicts 时,我们不需要提供第二个参数到 common.LoadInfoDict()。 指定第二个参数允许替换一些带有实际路径的属性,例如“selinux_fc”,'ramdisk_dir',在OTA生成期间不会使用。
  if OPTIONS.extracted_input is not None:
    OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
  else:
    with zipfile.ZipFile(args[0], 'r') as input_zip:
      OPTIONS.info_dict = common.LoadInfoDict(input_zip)

  logger.info("--- target info ---")
  common.DumpInfoDict(OPTIONS.info_dict)

  # Load the source build dict if applicable.
  #如果使用,加载源构建的dict
  if OPTIONS.incremental_source is not None:
    OPTIONS.target_info_dict = OPTIONS.info_dict
    with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)

    logger.info("--- source info ---")
    common.DumpInfoDict(OPTIONS.source_info_dict)
	  .....
  ab_update = OPTIONS.info_dict.get("ab_update") == "true"

#如果没有指定 package_key,则使用默认密钥对包进行签名。ab_updates上需要 package_keys,所以如果有ab_update正在创建
  if not OPTIONS.no_signing or ab_update:
    if OPTIONS.package_key is None:
      OPTIONS.package_key = OPTIONS.info_dict.get(
          "default_system_dev_certificate",
          "build/target/product/security/testkey")
    # Get signing keys
    OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
	....
# 解压原始包到一个中间文件夹,并将目录赋值给input_tmp
  if OPTIONS.extracted_input is not None:
    OPTIONS.input_tmp = OPTIONS.extracted_input
  else:
    logger.info("unzipping target target-files...")
    OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
  OPTIONS.target_tmp = OPTIONS.input_tmp
  ....
  # Generate a full OTA.构建OTA全量包
  if OPTIONS.incremental_source is None:
    with zipfile.ZipFile(args[0], 'r') as input_zip:
      WriteFullOTAPackage(
          input_zip,
          output_file=args[1])

  # Generate an incremental OTA.构建OTA增量包
  else:
    logger.info("unzipping source target-files...")
    OPTIONS.source_tmp = common.UnzipTemp(
        OPTIONS.incremental_source, UNZIP_PATTERN)
    with zipfile.ZipFile(args[0], 'r') as input_zip, \
        zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
      WriteBlockIncrementalOTAPackage(
          input_zip,
          source_zip,
          output_file=args[1])

    if OPTIONS.log_diff:
      with open(OPTIONS.log_diff, 'w') as out_file:
        import target_files_diff
        target_files_diff.recursiveDiff(
            '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)

(4) WriteFullOTAPackage函数的处理过程是先获得脚本的生成器。默认格式是edify。然后获得metadata元数据,此数据来至于Android的一些环境变量。然后获得设备配置参数比如api函数的版本。然后判断是否忽略时间戳。

def WriteFullOTAPackage(input_zip, output_file):
  target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)

#我们不知道它将安装在哪个版本之上。 我们期待 API只是不会经常改变。 同样对于 fstab,它可能在目标构建
  target_api_version = target_info["recovery_api_version"]
  script = edify_generator.EdifyGenerator(target_api_version, target_info)

  if target_info.oem_props and not OPTIONS.oem_no_mount:
    target_info.WriteMountOemScript(script)
  # 获取meta数据
  metadata = GetPackageMetadata(target_info)
  ......
# 如果阶段不是“2/3”或“3/3”:将恢复映像写入启动分区,设置舞台为“2/3”,重新启动以引导分区并重新启动恢复;
# 否则如果阶段是“2/3”:将恢复映像写入恢复分区,设置舞台为“3/3”,重启到恢复分区并重启恢复
# 别的:(阶段必须是“3/3”),设置舞台为“”,进行正常的完整包安装:擦除并安装系统、启动映像等。设置系统在第一次启动时更新恢复分区,正常完成脚本(允许恢复标记自己完成并重新启动)
  recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
                                         OPTIONS.input_tmp, "RECOVERY")
  if OPTIONS.two_step:
    if not target_info.get("multistage_support"):
      assert False, "two-step packages not supported by this build"
    fs = target_info["fstab"]["/misc"]
    assert fs.fs_type.upper() == "EMMC", \
        "two-step packages only supported on devices with EMMC /misc partitions"
    bcb_dev = {"bcb_dev": fs.device}
    common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
    script.AppendExtra("""
if get_stage("%(bcb_dev)s") == "2/3" then
""" % bcb_dev)

    # Stage 2/3: Write recovery image to /recovery (currently running /boot).
    script.Comment("Stage 2/3")
    script.WriteRawImage("/recovery", "recovery.img")
    script.AppendExtra("""
set_stage("%(bcb_dev)s", "3/3");
reboot_now("%(bcb_dev)s", "recovery");
else if get_stage("%(bcb_dev)s") == "3/3" then
""" % bcb_dev)

    # Stage 3/3: Make changes.
    script.Comment("Stage 3/3")

  # Dump fingerprints
  script.Print("Target: {}".format(target_info.fingerprint))

  device_specific.FullOTA_InstallBegin()

  system_progress = 0.75

  if OPTIONS.wipe_user_data:
    system_progress -= 0.1
  if HasVendorPartition(input_zip):
    system_progress -= 0.1

  script.ShowProgress(system_progress, 0)

# 调用函数获取meta数据
def GetPackageMetadata(target_info, source_info=None):
# 生成并返回元数据字典。它生成一个 dict() ,其中包含要写入 OTA 的信息包 (META-INF/com/android/metadata)。 它还处理检测基于全局选项降级/数据擦除。
# 参数:
#     target_info:保存目标构建信息的 BuildInfo 实例。
#     source_info:保存源构建信息的 BuildInfo 实例,或
#     如果生成完整的 OTA,则无。
# 返回: 要写入包元数据条目的字典。
  assert isinstance(target_info, BuildInfo)
  assert source_info is None or isinstance(source_info, BuildInfo)

(5)WriteFullOTAPackage函数做完准备工作后就开始生成升级用的脚本文件(updater-script)了。生成脚本文件后将上一步获得的metadata元数据写入到输出包out_zip

(6)至此一个完整的update.zip升级包就生成了。生成位置在:out/target/product/tcc8800/full_tcc8800_evm-ota-eng.mumu.20120315.155326.zip。将升级包拷贝到SD卡中就可以用来升级了。

//misc_info.txt

生成META目录下,可以查看到image的size、type等信息
该size数据是由BOARD_DTBOIMG_PARTITION_SIZE类似该宏定义,用于生成的最终ota包中img的大小(AB分区的大小)
修改于product/BoardConfig.mk文件中

//ab_partitions.txt
ab分区的image文件列表
生成在out/target/product/.../obj/PACKAGING/target_files_intermediates/..._target_files_eng.***/META/路径
make otapackage后,会在build/core/add_img_to_target_files.py脚本中,从该文件中读取列表然后查找image name

2.3 升级包目录

升级包解压后可以查看文件目录:

├── META-INF
│   └── com
│       └── android
│           ├── metadata  //升级包版本信息
│           └── otacert
├── payload.bin           //升级包数据,可用工具解析获取升级的image数据
└── payload_properties.txt  //包含FILE_HASH、FILE_SIZE、METADATA_HASH、METADATA_SIZE四个文件元信息

target包解压后:

target$ tree -L 1
.
├── BOOT
├── IMAGES
├── META
├── OTA
├── PREBUILT_IMAGES
├── RADIO
├── ROOT
├── SYSTEM
└── VENDOR

3、OTA升级

3.1 升级脚本和方法

老版本的升级需要进入Rccovery模式进行升级,A/B分区则使用update_engine相关

android/system/update_engine/scripts$ tree
.
├── blockdiff.py
├── brillo_update_payload
├── paycheck.py
├── payload_info.py
├── payload_info_unittest.py
├── run_unittests
├── test_paycheck.sh
├── update_device.py
└── update_payload

当AB系统升级时,有两种方式来调用updateengine,来实现升级:
(1)直接执行shell命令,调用update_engine_client,带参数来实现升级

//解压升级包
adb shell
update_engine_client --payload=file:///storage/5F49-FB9D/socupdate8g/payload.bin --update --headers="FILE_HASH=YP7Z1bFDv6O8C5LTWZ20JxTljXyoVitlCX27TBTyVDM=
FILE_SIZE=967460335
METADATA_HASH=1gpTz/Q7T1ysTu6suP8N2KVOfa+vKEdnJGnPsKcPiXw=
METADATA_SIZE=75378"

(2)应用层直接调用UpdateEngine的applyPayload方法来升级

调试方式打印日志:adb logcat -s update_engine
通过cat proc/cmdline查看升级前后的AB分区切换,判断升级是否成功

3.2 我的实操过程

1)解压压缩包
(2)将压缩包里的payload.bin push 到/sdcard/下 执行命令:
	 adb root
	 adb push payload.bin /sdcard/
 (3)push成功后,执行如下命令:
     adb root     //获取root权限
     adb shell    //进入板子里面
     cd /sdcard/  //进入sdcard目录
     setenforce 0 //表示设置SELinux成为permissive模式 临时关闭selinux防火墙
     getenforce  //显示SELinux的状态
     //getenforce 后输出应该是Permissive
 (4)打开压缩包里的payload.properties
     update_engine_client --payload=file:///sdcard/payload.bin --update -- headers="
     FILE_HASH=mSqCJaX8d6BnQmpppoTA31HVeqDXvcbnNsD3C/CxEY=
     FILE_SIZE=111191411
     METADATA_HASH=kgGWEIZRY69S2W1a3khL54YvoTGIrrE/pmn6+cnYgF4=
     METADATA_SIZE=572492"
     //file:后面是文件路径,我是把payload.bin文件推到sdcard里面,FILE_HASH后面一直到结束是payload.properties里面内容,每次OTA升级可以直接替换FILE_HASH后面的部分,其余不需要动 
     //我是底层开发调试,所以采用手动的方式,可以开发个APP自动完成
 (5)执行完上述命令之后可以另外开一个命令窗口抓log:
     adb logcat -b all |findstr -rn update_engine
 (6)出现:update sucessfully applied, waiting to reboot说明升级完成
 (7)需要重启看是否切到B分区
     adb reboot
     adb shell   //注意需要进入板子里查看
     getprop |grep "slot"    //如果显示[ro.boot.slot_suffix]: [_b]说明重启到B分区,升级成功
     //如果一开始不用adb root那么将无法执行 setenforce 0 ,此时可以另开一个窗口执行如下命令:
     //adb reboot bootloader
     //fastboot oem disable-selinux
     //fastboot reboot ------------------以上为整包升级流程,差分包类似

4、OTA升级遇到的问题

4.1 重复升级同版本报错

(1)删除/data/misc/update_engine/prefs目录下记录的信息文件

/data/misc/update_engine/prefs # ls -al
total 60
drwx------ 2 root root 4096 1970-01-01 08:00 .
drwx------ 3 root root 4096 1970-01-01 08:00 ..
-rw------- 1 root root   36 1970-01-01 08:00 boot-id
-rw------- 1 root root    1 2021-12-12 08:15 delta-update-failures
-rw------- 1 root root    2 1970-01-01 08:00 manifest-metadata-size
-rw------- 1 root root    2 1970-01-01 08:00 manifest-signature-size
-rw------- 1 root root   24 1970-01-01 08:00 previous-version
-rw------- 1 root root    1 1970-01-01 08:00 resumed-update-failures
-rw------- 1 root root    1 2021-12-12 08:15 total-bytes-downloaded
-rw------- 1 root root   88 2021-12-12 08:06 update-check-response-hash
-rw------- 1 root root   36 2021-12-12 08:15 update-completed-on-boot-id
-rw------- 1 root root    1 1970-01-01 08:00 update-state-next-data-length
-rw------- 1 root root    2 1970-01-01 08:00 update-state-next-data-offset
-rw------- 1 root root    2 1970-01-01 08:00 update-state-next-operation
-rw------- 1 root root    0 1970-01-01 08:00 update-state-sha-256-context
-rw------- 1 root root    0 1970-01-01 08:00 update-state-signature-blob
-rw------- 1 root root    0 1970-01-01 08:00 update-state-signed-sha-256-context
/data/misc/update_engine/prefs # rm -rf *

(2)修改/system/update_engine/update_attempter_android.cc文件,添加如下代码.
这样就会在升级完成后,删除/data/misc/update_engine/prefs/update-check-response-hash文件;再使用任意升级包升级,都会认为是一次全新的升级

diff --git a/update_attempter_android.cc b/update_attempter_android.cc
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -458,7 +458,7 @@ void UpdateAttempterAndroid::ProcessingDone(const ActionProcessor* processor,
       // Update succeeded.
       WriteUpdateCompletedMarker();
       prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
-
+      prefs_->Delete(kPrefsUpdateCheckResponseHash);
       LOG(INFO) << "Update successfully applied, waiting to reboot.";
       break;

4.2 回滚版本升级报错

update engine会校验版本构建的时间戳,修改将时间戳的校验删除即可

diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1686,13 +1686,14 @@ ErrorCode DeltaPerformer::ValidateManifest() {
     }
   }
 
+  /* Delete for updating to old version
   if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
     LOG(ERROR) << "The current OS build timestamp ("
                << hardware_->GetBuildTimestamp()
                << ") is newer than the maximum timestamp in the manifest ("
                << manifest_.max_timestamp() << ")";
     return ErrorCode::kPayloadTimestampError;
-  }
+  }*/

解决方法:
1)全量包:修改/system/update_engine/payload_consumer/delta_performer.cc文件,将时间戳校验的相关代码注释掉
2)差分包:差分包和全量包不同,如果想做新版本差分到旧版本的包,需要在使用ota_from_target_files.py脚本制作升级包时添加参数—override_timestamp,这样就可以跳过时间戳的检测

另外有一个开关可以直接关掉
我的路径:xxx/android/build/make/target/board/BoardConfigGsiCommon.mk
研究以下三个:
BOARD_AVB_ROLLBACK_INDEX :=0
BOARD_AVB_SYSTEM_ROLLBACK_INDEX :=$(PLATFORM_SECURITY_PATH_TIMESTAMP)
BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION :=1

4.3 差分包升级error code=20(kDownloadStateInitializationError)

错误码见:system/update_engine/common/error_code.h
(1)如果是如下log,则当前版本应该是userdebug版本,设备有被进行过remount操作,需要整包升级/线刷恢复

04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337259:ERROR:fec_file_descriptor.cc(30)] No ECC data in the passed file
//system b分区
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337493:ERROR:delta_performer.cc(430)] Unable to open ECC source partition system on slot B, file /dev/block/by-name/system_b: No such file or directory (2)
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337571:ERROR:delta_performer.cc(1135)] The hash of the source data on disk for this operation doesn't match the expected value. This could mean that the delta update payload was targeted for another version, or that the source partition was modified after it was installed, for example, by mounting a filesystem.
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337627:ERROR:delta_performer.cc(1140)] Expected:   sha256|hex = 8D224141934E427E63257E32134DA8B23FE280F8A2643365A6AD55701EAA8575
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337679:ERROR:delta_performer.cc(1143)] Calculated: sha256|hex = 3EEC85740D17A75F584976B29D8D62619E68257E6DC324D2206FDFE29E30DCA4
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337742:ERROR:delta_performer.cc(1154)] Operation source (offset:size) in blocks: 0:2,322:1,326:1,348:1,351:1,359:299,838:2,3044:202,3472:2,3749:1
04-01 18:33:13.337  2631  2631 W update_engine: [0401/183313.337820:WARNING:mount_history.cc(66)] Device was remounted R/W 2 times. Last remount happened on 2022-04-01 10:32:25.000 UTC.
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337900:ERROR:delta_performer.cc(1435)] source_fd != nullptr failed.
04-01 18:33:13.337  2631  2631 E update_engine: [0401/183313.337972:ERROR:delta_performer.cc(296)] Failed to perform BROTLI_BSDIFF operation 172, which is the operation 0 in partition "system"
04-01 18:33:13.338  2631  2631 E update_engine: [0401/183313.338040:ERROR:download_action.cc(336)] Error ErrorCode::kDownloadStateInitializationError (20) in DeltaPerformer's Write method when processing the received payload -- Terminating processing
04-01 18:33:13.338  2631  2631 I update_engine: [0401/183313.338527:INFO:delta_performer.cc(313)] Discarding 4265 unused downloaded bytes
04-01 18:33:13.338  2631  2631 I update_engine: [0401/183313.338670:INFO:multi_range_http_fetcher.cc(177)] Received transfer terminated.
04-01 18:33:13.338  2631  2631 I update_engine: [0401/183313.338739:INFO:multi_range_http_fetcher.cc(129)] TransferEnded w/ code 200
04-01 18:33:13.338  2631  2631 I update_engine: [0401/183313.338791:INFO:multi_range_http_fetcher.cc(131)] Terminating.
...
04-01 18:33:13.381  2631  2631 I update_engine: [0401/183313.381569:INFO:action_processor.cc(116)] ActionProcessor: finished DownloadAction with code ErrorCode::kDownloadStateInitializationError

(2)如果是其他问题,则检查对应分区的image,比如线刷包的image和target包是否一致,比如设备的image是否被改动(使用dd命令)

4.4 差分包升级error code=15(kNewRootfsVerificationError)

该错误码和15一样都是分区hash校验失败,一个是在升级刚开始,一个是在文件系统校验的时候。
这个操作的磁盘上的源数据的散列与预期值不匹配。这可能意味着增量更新有效负载是针对另一个版本的,或者是在安装之后修改了源分区,例如,通过安装文件系统。
原因: make otapackage 会对system.img重新打包 导致重新打包的system.img和out目录下的system.img Hash值不一致. 也就是线刷版本的system.img和OTA包的system.img不一致,整包升级会替换system.img, 而差分包升级则需要保证系统内部的system.img和整包中system.img一致才能升级成功(差分包中保存了通过sha256 Hash算法计算出整包system.img的值,通过这个值来确定两个system.img一致)
调试方法:
(1) dump获取车机的image文件

进入adb shell
在/dev/block/by-name/查看问题分区
使用dd if=/dev/block/by-name/system of=/sdacar/a.img bs=512 count=5命令获取到 (如果dump失败,尝试更改of路径)

(2) 解析image文件

//(1)确认文件属性

//车机dd出来的img
$ file system_old.img 
system_old.img: Linux rev 1.0 ext4 filesystem data, UUID=4729639d-b5f2-5cc1-a120-9ac5f788683c (extents) (large files) (huge files)
simg2img get.img result.img
//系统编译的img(需要转换解析)
G$ file system.img 
system.img: Android sparse image, version: 1.0, Total of 1310720 4096-byte output blocks in 86 input chunks.

//(2)如果是sparse文件属性,则进行转化:(android编译环境)

simg2img get.img result.img

(3)采用挂载分区的方式来解压打开img文件

g$ file system_old.img 
system_old.img: Linux rev 1.0 ext4 filesystem data, UUID=4729639d-b5f2-5cc1-a120-9ac5f788683c (needs journal recovery) (extents) (large files) (huge files)
$ mkdir 1
$ sudo mount system_old.img 1/ -o loop

(4)使用文件对比工具(比如beyond compare)对比两个image的md5值和文件目录

参考链接:https://cloud.tencent.com/developer/article/2126585

总结: 底层驱动注重积累----继续学习文章来源地址https://www.toymoban.com/news/detail-553480.html

到了这里,关于OTA升级学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

    container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: 内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。 为了支持链表,代码中要添加的头文件是linux/list.h。内核中链表实现核心部分的数据结构 是struct li

    2024年01月22日
    浏览(55)
  • Android 9.0 ota升级关于SettingsProvider新增和修改系统数据相关功能实现

      在9.0的系统rom定制化开发中,在进行ota升级的过程中,由于在SettingsProvider中新增了系统属性和修改某项系统属性值,但是在ota升级以后发现没有 更新,需要恢复出厂设置以后才会更改,但是恢复出厂设置 会丢掉一些数据,这是应为系统数据库没更新,所以需要在ota的时候

    2024年01月22日
    浏览(70)
  • Android 10.0 ota升级关于SettingsProvider新增和修改系统数据相关功能实现

      在10.0的系统rom定制化开发中,在进行ota升级的过程中,由于在SettingsProvider中新增了系统属性和修改某项系统属性值,但是在ota升级以后发现没有 更新,需要恢复出厂设置以后才会更改,但是恢复出厂设置 会丢掉一些数据,这是应为系统数据库没更新,所以需要在ota的时候

    2024年02月10日
    浏览(49)
  • 调试笔记-stm32的OTA/IAP 通过485升级固件

    背景:最近需要在stm32上实现通过rs485升级固件功能。经过几天搜索和调试,实现了功能。 目标:使用cubeIDE实现stm32F407VGT6,通过RS485升级固件 调试记录: 步骤1. 在keil环境下的rs485升级固件(含源码):STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2)_stm32串口升级_柒壹漆

    2024年02月11日
    浏览(53)
  • gd32f103vbt6 串口OTA升级3-linux端的部分

    本文主要是对linux端升级单片机程序的功能部分做一些介绍,包括一些软件流程。 2.1 rk3399cpu+gd32f103 2.2 连接方式:串口(115200,8N1)或者iic(本文没有介绍iic) 3.4.1  0 ~(0x5c00-1) : iap程序区,用于存放iap程序 3.4.2  0x5c00~(0x6000-1) : 这个1k用于存放一些标志位,以及程序的

    2024年02月17日
    浏览(71)
  • 【Linux驱动开发】编译Android12源码

    基于讯为电子rk3568教程 rk_android12.0_sdk_20220720.tar.gz 解压 设置屏幕配置 整体编译 Android 固件 使能编译环境 开始整体编译 rockdev/Image-rk3568_s // 编译成功镜像存放位置 单独编译u-boot (方法一) 编译完成后在 u-boot 目录会生成 rk356x_spl_loader_v1.13.112.bin 文件、uboot.img 文件、 resourc

    2024年02月05日
    浏览(48)
  • linux驱动开发笔记一

    Linux操作系统 1,Linux操作系统基础: 1),基础知识 第一块处理器:intel 4004 Intel 8008 ,i8086,…协处理器(主从机制); 80386,80586,奔腾,酷睿,四核; 1971年IBM发布个人电脑5150; deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse deb-src https://mirrors.tuna

    2023年04月13日
    浏览(43)
  • 【学习笔记:GPIO驱动开发】

    快门驱动芯片ms8837 输入信号是两个GPIO,最开始是在camera 驱动中调用 gpio_request() 申请GPIO,当 open/close camera 的时候就可以调用 gpio_direction_output() 来控制快门的开、关。 在dts 中加入引脚配置: 在camera 驱动中调用gpio_request() 去控制GPIO: 这样在open/close camera 的时间是可以正常调

    2024年02月03日
    浏览(43)
  • Linux 驱动学习笔记 ——(1)字符设备驱动

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习笔记 字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。 Linux 中一切皆文件,字符设备驱动加载成功后会在 /dev 目录下生成相

    2024年02月08日
    浏览(56)
  • linux驱动的学习 & 驱动开发初识

    在学习驱动和其开发之前,首先要知道 所谓驱动,其对象就是设备 。 1.1 主设备号次设备号: 在Linux中,各种设备都以文件的形式存在 /dev目录下 ,称为 设备文件 。 最上层的应用程序可以打开,关闭,读写这些设备文件,从而完成对设备的操作 。 为了管理这些设备,系统

    2024年02月04日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包