Android OTA升级流程

926 阅读5分钟

 一、OTA定义

    OTA(Over-the-Air Technology)空中下载技术,OTA升级是Android系统提供的标准软件升级方式。它功能强大,可以无损失升级系统,主要通过网络自动下载OTA升级包、自动升级,但是也支持通过下载OTA升级包到SD卡升级。

二、OTA 升级过程:

  1. ​Android系统收到服务端推送的OTA包,将OTA包下载至cache分区,获取包路径,验证签名,并向cache分区写入指令(表明下次启动时进入recovery模式并使用该OTA包进行升级),重启设备,步骤如下:

a.调用RecoverySystem类提供的verifyPackage方法进行签名验证:

209    public static void verifyPackage(File packageFile,
210                                     ProgressListener listener,
211                                     File deviceCertsZipFile)
212        throws IOException, GeneralSecurityException {

packageFile--升级文件

listener--进度监督器

deviceCertsZipFile--签名文件,如果为空,则使用系统默认的签名

在Recovery模式下进行升级时候也是会进行签名验证的,如果这里先不进行验证也不会有什么问题,但是我们建议在重启前先验证,以便及早发现问题。

 

b.如果签名验证没有问题,就执行RecoverySystem类的installPackage开始升级。创建目录/cache/recovery/,command文件保存在该目录下;如果存在command文件,将其删除;然后将上面一步生成的两个参数写入到command文件,最后重启设备。

614      @SystemApi
615      @RequiresPermission(android.Manifest.permission.RECOVERY)
616      public static void installPackage(Context context, File packageFile, boolean processed)
617              throws IOException {
618          synchronized (sRequestLock) {
             ...................................
656                  // If the package is on the /data partition, use the block map
657                  // file as the package name instead.
658                  filename = "@/cache/recovery/block.map";
659              }
660  
661              final String filenameArg = "--update_package=" + filename + "\n";
662              final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n";
663              final String securityArg = "--security\n";
664  
665              String command = filenameArg + localeArg;
666              if (securityUpdate) {
667                  command += securityArg;
668              }
669  
670              RecoverySystem rs = (RecoverySystem) context.getSystemService(
671                      Context.RECOVERY_SERVICE);
672              if (!rs.setupBcb(command)) {
673                  throw new IOException("Setup BCB failed");
674              }
                 ..............................
697              pm.reboot(reason);

2. 系统重启时会判断/cache/recovery目录下是否有command文件,如果存在就进入recovery模式,否则就正常启动。

进入到Recovery模式下,将执行Recovery.cpp的main函数,下面贴出关键代码片段

638    while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", OPTIONS,
639                              &option_index)) != -1) {
640      switch (arg) {
641        case 't':
642          // Handled in recovery_main.cpp
643          break;
644        case 'x':
645          just_exit = true;
646          break;
647        case 0: {
648          std::string option = OPTIONS[option_index].name;
649          if (option == "install_with_fuse") {
650            install_with_fuse = true;
651          } else if (option == "locale" || option == "fastboot" || option == "reason") {
652            // Handled in recovery_main.cpp
653          } else if (option == "prompt_and_wipe_data") {
654            should_prompt_and_wipe_data = true;
655          } else if (option == "rescue") {
656            rescue = true;
657          } else if (option == "retry_count") {
658            android::base::ParseInt(optarg, &retry_count, 0);
659          } else if (option == "security") {
660            security_update = true;
661          } else if (option == "sideload") {
662            sideload = true;
663          } else if (option == "sideload_auto_reboot") {
664            sideload = true;
665            sideload_auto_reboot = true;
666          } else if (option == "shutdown_after") {
667            shutdown_after = true;
668          } else if (option == "update_package") {
669            update_package = optarg;
670          } else if (option == "wipe_ab") {
671            should_wipe_ab = true;
672          } else if (option == "wipe_cache") {
673            should_wipe_cache = true;
674          } else if (option == "wipe_data") {
675            should_wipe_data = true;
676          } else if (option == "wipe_package_size") {
677            android::base::ParseUint(optarg, &wipe_package_size);
678          }
679          break;
680        }
681        case '?':
682          LOG(ERROR) << "Invalid command argument";
683          continue;
684      }
685    }

这是一个While循环,用来读取recovery的command参数,OPTIONS的不同选项定义如下

