六、Android recovery升级断电保护机制

本文详细解析了设备在升级过程中遇到断电后如何恢复升级的机制,包括命令写入流程、升级脚本执行流程、以及如何通过保存的状态信息继续未完成的升级过程。重点介绍了recovery模式下的流程控制、stash数据的使用和校验机制。

本篇将具体分析执行写入的流程,,整理完该流程后,将通过升级的log具体分析断电后重新升级的情况

 

一.如何断电后重启继续进入升级

几句话总结

1.调用framwork接口时会将command写入misc分区

2.升级过程中加入retry标志位,如果第二次升级,会将retry++

3.misc分区的标志位只有在finlsh_recovery方法中,即使升级过程中掉电,重新上电后,misc分区依然是boot-recovey,保证继续跑recovery的流程

二.update-script恢复执行流程

几句话总结

1.调用install_package方法中retry=1,判定为恢复升级

2.校验阶段,会解析transfer.list,move.bisdiff部分如果校验发现已经是升级后的block,则判定校验通过,对于stash部分,优先从上次保存的stash数据中读取进行校验

3.写入阶段,依然会解析transfer.list,首次升级过程中保存了last_command_index,会从中断处继续执行写入,已经写入成功的部分会被跳过

update-scrypt脚本是由updater解析和执行的,所以我们要对照失败包分析updater的代码,首先回忆下上一篇updae.cpp的main方法

1.前情回顾

int main(int argc, char** argv) {
  // Various things log information to stdout or stderr more or less
  // at random (though we've tried to standardize on stdout).  The
  // log file makes more sense if buffering is turned off so things
  // appear in the right order.
  setbuf(stdout, nullptr);
  setbuf(stderr, nullptr);

  // We don't have logcat yet under recovery. Update logs will always be written to stdout
  // (which is redirected to recovery.log).
  android::base::InitLogging(argv, &UpdaterLogger);
  //我们执行update-binary,首先还是看下时什么参数传进了main方法,以上次的经验,还是放在initlog之后
  fprintf(stderr, "\n============updater.cpp main============\n");
  int i;
  for (i = 0; i < argc; i++){
    fprintf(stderr, "argc && argv we need know how much the argc argv\n");
    fprintf(stderr, "argv %d is %s\n", i, argv[i]);
  }
  printf("\n========================================\n");
  if (argc != 4 && argc != 5) {
    LOG(ERROR) << "unexpected number of arguments: " << argc;
    return 1;
  }

  char* version = argv[1];
  if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
    // We support version 1, 2, or 3.
    LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
    return 2;
  }

  // Set up the pipe for sending commands back to the parent process.
  int fd = atoi(argv[2]);
  FILE* cmd_pipe = fdopen(fd, "wb");
  setlinebuf(cmd_pipe);

  // Extract the script from the package.
  // 从升级包中提取出updater-scrypt
  const char* package_filename = argv[3];
  //加载内存空间
  MemMapping map;
  if (!map.MapFile(package_filename)) {
    LOG(ERROR) << "failed to map package " << argv[3];
    return 3;
  }
  //获取压缩包
  ZipArchiveHandle za;
  int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
  if (open_err != 0) {
    LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
    CloseArchive(za);
    return 3;
  }
  //获取updater-scrypt脚本,读取到内存中
  ZipString script_name(SCRIPT_NAME);
  ZipEntry script_entry;
  int find_err = FindEntry(za, script_name, &script_entry);
  if (find_err != 0) {
    LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
               << ErrorCodeString(find_err);
    CloseArchive(za);
    return 4;
  }

  std::string script;
  script.resize(script_entry.uncompressed_length);
  int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
                                    script_entry.uncompressed_length);
  if (extract_err != 0) {
    LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
    CloseArchive(za);
    return 5;
  }
#if 1
    //打印出脚本的内容
    fprintf(stderr, "====== Updater-Script:\n");
    fprintf(stderr, "%s\n\n", script.c_str());
