詳細なOTAアップグレード(3)

夫を知っている紳士は、美しくなるほど純粋ではない、 

だからそれを実行するための数を暗唱し、

よく考えて

その人々のためにそれを扱い、

それを維持するために被害者を排除します。

                                               Xunziの「奨励学習」から

 

以下は、Android 10リカバリーのソースコード分析です

コードのソースは次のとおりです:https : //www.androidos.net.cn/android/10.0.0_r6/xref

前の2つの章では、主にOTAの基本、OTAアップグレードの詳細な説明(2)、  OTAアップグレードの詳細な説明(1)について説明します。このセクションでは、主にzipアップグレードパッケージを使用してリカバリをアップグレードする方法について説明します。

まず、次のように、ファイルレベルからアップグレード関数の呼び出しプロセスについて説明します。

recovery-main.cppはアップグレードのメインの入り口です。

recovery.cppは、回復アップグレードを開始するプロセスです

install / install.cppは、アップグレード(call updater)を実行するための処理フローです

updater / updater.cppは、アップグレードを完了するためのコアプロセスです。

Androidリカバリのアップグレードコードパスは次のとおりです:起動可能/リカバリ/

主なエントリコードは、recovery-main.cppです。

1.ログ関連作業の準備

  // We don't have logcat yet under recovery; so we'll print error on screen and log to stdout
  // (which is redirected to recovery.log) as we used to do.
  android::base::InitLogging(argv, &UiLogger);

  // Take last pmsg contents and rewrite it to the current pmsg session.
  static constexpr const char filter[] = "recovery/";
  // Do we need to rotate?
  bool do_rotate = false;

  __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &do_rotate);
  // Take action to refresh pmsg contents
  __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);

  time_t start = time(nullptr);

  // redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger
  // instances with different timestamps.
  redirect_stdio(Paths::Get().temporary_log_file().c_str());

2. load_volume_table();システムパーティション情報をロードし、これに注意してマウントパーティションを理解する

.mount_point = "/ tmp"、.fs_type = "ramdisk"、.blk_device = "ramdisk"、.length = 0 

mount_point-マウントポイント

fs_type-パーティションタイプ

blk_device-デバイスブロック名

長さ-パーティションサイズ

3. / cacheパーティションをマウントします。アップグレードコマンドはこのパーティションの下に配置されます

  has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;

4.アップグレードされたパラメータを取得し、BCBブロック情報を書き込みます。実際、ここでは2つのアクションが完了しています

std::vector<std::string> args = get_args(argc, argv);

if (!update_bootloader_message(options, &err)) {
    LOG(ERROR) << "Failed to set BCB message: " << err;
  }

a。その他のパーティションパーティションを読み取り、リカバリモードアップグレードマークをその他のパーティションに書き込みます。これは、電源がオフになった後もアップグレードを続行するためです。アップグレード中に電源が失われた後、次回システムを再起動すると、ブートローダーがこれを読み取りますこの機能を完了するには、リカバリモードでupdate_bootloader_message関数をマークして再入力します。

b。/ cache / recovery / commandからアップグレードパラメータを読み取ります。リカバリ起動プロセスにパラメータが含まれていない場合、コマンドファイルインターフェイスには実際には非常に詳細な説明があります

 * The arguments which may be supplied in the recovery.command file:
 *   --update_package=path - verify install an OTA package file
 *   --wipe_data - erase user data (and cache), then reboot
 *   --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user
 *       data (and cache), then reboot
 *   --wipe_cache - wipe cache (but not user data), then reboot
 *   --show_text - show the recovery text menu, used by some bootloader (e.g. http://b/36872519).
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
 *   --just_exit - do nothing; exit and reboot

5. recovery_ui_ext.soをロードして、アップグレード、アップグレードの進行状況、アップグレードの結果などの情報を画面に表示します。ここで言うことはあまりありません。

  static constexpr const char* kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so";
  // Intentionally not calling dlclose(3) to avoid potential gotchas (e.g. `make_device` may have
  // handed out pointers to code or static [or thread-local] data and doesn't collect them all back
  // in on dlclose).
  void* librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);

  using MakeDeviceType = decltype(&make_device);
  MakeDeviceType make_device_func = nullptr;
  if (librecovery_ui_ext == nullptr) {
    printf("Failed to dlopen %s: %s\n", kDefaultLibRecoveryUIExt, dlerror());
  } else {
    reinterpret_cast<void*&>(make_device_func) = dlsym(librecovery_ui_ext, "make_device");
    if (make_device_func == nullptr) {
      printf("Failed to dlsym make_device: %s\n", dlerror());
    }
  }