593    static constexpr struct option OPTIONS[] = {
594      { "fastboot", no_argument, nullptr, 0 },
595      { "install_with_fuse", no_argument, nullptr, 0 },
596      { "just_exit", no_argument, nullptr, 'x' },
597      { "locale", required_argument, nullptr, 0 },
598      { "prompt_and_wipe_data", no_argument, nullptr, 0 },
599      { "reason", required_argument, nullptr, 0 },
600      { "rescue", no_argument, nullptr, 0 },
601      { "retry_count", required_argument, nullptr, 0 },
602      { "security", no_argument, nullptr, 0 },
603      { "show_text", no_argument, nullptr, 't' },
604      { "shutdown_after", no_argument, nullptr, 0 },
605      { "sideload", no_argument, nullptr, 0 },
606      { "sideload_auto_reboot", no_argument, nullptr, 0 },
607      { "update_package", required_argument, nullptr, 0 },
608      { "wipe_ab", no_argument, nullptr, 0 },
609      { "wipe_cache", no_argument, nullptr, 0 },
610      { "wipe_data", no_argument, nullptr, 0 },
611      { "wipe_package_size", required_argument, nullptr, 0 },
612      { nullptr, 0, nullptr, 0 },
613    };

显然,根据第二步写入的命令文件内容,将为update_package 赋值。

725    if (update_package != nullptr) {
726      // It's not entirely true that we will modify the flash. But we want
727      // to log the update attempt since update_package is non-NULL.
728      save_current_log = true;
729  
730      if (int required_battery_level; retry_count == 0 && !IsBatteryOk(&required_battery_level)) {
731        ui->Print("battery capacity is not enough for installing package: %d%% needed\n",
732                  required_battery_level);
733        // Log the error code to last_install when installation skips due to low battery.
734        log_failure_code(kLowBattery, update_package);
735        status = INSTALL_SKIPPED;
736      } else if (retry_count == 0 && bootreason_in_blocklist()) {
737        // Skip update-on-reboot when bootreason is kernel_panic or similar
738        ui->Print("bootreason is in the blocklist; skip OTA installation\n");
739        log_failure_code(kBootreasonInBlocklist, update_package);
740        status = INSTALL_SKIPPED;
741      } else {
742        // It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later
743        // identify the interrupted update due to unexpected reboots.
744        if (retry_count == 0) {
745          set_retry_bootloader_message(retry_count + 1, args);
746        }
747  
748        bool should_use_fuse = false;
749        if (!SetupPackageMount(update_package, &should_use_fuse)) {
750          LOG(INFO) << "Failed to set up the package access, skipping installation";
751          status = INSTALL_ERROR;
752        } else if (install_with_fuse || should_use_fuse) {
753          LOG(INFO) << "Installing package " << update_package << " with fuse";
754          status = InstallWithFuseFromPath(update_package, device);
755        } else if (auto memory_package = Package::CreateMemoryPackage(
756                       update_package,
757                       std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
758                   memory_package != nullptr) {
759          status = InstallPackage(memory_package.get(), update_package, should_wipe_cache,
760                                  retry_count, device);
761        } else {
762          // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size.
763          // In such cases, we will try to install the package with fuse. This is not the default
764          // installation method because it introduces a layer of indirection from the kernel space.
765          LOG(WARNING) << "Failed to memory map package " << update_package
766                       << "; falling back to install with fuse";
767          status = InstallWithFuseFromPath(update_package, device);
768        }

update_package不为空,执行install_package方法。

我们也可以看到擦除数据、缓存的实现也是在这个里执行的,这里就不展开了。

 

  1. 在install.cpp进行升级操作

具体的升级过程都是在install.cpp中执行的,先看install_package方法,

588  InstallResult InstallPackage(Package* package, const std::string_view package_id,
589                               bool should_wipe_cache, int retry_count, Device* device) {
590    auto ui = device->GetUI();
591    auto start = std::chrono::system_clock::now();
592  
593    int start_temperature = GetMaxValueFromThermalZone();
594    int max_temperature = start_temperature;
595  
596    InstallResult result;
597    std::vector<std::string> log_buffer;
598  
599    ui->Print("Supported API: %d\n", kRecoveryApiVersion);
600  
601    ui->Print("Finding update package...\n");
602    LOG(INFO) << "Update package id: " << package_id;
603    if (!package) {
604      log_buffer.push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
605      result = INSTALL_CORRUPT;
606    } else if (setup_install_mounts() != 0) {
607      LOG(ERROR) << "failed to set up expected mounts for install; aborting";
608      result = INSTALL_ERROR;
609    } else {
610      bool updater_wipe_cache = false;
611      result = VerifyAndInstallPackage(package, &updater_wipe_cache, &log_buffer, retry_count,
612                                       &max_temperature, device);
613      should_wipe_cache = should_wipe_cache || updater_wipe_cache;
614    }
615  
616    // Measure the time spent to apply OTA update in seconds.
617    std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
618    int time_total = static_cast<int>(duration.count());
619  
620    bool has_cache = volume_for_mount_point("/cache") != nullptr;
621    // Skip logging the uncrypt_status on devices without /cache.
622    if (has_cache) {
623      static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
624      if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
625        LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
626      } else {
627        std::string uncrypt_status;
628        if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
629          PLOG(WARNING) << "failed to read uncrypt status";
630        } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
631          LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
632        } else {
633          log_buffer.push_back(android::base::Trim(uncrypt_status));
634        }
635      }
636    }
637  
638    // The first two lines need to be the package name and install result.
639    std::vector<std::string> log_header = {
640      std::string(package_id),
641      result == INSTALL_SUCCESS ? "1" : "0",
642      "time_total: " + std::to_string(time_total),
643      "retry: " + std::to_string(retry_count),
644    };
645  
646    int end_temperature = GetMaxValueFromThermalZone();
647    max_temperature = std::max(end_temperature, max_temperature);
648    if (start_temperature > 0) {
649      log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
650    }
651    if (end_temperature > 0) {
652      log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
653    }
654    if (max_temperature > 0) {
655      log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
656    }
657  
658    std::string log_content =
659        android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n";
660    const std::string& install_file = Paths::Get().temporary_install_file();
661    if (!android::base::WriteStringToFile(log_content, install_file)) {
662      PLOG(ERROR) << "failed to write " << install_file;
663    }
664  
665    // Write a copy into last_log.
666    LOG(INFO) << log_content;
667  
668    if (result == INSTALL_SUCCESS && should_wipe_cache) {
669      if (!WipeCache(ui, nullptr)) {
670        result = INSTALL_ERROR;
671      }
672    }
673  
674    return result;
675  }

