一、OTA定义
OTA(Over-the-Air Technology)空中下载技术,OTA升级是Android系统提供的标准软件升级方式。它功能强大,可以无损失升级系统,主要通过网络自动下载OTA升级包、自动升级,但是也支持通过下载OTA升级包到SD卡升级。
二、OTA 升级过程:
- 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方法。
我们也可以看到擦除数据、缓存的实现也是在这个里执行的,这里就不展开了。
- 在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分区分别执行不同更新流程。
- finish_recovery,重启
873 // Save logs and clean up before rebooting or shutting down.
874 FinishRecovery(ui);
上一步完成之后,回到main函数,
保存升级过程中的log,清除临时文件,包括command文件(不清除的话,下次重启还会进入recovery模式),最后重启。以上就是升级的一个流程。