6.非高速ブートモードのアップグレード後、リカバリモードのアップグレードが開始されます。start_recovery

auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);

recovery.cppを入力します 

1.パラメータ分析。これらのパラメータは実際には/ cache / recovery / commandから取得され、上記のget_argを介してargsに読み込まれています。

2.インターフェイスにさまざまなUI情報が表示され、電源チェックは補助的なアクションを待ちます。

3.関数はInstall Upgrade Packageという名前ですが、アップグレードパッケージのインストールは実際にはまだ開始されていません

status = install_package(update_package, should_wipe_cache, true, retry_count, ui);

4.インストールが終了すると、finish_recovery()は仕上げ作業を完了し、ログを保存し、BCBのマークをクリアして、デバイスを再起動します。

static void finish_recovery() {
  std::string locale = ui->GetLocale();
  // Save the locale to cache, so if recovery is next started up without a '--locale' argument
  // (e.g., directly from the bootloader) it will use the last-known locale.
  if (!locale.empty() && has_cache) {
    LOG(INFO) << "Saving locale \"" << locale << "\"";
    if (ensure_path_mounted(LOCALE_FILE) != 0) {
      LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
    } else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
      PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
    }
  }

  copy_logs(save_current_log, has_cache, sehandle);

  // Reset to normal system boot so recovery won't cycle indefinitely.
  std::string err;
  if (!clear_bootloader_message(&err)) {
    LOG(ERROR) << "Failed to clear BCB message: " << err;
  }

  // Remove the command file, so recovery won't repeat indefinitely.
  if (has_cache) {
    if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
      LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
    }
    ensure_path_unmounted(CACHE_ROOT);
  }

  sync();  // For good measure.
}

install / install.cpp

1. install.cppが実際にアップグレードパッケージをインストールするための準備アクションに入ります。install_packageはすぐ上にありますが、ここは実際にinstall_packageです

result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
                                    retry_count, &max_temperature, ui);

2、

static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
                                  std::vector<std::string>* log_buffer, int retry_count,
                                  int* max_temperature, RecoveryUI* ui) {
  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
  ui->Print("Finding update package...\n");
  // Give verification half the progress bar...
  ui->SetProgressType(RecoveryUI::DETERMINATE);
  ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
  LOG(INFO) << "Update location: " << path;

  // Map the update package into memory.
  ui->Print("Opening update package...\n");

  if (needs_mount) {
    if (path[0] == '@') {
      ensure_path_mounted(path.substr(1));
    } else {
      ensure_path_mounted(path);
    }
  }

  /* 将zip映射到内存中 */
  auto package = Package::CreateMemoryPackage(
      path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
  if (!package) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
    return INSTALL_CORRUPT;
  }

  // Verify package.进行zip包进行签名校验
  if (!verify_package(package.get(), ui)) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
    return INSTALL_CORRUPT;
  }

  // Try to open the package.打开zip包
  ZipArchiveHandle zip = package->GetZipArchiveHandle();
  if (!zip) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
    return INSTALL_CORRUPT;
  }

  // Additionally verify the compatibility of the package if it's a fresh install.
  if (retry_count == 0 && !verify_package_compatibility(zip)) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
    return INSTALL_CORRUPT;
  }

  // Verify and install the contents of the package.
  ui->Print("Installing update...\n");
  if (retry_count > 0) {
    ui->Print("Retry attempt: %d\n", retry_count);
  }
  ui->SetEnableReboot(false);
  int result =
      /* 执行升级updater进程进行升级 */
      try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
  ui->SetEnableReboot(true);
  ui->Print("\n");

  return result;
}

updater / updater.cpp

1.アップグレードパッケージからメタデータ情報を読み取る

ReadMetadataFromPackage(zip, &metadata)

2.アップグレードパッケージからアップデータプロセスを読み取ります