#endif
  // Configure edify's functions.
  //注册脚本中语句处理函数,识别脚本中的命令
  RegisterBuiltins();
  RegisterInstallFunctions();
  RegisterBlockImageFunctions();
  RegisterDeviceExtensions();

  // Parse the script.
  // 智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象, unique_ptr 独占所指向的对象
  // 解析update-scrypt脚本
  std::unique_ptr<Expr> root;
  int error_count = 0;
  int error = parse_string(script.c_str(), &root, &error_count);
  if (error != 0 || error_count > 0) {
    LOG(ERROR) << error_count << " parse errors";
    CloseArchive(za);
    return 6;
  }

  sehandle = selinux_android_file_context_handle();
  selinux_android_set_sehandle(sehandle);

  if (!sehandle) {
    fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
  }

  // Evaluate the parsed script. 执行解析后的脚本

  UpdaterInfo updater_info;
  updater_info.cmd_pipe = cmd_pipe;
  updater_info.package_zip = za;
  updater_info.version = atoi(version);
  updater_info.package_zip_addr = map.addr;
  updater_info.package_zip_len = map.length;

  State state(script, &updater_info);
  //加入retry标准位,判断是否为二次升级
  if (argc == 5) {
    if (strcmp(argv[4], "retry") == 0) {
      state.is_retry = true;
    } else {
      printf("unexpected argument: %s", argv[4]);
    }
  }
  ota_io_init(za, state.is_retry);

  std::string result;
  //执行脚本,并获取的返回值
  bool status = Evaluate(&state, root, &result);

  if (have_eio_error) {
    fprintf(cmd_pipe, "retry_update\n");
  }
  //根据status判断是否升级成功,如果失败,打印出对应的log
  if (!status) {
    if (state.errmsg.empty()) {
      LOG(ERROR) << "script aborted (no error message)";
      fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
    } else {
      LOG(ERROR) << "script aborted: " << state.errmsg;
      const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
      for (const std::string& line : lines) {
        // Parse the error code in abort message.
        // Example: "E30: This package is for bullhead devices."
        if (!line.empty() && line[0] == 'E') {
          if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
            LOG(ERROR) << "Failed to parse error code: [" << line << "]";
          }
        }
        fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
      }
    }

    // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
    // a more specific code has been set in errmsg.
    if (state.error_code == kNoError) {
      state.error_code = kScriptExecutionFailure;
    }
    fprintf(cmd_pipe, "log error: %d\n", state.error_code);
    // Cause code should provide additional information about the abort.
    if (state.cause_code != kNoCause) {
      fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
      if (state.cause_code == kPatchApplicationFailure) {
        LOG(INFO) << "Patch application failed, retry update.";
        fprintf(cmd_pipe, "retry_update\n");
      }
    }

    if (updater_info.package_zip) {
      CloseArchive(updater_info.package_zip);
    }
    return 7;
  } else {
    fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
  }

  if (updater_info.package_zip) {
    CloseArchive(updater_info.package_zip);
  }

  return 0;
}

RegisterBlockImageFunctions();

updater.cpp
void RegisterBlockImageFunctions() {
  RegisterFunction("block_image_verify", BlockImageVerifyFn);
  RegisterFunction("block_image_update", BlockImageUpdateFn);
  RegisterFunction("block_image_recover", BlockImageRecoverFn);
  RegisterFunction("check_first_block", CheckFirstBlockFn);
  RegisterFunction("range_sha1", RangeSha1Fn);
}

 

开始进入正题,对scrypt脚本中比较重要的部分进行分析,可以分为两个模块,执行校验和执行写入,这里只分析system的情况,vendor和product的情况类似

1.range_sha1

2.block_image_verify

3.check_first_block

4.block_image_recover

5.block_image_update