这个方法中首先创建了log文件,升级过程包括出错的信息都会写到这个文件中,便于后续的分析工作。继续跟进, VerifyAndInstallPackage,

559  static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache,
560                                               std::vector<std::string>* log_buffer, int retry_count,
561                                               int* max_temperature, Device* device) {
562    auto ui = device->GetUI();
563    ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
564    // Give verification half the progress bar...
565    ui->SetProgressType(RecoveryUI::DETERMINATE);
566    ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
567  
568    // Verify package.
569    if (!verify_package(package, ui)) {
570      log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
571      return INSTALL_CORRUPT;
572    }
573  
574    // Verify and install the contents of the package.
575    ui->Print("Installing update...\n");
576    if (retry_count > 0) {
577      ui->Print("Retry attempt: %d\n", retry_count);
578    }
579    ui->SetEnableReboot(false);
580    auto result =
581        TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, device);
582    ui->SetEnableReboot(true);
583    ui->Print("\n");
584  
585    return result;
586  }

 

4、执行升级脚本文件,开始升级

580    auto result =
581        TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, device);

 

TryUpdateBinary方法用来处理升级包,进行系统更新。

// If the package contains an update binary, extract it and run it.
356  static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache,
357                                       std::vector<std::string>* log_buffer, int retry_count,
358                                       int* max_temperature, Device* device) {
        ................................
440    if (auto setup_result =
441            package_is_ab
442                ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
443                : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
444        !setup_result) {
445      log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
446      return INSTALL_CORRUPT;
447    }

 

针对AB分区分别执行不同更新流程。

 

  1. finish_recovery,重启
873    // Save logs and clean up before rebooting or shutting down.
874    FinishRecovery(ui);

上一步完成之后,回到main函数,

保存升级过程中的log,清除临时文件,包括command文件(不清除的话,下次重启还会进入recovery模式),最后重启。以上就是升级的一个流程。

  OTA.png

参考文献: blog.csdn.net/Eqiqi/artic…

www.likecs.com/show-203480…