int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
                             int status_fd, std::vector<std::string>* cmd) {
  CHECK(cmd != nullptr);

  // In non-A/B updates we extract the update binary from the package.
  static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
  ZipString binary_name(UPDATE_BINARY_NAME);
  ZipEntry binary_entry;
  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
    LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
    return INSTALL_CORRUPT;
  }

  const std::string binary_path = Paths::Get().temporary_update_binary();
  unlink(binary_path.c_str());
  android::base::unique_fd fd(
      open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
  if (fd == -1) {
    PLOG(ERROR) << "Failed to create " << binary_path;
    return INSTALL_ERROR;
  }

  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
  if (error != 0) {
    LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
    return INSTALL_ERROR;
  }

  // When executing the update binary contained in the package, the arguments passed are:
  //   - the version number for this interface
  //   - an FD to which the program can write in order to update the progress bar.
  //   - the name of the package zip file.
  //   - an optional argument "retry" if this update is a retry of a failed update attempt.
  *cmd = {
    binary_path,
    std::to_string(kRecoveryApiVersion),
    std::to_string(status_fd),
    package,
  };
  if (retry_count > 0) {
    cmd->push_back("retry");
  }
  return 0;
}

4.子プロセスが読み取り側を閉じ、親プロセスが書き込み側を閉じるパイプラインを作成しますこれは、子プロセスから親プロセスへの一方向の情報通信を確実にするためです。

android::base::Pipe(&pipe_read, &pipe_write, 0)

5.子プロセスを作成し、子プロセスでupdate-binaryプロセスを実行します

  if (pid == 0) {
    umask(022);
    pipe_read.reset();

    // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
    auto chr_args = StringVectorToNullTerminatedArray(args);
    /* chr_args[0] 其实就是升级包中的 META-INF/com/google/android/update-binary */
    execv(chr_args[0], chr_args.data());
    // We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
    // hang. This deadlock results from an improperly copied mutex in the ui functions.
    // (Bug: 34769056)
    fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
    _exit(EXIT_FAILURE);
  }

6.リカバリは、子プロセス、進行状況、ui_printなどの情報を取得して表示します。

FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
while (fgets(buffer, sizeof(buffer), from_child) != nullptr) 

execvがアップグレードプロセスを実行した後、updater / updater.cppで作業が完了します。

1.ここでの主なコアは、アップデータスクリプト内のコマンドを実行するスクリプトパーサーを構築することです。スクリプトパーサーの構築と実行の方法については、あまり明確ではありません。

2.アップグレードパッケージをインストールするためのコアプログラムは、Configure edifyの関数に登録されているコールバック関数です。

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

  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.
  /* 从包中提取脚本 */
  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;
  }

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

  // Configure edify's functions.
  /* 注册updater-script中的回调函数 这里主要是一些断言函数 abort assert*/
  RegisterBuiltins();

  /* 这里主要是一些安装升级包的函数 主要是对有文件系统的分区来说*/
  RegisterInstallFunctions();

  /* 这里主要注册对裸分区进行升级的函数 */
  RegisterBlockImageFunctions();
  
  RegisterDynamicPartitionsFunctions();
  RegisterDeviceExtensions();

  // Parse the script.
  std::unique_ptr<Expr> root;
  int error_count = 0;
  int error = ParseString(script, &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);

  if (argc == 5) {
    if (strcmp(argv[4], "retry") == 0) {
      state.is_retry = true;
    } else {
      printf("unexpected argument: %s", argv[4]);
    }
  }

  std::string result;
  bool status = Evaluate(&state, root, &result);

  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");
      } else if (state.cause_code == kEioFailure) {
        LOG(INFO) << "Update failed due to EIO, 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;
}

上記は、Android OTAに基づくリカバリモードのアップグレードプロセスです。アップグレードプロセス全体の傾向を整理するためにここにいます。多くの場所がまだ十分に詳細ではありません。読者が理解できることを願っています。ここでのコアとキーポイントは次のとおりです。

1.メインシステムとリカバリアップグレードシステム、およびアップグレードメッセージの転送

2.メインシステムのforkサブプロセスがアップグレードプロセスを実行し、パイプを介して情報を交換します

3.コマンドの使用とアップデータでの実行を分離します。コマンドはアップデータスクリプトにあり、実行はアップデートバイナリにあります。

4.アップグレードプログラムはアップグレードパッケージを通じて提供されるため、コアアップグレードプロセスには毎回変更または最適化する機会があり、システムでアップグレードプロセスを事前設定するものよりもはるかに柔軟です。

WeChatパブリックアカウント[ 埋め込まれたC部族 ]をフォローし、より重要な記事、膨大なプログラミング資料を入手して、一緒に成長して成長させましょう。

おすすめ

転載: blog.csdn.net/weixin_35933684/article/details/103061686