if (range_sha1("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,8069,8332,8333,10077,10079,11773,11775,11807,11810,11811,11813,11822,11823,11832,11834,11836,11838,11841,11844,11846,11848,11860,11863,12348,12350,12716,12718,13134,13136,13164,13165,13325,13326,13483,13484,13695,13696,13939,13940,14035,14036,14143,14144,15075,15076,15248,15249,15593,15594,17039,17040,17065,17066,17837,17838,18608,18609,18873,18874,19122,19123,19124,19125,19719,19720,19935,19936,20076,20077,21520,21521,21547,21548,21549,21550,25122,25123,25139,25140,25253,25254,25470,25471,25616,25617,25811,25812,25854,25855,25931,25932,27882,27884,27928,27929,27944,27945,27946,27948,29246,29247,29351,29352,29560,29564,30079,30082,30597,30602,30873,30876,30879,30881,30892,30893,31299,31303,31942,31943,31953,31954,31971,31975,31994,31995,32013,32017,32056,32059,32069,32071,32134,32139,32185,32186,32205,32209,32227,32228,32235,32237,32246,32248,32251,32252,32508,32510,32768,32770,32927,32928,33555,33557,33566,33567,33573,33577,33582,33583,33593,33594,33602,33604,33610,33612,33615,33616,33629,33633,33644,33649,33937,33938,33945,33947,33953,33955,33958,33959,34472,34476,34592,34594,34600,34601,34741,34745,34931,34932,35021,35023,35170,35172,35177,35180,35473,35477,36049,36050,36052,36057,50293,50294,53560,53561,53570,53574,53583,53584,53593,53597,53602,53607,62299,62300,65536,65537,75076,75081,75095,75096,75173,75177,75345,75346,75405,75407,75479,75481,75486,75489,75516,75518,75546,98306,98463,98464,98970,131073,131579,163842,163999,164000,164506,196609,197115,229378,229535,229536,230042,262145,262651,294914,295071,295072,295578,327681,328187,360449,360955,393217,393723,425985,426491,447444,622592,622593,645127,648706,649730,650210,650217,655360") == "8a083377999ff3720a5a650d10ab597bf92f826c" || block_image_verify("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat")) then
ui_print("Verified system image...");
else
check_first_block("/dev/block/platform/bootdevice/by-name/system");
ifelse (block_image_recover("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,8069,8332,8333,10077,10079,11773,11775,11807,11810,11811,11813,11822,11823,11832,11834,11836,11838,11841,11844,11846,11848,11860,11863,12348,12350,12716,12718,13134,13136,13164,13165,13325,13326,13483,13484,13695,13696,13939,13940,14035,14036,14143,14144,15075,15076,15248,15249,15593,15594,17039,17040,17065,17066,17837,17838,18608,18609,18873,18874,19122,19123,19124,19125,19719,19720,19935,19936,20076,20077,21520,21521,21547,21548,21549,21550,25122,25123,25139,25140,25253,25254,25470,25471,25616,25617,25811,25812,25854,25855,25931,25932,27882,27884,27928,27929,27944,27945,27946,27948,29246,29247,29351,29352,29560,29564,30079,30082,30597,30602,30873,30876,30879,30881,30892,30893,31299,31303,31942,31943,31953,31954,31971,31975,31994,31995,32013,32017,32056,32059,32069,32071,32134,32139,32185,32186,32205,32209,32227,32228,32235,32237,32246,32248,32251,32252,32508,32510,32768,32770,32927,32928,33555,33557,33566,33567,33573,33577,33582,33583,33593,33594,33602,33604,33610,33612,33615,33616,33629,33633,33644,33649,33937,33938,33945,33947,33953,33955,33958,33959,34472,34476,34592,34594,34600,34601,34741,34745,34931,34932,35021,35023,35170,35172,35177,35180,35473,35477,36049,36050,36052,36057,50293,50294,53560,53561,53570,53574,53583,53584,53593,53597,53602,53607,62299,62300,65536,65537,75076,75081,75095,75096,75173,75177,75345,75346,75405,75407,75479,75481,75486,75489,75516,75518,75546,98306,98463,98464,98970,131073,131579,163842,163999,164000,164506,196609,197115,229378,229535,229536,230042,262145,262651,294914,295071,295072,295578,327681,328187,360449,360955,393217,393723,425985,426491,447444,622592,622593,645127,648706,649730,650210,650217,655360") && block_image_verify("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat"), ui_print("system recovered successfully."), abort("E1004: system partition fails to recover"));
endif;
if (range_sha1("/dev/block/platform/bootdevice/by-name/vendor", "134,1,125,472,483,1720,1721,3963,3965,4552,4553,6502,6504,6519,6520,6535,6536,6582,6584,6941,6944,7223,7224,7768,7769,8067,8068,8403,8404,9872,9873,14221,14222,15475,15476,16519,16520,16756,16757,18377,18378,19010,19011,21856,21857,29115,29116,30085,30086,30463,30465,30473,30475,30502,30503,30508,30509,30538,30539,30574,30575,30673,30675,30683,30684,30703,30704,30743,30744,30905,30906,30907,30909,30923,30924,30929,30930,30940,30941,31494,31495,31527,31528,31979,31980,32768,32770,32803,32804,34904,34905,35433,35434,41785,41786,45487,45488,46020,46021,48394,48395,48636,48637,51841,51842,56276,56277,57302,57303,65536,65537,66830,66831,68681,68682,70651,70653,70658,70659,70664,70665,70670,70671,70709,70712,70716,70718,98304,98306,131072,131073,139096,140194,140201,141312") == "40c9fefe4c4c61b55dc1a6faea1c12971f1f05fb" || block_image_verify("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat")) then
ui_print("Verified vendor image...");
else
check_first_block("/dev/block/platform/bootdevice/by-name/vendor");
ifelse (block_image_recover("/dev/block/platform/bootdevice/by-name/vendor", "134,1,125,472,483,1720,1721,3963,3965,4552,4553,6502,6504,6519,6520,6535,6536,6582,6584,6941,6944,7223,7224,7768,7769,8067,8068,8403,8404,9872,9873,14221,14222,15475,15476,16519,16520,16756,16757,18377,18378,19010,19011,21856,21857,29115,29116,30085,30086,30463,30465,30473,30475,30502,30503,30508,30509,30538,30539,30574,30575,30673,30675,30683,30684,30703,30704,30743,30744,30905,30906,30907,30909,30923,30924,30929,30930,30940,30941,31494,31495,31527,31528,31979,31980,32768,32770,32803,32804,34904,34905,35433,35434,41785,41786,45487,45488,46020,46021,48394,48395,48636,48637,51841,51842,56276,56277,57302,57303,65536,65537,66830,66831,68681,68682,70651,70653,70658,70659,70664,70665,70670,70671,70709,70712,70716,70718,98304,98306,131072,131073,139096,140194,140201,141312") && block_image_verify("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat"), ui_print("vendor recovered successfully."), abort("E2004: vendor partition fails to recover"));
endif;

# ---- start making changes here ----

ui_print("Patching system image after verification.");
show_progress(0.800000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
ui_print("Patching vendor image after verification.");
show_progress(0.100000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat") ||
  abort("E2001: Failed to update vendor image.");

2.range_sha1  RangeSha1Fn方法

range_sha1("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,80.......") == "8a083377999ff3720a5a650d10ab597bf92f826c"

这里面的参数值怎么来的呢?

2.1 参数分析

 

ota_from_target_files

def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
......
......
  # Verify the existing partitions.
  system_diff.WriteVerifyScript(script, touched_blocks_only=True)
  if vendor_diff:
    vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)

 

  def WriteVerifyScript(self, script, touched_blocks_only=False):
    partition = self.partition

    # full OTA
    if not self.src:
      script.Print("Image %s will be patched unconditionally." % (partition,))

    # incremental OTA
    else:
      #差分情况下touched_blocks_only=True
      if touched_blocks_only:
        #这两个参数都源于blockimgdiff.py,因为比较简单,不再做具体说明
        #self.touched_src_ranges = self.touched_src_ranges.union(xf.src_ranges)
        #ranges是src_ranges的并集
        ranges = self.touched_src_ranges
        #self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges)
        #expected
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值